Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
7a1f964201 | |||
3dffbb6cf1 | |||
79f1798b68 | |||
dd08d421a1 | |||
a2d4299f2c | |||
2598b59de7 | |||
20b4617289 | |||
958bb0da04 | |||
4ba8f1989b | |||
c04b4d795a | |||
f1b5ba9231 | |||
50b524ba1e | |||
680ceb7d65 | |||
ea186d5ccd | |||
10455044f1 | |||
31150fe6e8 | |||
9223066123 |
231
CHANGELOG.md
231
CHANGELOG.md
@ -1,239 +1,10 @@
|
|||||||
<a name="2.2.4"></a>
|
|
||||||
## [2.2.4](https://github.com/angular/angular/compare/2.2.3...2.2.4) (2016-11-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **common:** update DatePipe to allow closure compilation ([eba53fd](https://github.com/angular/angular/commit/eba53fd))
|
|
||||||
* **compiler:** fix performance regression caused by 5b0f9e2 ([ee2d6e5](https://github.com/angular/angular/commit/ee2d6e5)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
|
||||||
* **compiler-cli:** fix paths in source maps to be relative ([eb173bc](https://github.com/angular/angular/commit/eb173bc)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.3"></a>
|
|
||||||
## [2.2.3](https://github.com/angular/angular/compare/2.2.2...2.2.3) (2016-11-23)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** Revert: fix versions of `@angular/tsc-wrapped` ([015ca47](https://github.com/angular/angular/commit/015ca47))
|
|
||||||
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.2"></a>
|
|
||||||
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
|
||||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
|
|
||||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
|
|
||||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
|
|
||||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
|
|
||||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
|
|
||||||
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
|
|
||||||
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
|
|
||||||
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
|
||||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.3.0-beta.0"></a>
|
|
||||||
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** assert xliff messages have translations ([7908679](https://github.com/angular/angular/commit/7908679)), closes [#12815](https://github.com/angular/angular/issues/12815) [#12604](https://github.com/angular/angular/issues/12604)
|
|
||||||
* **compiler:** updates hash algo for xmb/xtb files ([2f14415](https://github.com/angular/angular/commit/2f14415))
|
|
||||||
* **core:** fix placeholders handling in i18n. ([76e4911](https://github.com/angular/angular/commit/76e4911)), closes [#12512](https://github.com/angular/angular/issues/12512)
|
|
||||||
* **core:** misc i18n fixes ([ed5e98d](https://github.com/angular/angular/commit/ed5e98d))
|
|
||||||
* **core:** xmb serializer uses decimal messaged IDs ([08c038e](https://github.com/angular/angular/commit/08c038e)), closes [#12511](https://github.com/angular/angular/issues/12511)
|
|
||||||
* **platform-browser:** enable AOT ([efbbefd](https://github.com/angular/angular/commit/efbbefd)), closes [#12783](https://github.com/angular/angular/issues/12783)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **core:** add `attachView` / `detachView` to ApplicationRef ([9f7d32a](https://github.com/angular/angular/commit/9f7d32a)), closes [#9293](https://github.com/angular/angular/issues/9293)
|
|
||||||
* **core:** expose `ViewRef` as `ChangeDetectorRef` ([1b5384e](https://github.com/angular/angular/commit/1b5384e)), closes [#12722](https://github.com/angular/angular/issues/12722)
|
|
||||||
* **core:** implements a decimal fingerprint for i18n ([582550a](https://github.com/angular/angular/commit/582550a))
|
|
||||||
* **router:** register router with ngprobe ([c2fae72](https://github.com/angular/angular/commit/c2fae72))
|
|
||||||
* **router_link:** add skipLocationChange and replaceUrl inputs ([#12850](https://github.com/angular/angular/issues/12850)) ([46d1502](https://github.com/angular/angular/commit/46d1502))
|
|
||||||
|
|
||||||
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
|
|
||||||
|
|
||||||
<a name="2.2.1"></a>
|
|
||||||
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** only pass in same typed players as previous players into web-animations ([#12907](https://github.com/angular/angular/issues/12907)) ([583d283](https://github.com/angular/angular/commit/583d283))
|
|
||||||
* **animations:** retain styling when transition destinations are changed ([#12208](https://github.com/angular/angular/issues/12208)) ([5c46c49](https://github.com/angular/angular/commit/5c46c49)), closes [#9661](https://github.com/angular/angular/issues/9661)
|
|
||||||
* **core:** support `ngTemplateOutlet` in production mode ([#12921](https://github.com/angular/angular/issues/12921)) ([4628798](https://github.com/angular/angular/commit/4628798)), closes [#12911](https://github.com/angular/angular/issues/12911)
|
|
||||||
* **http:** correctly handle response body for 204 status code ([21a4de9](https://github.com/angular/angular/commit/21a4de9)), closes [#12830](https://github.com/angular/angular/issues/12830) [#12393](https://github.com/angular/angular/issues/12393)
|
|
||||||
* **http:** return request url if it cannot be retrieved from response ([845ea23](https://github.com/angular/angular/commit/845ea23)), closes [#12837](https://github.com/angular/angular/issues/12837)
|
|
||||||
* **upgrade:** make AoT ngUpgrade work with the testability API and resumeBootstrap() ([#12910](https://github.com/angular/angular/issues/12910)) ([dc1662a](https://github.com/angular/angular/commit/dc1662a))
|
|
||||||
* **platform-browser:** fix disableDebugTools() ([#12918](https://github.com/angular/angular/issues/12918)) ([7b67bad](https://github.com/angular/angular/commit/7b67bad))
|
|
||||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([364642d](https://github.com/angular/angular/commit/364642d))
|
|
||||||
* **router:** removes a peer dependency from router to upgrade ([1dcf1f4](https://github.com/angular/angular/commit/1dcf1f4))
|
|
||||||
* **forms** allow for null values in HTML select options bound with ngValue ([e0ce545](https://github.com/angular/angular/commit/e0ce545)), closes [#10349](https://github.com/angular/angular/issues/10349)
|
|
||||||
* **router:** should not create a route state if navigation is canceled ([#12868](https://github.com/angular/angular/issues/12868)) ([dabaf85](https://github.com/angular/angular/commit/dabaf85)), closes [#12776](https://github.com/angular/angular/issues/12776)
|
|
||||||
* **common:** select should allow for null values in HTML select options bound with ngValue ([e02c180](https://github.com/angular/angular/commit/e02c180)), closes [#12829](https://github.com/angular/angular/issues/12829)
|
|
||||||
* **compiler-cli:** support ctorParams in function closure ([#12876](https://github.com/angular/angular/issues/12876)) ([6cdc3b5](https://github.com/angular/angular/commit/6cdc3b5))
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.0"></a>
|
|
||||||
# [2.2.0 upgrade-firebooster](https://github.com/angular/angular/compare/2.2.0-rc.0...2.2.0) (2016-11-14)
|
|
||||||
|
|
||||||
### Features (summary of all features from 2.2.0-beta.0 - 2.2.0-rc.0 releases)
|
|
||||||
|
|
||||||
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
|
|
||||||
* **core:** map 'for' attribute to 'htmlFor' property ([#10546](https://github.com/angular/angular/issues/10546)) ([634b3bb](https://github.com/angular/angular/commit/634b3bb)), closes [#7516](https://github.com/angular/angular/issues/7516)
|
|
||||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
|
||||||
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
|
|
||||||
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
|
|
||||||
* **forms:** add emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
|
|
||||||
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
|
|
||||||
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
|
|
||||||
* **router:** add a provider making angular1/angular2 integration easier ([#12769](https://github.com/angular/angular/issues/12769)) ([6e35d13](https://github.com/angular/angular/commit/6e35d13))
|
|
||||||
* **router:** add support for custom url matchers ([7340735](https://github.com/angular/angular/commit/7340735)), closes [#12442](https://github.com/angular/angular/issues/12442) [#12772](https://github.com/angular/angular/issues/12772)
|
|
||||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
|
||||||
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
|
|
||||||
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
|
|
||||||
* **upgrade:** add support for `require` in UpgradeComponent ([fe1d0e2](https://github.com/angular/angular/commit/fe1d0e2))
|
|
||||||
* **upgrade:** add/improve support for lifecycle hooks in UpgradeComponent ([469010e](https://github.com/angular/angular/commit/469010e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **compiler:** introduce direct rendering ([9c23884](https://github.com/angular/angular/commit/9c23884))
|
|
||||||
* **core:** don’t use `DomAdapter` nor zone for regular events ([648ce59](https://github.com/angular/angular/commit/648ce59))
|
|
||||||
* **core:** use `array.push` / `array.pop` instead of `splice` if possible ([0fc11a4](https://github.com/angular/angular/commit/0fc11a4))
|
|
||||||
* **platform-browser:** cache plugin resolution in the EventManager ([73593d4](https://github.com/angular/angular/commit/73593d4)), closes [#12824](https://github.com/angular/angular/issues/12824)
|
|
||||||
* **platform-browser:** don’t use `DomAdapter` any more ([d708a88](https://github.com/angular/angular/commit/d708a88))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** allow animations to be destroyed manually ([#12719](https://github.com/angular/angular/issues/12719)) ([fe35bc3](https://github.com/angular/angular/commit/fe35bc3)), closes [#12456](https://github.com/angular/angular/issues/12456)
|
|
||||||
* **animations:** always normalize style properties and values during compilation ([#12755](https://github.com/angular/angular/issues/12755)) ([a0e9fde](https://github.com/angular/angular/commit/a0e9fde)), closes [#11582](https://github.com/angular/angular/issues/11582) [#12481](https://github.com/angular/angular/issues/12481)
|
|
||||||
* **animations:** always trigger animations after the change detection check ([#12713](https://github.com/angular/angular/issues/12713)) ([383f23b](https://github.com/angular/angular/commit/383f23b))
|
|
||||||
* **animations:** ensure animations work with web-workers ([#12656](https://github.com/angular/angular/issues/12656)) ([19e869e](https://github.com/angular/angular/commit/19e869e))
|
|
||||||
* **animations:** ensure web-animations are caught within the Angular zone ([f80a157](https://github.com/angular/angular/commit/f80a157)), closes [#11881](https://github.com/angular/angular/issues/11881) [#11712](https://github.com/angular/angular/issues/11712) [#12355](https://github.com/angular/angular/issues/12355) [#11881](https://github.com/angular/angular/issues/11881) [#12546](https://github.com/angular/angular/issues/12546) [#12707](https://github.com/angular/angular/issues/12707) [#12774](https://github.com/angular/angular/issues/12774)
|
|
||||||
* **common:** `NgSwitch` - don’t create the default case if another case matches ([#12726](https://github.com/angular/angular/issues/12726)) ([d8f23f4](https://github.com/angular/angular/commit/d8f23f4)), closes [#11297](https://github.com/angular/angular/issues/11297) [#9420](https://github.com/angular/angular/issues/9420)
|
|
||||||
* **common:** I18nSelectPipe selects other case on default ([4708b24](https://github.com/angular/angular/commit/4708b24))
|
|
||||||
* **common:** no TZ Offset added by DatePipe for dates without time ([#12380](https://github.com/angular/angular/issues/12380)) ([2aba8b0](https://github.com/angular/angular/commit/2aba8b0))
|
|
||||||
* **common:** NgClass should throw a descriptive error when CSS class is not a string ([#12662](https://github.com/angular/angular/issues/12662)) ([f3793b5](https://github.com/angular/angular/commit/f3793b5)), closes [#12586](https://github.com/angular/angular/issues/12586)
|
|
||||||
* **common:** DatePipe should handle empty string ([#12374](https://github.com/angular/angular/issues/12374)) ([3dc6177](https://github.com/angular/angular/commit/3dc6177))
|
|
||||||
* **compiler:** don't convert undefined to null literals ([#11503](https://github.com/angular/angular/issues/11503)) ([f0cdb42](https://github.com/angular/angular/commit/f0cdb42)), closes [#11493](https://github.com/angular/angular/issues/11493)
|
|
||||||
* **compiler:** generate safe access strictNullChecks compatible code ([#12800](https://github.com/angular/angular/issues/12800)) ([a965d11](https://github.com/angular/angular/commit/a965d11)), closes [#12795](https://github.com/angular/angular/issues/12795)
|
|
||||||
* **compiler:** support more than 9 interpolations ([#12710](https://github.com/angular/angular/issues/12710)) ([22c021c](https://github.com/angular/angular/commit/22c021c)), closes [#10253](https://github.com/angular/angular/issues/10253)
|
|
||||||
* **compiler:** use the other case by default in ICU messages ([55dc0e4](https://github.com/angular/angular/commit/55dc0e4))
|
|
||||||
* **compiler-cli:** suppress closure compiler suspiciousCode check in codegen ([#12666](https://github.com/angular/angular/issues/12666)) ([7103754](https://github.com/angular/angular/commit/7103754))
|
|
||||||
* **compiler-cli:** suppress two more closure compiler checks in codegen ([#12698](https://github.com/angular/angular/issues/12698)) ([77cbf7f](https://github.com/angular/angular/commit/77cbf7f))
|
|
||||||
* **core:** allow to query content of templates that are stamped out at a different place ([f2bbef3](https://github.com/angular/angular/commit/f2bbef3)), closes [#12283](https://github.com/angular/angular/issues/12283) [#12094](https://github.com/angular/angular/issues/12094)
|
|
||||||
* **core:** apply host attributes to root elements ([#12761](https://github.com/angular/angular/issues/12761)) ([ad3bf6c](https://github.com/angular/angular/commit/ad3bf6c)), closes [#12744](https://github.com/angular/angular/issues/12744)
|
|
||||||
* **core:** ensure that component views that have no bindings recurse into nested components / view containers. ([051d748](https://github.com/angular/angular/commit/051d748))
|
|
||||||
* **core:** fix pseudo-selector shimming ([#12754](https://github.com/angular/angular/issues/12754)) ([acbf1d8](https://github.com/angular/angular/commit/acbf1d8)), closes [#12730](https://github.com/angular/angular/issues/12730) [#12354](https://github.com/angular/angular/issues/12354)
|
|
||||||
* **forms:** check if registerOnValidatorChange exists on validator before trying to invoke it ([#12801](https://github.com/angular/angular/issues/12801)) ([ef88147](https://github.com/angular/angular/commit/ef88147)), closes [#12593](https://github.com/angular/angular/issues/12593)
|
|
||||||
* **forms:** getRawValue returns any instead of Object ([#12599](https://github.com/angular/angular/issues/12599)) ([09092ac](https://github.com/angular/angular/commit/09092ac))
|
|
||||||
* **http:** preserve header case when copying headers ([#12697](https://github.com/angular/angular/issues/12697)) ([121e508](https://github.com/angular/angular/commit/121e508))
|
|
||||||
* **router:** advance a route only after its children have been deactivated ([#12676](https://github.com/angular/angular/issues/12676)) ([9ddf9b3](https://github.com/angular/angular/commit/9ddf9b3)), closes [#11715](https://github.com/angular/angular/issues/11715)
|
|
||||||
* **router:** avoid router initialization for non root components ([2a4bf9a](https://github.com/angular/angular/commit/2a4bf9a)), closes [#12338](https://github.com/angular/angular/issues/12338) [#12814](https://github.com/angular/angular/issues/12814)
|
|
||||||
* **router:** check if windows.console exists before using it ([#12348](https://github.com/angular/angular/issues/12348)) ([7886561](https://github.com/angular/angular/commit/7886561))
|
|
||||||
* **router:** correctly export concatMap operator in es5 ([#12430](https://github.com/angular/angular/issues/12430)) ([e25baa0](https://github.com/angular/angular/commit/e25baa0))
|
|
||||||
* **router:** do not require the creation of empty-path routes when no url left ([2c11093](https://github.com/angular/angular/commit/2c11093)), closes [#12133](https://github.com/angular/angular/issues/12133)
|
|
||||||
* **router:** ignore null or undefined query parameters ([#12333](https://github.com/angular/angular/issues/12333)) ([3052fb2](https://github.com/angular/angular/commit/3052fb2))
|
|
||||||
* **router:** incorrect injector is used when instantiating components loaded lazily ([#12817](https://github.com/angular/angular/issues/12817)) ([52be848](https://github.com/angular/angular/commit/52be848))
|
|
||||||
* **router:** resolve guard observables on the first emit ([#10412](https://github.com/angular/angular/issues/10412)) ([2e78b76](https://github.com/angular/angular/commit/2e78b76))
|
|
||||||
* **router:** Route.isActive also compares query params ([#12321](https://github.com/angular/angular/issues/12321)) ([785b7b6](https://github.com/angular/angular/commit/785b7b6))
|
|
||||||
* **router:** router should not swallow "unhandled" errors ([e5a753e](https://github.com/angular/angular/commit/e5a753e)), closes [#12802](https://github.com/angular/angular/issues/12802)
|
|
||||||
* **router:** throw an error when encounter undefined route ([#12389](https://github.com/angular/angular/issues/12389)) ([77dc1ab](https://github.com/angular/angular/commit/77dc1ab))
|
|
||||||
* **platform-browser:** enableDebugTools should create AngularTools by merging into context.ng ([#12003](https://github.com/angular/angular/issues/12003)) ([b2cf379](https://github.com/angular/angular/commit/b2cf379)), closes [#12002](https://github.com/angular/angular/issues/12002)
|
|
||||||
* **platform-browser:** provide the ability to register global hammer.js events ([768cddb](https://github.com/angular/angular/commit/768cddb)), closes [#12797](https://github.com/angular/angular/issues/12797)
|
|
||||||
* **tsc-wrapped:** harden collector against invalid asts ([#12793](https://github.com/angular/angular/issues/12793)) ([69f87ca](https://github.com/angular/angular/commit/69f87ca))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.0-rc.0"></a>
|
|
||||||
# [2.2.0-rc.0](https://github.com/angular/angular/compare/2.2.0-beta.1...2.2.0-rc.0) (2016-11-02)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** dedupe NgModule declarations, … ([a178bc6](https://github.com/angular/angular/commit/a178bc6))
|
|
||||||
* **compiler:** don’t double bind functions ([e391cac](https://github.com/angular/angular/commit/e391cac))
|
|
||||||
* **compiler:** Don’t throw on empty property bindings ([642c1db](https://github.com/angular/angular/commit/642c1db)), closes [#12583](https://github.com/angular/angular/issues/12583)
|
|
||||||
* **compiler:** support multiple components in a view container ([6fda972](https://github.com/angular/angular/commit/6fda972))
|
|
||||||
* **core:** improve error when multiple components match the same element ([e9fd864](https://github.com/angular/angular/commit/e9fd864)), closes [#7067](https://github.com/angular/angular/issues/7067)
|
|
||||||
* **router:** call data observers when the path changes ([1de04b2](https://github.com/angular/angular/commit/1de04b2))
|
|
||||||
* **router:** CanDeactivate receives a wrong component ([830a780](https://github.com/angular/angular/commit/830a780)), closes [#12592](https://github.com/angular/angular/issues/12592)
|
|
||||||
* **router:** rerun resolvers when url changes ([fe47e6b](https://github.com/angular/angular/commit/fe47e6b)), closes [#12603](https://github.com/angular/angular/issues/12603)
|
|
||||||
* **router:** reset URL to the stable state when a navigation gets canceled ([d509ee0](https://github.com/angular/angular/commit/d509ee0)), closes [#10321](https://github.com/angular/angular/issues/10321)
|
|
||||||
* **router:** routerLink should not prevent default on non-link elements ([8e221b8](https://github.com/angular/angular/commit/8e221b8))
|
|
||||||
* **router:** run navigations serially ([091c390](https://github.com/angular/angular/commit/091c390)), closes [#11754](https://github.com/angular/angular/issues/11754)
|
|
||||||
* **upgrade:** silent bootstrap failures ([fa93fd6](https://github.com/angular/angular/commit/fa93fd6)), closes [#12062](https://github.com/angular/angular/issues/12062)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.1.2"></a>
|
|
||||||
# [2.1.2](https://github.com/angular/angular/compare/2.1.1...2.1.2) (2016-10-27)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** don't access view local variables nor pipes in host expressions ([#12396](https://github.com/angular/angular/issues/12396)) ([867494a](https://github.com/angular/angular/commit/867494a)), closes [#12004](https://github.com/angular/angular/issues/12004) [#12071](https://github.com/angular/angular/issues/12071)
|
|
||||||
* **compiler:** walk third party modules ([#12453](https://github.com/angular/angular/issues/12453)) ([a838aba](https://github.com/angular/angular/commit/a838aba)), closes [#11889](https://github.com/angular/angular/issues/11889) [#12428](https://github.com/angular/angular/issues/12428)
|
|
||||||
* **compiler:** remove double exports of template_ast ([7742ec0](https://github.com/angular/angular/commit/7742ec0))
|
|
||||||
* **compiler:** use Maps instead of objects in selector implementation ([d321b0e](https://github.com/angular/angular/commit/d321b0e))
|
|
||||||
* **compiler-cli:** fix types ([ef15364](https://github.com/angular/angular/commit/ef15364))
|
|
||||||
* **compiler-cli:** assert that all pipes and directives are declared by a module ([7221632](https://github.com/angular/angular/commit/7221632))
|
|
||||||
* **http:** overwrite already set xsrf header ([b4265e0](https://github.com/angular/angular/commit/b4265e0))
|
|
||||||
* **router:** add a test to make sure canDeactivate guards are called for aux routes ([fc60fa7](https://github.com/angular/angular/commit/fc60fa7)), closes [#11345](https://github.com/angular/angular/issues/11345)
|
|
||||||
* **router:** canDeactivate guards are not triggered for componentless routes ([b741853](https://github.com/angular/angular/commit/b741853)), closes [#12375](https://github.com/angular/angular/issues/12375)
|
|
||||||
* **router:** change router not to deactivate aux routes when navigating from a componentless routes ([52a853e](https://github.com/angular/angular/commit/52a853e))
|
|
||||||
* **router:** disallow component routes with named outlets ([8f2fa0f](https://github.com/angular/angular/commit/8f2fa0f)), closes [#11208](https://github.com/angular/angular/issues/11208) [#11082](https://github.com/angular/angular/issues/11082)
|
|
||||||
* **router:** preserve resolve data ([6ccbfd4](https://github.com/angular/angular/commit/6ccbfd4)), closes [#12306](https://github.com/angular/angular/issues/12306)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.2.0-beta.1"></a>
|
|
||||||
# [2.2.0-beta.1](https://github.com/angular/angular/compare/2.2.0-beta.0...2.2.0-beta.1) (2016-10-27)
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES (only for beta version users)
|
|
||||||
|
|
||||||
* upgrade: Four newly added APIs in 2.2.0-beta:
|
|
||||||
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
|
||||||
Import these from @angular/upgrade/static instead.
|
|
||||||
|
|
||||||
|
|
||||||
Note: The 2.2.0-beta.1 release also contains all the changes present in the 2.1.2 release.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [2.1.1](https://github.com/angular/angular/compare/2.1.0...2.1.1) (2016-10-20)
|
# [2.1.1](https://github.com/angular/angular/compare/2.1.0...2.1.1) (2016-10-20)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **compiler:** generate aot code for animation trigger output events ([#12291](https://github.com/angular/angular/issues/12291)) ([6e5f8b5](https://github.com/angular/angular/commit/6e5f8b5)), closes [#11707](https://github.com/angular/angular/issues/11707)
|
* **compiler:** generate aot code for animation trigger output events ([#12291](https://github.com/angular/angular/issues/12291)) ([6e5f8b5](https://github.com/angular/angular/commit/6e5f8b5)), closes [#11707](https://github.com/angular/angular/issues/11707)
|
||||||
* **compiler:** don't redeclare a var in the same scope ([#12386](https://github.com/angular/angular/issues/12386)) ([cca4a5c](https://github.com/angular/angular/commit/cca4a5c))
|
* **compiler:** don't redeclare a var in the same scope ([#12386](https://github.com/angular/angular/issues/12386)) ([cca4a5c](https://github.com/angular/angular/commit/cca4a5c))
|
||||||
* **core:** fix decorator default values ([bd1dcb5](https://github.com/angular/angular/commit/bd1dcb5))
|
* **core:** fix decorator defalut values ([bd1dcb5](https://github.com/angular/angular/commit/bd1dcb5))
|
||||||
* **core:** fix property decorators ([3993279](https://github.com/angular/angular/commit/3993279)), closes [#12224](https://github.com/angular/angular/issues/12224)
|
* **core:** fix property decorators ([3993279](https://github.com/angular/angular/commit/3993279)), closes [#12224](https://github.com/angular/angular/issues/12224)
|
||||||
* **http:** make normalizeMethodName optimizer-compatible. ([#12370](https://github.com/angular/angular/issues/12370)) ([8409b65](https://github.com/angular/angular/commit/8409b65))
|
* **http:** make normalizeMethodName optimizer-compatible. ([#12370](https://github.com/angular/angular/issues/12370)) ([8409b65](https://github.com/angular/angular/commit/8409b65))
|
||||||
* **router:** correctly export filter operator in es5 ([#12286](https://github.com/angular/angular/issues/12286)) ([27d7677](https://github.com/angular/angular/commit/27d7677))
|
* **router:** correctly export filter operator in es5 ([#12286](https://github.com/angular/angular/issues/12286)) ([27d7677](https://github.com/angular/angular/commit/27d7677))
|
||||||
|
@ -17,7 +17,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
|||||||
|
|
||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||||
|
|
||||||
StackOverflow is a much better place to ask questions since:
|
StackOverflow is a much better place to ask questions since:
|
||||||
|
|
||||||
|
77
build.sh
77
build.sh
@ -14,16 +14,12 @@ PACKAGES=(core
|
|||||||
platform-webworker
|
platform-webworker
|
||||||
platform-webworker-dynamic
|
platform-webworker-dynamic
|
||||||
http
|
http
|
||||||
upgrade
|
|
||||||
router
|
router
|
||||||
|
upgrade
|
||||||
compiler-cli
|
compiler-cli
|
||||||
benchpress)
|
benchpress)
|
||||||
BUILD_ALL=true
|
BUILD_ALL=true
|
||||||
BUNDLE=true
|
BUNDLE=true
|
||||||
VERSION_PREFIX=$(node -p "require('./package.json').version")
|
|
||||||
VERSION_SUFFIX="-$(git log --oneline -1 | awk '{print $1}')"
|
|
||||||
ROUTER_VERSION_PREFIX=$(node -p "require('./package.json').version.replace(/^2/, '3')")
|
|
||||||
REMOVE_BENCHPRESS=false
|
|
||||||
|
|
||||||
for ARG in "$@"; do
|
for ARG in "$@"; do
|
||||||
case "$ARG" in
|
case "$ARG" in
|
||||||
@ -35,10 +31,6 @@ for ARG in "$@"; do
|
|||||||
--bundle=*)
|
--bundle=*)
|
||||||
BUNDLE=( "${ARG#--bundle=}" )
|
BUNDLE=( "${ARG#--bundle=}" )
|
||||||
;;
|
;;
|
||||||
--publish)
|
|
||||||
VERSION_SUFFIX=""
|
|
||||||
REMOVE_BENCHPRESS=true
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "Unknown option $ARG."
|
echo "Unknown option $ARG."
|
||||||
exit 1
|
exit 1
|
||||||
@ -46,10 +38,6 @@ for ARG in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
|
|
||||||
ROUTER_VERSION="${ROUTER_VERSION_PREFIX}${VERSION_SUFFIX}"
|
|
||||||
echo "====== BUILDING: Version ${VERSION} (Router ${ROUTER_VERSION})"
|
|
||||||
|
|
||||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||||
@ -75,11 +63,10 @@ if [[ ${BUILD_ALL} == true ]]; then
|
|||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
ln -s ../../../../node_modules/base64-js .
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
ln -s ../../../../node_modules/rxjs .
|
ln -s ../../../../node_modules/rxjs .
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
ln -s ../../../../node_modules/hammerjs/hammer.js .
|
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
echo "====== Copying files needed for benchmarks ====="
|
echo "====== Copying files needed for benchmarks ====="
|
||||||
@ -91,6 +78,7 @@ if [[ ${BUILD_ALL} == true ]]; then
|
|||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
ln -s ../../../../node_modules/rxjs .
|
ln -s ../../../../node_modules/rxjs .
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
@ -113,29 +101,14 @@ do
|
|||||||
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
|
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
|
||||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||||
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
||||||
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
|
||||||
UMD_UPGRADE_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.js
|
|
||||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||||
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.min.js
|
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||||
UMD_UPGRADE_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.min.js
|
|
||||||
|
|
||||||
if [[ ${PACKAGE} != router ]]; then
|
|
||||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
|
||||||
fi
|
|
||||||
if [[ ${PACKAGE} == router ]]; then
|
|
||||||
LICENSE_BANNER=${PWD}/modules/@angular/router-license-banner.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf ${DESTDIR}
|
rm -rf ${DESTDIR}
|
||||||
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||||
|
|
||||||
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
|
|
||||||
$TSC -p ${SRCDIR}/tsconfig-upgrade.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||||
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||||
|
|
||||||
@ -184,49 +157,9 @@ do
|
|||||||
cat ${UMD_TESTING_ES5_PATH} >> ${UMD_TESTING_ES5_PATH}.tmp
|
cat ${UMD_TESTING_ES5_PATH} >> ${UMD_TESTING_ES5_PATH}.tmp
|
||||||
mv ${UMD_TESTING_ES5_PATH}.tmp ${UMD_TESTING_ES5_PATH}
|
mv ${UMD_TESTING_ES5_PATH}.tmp ${UMD_TESTING_ES5_PATH}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -e rollup-static.config.js ]]; then
|
|
||||||
echo "====== Rollup ${PACKAGE} static"
|
|
||||||
../../../node_modules/.bin/rollup -c rollup-static.config.js
|
|
||||||
# create dir because it doesn't exist yet, we should move the src code here and remove this line
|
|
||||||
mkdir ${DESTDIR}/static
|
|
||||||
echo "{\"main\": \"../bundles/${PACKAGE}-static.umd.js\"}" > ${DESTDIR}/static/package.json
|
|
||||||
cat ${LICENSE_BANNER} > ${UMD_STATIC_ES5_PATH}.tmp
|
|
||||||
cat ${UMD_STATIC_ES5_PATH} >> ${UMD_STATIC_ES5_PATH}.tmp
|
|
||||||
mv ${UMD_STATIC_ES5_PATH}.tmp ${UMD_STATIC_ES5_PATH}
|
|
||||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_STATIC_ES5_MIN_PATH} ${UMD_STATIC_ES5_PATH}
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -e rollup-upgrade.config.js ]]; then
|
|
||||||
echo "====== Rollup ${PACKAGE} upgrade"
|
|
||||||
../../../node_modules/.bin/rollup -c rollup-upgrade.config.js
|
|
||||||
# create dir because it doesn't exist yet, we should move the src code here and remove this line
|
|
||||||
mkdir ${DESTDIR}/upgrade
|
|
||||||
echo "{\"main\": \"../bundles/${PACKAGE}-upgrade.umd.js\"}" > ${DESTDIR}/upgrade/package.json
|
|
||||||
cat ${LICENSE_BANNER} > ${UMD_UPGRADE_ES5_PATH}.tmp
|
|
||||||
cat ${UMD_UPGRADE_ES5_PATH} >> ${UMD_UPGRADE_ES5_PATH}.tmp
|
|
||||||
mv ${UMD_UPGRADE_ES5_PATH}.tmp ${UMD_UPGRADE_ES5_PATH}
|
|
||||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
|
|
||||||
fi
|
|
||||||
) 2>&1 | grep -v "as external dependency"
|
) 2>&1 | grep -v "as external dependency"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(
|
|
||||||
echo "====== VERSION: Updating version references"
|
|
||||||
cd ${DESTDIR}
|
|
||||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
|
||||||
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
|
||||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g\" $""(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .)"
|
|
||||||
perl -p -i -e "s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g" $(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .) < /dev/null 2> /dev/null
|
|
||||||
)
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "====== Building examples: ./modules/@angular/examples/build.sh ====="
|
|
||||||
./modules/@angular/examples/build.sh
|
./modules/@angular/examples/build.sh
|
||||||
|
|
||||||
if [[ ${REMOVE_BENCHPRESS} == true ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "==== Removing benchpress from publication"
|
|
||||||
rm -r dist/packages-dist/benchpress
|
|
||||||
fi
|
|
||||||
|
@ -4,7 +4,7 @@ machine:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- npm install -g npm@3.6.0
|
- npm install -g npm
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
32
gulpfile.js
32
gulpfile.js
@ -58,7 +58,6 @@ const entrypoints = [
|
|||||||
//'dist/packages-dist/compiler/index.d.ts',
|
//'dist/packages-dist/compiler/index.d.ts',
|
||||||
//'dist/packages-dist/compiler/testing.d.ts',
|
//'dist/packages-dist/compiler/testing.d.ts',
|
||||||
'dist/packages-dist/upgrade/index.d.ts',
|
'dist/packages-dist/upgrade/index.d.ts',
|
||||||
'dist/packages-dist/upgrade/static.d.ts',
|
|
||||||
'dist/packages-dist/platform-browser/index.d.ts',
|
'dist/packages-dist/platform-browser/index.d.ts',
|
||||||
'dist/packages-dist/platform-browser/testing/index.d.ts',
|
'dist/packages-dist/platform-browser/testing/index.d.ts',
|
||||||
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
||||||
@ -125,31 +124,38 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
|||||||
.on('close', done);
|
.on('close', done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
|
||||||
|
// focused tests is found.
|
||||||
|
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
|
||||||
|
// tests in our code base.
|
||||||
|
gulp.task('check-tests', function() {
|
||||||
|
const ddescribeIit = require('gulp-ddescribe-iit');
|
||||||
|
return gulp
|
||||||
|
.src([
|
||||||
|
'modules/**/*.spec.ts',
|
||||||
|
'modules/**/*_spec.ts',
|
||||||
|
])
|
||||||
|
.pipe(ddescribeIit({allowDisabledTests: true}));
|
||||||
|
});
|
||||||
|
|
||||||
// Check the coding standards and programming errors
|
// Check the coding standards and programming errors
|
||||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||||
const tslint = require('gulp-tslint');
|
const tslint = require('gulp-tslint');
|
||||||
// Built-in rules are at
|
// Built-in rules are at
|
||||||
// https://palantir.github.io/tslint/rules/
|
// https://github.com/palantir/tslint#supported-rules
|
||||||
const tslintConfig = require('./tslint.json');
|
const tslintConfig = require('./tslint.json');
|
||||||
return gulp
|
return gulp
|
||||||
.src([
|
.src([
|
||||||
// todo(vicb): add .js files when supported
|
// todo(vicb): add .js files when supported
|
||||||
// see https://github.com/palantir/tslint/pull/1515
|
// see https://github.com/palantir/tslint/pull/1515
|
||||||
'./modules/**/*.ts',
|
'modules/@angular/**/*.ts',
|
||||||
'./tools/**/*.ts',
|
'modules/benchpress/**/*.ts',
|
||||||
'./*.ts',
|
'./*.ts',
|
||||||
|
|
||||||
// Ignore TypeScript mocks because it's not managed by us
|
|
||||||
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
|
||||||
|
|
||||||
// Ignore generated files due to lack of copyright header
|
|
||||||
// todo(alfaproject): make generated files lintable
|
|
||||||
'!**/*.d.ts',
|
|
||||||
'!**/*.ngfactory.ts',
|
|
||||||
])
|
])
|
||||||
.pipe(tslint({
|
.pipe(tslint({
|
||||||
tslint: require('tslint').default,
|
tslint: require('tslint').default,
|
||||||
configuration: tslintConfig,
|
configuration: tslintConfig,
|
||||||
|
rulesDirectory: 'dist/tools/tslint',
|
||||||
formatter: 'prose',
|
formatter: 'prose',
|
||||||
}))
|
}))
|
||||||
.pipe(tslint.report({emitError: true}));
|
.pipe(tslint.report({emitError: true}));
|
||||||
|
@ -23,7 +23,7 @@ module.exports = function(config) {
|
|||||||
|
|
||||||
'node_modules/core-js/client/core.js',
|
'node_modules/core-js/client/core.js',
|
||||||
// include Angular v1 for upgrade module testing
|
// include Angular v1 for upgrade module testing
|
||||||
'node_modules/angular/angular.js',
|
'node_modules/angular/angular.min.js',
|
||||||
|
|
||||||
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||||
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
|
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
|
||||||
@ -90,17 +90,17 @@ module.exports = function(config) {
|
|||||||
project: 'Angular2',
|
project: 'Angular2',
|
||||||
startTunnel: false,
|
startTunnel: false,
|
||||||
retryLimit: 3,
|
retryLimit: 3,
|
||||||
timeout: 1800,
|
timeout: 600,
|
||||||
pollingTimeout: 10000,
|
pollingTimeout: 10000,
|
||||||
},
|
},
|
||||||
|
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
captureTimeout: 180000,
|
captureTimeout: 60000,
|
||||||
browserDisconnectTimeout: 180000,
|
browserDisconnectTimeout: 60000,
|
||||||
browserDisconnectTolerance: 3,
|
browserDisconnectTolerance: 3,
|
||||||
browserNoActivityTimeout: 300000,
|
browserNoActivityTimeout: 60000,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.TRAVIS) {
|
if (process.env.TRAVIS) {
|
||||||
|
@ -10,7 +10,7 @@ declare var exportFunction: any;
|
|||||||
declare var unsafeWindow: any;
|
declare var unsafeWindow: any;
|
||||||
|
|
||||||
exportFunction(function() {
|
exportFunction(function() {
|
||||||
const curTime = unsafeWindow.performance.now();
|
var curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('startProfiler', curTime);
|
(<any>self).port.emit('startProfiler', curTime);
|
||||||
}, unsafeWindow, {defineAs: 'startProfiler'});
|
}, unsafeWindow, {defineAs: 'startProfiler'});
|
||||||
|
|
||||||
@ -28,11 +28,11 @@ exportFunction(function() {
|
|||||||
}, unsafeWindow, {defineAs: 'forceGC'});
|
}, unsafeWindow, {defineAs: 'forceGC'});
|
||||||
|
|
||||||
exportFunction(function(name: string) {
|
exportFunction(function(name: string) {
|
||||||
const curTime = unsafeWindow.performance.now();
|
var curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('markStart', name, curTime);
|
(<any>self).port.emit('markStart', name, curTime);
|
||||||
}, unsafeWindow, {defineAs: 'markStart'});
|
}, unsafeWindow, {defineAs: 'markStart'});
|
||||||
|
|
||||||
exportFunction(function(name: string) {
|
exportFunction(function(name: string) {
|
||||||
const curTime = unsafeWindow.performance.now();
|
var curTime = unsafeWindow.performance.now();
|
||||||
(<any>self).port.emit('markEnd', name, curTime);
|
(<any>self).port.emit('markEnd', name, curTime);
|
||||||
}, unsafeWindow, {defineAs: 'markEnd'});
|
}, unsafeWindow, {defineAs: 'markEnd'});
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {Cc, Ci, Cu} = require('chrome');
|
var {Cc, Ci, Cu} = require('chrome');
|
||||||
const os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
var os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||||
const ParserUtil = require('./parser_util');
|
var ParserUtil = require('./parser_util');
|
||||||
|
|
||||||
class Profiler {
|
class Profiler {
|
||||||
private _profiler: any;
|
private _profiler: any;
|
||||||
@ -26,8 +26,8 @@ class Profiler {
|
|||||||
stop() { this._profiler.StopProfiler(); }
|
stop() { this._profiler.StopProfiler(); }
|
||||||
|
|
||||||
getProfilePerfEvents() {
|
getProfilePerfEvents() {
|
||||||
const profileData = this._profiler.getProfileData();
|
var profileData = this._profiler.getProfileData();
|
||||||
let perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
var perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||||
perfEvents = this._mergeMarkerEvents(perfEvents);
|
perfEvents = this._mergeMarkerEvents(perfEvents);
|
||||||
perfEvents.sort(function(event1: any, event2: any) {
|
perfEvents.sort(function(event1: any, event2: any) {
|
||||||
return event1.ts - event2.ts;
|
return event1.ts - event2.ts;
|
||||||
@ -55,9 +55,9 @@ function forceGC() {
|
|||||||
os.notifyObservers(null, 'child-gc-request', null);
|
os.notifyObservers(null, 'child-gc-request', null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mod = require('sdk/page-mod');
|
var mod = require('sdk/page-mod');
|
||||||
const data = require('sdk/self').data;
|
var data = require('sdk/self').data;
|
||||||
const profiler = new Profiler();
|
var profiler = new Profiler();
|
||||||
mod.PageMod({
|
mod.PageMod({
|
||||||
include: ['*'],
|
include: ['*'],
|
||||||
contentScriptFile: data.url('installed_script.js'),
|
contentScriptFile: data.url('installed_script.js'),
|
||||||
|
@ -12,11 +12,11 @@
|
|||||||
* within the perf profile.
|
* within the perf profile.
|
||||||
*/
|
*/
|
||||||
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||||
const inProgressEvents = new Map(); // map from event name to start time
|
var inProgressEvents = new Map(); // map from event name to start time
|
||||||
const finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
var finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||||
const addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
var addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||||
const categorizedEventName = categorizeEvent(eventName);
|
var categorizedEventName = categorizeEvent(eventName);
|
||||||
let args: {[key: string]: any} = undefined;
|
var args: {[key: string]: any} = undefined;
|
||||||
if (categorizedEventName == 'gc') {
|
if (categorizedEventName == 'gc') {
|
||||||
// TODO: We cannot measure heap size at the moment
|
// TODO: We cannot measure heap size at the moment
|
||||||
args = {usedHeapSize: 0};
|
args = {usedHeapSize: 0};
|
||||||
@ -31,17 +31,17 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const samples = perfProfile.threads[0].samples;
|
var samples = perfProfile.threads[0].samples;
|
||||||
// In perf profile, firefox samples all the frames in set time intervals. Here
|
// In perf profile, firefox samples all the frames in set time intervals. Here
|
||||||
// we go through all the samples and construct the start and end time for each
|
// we go through all the samples and construct the start and end time for each
|
||||||
// event.
|
// event.
|
||||||
for (let i = 0; i < samples.length; ++i) {
|
for (var i = 0; i < samples.length; ++i) {
|
||||||
const sample = samples[i];
|
var sample = samples[i];
|
||||||
const sampleTime = sample.time;
|
var sampleTime = sample.time;
|
||||||
|
|
||||||
// Add all the frames into a set so it's easier/faster to find the set
|
// Add all the frames into a set so it's easier/faster to find the set
|
||||||
// differences
|
// differences
|
||||||
const sampleFrames = new Set();
|
var sampleFrames = new Set();
|
||||||
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
||||||
sampleFrames.add(frame['location']);
|
sampleFrames.add(frame['location']);
|
||||||
});
|
});
|
||||||
@ -49,7 +49,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
|||||||
// If an event is in the inProgressEvents map, but not in the current sample,
|
// If an event is in the inProgressEvents map, but not in the current sample,
|
||||||
// then it must have just finished. We add this event to the finishedEvents
|
// then it must have just finished. We add this event to the finishedEvents
|
||||||
// array and remove it from the inProgressEvents map.
|
// array and remove it from the inProgressEvents map.
|
||||||
const previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
var previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
||||||
inProgressEvents.forEach(function(startTime, eventName) {
|
inProgressEvents.forEach(function(startTime, eventName) {
|
||||||
if (!(sampleFrames.has(eventName))) {
|
if (!(sampleFrames.has(eventName))) {
|
||||||
addFinishedEvent(eventName, startTime, previousSampleTime);
|
addFinishedEvent(eventName, startTime, previousSampleTime);
|
||||||
@ -69,7 +69,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
|||||||
|
|
||||||
// If anything is still in progress, we need to included it as a finished event
|
// If anything is still in progress, we need to included it as a finished event
|
||||||
// since recording ended.
|
// since recording ended.
|
||||||
const lastSampleTime = samples[samples.length - 1].time;
|
var lastSampleTime = samples[samples.length - 1].time;
|
||||||
inProgressEvents.forEach(function(startTime, eventName) {
|
inProgressEvents.forEach(function(startTime, eventName) {
|
||||||
addFinishedEvent(eventName, startTime, lastSampleTime);
|
addFinishedEvent(eventName, startTime, lastSampleTime);
|
||||||
});
|
});
|
||||||
|
@ -6,15 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const q = require('q');
|
var q = require('q');
|
||||||
const FirefoxProfile = require('firefox-profile');
|
var FirefoxProfile = require('firefox-profile');
|
||||||
const jpm = require('jpm/lib/xpi');
|
var jpm = require('jpm/lib/xpi');
|
||||||
const pathUtil = require('path');
|
var pathUtil = require('path');
|
||||||
|
|
||||||
const PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
var PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||||
|
|
||||||
exports.getAbsolutePath = function(path: string) {
|
exports.getAbsolutePath = function(path: string) {
|
||||||
const normalizedPath = pathUtil.normalize(path);
|
var normalizedPath = pathUtil.normalize(path);
|
||||||
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
||||||
// Already absolute path
|
// Already absolute path
|
||||||
return normalizedPath;
|
return normalizedPath;
|
||||||
@ -24,12 +24,12 @@ exports.getAbsolutePath = function(path: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.getFirefoxProfile = function(extensionPath: string) {
|
exports.getFirefoxProfile = function(extensionPath: string) {
|
||||||
const deferred = q.defer();
|
var deferred = q.defer();
|
||||||
|
|
||||||
const firefoxProfile = new FirefoxProfile();
|
var firefoxProfile = new FirefoxProfile();
|
||||||
firefoxProfile.addExtensions([extensionPath], () => {
|
firefoxProfile.addExtensions([extensionPath], () => {
|
||||||
firefoxProfile.encoded((encodedProfile: any) => {
|
firefoxProfile.encoded((encodedProfile: any) => {
|
||||||
const multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
var multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||||
deferred.resolve(multiCapabilities);
|
deferred.resolve(multiCapabilities);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -38,10 +38,10 @@ exports.getFirefoxProfile = function(extensionPath: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.getFirefoxProfileWithExtension = function() {
|
exports.getFirefoxProfileWithExtension = function() {
|
||||||
const absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
var absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||||
const packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
var packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||||
|
|
||||||
const savedCwd = process.cwd();
|
var savedCwd = process.cwd();
|
||||||
process.chdir(absPackageJsonDir);
|
process.chdir(absPackageJsonDir);
|
||||||
|
|
||||||
return jpm(packageJson).then((xpiPath: string) => {
|
return jpm(packageJson).then((xpiPath: string) => {
|
||||||
|
@ -55,9 +55,9 @@ export class MultiMetric extends Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
||||||
const result: {[key: string]: string} = {};
|
var result: {[key: string]: string} = {};
|
||||||
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('MultiMetric.children');
|
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||||
|
@ -56,7 +56,7 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe(): {[key: string]: string} {
|
describe(): {[key: string]: string} {
|
||||||
const res: {[key: string]: any} = {
|
var res: {[key: string]: any} = {
|
||||||
'scriptTime': 'script execution time in ms, including gc and render',
|
'scriptTime': 'script execution time in ms, including gc and render',
|
||||||
'pureScriptTime': 'script execution time in ms, without gc nor render'
|
'pureScriptTime': 'script execution time in ms, without gc nor render'
|
||||||
};
|
};
|
||||||
@ -80,7 +80,7 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
if (this._captureFrames) {
|
if (this._captureFrames) {
|
||||||
if (!this._perfLogFeatures.frameCapture) {
|
if (!this._perfLogFeatures.frameCapture) {
|
||||||
const warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
var warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
||||||
// using dot syntax for metric name to keep them grouped together in console reporter
|
// using dot syntax for metric name to keep them grouped together in console reporter
|
||||||
res['frameTime.mean'] = warningMsg;
|
res['frameTime.mean'] = warningMsg;
|
||||||
res['frameTime.worst'] = warningMsg;
|
res['frameTime.worst'] = warningMsg;
|
||||||
@ -93,14 +93,14 @@ export class PerflogMetric extends Metric {
|
|||||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const name in this._microMetrics) {
|
for (let name in this._microMetrics) {
|
||||||
res[name] = this._microMetrics[name];
|
res[name] = this._microMetrics[name];
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginMeasure(): Promise<any> {
|
beginMeasure(): Promise<any> {
|
||||||
let resultPromise = Promise.resolve(null);
|
var resultPromise = Promise.resolve(null);
|
||||||
if (this._forceGc) {
|
if (this._forceGc) {
|
||||||
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ export class PerflogMetric extends Metric {
|
|||||||
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
||||||
return this._endMeasure(true).then((measureValues) => {
|
return this._endMeasure(true).then((measureValues) => {
|
||||||
// disable frame capture for measurements during forced gc
|
// disable frame capture for measurements during forced gc
|
||||||
const originalFrameCaptureValue = this._captureFrames;
|
var originalFrameCaptureValue = this._captureFrames;
|
||||||
this._captureFrames = false;
|
this._captureFrames = false;
|
||||||
return this._driverExtension.gc()
|
return this._driverExtension.gc()
|
||||||
.then((_) => this._endMeasure(restartMeasure))
|
.then((_) => this._endMeasure(restartMeasure))
|
||||||
@ -137,8 +137,8 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
||||||
const markName = this._markName(this._measureCount - 1);
|
var markName = this._markName(this._measureCount - 1);
|
||||||
const nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||||
return this._driverExtension.timeEnd(markName, nextMarkName)
|
return this._driverExtension.timeEnd(markName, nextMarkName)
|
||||||
.then((_) => this._readUntilEndMark(markName));
|
.then((_) => this._readUntilEndMark(markName));
|
||||||
}
|
}
|
||||||
@ -150,26 +150,26 @@ export class PerflogMetric extends Metric {
|
|||||||
}
|
}
|
||||||
return this._driverExtension.readPerfLog().then((events) => {
|
return this._driverExtension.readPerfLog().then((events) => {
|
||||||
this._addEvents(events);
|
this._addEvents(events);
|
||||||
const result = this._aggregateEvents(this._remainingEvents, markName);
|
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||||
if (result) {
|
if (result) {
|
||||||
this._remainingEvents = events;
|
this._remainingEvents = events;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
let resolve: (result: any) => void;
|
var resolve: (result: any) => void;
|
||||||
const promise = new Promise(res => { resolve = res; });
|
var promise = new Promise(res => { resolve = res; });
|
||||||
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addEvents(events: PerfLogEvent[]) {
|
private _addEvents(events: PerfLogEvent[]) {
|
||||||
let needSort = false;
|
var needSort = false;
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (event['ph'] === 'X') {
|
if (event['ph'] === 'X') {
|
||||||
needSort = true;
|
needSort = true;
|
||||||
const startEvent: PerfLogEvent = {};
|
var startEvent: PerfLogEvent = {};
|
||||||
const endEvent: PerfLogEvent = {};
|
var endEvent: PerfLogEvent = {};
|
||||||
for (const prop in event) {
|
for (let prop in event) {
|
||||||
startEvent[prop] = event[prop];
|
startEvent[prop] = event[prop];
|
||||||
endEvent[prop] = event[prop];
|
endEvent[prop] = event[prop];
|
||||||
}
|
}
|
||||||
@ -185,14 +185,14 @@ export class PerflogMetric extends Metric {
|
|||||||
if (needSort) {
|
if (needSort) {
|
||||||
// Need to sort because of the ph==='X' events
|
// Need to sort because of the ph==='X' events
|
||||||
this._remainingEvents.sort((a, b) => {
|
this._remainingEvents.sort((a, b) => {
|
||||||
const diff = a['ts'] - b['ts'];
|
var diff = a['ts'] - b['ts'];
|
||||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _aggregateEvents(events: PerfLogEvent[], markName: string): {[key: string]: number} {
|
private _aggregateEvents(events: PerfLogEvent[], markName: string): {[key: string]: number} {
|
||||||
const result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
var result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||||
if (this._perfLogFeatures.gc) {
|
if (this._perfLogFeatures.gc) {
|
||||||
result['gcTime'] = 0;
|
result['gcTime'] = 0;
|
||||||
result['majorGcTime'] = 0;
|
result['majorGcTime'] = 0;
|
||||||
@ -207,7 +207,7 @@ export class PerflogMetric extends Metric {
|
|||||||
result['frameTime.worst'] = 0;
|
result['frameTime.worst'] = 0;
|
||||||
result['frameTime.smooth'] = 0;
|
result['frameTime.smooth'] = 0;
|
||||||
}
|
}
|
||||||
for (const name in this._microMetrics) {
|
for (let name in this._microMetrics) {
|
||||||
result[name] = 0;
|
result[name] = 0;
|
||||||
}
|
}
|
||||||
if (this._receivedData) {
|
if (this._receivedData) {
|
||||||
@ -217,11 +217,11 @@ export class PerflogMetric extends Metric {
|
|||||||
result['requestCount'] = 0;
|
result['requestCount'] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let markStartEvent: PerfLogEvent = null;
|
var markStartEvent: PerfLogEvent = null;
|
||||||
let markEndEvent: PerfLogEvent = null;
|
var markEndEvent: PerfLogEvent = null;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
const ph = event['ph'];
|
var ph = event['ph'];
|
||||||
const name = event['name'];
|
var name = event['name'];
|
||||||
if (ph === 'B' && name === markName) {
|
if (ph === 'B' && name === markName) {
|
||||||
markStartEvent = event;
|
markStartEvent = event;
|
||||||
} else if (ph === 'I' && name === 'navigationStart') {
|
} else if (ph === 'I' && name === 'navigationStart') {
|
||||||
@ -237,23 +237,23 @@ export class PerflogMetric extends Metric {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let gcTimeInScript = 0;
|
var gcTimeInScript = 0;
|
||||||
let renderTimeInScript = 0;
|
var renderTimeInScript = 0;
|
||||||
|
|
||||||
const frameTimestamps: number[] = [];
|
var frameTimestamps: number[] = [];
|
||||||
const frameTimes: number[] = [];
|
var frameTimes: number[] = [];
|
||||||
let frameCaptureStartEvent: PerfLogEvent = null;
|
var frameCaptureStartEvent: PerfLogEvent = null;
|
||||||
let frameCaptureEndEvent: PerfLogEvent = null;
|
var frameCaptureEndEvent: PerfLogEvent = null;
|
||||||
|
|
||||||
const intervalStarts: {[key: string]: PerfLogEvent} = {};
|
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||||
const intervalStartCount: {[key: string]: number} = {};
|
var intervalStartCount: {[key: string]: number} = {};
|
||||||
|
|
||||||
let inMeasureRange = false;
|
var inMeasureRange = false;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
const ph = event['ph'];
|
var ph = event['ph'];
|
||||||
let name = event['name'];
|
var name = event['name'];
|
||||||
let microIterations = 1;
|
var microIterations = 1;
|
||||||
const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||||
if (microIterationsMatch) {
|
if (microIterationsMatch) {
|
||||||
name = microIterationsMatch[1];
|
name = microIterationsMatch[1];
|
||||||
microIterations = parseInt(microIterationsMatch[2], 10);
|
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||||
@ -307,15 +307,15 @@ export class PerflogMetric extends Metric {
|
|||||||
} else if ((ph === 'E') && intervalStarts[name]) {
|
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||||
intervalStartCount[name]--;
|
intervalStartCount[name]--;
|
||||||
if (intervalStartCount[name] === 0) {
|
if (intervalStartCount[name] === 0) {
|
||||||
const startEvent = intervalStarts[name];
|
var startEvent = intervalStarts[name];
|
||||||
const duration = (event['ts'] - startEvent['ts']);
|
var duration = (event['ts'] - startEvent['ts']);
|
||||||
intervalStarts[name] = null;
|
intervalStarts[name] = null;
|
||||||
if (name === 'gc') {
|
if (name === 'gc') {
|
||||||
result['gcTime'] += duration;
|
result['gcTime'] += duration;
|
||||||
const amount =
|
var amount =
|
||||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||||
result['gcAmount'] += amount;
|
result['gcAmount'] += amount;
|
||||||
const majorGc = event['args']['majorGc'];
|
var majorGc = event['args']['majorGc'];
|
||||||
if (majorGc && majorGc) {
|
if (majorGc && majorGc) {
|
||||||
result['majorGcTime'] += duration;
|
result['majorGcTime'] += duration;
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
||||||
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
|
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
|
||||||
const firstFrame = frameTimes[0];
|
var firstFrame = frameTimes[0];
|
||||||
result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
|
result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
|
||||||
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
||||||
result['frameTime.smooth'] =
|
result['frameTime.smooth'] =
|
||||||
@ -361,11 +361,11 @@ export class PerflogMetric extends Metric {
|
|||||||
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||||
|
|
||||||
const _MAX_RETRY_COUNT = 20;
|
var _MAX_RETRY_COUNT = 20;
|
||||||
const _MARK_NAME_PREFIX = 'benchpress';
|
var _MARK_NAME_PREFIX = 'benchpress';
|
||||||
|
|
||||||
const _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
||||||
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
|
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
|
||||||
const _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
||||||
|
@ -33,12 +33,12 @@ export class UserMetric extends Metric {
|
|||||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||||
let resolve: (result: any) => void;
|
let resolve: (result: any) => void;
|
||||||
let reject: (error: any) => void;
|
let reject: (error: any) => void;
|
||||||
const promise = new Promise((res, rej) => {
|
let promise = new Promise((res, rej) => {
|
||||||
resolve = res;
|
resolve = res;
|
||||||
reject = rej;
|
reject = rej;
|
||||||
});
|
});
|
||||||
const adapter = this._wdAdapter;
|
let adapter = this._wdAdapter;
|
||||||
const names = Object.keys(this._userMetrics);
|
let names = Object.keys(this._userMetrics);
|
||||||
|
|
||||||
function getAndClearValues() {
|
function getAndClearValues() {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||||
@ -46,7 +46,7 @@ export class UserMetric extends Metric {
|
|||||||
if (values.every(v => typeof v === 'number')) {
|
if (values.every(v => typeof v === 'number')) {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||||
.then((_: any[]) => {
|
.then((_: any[]) => {
|
||||||
const map: {[k: string]: any} = {};
|
let map: {[k: string]: any} = {};
|
||||||
for (let i = 0, n = names.length; i < n; i++) {
|
for (let i = 0, n = names.length; i < n; i++) {
|
||||||
map[names[i]] = values[i];
|
map[names[i]] = values[i];
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,8 @@ export class ConsoleReporter extends Reporter {
|
|||||||
];
|
];
|
||||||
|
|
||||||
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
||||||
let result = '';
|
var result = '';
|
||||||
for (let i = 0; i < columnWidth - value.length; i++) {
|
for (var i = 0; i < columnWidth - value.length; i++) {
|
||||||
result += fill;
|
result += fill;
|
||||||
}
|
}
|
||||||
return result + value;
|
return result + value;
|
||||||
@ -49,7 +49,7 @@ export class ConsoleReporter extends Reporter {
|
|||||||
private _printDescription(sampleDescription: SampleDescription) {
|
private _printDescription(sampleDescription: SampleDescription) {
|
||||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||||
this._print('Description:');
|
this._print('Description:');
|
||||||
const props = sortedProps(sampleDescription.description);
|
var props = sortedProps(sampleDescription.description);
|
||||||
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
||||||
this._print('Metrics:');
|
this._print('Metrics:');
|
||||||
this._metricNames.forEach((metricName) => {
|
this._metricNames.forEach((metricName) => {
|
||||||
@ -61,8 +61,8 @@ export class ConsoleReporter extends Reporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||||
const formattedValues = this._metricNames.map(metricName => {
|
var formattedValues = this._metricNames.map(metricName => {
|
||||||
const value = measureValues.values[metricName];
|
var value = measureValues.values[metricName];
|
||||||
return formatNum(value);
|
return formatNum(value);
|
||||||
});
|
});
|
||||||
this._printStringRow(formattedValues);
|
this._printStringRow(formattedValues);
|
||||||
|
@ -38,7 +38,7 @@ export class JsonFileReporter extends Reporter {
|
|||||||
sortedProps(this._description.metrics).forEach((metricName) => {
|
sortedProps(this._description.metrics).forEach((metricName) => {
|
||||||
stats[metricName] = formatStats(validSample, metricName);
|
stats[metricName] = formatStats(validSample, metricName);
|
||||||
});
|
});
|
||||||
const content = JSON.stringify(
|
var content = JSON.stringify(
|
||||||
{
|
{
|
||||||
'description': this._description,
|
'description': this._description,
|
||||||
'stats': stats,
|
'stats': stats,
|
||||||
@ -46,7 +46,7 @@ export class JsonFileReporter extends Reporter {
|
|||||||
'validSample': validSample,
|
'validSample': validSample,
|
||||||
},
|
},
|
||||||
null, 2);
|
null, 2);
|
||||||
const filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||||
return this._writeFile(filePath, content);
|
return this._writeFile(filePath, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,4 +39,4 @@ export class MultiReporter extends Reporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('MultiReporter.children');
|
var _CHILDREN = new OpaqueToken('MultiReporter.children');
|
||||||
|
@ -18,10 +18,10 @@ export function sortedProps(obj: {[key: string]: any}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
||||||
const samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
var samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||||
const mean = Statistic.calculateMean(samples);
|
var mean = Statistic.calculateMean(samples);
|
||||||
const cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
var cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||||
const formattedMean = formatNum(mean);
|
var formattedMean = formatNum(mean);
|
||||||
// Note: Don't use the unicode character for +- as it might cause
|
// Note: Don't use the unicode character for +- as it might cause
|
||||||
// hickups for consoles...
|
// hickups for consoles...
|
||||||
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||||
|
@ -45,7 +45,7 @@ export class Runner {
|
|||||||
providers?: Provider[],
|
providers?: Provider[],
|
||||||
userMetrics?: {[key: string]: string}
|
userMetrics?: {[key: string]: string}
|
||||||
}): Promise<SampleState> {
|
}): Promise<SampleState> {
|
||||||
const sampleProviders: Provider[] = [
|
var sampleProviders: Provider[] = [
|
||||||
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
||||||
{provide: Options.EXECUTE, useValue: execute}
|
{provide: Options.EXECUTE, useValue: execute}
|
||||||
];
|
];
|
||||||
@ -62,33 +62,33 @@ export class Runner {
|
|||||||
sampleProviders.push(providers);
|
sampleProviders.push(providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||||
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
var adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
const capabilities = args[0];
|
var capabilities = args[0];
|
||||||
const userAgent = args[1];
|
var userAgent = args[1];
|
||||||
|
|
||||||
// This might still create instances twice. We are creating a new injector with all the
|
// This might still create instances twice. We are creating a new injector with all the
|
||||||
// providers.
|
// providers.
|
||||||
// Only WebDriverAdapter is reused.
|
// Only WebDriverAdapter is reused.
|
||||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||||
// injectors are handled better.
|
// injectors are handled better.
|
||||||
const injector = ReflectiveInjector.resolveAndCreate([
|
var injector = ReflectiveInjector.resolveAndCreate([
|
||||||
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
||||||
{provide: Options.USER_AGENT, useValue: userAgent},
|
{provide: Options.USER_AGENT, useValue: userAgent},
|
||||||
{provide: WebDriverAdapter, useValue: adapter}
|
{provide: WebDriverAdapter, useValue: adapter}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const sampler = injector.get(Sampler);
|
var sampler = injector.get(Sampler);
|
||||||
return sampler.sample();
|
return sampler.sample();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _DEFAULT_PROVIDERS = [
|
var _DEFAULT_PROVIDERS = [
|
||||||
Options.DEFAULT_PROVIDERS,
|
Options.DEFAULT_PROVIDERS,
|
||||||
Sampler.PROVIDERS,
|
Sampler.PROVIDERS,
|
||||||
ConsoleReporter.PROVIDERS,
|
ConsoleReporter.PROVIDERS,
|
||||||
|
@ -49,7 +49,7 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||||
let resultPromise: Promise<SampleState>;
|
var resultPromise: Promise<SampleState>;
|
||||||
if (this._prepare !== Options.NO_PREPARE) {
|
if (this._prepare !== Options.NO_PREPARE) {
|
||||||
resultPromise = this._driver.waitFor(this._prepare);
|
resultPromise = this._driver.waitFor(this._prepare);
|
||||||
} else {
|
} else {
|
||||||
@ -64,10 +64,10 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
||||||
const measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||||
const completeSample = state.completeSample.concat([measureValues]);
|
var completeSample = state.completeSample.concat([measureValues]);
|
||||||
const validSample = this._validator.validate(completeSample);
|
var validSample = this._validator.validate(completeSample);
|
||||||
let resultPromise = this._reporter.reportMeasureValues(measureValues);
|
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||||
if (isPresent(validSample)) {
|
if (isPresent(validSample)) {
|
||||||
resultPromise =
|
resultPromise =
|
||||||
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
||||||
|
@ -12,14 +12,14 @@ export class Statistic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static calculateMean(samples: number[]) {
|
static calculateMean(samples: number[]) {
|
||||||
let total = 0;
|
var total = 0;
|
||||||
// TODO: use reduce
|
// TODO: use reduce
|
||||||
samples.forEach(x => total += x);
|
samples.forEach(x => total += x);
|
||||||
return total / samples.length;
|
return total / samples.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static calculateStandardDeviation(samples: number[], mean: number) {
|
static calculateStandardDeviation(samples: number[], mean: number) {
|
||||||
let deviation = 0;
|
var deviation = 0;
|
||||||
// TODO: use reduce
|
// TODO: use reduce
|
||||||
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
||||||
deviation = deviation / (samples.length);
|
deviation = deviation / (samples.length);
|
||||||
@ -30,9 +30,9 @@ export class Statistic {
|
|||||||
static calculateRegressionSlope(
|
static calculateRegressionSlope(
|
||||||
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
||||||
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
||||||
let dividendSum = 0;
|
var dividendSum = 0;
|
||||||
let divisorSum = 0;
|
var divisorSum = 0;
|
||||||
for (let i = 0; i < xValues.length; i++) {
|
for (var i = 0; i < xValues.length; i++) {
|
||||||
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
||||||
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,13 @@
|
|||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Statistic} from '../statistic';
|
import {Statistic} from '../statistic';
|
||||||
import {Validator} from '../validator';
|
import {Validator} from '../validator';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A validator that checks the regression slope of a specific metric.
|
* A validator that checks the regression slope of a specific metric.
|
||||||
* Waits for the regression slope to be >=0.
|
* Waits for the regression slope to be >=0.
|
||||||
@ -37,17 +40,17 @@ export class RegressionSlopeValidator extends Validator {
|
|||||||
|
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||||
if (completeSample.length >= this._sampleSize) {
|
if (completeSample.length >= this._sampleSize) {
|
||||||
const latestSample =
|
var latestSample = ListWrapper.slice(
|
||||||
completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||||
const xValues: number[] = [];
|
var xValues: number[] = [];
|
||||||
const yValues: number[] = [];
|
var yValues: number[] = [];
|
||||||
for (let i = 0; i < latestSample.length; i++) {
|
for (var i = 0; i < latestSample.length; i++) {
|
||||||
// For now, we only use the array index as x value.
|
// For now, we only use the array index as x value.
|
||||||
// TODO(tbosch): think about whether we should use time here instead
|
// TODO(tbosch): think about whether we should use time here instead
|
||||||
xValues.push(i);
|
xValues.push(i);
|
||||||
yValues.push(latestSample[i].values[this._metric]);
|
yValues.push(latestSample[i].values[this._metric]);
|
||||||
}
|
}
|
||||||
const regressionSlope = Statistic.calculateRegressionSlope(
|
var regressionSlope = Statistic.calculateRegressionSlope(
|
||||||
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
||||||
return regressionSlope >= 0 ? latestSample : null;
|
return regressionSlope >= 0 ? latestSample : null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,9 +8,12 @@
|
|||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Validator} from '../validator';
|
import {Validator} from '../validator';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A validator that waits for the sample to have a certain size.
|
* A validator that waits for the sample to have a certain size.
|
||||||
*/
|
*/
|
||||||
@ -25,7 +28,8 @@ export class SizeValidator extends Validator {
|
|||||||
|
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||||
if (completeSample.length >= this._sampleSize) {
|
if (completeSample.length >= this._sampleSize) {
|
||||||
return completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
return ListWrapper.slice(
|
||||||
|
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export type PerfLogEvent = {
|
|||||||
*/
|
*/
|
||||||
export abstract class WebDriverExtension {
|
export abstract class WebDriverExtension {
|
||||||
static provideFirstSupported(childTokens: any[]): any[] {
|
static provideFirstSupported(childTokens: any[]): any[] {
|
||||||
const res = [
|
var res = [
|
||||||
{
|
{
|
||||||
provide: _CHILDREN,
|
provide: _CHILDREN,
|
||||||
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
||||||
@ -43,7 +43,7 @@ export abstract class WebDriverExtension {
|
|||||||
{
|
{
|
||||||
provide: WebDriverExtension,
|
provide: WebDriverExtension,
|
||||||
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
||||||
let delegate: WebDriverExtension;
|
var delegate: WebDriverExtension;
|
||||||
children.forEach(extension => {
|
children.forEach(extension => {
|
||||||
if (extension.supports(capabilities)) {
|
if (extension.supports(capabilities)) {
|
||||||
delegate = extension;
|
delegate = extension;
|
||||||
@ -101,4 +101,4 @@ export class PerfLogFeatures {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
var _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
||||||
|
@ -34,7 +34,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
if (!userAgent) {
|
if (!userAgent) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||||
if (!v) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
let script = `console.timeEnd('${name}');`;
|
var script = `console.timeEnd('${name}');`;
|
||||||
if (restartName) {
|
if (restartName) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
@ -67,9 +67,9 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
return this._driver.executeScript('1+1')
|
return this._driver.executeScript('1+1')
|
||||||
.then((_) => this._driver.logs('performance'))
|
.then((_) => this._driver.logs('performance'))
|
||||||
.then((entries) => {
|
.then((entries) => {
|
||||||
const events: PerfLogEvent[] = [];
|
var events: PerfLogEvent[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
const message = JSON.parse(entry['message'])['message'];
|
var message = JSON.parse(entry['message'])['message'];
|
||||||
if (message['method'] === 'Tracing.dataCollected') {
|
if (message['method'] === 'Tracing.dataCollected') {
|
||||||
events.push(message['params']);
|
events.push(message['params']);
|
||||||
}
|
}
|
||||||
@ -95,8 +95,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||||
const name = event['name'];
|
var name = event['name'];
|
||||||
const args = event['args'];
|
var args = event['args'];
|
||||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||||
return normalizeEvent(event, {'name': name});
|
return normalizeEvent(event, {'name': name});
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
@ -109,7 +109,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
// new surfaces framework (not broadly enabled yet)
|
// new surfaces framework (not broadly enabled yet)
|
||||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||||
// always available if something is rendered
|
// always available if something is rendered
|
||||||
const frameCount = event['args']['data']['frame_count'];
|
var frameCount = event['args']['data']['frame_count'];
|
||||||
if (frameCount > 1) {
|
if (frameCount > 1) {
|
||||||
throw new Error('multi-frame render stats not supported');
|
throw new Error('multi-frame render stats not supported');
|
||||||
}
|
}
|
||||||
@ -122,14 +122,14 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||||
const normArgs = {
|
var normArgs = {
|
||||||
'majorGc': true,
|
'majorGc': true,
|
||||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||||
const normArgs = {
|
var normArgs = {
|
||||||
'majorGc': false,
|
'majorGc': false,
|
||||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
@ -151,11 +151,11 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
|
||||||
const normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
let normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
||||||
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
||||||
const data = args['data'];
|
let data = args['data'];
|
||||||
const normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||||
return normalizeEvent(event, {'name': 'navigationStart'});
|
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||||
@ -168,7 +168,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
private _isEvent(
|
private _isEvent(
|
||||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||||
expectedName: string = null): boolean {
|
expectedName: string = null): boolean {
|
||||||
const hasCategories = expectedCategories.reduce(
|
var hasCategories = expectedCategories.reduce(
|
||||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||||
let ph = chromeEvent['ph'].toUpperCase();
|
var ph = chromeEvent['ph'].toUpperCase();
|
||||||
if (ph === 'S') {
|
if (ph === 'S') {
|
||||||
ph = 'B';
|
ph = 'B';
|
||||||
} else if (ph === 'F') {
|
} else if (ph === 'F') {
|
||||||
@ -192,16 +192,16 @@ function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent):
|
|||||||
// mark events from navigation timing
|
// mark events from navigation timing
|
||||||
ph = 'I';
|
ph = 'I';
|
||||||
}
|
}
|
||||||
const result: {[key: string]: any} =
|
var result: {[key: string]: any} =
|
||||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||||
if (ph === 'X') {
|
if (ph === 'X') {
|
||||||
let dur = chromeEvent['dur'];
|
var dur = chromeEvent['dur'];
|
||||||
if (dur === undefined) {
|
if (dur === undefined) {
|
||||||
dur = chromeEvent['tdur'];
|
dur = chromeEvent['tdur'];
|
||||||
}
|
}
|
||||||
result['dur'] = !dur ? 0.0 : dur / 1000;
|
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||||
}
|
}
|
||||||
for (const prop in data) {
|
for (let prop in data) {
|
||||||
result[prop] = data[prop];
|
result[prop] = data[prop];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -34,7 +34,7 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
let script = 'window.markEnd("' + name + '");';
|
var script = 'window.markEnd("' + name + '");';
|
||||||
if (isPresent(restartName)) {
|
if (isPresent(restartName)) {
|
||||||
script += 'window.markStart("' + restartName + '");';
|
script += 'window.markStart("' + restartName + '");';
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
let script = `console.timeEnd('${name}');`;
|
var script = `console.timeEnd('${name}');`;
|
||||||
if (isPresent(restartName)) {
|
if (isPresent(restartName)) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
@ -39,9 +39,9 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
return this._driver.executeScript('1+1')
|
return this._driver.executeScript('1+1')
|
||||||
.then((_) => this._driver.logs('performance'))
|
.then((_) => this._driver.logs('performance'))
|
||||||
.then((entries) => {
|
.then((entries) => {
|
||||||
const records: any[] = [];
|
var records: any[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
const message = JSON.parse(entry['message'])['message'];
|
var message = JSON.parse(entry['message'])['message'];
|
||||||
if (message['method'] === 'Timeline.eventRecorded') {
|
if (message['method'] === 'Timeline.eventRecorded') {
|
||||||
records.push(message['params']['record']);
|
records.push(message['params']['record']);
|
||||||
}
|
}
|
||||||
@ -56,13 +56,13 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
events = [];
|
events = [];
|
||||||
}
|
}
|
||||||
records.forEach((record) => {
|
records.forEach((record) => {
|
||||||
let endEvent: PerfLogEvent = null;
|
var endEvent: PerfLogEvent = null;
|
||||||
const type = record['type'];
|
var type = record['type'];
|
||||||
const data = record['data'];
|
var data = record['data'];
|
||||||
const startTime = record['startTime'];
|
var startTime = record['startTime'];
|
||||||
const endTime = record['endTime'];
|
var endTime = record['endTime'];
|
||||||
|
|
||||||
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
if (type === 'FunctionCall' && (isBlank(data) || data['scriptName'] !== 'InjectedScript')) {
|
||||||
events.push(createStartEvent('script', startTime));
|
events.push(createStartEvent('script', startTime));
|
||||||
endEvent = createEndEvent('script', endTime);
|
endEvent = createEndEvent('script', endTime);
|
||||||
} else if (type === 'Time') {
|
} else if (type === 'Time') {
|
||||||
@ -95,7 +95,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
function createEvent(
|
function createEvent(
|
||||||
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||||
const result: PerfLogEvent = {
|
var result: PerfLogEvent = {
|
||||||
'cat': 'timeline',
|
'cat': 'timeline',
|
||||||
'name': name,
|
'name': name,
|
||||||
'ts': time,
|
'ts': time,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
require('core-js');
|
require('core-js');
|
||||||
require('reflect-metadata');
|
require('reflect-metadata');
|
||||||
const testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
var testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||||
|
|
||||||
exports.config = {
|
exports.config = {
|
||||||
specs: ['spec.js', 'sample_benchmark.js'],
|
specs: ['spec.js', 'sample_benchmark.js'],
|
||||||
|
@ -10,10 +10,10 @@ import {convertPerfProfileToEvents} from '../../src/firefox_extension/lib/parser
|
|||||||
|
|
||||||
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||||
expect(actualEvents.length == expectedEvents.length);
|
expect(actualEvents.length == expectedEvents.length);
|
||||||
for (let i = 0; i < actualEvents.length; ++i) {
|
for (var i = 0; i < actualEvents.length; ++i) {
|
||||||
const actualEvent = actualEvents[i];
|
var actualEvent = actualEvents[i];
|
||||||
const expectedEvent = expectedEvents[i];
|
var expectedEvent = expectedEvents[i];
|
||||||
for (const key in actualEvent) {
|
for (var key in actualEvent) {
|
||||||
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,17 +22,17 @@ function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
|||||||
export function main() {
|
export function main() {
|
||||||
describe('convertPerfProfileToEvents', function() {
|
describe('convertPerfProfileToEvents', function() {
|
||||||
it('should convert single instantaneous event', function() {
|
it('should convert single instantaneous event', function() {
|
||||||
const profileData = {
|
var profileData = {
|
||||||
threads: [
|
threads: [
|
||||||
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert single non-instantaneous event', function() {
|
it('should convert single non-instantaneous event', function() {
|
||||||
const profileData = {
|
var profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -41,13 +41,13 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(
|
assertEventsEqual(
|
||||||
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert multiple instantaneous events', function() {
|
it('should convert multiple instantaneous events', function() {
|
||||||
const profileData = {
|
var profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -55,13 +55,13 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(
|
assertEventsEqual(
|
||||||
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert multiple mixed events', function() {
|
it('should convert multiple mixed events', function() {
|
||||||
const profileData = {
|
var profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -71,7 +71,7 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [
|
assertEventsEqual(perfEvents, [
|
||||||
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
||||||
{ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'}
|
{ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'}
|
||||||
@ -79,13 +79,13 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add args to gc events', function() {
|
it('should add args to gc events', function() {
|
||||||
const profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
var profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip unknown events', function() {
|
it('should skip unknown events', function() {
|
||||||
const profileData = {
|
var profileData = {
|
||||||
threads: [{
|
threads: [{
|
||||||
samples: [
|
samples: [
|
||||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||||
@ -93,7 +93,7 @@ export function main() {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,10 +6,8 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {$, browser} from 'protractor';
|
var benchpress = require('../../index.js');
|
||||||
|
var runner = new benchpress.Runner([
|
||||||
const benchpress = require('../../index.js');
|
|
||||||
const runner = new benchpress.Runner([
|
|
||||||
// use protractor as Webdriver client
|
// use protractor as Webdriver client
|
||||||
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||||
// use RegressionSlopeValidator to validate samples
|
// use RegressionSlopeValidator to validate samples
|
||||||
|
@ -6,12 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* tslint:disable:no-console */
|
var assertEventsContainsName = function(events: any[], eventName: string) {
|
||||||
import {browser} from 'protractor';
|
var found = false;
|
||||||
|
for (var i = 0; i < events.length; ++i) {
|
||||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
|
||||||
let found = false;
|
|
||||||
for (let i = 0; i < events.length; ++i) {
|
|
||||||
if (events[i].name == eventName) {
|
if (events[i].name == eventName) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@ -21,7 +18,7 @@ const assertEventsContainsName = function(events: any[], eventName: string) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('firefox extension', function() {
|
describe('firefox extension', function() {
|
||||||
const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
var TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||||
|
|
||||||
it('should measure performance', function() {
|
it('should measure performance', function() {
|
||||||
browser.sleep(3000); // wait for extension to load
|
browser.sleep(3000); // wait for extension to load
|
||||||
|
@ -11,12 +11,12 @@ import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createMetric(ids: any[]) {
|
function createMetric(ids: any[]) {
|
||||||
const m = ReflectiveInjector
|
var m = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||||
MultiMetric.provideWith(ids)
|
MultiMetric.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiMetric);
|
.get(MultiMetric);
|
||||||
return Promise.resolve(m);
|
return Promise.resolve(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ class MockMetric extends Metric {
|
|||||||
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
||||||
|
|
||||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||||
const result: {[key: string]: any} = {};
|
var result: {[key: string]: any} = {};
|
||||||
result[this._id] = {'restart': restart};
|
result[this._id] = {'restart': restart};
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe(): {[key: string]: string} {
|
describe(): {[key: string]: string} {
|
||||||
const result: {[key: string]: string} = {};
|
var result: {[key: string]: string} = {};
|
||||||
result[this._id] = 'describe';
|
result[this._id] = 'describe';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ import {isPresent} from '../../src/facade/lang';
|
|||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
let commandLog: any[];
|
var commandLog: any[];
|
||||||
const eventFactory = new TraceEventFactory('timeline', 'pid0');
|
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createMetric(
|
function createMetric(
|
||||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||||
@ -34,7 +34,7 @@ export function main() {
|
|||||||
if (!microMetrics) {
|
if (!microMetrics) {
|
||||||
microMetrics = {};
|
microMetrics = {};
|
||||||
}
|
}
|
||||||
const providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||||
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
||||||
provide: PerflogMetric.SET_TIMEOUT,
|
provide: PerflogMetric.SET_TIMEOUT,
|
||||||
@ -66,7 +66,7 @@ export function main() {
|
|||||||
describe('perflog metric', () => {
|
describe('perflog metric', () => {
|
||||||
|
|
||||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||||
const res: string[] = [];
|
var res: string[] = [];
|
||||||
res.push(...Object.keys(stringMap));
|
res.push(...Object.keys(stringMap));
|
||||||
res.sort();
|
res.sort();
|
||||||
return res;
|
return res;
|
||||||
@ -102,15 +102,15 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should describe itself based on micro metrics', () => {
|
it('should describe itself based on micro metrics', () => {
|
||||||
const description =
|
var description =
|
||||||
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
||||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should describe itself if frame capture is requested and available', () => {
|
it('should describe itself if frame capture is requested and available', () => {
|
||||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
var description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||||
captureFrames: true
|
captureFrames: true
|
||||||
}).describe();
|
}).describe();
|
||||||
expect(description['frameTime.mean']).not.toContain('WARNING');
|
expect(description['frameTime.mean']).not.toContain('WARNING');
|
||||||
expect(description['frameTime.best']).not.toContain('WARNING');
|
expect(description['frameTime.best']).not.toContain('WARNING');
|
||||||
expect(description['frameTime.worst']).not.toContain('WARNING');
|
expect(description['frameTime.worst']).not.toContain('WARNING');
|
||||||
@ -118,9 +118,9 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should describe itself if frame capture is requested and not available', () => {
|
it('should describe itself if frame capture is requested and not available', () => {
|
||||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
var description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||||
captureFrames: true
|
captureFrames: true
|
||||||
}).describe();
|
}).describe();
|
||||||
expect(description['frameTime.mean']).toContain('WARNING');
|
expect(description['frameTime.mean']).toContain('WARNING');
|
||||||
expect(description['frameTime.best']).toContain('WARNING');
|
expect(description['frameTime.best']).toContain('WARNING');
|
||||||
expect(description['frameTime.worst']).toContain('WARNING');
|
expect(description['frameTime.worst']).toContain('WARNING');
|
||||||
@ -131,7 +131,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should not force gc and mark the timeline',
|
it('should not force gc and mark the timeline',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const metric = createMetric([[]], null);
|
var metric = createMetric([[]], null);
|
||||||
metric.beginMeasure().then((_) => {
|
metric.beginMeasure().then((_) => {
|
||||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should force gc and mark the timeline',
|
it('should force gc and mark the timeline',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const metric = createMetric([[]], null, {forceGc: true});
|
var metric = createMetric([[]], null, {forceGc: true});
|
||||||
metric.beginMeasure().then((_) => {
|
metric.beginMeasure().then((_) => {
|
||||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||||
|
|
||||||
@ -155,11 +155,11 @@ export function main() {
|
|||||||
|
|
||||||
it('should mark and aggregate events in between the marks',
|
it('should mark and aggregate events in between the marks',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const events = [[
|
var events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10)
|
eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10)
|
||||||
]];
|
]];
|
||||||
const metric = createMetric(events, null);
|
var metric = createMetric(events, null);
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
||||||
@ -172,13 +172,13 @@ export function main() {
|
|||||||
|
|
||||||
it('should mark and aggregate events since navigationStart',
|
it('should mark and aggregate events since navigationStart',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const events = [[
|
var events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||||
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||||
eventFactory.markEnd('benchpress0', 10)
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
]];
|
]];
|
||||||
const metric = createMetric(events, null);
|
var metric = createMetric(events, null);
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(data['scriptTime']).toBe(1);
|
expect(data['scriptTime']).toBe(1);
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const events = [
|
var events = [
|
||||||
[
|
[
|
||||||
eventFactory.markStart('benchpress0', 0),
|
eventFactory.markStart('benchpress0', 0),
|
||||||
eventFactory.markEnd('benchpress0', 1),
|
eventFactory.markEnd('benchpress0', 1),
|
||||||
@ -195,7 +195,7 @@ export function main() {
|
|||||||
],
|
],
|
||||||
[eventFactory.markEnd('benchpress1', 3)]
|
[eventFactory.markEnd('benchpress1', 3)]
|
||||||
];
|
];
|
||||||
const metric = createMetric(events, null);
|
var metric = createMetric(events, null);
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
.then((_) => metric.endMeasure(true))
|
.then((_) => metric.endMeasure(true))
|
||||||
.then((_) => metric.endMeasure(true))
|
.then((_) => metric.endMeasure(true))
|
||||||
@ -211,7 +211,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should loop and aggregate until the end mark is present',
|
it('should loop and aggregate until the end mark is present',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const events = [
|
var events = [
|
||||||
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
||||||
[eventFactory.end('script', 2)],
|
[eventFactory.end('script', 2)],
|
||||||
[
|
[
|
||||||
@ -219,7 +219,7 @@ export function main() {
|
|||||||
eventFactory.markEnd('benchpress0', 10)
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
const metric = createMetric(events, null);
|
var metric = createMetric(events, null);
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog',
|
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog',
|
||||||
@ -233,7 +233,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should store events after the end mark for the next call',
|
it('should store events after the end mark for the next call',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const events = [
|
var events = [
|
||||||
[
|
[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
||||||
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
||||||
@ -244,7 +244,7 @@ export function main() {
|
|||||||
eventFactory.markEnd('benchpress1', 6)
|
eventFactory.markEnd('benchpress1', 6)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
const metric = createMetric(events, null);
|
var metric = createMetric(events, null);
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
.then((_) => metric.endMeasure(true))
|
.then((_) => metric.endMeasure(true))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
@ -263,7 +263,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe('with forced gc', () => {
|
describe('with forced gc', () => {
|
||||||
let events: PerfLogEvent[][];
|
var events: PerfLogEvent[][];
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
events = [[
|
events = [[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
@ -276,7 +276,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const metric = createMetric(events, null, {forceGc: true});
|
var metric = createMetric(events, null, {forceGc: true});
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
expect(commandLog).toEqual([
|
expect(commandLog).toEqual([
|
||||||
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
||||||
@ -291,7 +291,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should restart after the forced gc if needed',
|
it('should restart after the forced gc if needed',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const metric = createMetric(events, null, {forceGc: true});
|
var metric = createMetric(events, null, {forceGc: true});
|
||||||
metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => {
|
metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => {
|
||||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ export function main() {
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
events.unshift(eventFactory.markStart('benchpress0', 0));
|
events.unshift(eventFactory.markStart('benchpress0', 0));
|
||||||
events.push(eventFactory.markEnd('benchpress0', 10));
|
events.push(eventFactory.markEnd('benchpress0', 10));
|
||||||
const metric = createMetric([events], null, {
|
var metric = createMetric([events], null, {
|
||||||
microMetrics: microMetrics,
|
microMetrics: microMetrics,
|
||||||
captureFrames: captureFrames,
|
captureFrames: captureFrames,
|
||||||
receivedData: receivedData,
|
receivedData: receivedData,
|
||||||
@ -502,8 +502,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should ignore events from different processed as the start mark',
|
it('should ignore events from different processed as the start mark',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
var otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||||
const metric = createMetric(
|
var metric = createMetric(
|
||||||
[[
|
[[
|
||||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
||||||
eventFactory.end('script', 5, null),
|
eventFactory.end('script', 5, null),
|
||||||
@ -685,7 +685,7 @@ class MockDriverExtension extends WebDriverExtension {
|
|||||||
readPerfLog(): Promise<any> {
|
readPerfLog(): Promise<any> {
|
||||||
this._commandLog.push('readPerfLog');
|
this._commandLog.push('readPerfLog');
|
||||||
if (this._perfLogs.length > 0) {
|
if (this._perfLogs.length > 0) {
|
||||||
const next = this._perfLogs[0];
|
var next = this._perfLogs[0];
|
||||||
this._perfLogs.shift();
|
this._perfLogs.shift();
|
||||||
return Promise.resolve(next);
|
return Promise.resolve(next);
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,7 +12,7 @@ import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/te
|
|||||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
let wdAdapter: MockDriverAdapter;
|
var wdAdapter: MockDriverAdapter;
|
||||||
|
|
||||||
function createMetric(
|
function createMetric(
|
||||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
userMetrics = {};
|
userMetrics = {};
|
||||||
}
|
}
|
||||||
wdAdapter = new MockDriverAdapter();
|
wdAdapter = new MockDriverAdapter();
|
||||||
const providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
||||||
{provide: Options.USER_METRICS, useValue: userMetrics},
|
{provide: Options.USER_METRICS, useValue: userMetrics},
|
||||||
{provide: WebDriverAdapter, useValue: wdAdapter}
|
{provide: WebDriverAdapter, useValue: wdAdapter}
|
||||||
@ -45,7 +45,7 @@ export function main() {
|
|||||||
describe('endMeasure', () => {
|
describe('endMeasure', () => {
|
||||||
it('should stop measuring when all properties have numeric values',
|
it('should stop measuring when all properties have numeric values',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const metric = createMetric(
|
let metric = createMetric(
|
||||||
[[]], new PerfLogFeatures(),
|
[[]], new PerfLogFeatures(),
|
||||||
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
||||||
metric.beginMeasure()
|
metric.beginMeasure()
|
||||||
@ -71,7 +71,7 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
executeScript(script: string): any {
|
executeScript(script: string): any {
|
||||||
// Just handles `return window.propName` ignores `delete window.propName`.
|
// Just handles `return window.propName` ignores `delete window.propName`.
|
||||||
if (script.indexOf('return window.') == 0) {
|
if (script.indexOf('return window.') == 0) {
|
||||||
const metricName = script.substring('return window.'.length);
|
let metricName = script.substring('return window.'.length);
|
||||||
return Promise.resolve(this.data[metricName]);
|
return Promise.resolve(this.data[metricName]);
|
||||||
} else if (script.indexOf('delete window.') == 0) {
|
} else if (script.indexOf('delete window.') == 0) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
|
@ -14,8 +14,8 @@ import {isBlank, isPresent} from '../../src/facade/lang';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('console reporter', () => {
|
describe('console reporter', () => {
|
||||||
let reporter: ConsoleReporter;
|
var reporter: ConsoleReporter;
|
||||||
let log: string[];
|
var log: string[];
|
||||||
|
|
||||||
function createReporter(
|
function createReporter(
|
||||||
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
||||||
@ -28,10 +28,10 @@ export function main() {
|
|||||||
if (!descriptions) {
|
if (!descriptions) {
|
||||||
descriptions = [];
|
descriptions = [];
|
||||||
}
|
}
|
||||||
if (sampleId == null) {
|
if (isBlank(sampleId)) {
|
||||||
sampleId = 'null';
|
sampleId = 'null';
|
||||||
}
|
}
|
||||||
const providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
ConsoleReporter.PROVIDERS, {
|
ConsoleReporter.PROVIDERS, {
|
||||||
provide: SampleDescription,
|
provide: SampleDescription,
|
||||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||||
|
@ -13,7 +13,7 @@ import {isPresent} from '../../src/facade/lang';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('file reporter', () => {
|
describe('file reporter', () => {
|
||||||
let loggedFile: any;
|
var loggedFile: any;
|
||||||
|
|
||||||
function createReporter({sampleId, descriptions, metrics, path}: {
|
function createReporter({sampleId, descriptions, metrics, path}: {
|
||||||
sampleId: string,
|
sampleId: string,
|
||||||
@ -21,7 +21,7 @@ export function main() {
|
|||||||
metrics: {[key: string]: string},
|
metrics: {[key: string]: string},
|
||||||
path: string
|
path: string
|
||||||
}) {
|
}) {
|
||||||
const providers = [
|
var providers = [
|
||||||
JsonFileReporter.PROVIDERS, {
|
JsonFileReporter.PROVIDERS, {
|
||||||
provide: SampleDescription,
|
provide: SampleDescription,
|
||||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||||
@ -49,9 +49,9 @@ export function main() {
|
|||||||
.reportSample(
|
.reportSample(
|
||||||
[mv(0, 0, {'a': 3, 'b': 6})],
|
[mv(0, 0, {'a': 3, 'b': 6})],
|
||||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||||
const regExp = /somePath\/someId_\d+\.json/;
|
var regExp = /somePath\/someId_\d+\.json/;
|
||||||
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
||||||
const parsedContent = JSON.parse(loggedFile['content']);
|
var parsedContent = JSON.parse(loggedFile['content']);
|
||||||
expect(parsedContent).toEqual({
|
expect(parsedContent).toEqual({
|
||||||
'description': {
|
'description': {
|
||||||
'id': 'someId',
|
'id': 'someId',
|
||||||
|
@ -12,12 +12,12 @@ import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createReporters(ids: any[]) {
|
function createReporters(ids: any[]) {
|
||||||
const r = ReflectiveInjector
|
var r = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||||
MultiReporter.provideWith(ids)
|
MultiReporter.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiReporter);
|
.get(MultiReporter);
|
||||||
return Promise.resolve(r);
|
return Promise.resolve(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should reportMeasureValues to all',
|
it('should reportMeasureValues to all',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const mv = new MeasureValues(0, new Date(), {});
|
var mv = new MeasureValues(0, new Date(), {});
|
||||||
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||||
|
|
||||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||||
@ -34,9 +34,9 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const completeSample =
|
var completeSample =
|
||||||
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||||
const validSample = [completeSample[1]];
|
var validSample = [completeSample[1]];
|
||||||
|
|
||||||
createReporters(['m1', 'm2'])
|
createReporters(['m1', 'm2'])
|
||||||
.then((r) => r.reportSample(completeSample, validSample))
|
.then((r) => r.reportSample(completeSample, validSample))
|
||||||
|
@ -12,8 +12,8 @@ import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('runner', () => {
|
describe('runner', () => {
|
||||||
let injector: ReflectiveInjector;
|
var injector: ReflectiveInjector;
|
||||||
let runner: Runner;
|
var runner: Runner;
|
||||||
|
|
||||||
function createRunner(defaultProviders: any[] = null): Runner {
|
function createRunner(defaultProviders: any[] = null): Runner {
|
||||||
if (!defaultProviders) {
|
if (!defaultProviders) {
|
||||||
@ -76,7 +76,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should provide Options.EXECUTE',
|
it('should provide Options.EXECUTE',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const execute = () => {};
|
var execute = () => {};
|
||||||
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
||||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||||
async.done();
|
async.done();
|
||||||
@ -85,7 +85,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should provide Options.PREPARE',
|
it('should provide Options.PREPARE',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const prepare = () => {};
|
var prepare = () => {};
|
||||||
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
||||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -12,10 +12,10 @@ import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, V
|
|||||||
import {isBlank, isPresent} from '../src/facade/lang';
|
import {isBlank, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
const EMPTY_EXECUTE = () => {};
|
var EMPTY_EXECUTE = () => {};
|
||||||
|
|
||||||
describe('sampler', () => {
|
describe('sampler', () => {
|
||||||
let sampler: Sampler;
|
var sampler: Sampler;
|
||||||
|
|
||||||
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
||||||
driver?: any,
|
driver?: any,
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
prepare?: any,
|
prepare?: any,
|
||||||
execute?: any
|
execute?: any
|
||||||
} = {}) {
|
} = {}) {
|
||||||
let time = 1000;
|
var time = 1000;
|
||||||
if (!metric) {
|
if (!metric) {
|
||||||
metric = new MockMetric([]);
|
metric = new MockMetric([]);
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ export function main() {
|
|||||||
if (isBlank(driver)) {
|
if (isBlank(driver)) {
|
||||||
driver = new MockDriverAdapter([]);
|
driver = new MockDriverAdapter([]);
|
||||||
}
|
}
|
||||||
const providers = [
|
var providers = [
|
||||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||||
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
||||||
@ -50,10 +50,10 @@ export function main() {
|
|||||||
|
|
||||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const log: any[] = [];
|
var log: any[] = [];
|
||||||
let count = 0;
|
var count = 0;
|
||||||
const driver = new MockDriverAdapter([], (callback: Function) => {
|
var driver = new MockDriverAdapter([], (callback: Function) => {
|
||||||
const result = callback();
|
var result = callback();
|
||||||
log.push(result);
|
log.push(result);
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
});
|
});
|
||||||
@ -73,8 +73,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
let workCount = 0;
|
var workCount = 0;
|
||||||
const log: any[] = [];
|
var log: any[] = [];
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(log),
|
metric: createCountingMetric(log),
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
@ -98,8 +98,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const log: any[] = [];
|
var log: any[] = [];
|
||||||
let workCount = 0;
|
var workCount = 0;
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(log),
|
metric: createCountingMetric(log),
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
@ -120,14 +120,14 @@ export function main() {
|
|||||||
|
|
||||||
it('should only collect metrics for execute and ignore metrics from prepare',
|
it('should only collect metrics for execute and ignore metrics from prepare',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
let scriptTime = 0;
|
var scriptTime = 0;
|
||||||
let iterationCount = 1;
|
var iterationCount = 1;
|
||||||
createSampler({
|
createSampler({
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
metric: new MockMetric(
|
metric: new MockMetric(
|
||||||
[],
|
[],
|
||||||
() => {
|
() => {
|
||||||
const result = Promise.resolve({'script': scriptTime});
|
var result = Promise.resolve({'script': scriptTime});
|
||||||
scriptTime = 0;
|
scriptTime = 0;
|
||||||
return result;
|
return result;
|
||||||
}),
|
}),
|
||||||
@ -147,8 +147,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should call the validator for every execution and store the valid sample',
|
it('should call the validator for every execution and store the valid sample',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const log: any[] = [];
|
var log: any[] = [];
|
||||||
const validSample = [mv(null, null, {})];
|
var validSample = [mv(null, null, {})];
|
||||||
|
|
||||||
createSampler({
|
createSampler({
|
||||||
metric: createCountingMetric(),
|
metric: createCountingMetric(),
|
||||||
@ -174,8 +174,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should report the metric values',
|
it('should report the metric values',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const log: any[] = [];
|
var log: any[] = [];
|
||||||
const validSample = [mv(null, null, {})];
|
var validSample = [mv(null, null, {})];
|
||||||
createSampler({
|
createSampler({
|
||||||
validator: createCountingValidator(2, validSample),
|
validator: createCountingValidator(2, validSample),
|
||||||
metric: createCountingMetric(),
|
metric: createCountingMetric(),
|
||||||
@ -220,7 +220,7 @@ function createCountingValidator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createCountingMetric(log: any[] = []) {
|
function createCountingMetric(log: any[] = []) {
|
||||||
let scriptTime = 0;
|
var scriptTime = 0;
|
||||||
return new MockMetric(log, () => ({'script': scriptTime++}));
|
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,8 +239,7 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
class MockValidator extends Validator {
|
class MockValidator extends Validator {
|
||||||
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
||||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||||
const stableSample =
|
var stableSample = isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||||
isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
|
||||||
this._log.push(['validate', completeSample, stableSample]);
|
this._log.push(['validate', completeSample, stableSample]);
|
||||||
return stableSample;
|
return stableSample;
|
||||||
}
|
}
|
||||||
@ -253,7 +252,7 @@ class MockMetric extends Metric {
|
|||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
endMeasure(restart: boolean) {
|
endMeasure(restart: boolean) {
|
||||||
const measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||||
this._log.push(['endMeasure', restart, measureValues]);
|
this._log.push(['endMeasure', restart, measureValues]);
|
||||||
return Promise.resolve(measureValues);
|
return Promise.resolve(measureValues);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export class TraceEventFactory {
|
|||||||
constructor(private _cat: string, private _pid: string) {}
|
constructor(private _cat: string, private _pid: string) {}
|
||||||
|
|
||||||
create(ph: any, name: string, time: number, args: any = null) {
|
create(ph: any, name: string, time: number, args: any = null) {
|
||||||
const res:
|
var res:
|
||||||
PerfLogEvent = {'name': name, 'cat': this._cat, 'ph': ph, 'ts': time, 'pid': this._pid};
|
PerfLogEvent = {'name': name, 'cat': this._cat, 'ph': ph, 'ts': time, 'pid': this._pid};
|
||||||
if (isPresent(args)) {
|
if (isPresent(args)) {
|
||||||
res['args'] = args;
|
res['args'] = args;
|
||||||
@ -34,7 +34,7 @@ export class TraceEventFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
complete(name: string, time: number, duration: number, args: any = null) {
|
complete(name: string, time: number, duration: number, args: any = null) {
|
||||||
const res = this.create('X', name, time, args);
|
var res = this.create('X', name, time, args);
|
||||||
res['dur'] = duration;
|
res['dur'] = duration;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,11 @@
|
|||||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
||||||
|
import {ListWrapper} from '../../src/facade/collection';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('regression slope validator', () => {
|
describe('regression slope validator', () => {
|
||||||
let validator: RegressionSlopeValidator;
|
var validator: RegressionSlopeValidator;
|
||||||
|
|
||||||
function createValidator({size, metric}: {size: number, metric: string}) {
|
function createValidator({size, metric}: {size: number, metric: string}) {
|
||||||
validator = ReflectiveInjector
|
validator = ReflectiveInjector
|
||||||
@ -42,16 +43,18 @@ export function main() {
|
|||||||
|
|
||||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
createValidator({size: 2, metric: 'script'});
|
||||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||||
|
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||||
createValidator({size: 2, metric: 'script'});
|
createValidator({size: 2, metric: 'script'});
|
||||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||||
|
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -9,10 +9,11 @@
|
|||||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||||
|
import {ListWrapper} from '../../src/facade/collection';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('size validator', () => {
|
describe('size validator', () => {
|
||||||
let validator: SizeValidator;
|
var validator: SizeValidator;
|
||||||
|
|
||||||
function createValidator(size: number) {
|
function createValidator(size: number) {
|
||||||
validator =
|
validator =
|
||||||
@ -35,9 +36,10 @@ export function main() {
|
|||||||
|
|
||||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||||
createValidator(2);
|
createValidator(2);
|
||||||
const sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||||
|
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -14,23 +14,23 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('chrome driver extension', () => {
|
describe('chrome driver extension', () => {
|
||||||
const CHROME45_USER_AGENT =
|
var CHROME45_USER_AGENT =
|
||||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
||||||
|
|
||||||
let log: any[];
|
var log: any[];
|
||||||
let extension: ChromeDriverExtension;
|
var extension: ChromeDriverExtension;
|
||||||
|
|
||||||
const blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
var blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||||
const v8Events = new TraceEventFactory('v8', 'pid0');
|
var v8Events = new TraceEventFactory('v8', 'pid0');
|
||||||
const v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||||
const chromeTimelineEvents =
|
var chromeTimelineEvents =
|
||||||
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||||
const chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
var chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||||
const chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
var chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||||
const chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
var chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||||
const chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
var chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||||
const benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
var benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createExtension(
|
function createExtension(
|
||||||
perfRecords: any[] = null, userAgent: string = null,
|
perfRecords: any[] = null, userAgent: string = null,
|
||||||
@ -101,7 +101,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
it('should normalize "tdur" to "dur"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||||
event['tdur'] = 5500;
|
event['tdur'] = 5500;
|
||||||
createExtension([event]).readPerfLog().then((events) => {
|
createExtension([event]).readPerfLog().then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
|
@ -13,10 +13,10 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ios driver extension', () => {
|
describe('ios driver extension', () => {
|
||||||
let log: any[];
|
var log: any[];
|
||||||
let extension: IOsDriverExtension;
|
var extension: IOsDriverExtension;
|
||||||
|
|
||||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||||
if (!perfRecords) {
|
if (!perfRecords) {
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent, stringify} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
@ -29,11 +31,11 @@ import {isPresent, stringify} from '../facade/lang';
|
|||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* The CSS classes are updated as follows, depending on the type of the expression evaluation:
|
* The CSS classes are updated as follow depending on the type of the expression evaluation:
|
||||||
* - `string` - the CSS classes listed in the string (space delimited) are added,
|
* - `string` - the CSS classes listed in a string (space delimited) are added,
|
||||||
* - `Array` - the CSS classes declared as Array elements are added,
|
* - `Array` - the CSS classes (Array elements) are added,
|
||||||
* - `Object` - keys are CSS classes that get added when the expression given in the value
|
* - `Object` - keys are CSS class names that get added when the expression given in the value
|
||||||
* evaluates to a truthy value, otherwise they are removed.
|
* evaluates to a truthy value, otherwise class are removed.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -48,6 +50,7 @@ export class NgClass implements DoCheck {
|
|||||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
|
|
||||||
|
|
||||||
@Input('class')
|
@Input('class')
|
||||||
set klass(v: string) {
|
set klass(v: string) {
|
||||||
this._applyInitialClasses(true);
|
this._applyInitialClasses(true);
|
||||||
@ -108,14 +111,8 @@ export class NgClass implements DoCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _applyIterableChanges(changes: any): void {
|
private _applyIterableChanges(changes: any): void {
|
||||||
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
changes.forEachAddedItem(
|
||||||
if (typeof record.item === 'string') {
|
(record: CollectionChangeRecord) => this._toggleClass(record.item, true));
|
||||||
this._toggleClass(record.item, true);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
changes.forEachRemovedItem(
|
changes.forEachRemovedItem(
|
||||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
||||||
|
@ -150,13 +150,13 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||||
viewRef.context.index = i;
|
viewRef.context.index = i;
|
||||||
viewRef.context.count = ilen;
|
viewRef.context.count = ilen;
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.forEachIdentityChange((record: any) => {
|
changes.forEachIdentityChange((record: any) => {
|
||||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||||
viewRef.context.$implicit = record.item;
|
viewRef.context.$implicit = record.item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
|||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* The styles are updated according to the value of the expression evaluation:
|
* The styles are updated according to the value of the expression evaluation:
|
||||||
* - keys are style names with an optional `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
* - keys are style names with an option `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||||
* - values are the values assigned to those properties (expressed in the given unit).
|
* - values are the values assigned to those properties (expressed in the given unit).
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
|
@ -6,31 +6,19 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
|
import {ListWrapper} from '../facade/collection';
|
||||||
|
|
||||||
|
const _CASE_DEFAULT = {};
|
||||||
|
|
||||||
export class SwitchView {
|
export class SwitchView {
|
||||||
private _created = false;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
||||||
|
|
||||||
create(): void {
|
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||||
this._created = true;
|
|
||||||
this._viewContainerRef.createEmbeddedView(this._templateRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void { this._viewContainerRef.clear(); }
|
||||||
this._created = false;
|
|
||||||
this._viewContainerRef.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
enforceState(created: boolean) {
|
|
||||||
if (created && !this._created) {
|
|
||||||
this.create();
|
|
||||||
} else if (!created && this._created) {
|
|
||||||
this.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,52 +64,92 @@ export class SwitchView {
|
|||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngSwitch]'})
|
@Directive({selector: '[ngSwitch]'})
|
||||||
export class NgSwitch {
|
export class NgSwitch {
|
||||||
private _defaultViews: SwitchView[];
|
private _switchValue: any;
|
||||||
private _defaultUsed = false;
|
private _useDefault: boolean = false;
|
||||||
private _caseCount = 0;
|
private _valueViews = new Map<any, SwitchView[]>();
|
||||||
private _lastCaseCheckIndex = 0;
|
private _activeViews: SwitchView[] = [];
|
||||||
private _lastCasesMatched = false;
|
|
||||||
private _ngSwitch: any;
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngSwitch(newValue: any) {
|
set ngSwitch(value: any) {
|
||||||
this._ngSwitch = newValue;
|
// Set of views to display for this value
|
||||||
if (this._caseCount === 0) {
|
let views = this._valueViews.get(value);
|
||||||
this._updateDefaultCases(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
if (views) {
|
||||||
_addCase(): number { return this._caseCount++; }
|
this._useDefault = false;
|
||||||
|
} else {
|
||||||
/** @internal */
|
// No view to display for the current value -> default case
|
||||||
_addDefault(view: SwitchView) {
|
// Nothing to do if the default case was already active
|
||||||
if (!this._defaultViews) {
|
if (this._useDefault) {
|
||||||
this._defaultViews = [];
|
return;
|
||||||
}
|
|
||||||
this._defaultViews.push(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_matchCase(value: any): boolean {
|
|
||||||
const matched = value == this._ngSwitch;
|
|
||||||
this._lastCasesMatched = this._lastCasesMatched || matched;
|
|
||||||
this._lastCaseCheckIndex++;
|
|
||||||
if (this._lastCaseCheckIndex === this._caseCount) {
|
|
||||||
this._updateDefaultCases(!this._lastCasesMatched);
|
|
||||||
this._lastCaseCheckIndex = 0;
|
|
||||||
this._lastCasesMatched = false;
|
|
||||||
}
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateDefaultCases(useDefault: boolean) {
|
|
||||||
if (this._defaultViews && useDefault !== this._defaultUsed) {
|
|
||||||
this._defaultUsed = useDefault;
|
|
||||||
for (let i = 0; i < this._defaultViews.length; i++) {
|
|
||||||
const defaultView = this._defaultViews[i];
|
|
||||||
defaultView.enforceState(useDefault);
|
|
||||||
}
|
}
|
||||||
|
this._useDefault = true;
|
||||||
|
views = this._valueViews.get(_CASE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._emptyAllActiveViews();
|
||||||
|
this._activateViews(views);
|
||||||
|
this._switchValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
|
||||||
|
this._deregisterView(oldCase, view);
|
||||||
|
this._registerView(newCase, view);
|
||||||
|
|
||||||
|
if (oldCase === this._switchValue) {
|
||||||
|
view.destroy();
|
||||||
|
ListWrapper.remove(this._activeViews, view);
|
||||||
|
} else if (newCase === this._switchValue) {
|
||||||
|
if (this._useDefault) {
|
||||||
|
this._useDefault = false;
|
||||||
|
this._emptyAllActiveViews();
|
||||||
|
}
|
||||||
|
view.create();
|
||||||
|
this._activeViews.push(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to default when there is no more active ViewContainers
|
||||||
|
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||||
|
this._useDefault = true;
|
||||||
|
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _emptyAllActiveViews(): void {
|
||||||
|
const activeContainers = this._activeViews;
|
||||||
|
for (var i = 0; i < activeContainers.length; i++) {
|
||||||
|
activeContainers[i].destroy();
|
||||||
|
}
|
||||||
|
this._activeViews = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _activateViews(views?: SwitchView[]): void {
|
||||||
|
if (views) {
|
||||||
|
for (var i = 0; i < views.length; i++) {
|
||||||
|
views[i].create();
|
||||||
|
}
|
||||||
|
this._activeViews = views;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
_registerView(value: any, view: SwitchView): void {
|
||||||
|
let views = this._valueViews.get(value);
|
||||||
|
if (!views) {
|
||||||
|
views = [];
|
||||||
|
this._valueViews.set(value, views);
|
||||||
|
}
|
||||||
|
views.push(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deregisterView(value: any, view: SwitchView): void {
|
||||||
|
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||||
|
if (value === _CASE_DEFAULT) return;
|
||||||
|
const views = this._valueViews.get(value);
|
||||||
|
if (views.length == 1) {
|
||||||
|
this._valueViews.delete(value);
|
||||||
|
} else {
|
||||||
|
ListWrapper.remove(views, view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,20 +179,24 @@ export class NgSwitch {
|
|||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngSwitchCase]'})
|
@Directive({selector: '[ngSwitchCase]'})
|
||||||
export class NgSwitchCase implements DoCheck {
|
export class NgSwitchCase {
|
||||||
|
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||||
|
private _value: any = _CASE_DEFAULT;
|
||||||
private _view: SwitchView;
|
private _view: SwitchView;
|
||||||
|
private _switch: NgSwitch;
|
||||||
@Input()
|
|
||||||
ngSwitchCase: any;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||||
@Host() private ngSwitch: NgSwitch) {
|
@Host() ngSwitch: NgSwitch) {
|
||||||
ngSwitch._addCase();
|
this._switch = ngSwitch;
|
||||||
this._view = new SwitchView(viewContainer, templateRef);
|
this._view = new SwitchView(viewContainer, templateRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); }
|
@Input()
|
||||||
|
set ngSwitchCase(value: any) {
|
||||||
|
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||||
|
this._value = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,7 +226,7 @@ export class NgSwitchCase implements DoCheck {
|
|||||||
export class NgSwitchDefault {
|
export class NgSwitchDefault {
|
||||||
constructor(
|
constructor(
|
||||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||||
@Host() ngSwitch: NgSwitch) {
|
@Host() sswitch: NgSwitch) {
|
||||||
ngSwitch._addDefault(new SwitchView(viewContainer, templateRef));
|
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,19 +64,19 @@ export class HashLocationStrategy extends LocationStrategy {
|
|||||||
path(includeHash: boolean = false): string {
|
path(includeHash: boolean = false): string {
|
||||||
// the hash value is always prefixed with a `#`
|
// the hash value is always prefixed with a `#`
|
||||||
// and if it is empty then it will stay empty
|
// and if it is empty then it will stay empty
|
||||||
let path = this._platformLocation.hash;
|
var path = this._platformLocation.hash;
|
||||||
if (!isPresent(path)) path = '#';
|
if (!isPresent(path)) path = '#';
|
||||||
|
|
||||||
return path.length > 0 ? path.substring(1) : path;
|
return path.length > 0 ? path.substring(1) : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareExternalUrl(internal: string): string {
|
prepareExternalUrl(internal: string): string {
|
||||||
const url = Location.joinWithSlash(this._baseHref, internal);
|
var url = Location.joinWithSlash(this._baseHref, internal);
|
||||||
return url.length > 0 ? ('#' + url) : url;
|
return url.length > 0 ? ('#' + url) : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||||
if (url.length == 0) {
|
if (url.length == 0) {
|
||||||
url = this._platformLocation.pathname;
|
url = this._platformLocation.pathname;
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
replaceState(state: any, title: string, path: string, queryParams: string) {
|
||||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||||
if (url.length == 0) {
|
if (url.length == 0) {
|
||||||
url = this._platformLocation.pathname;
|
url = this._platformLocation.pathname;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ export class Location {
|
|||||||
if (end.length == 0) {
|
if (end.length == 0) {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
let slashes = 0;
|
var slashes = 0;
|
||||||
if (start.endsWith('/')) {
|
if (start.endsWith('/')) {
|
||||||
slashes++;
|
slashes++;
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,12 @@ export class PathLocationStrategy extends LocationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||||
this._platformLocation.pushState(state, title, externalUrl);
|
this._platformLocation.pushState(state, title, externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceState(state: any, title: string, url: string, queryParams: string) {
|
replaceState(state: any, title: string, url: string, queryParams: string) {
|
||||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||||
this._platformLocation.replaceState(state, title, externalUrl);
|
this._platformLocation.replaceState(state, title, externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
|
|||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||||
*
|
*
|
||||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||||
* to the view. The Observable continuously updates the view with the current time.
|
* to the view. The Observable continuesly updates the view with the current time.
|
||||||
*
|
*
|
||||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {DateFormatter} from '../facade/intl';
|
import {DateFormatter} from '../facade/intl';
|
||||||
import {NumberWrapper, isDate} from '../facade/lang';
|
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
@ -33,30 +33,27 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* | Component | Symbol | Narrow | Short Form | Long Form | Numeric | 2-digit |
|
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||||
* |-----------|:------:|--------|--------------|-------------------|-----------|-----------|
|
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||||
* | era | G | G (A) | GGG (AD) | GGGG (Anno Domini)| - | - |
|
* | era | G | G (AD) | GGGG (Anno Domini)| - | - |
|
||||||
* | year | y | - | - | - | y (2015) | yy (15) |
|
* | year | y | - | - | y (2015) | yy (15) |
|
||||||
* | month | M | L (S) | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
* | month | M | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||||
* | day | d | - | - | - | d (3) | dd (03) |
|
* | day | d | - | - | d (3) | dd (03) |
|
||||||
* | weekday | E | E (S) | EEE (Sun) | EEEE (Sunday) | - | - |
|
* | weekday | E | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||||
* | hour | j | - | - | - | j (13) | jj (13) |
|
* | hour | j | - | - | j (13) | jj (13) |
|
||||||
* | hour12 | h | - | - | - | h (1 PM) | hh (01 PM)|
|
* | hour12 | h | - | - | h (1 PM) | hh (01 PM)|
|
||||||
* | hour24 | H | - | - | - | H (13) | HH (13) |
|
* | hour24 | H | - | - | H (13) | HH (13) |
|
||||||
* | minute | m | - | - | - | m (5) | mm (05) |
|
* | minute | m | - | - | m (5) | mm (05) |
|
||||||
* | second | s | - | - | - | s (9) | ss (09) |
|
* | second | s | - | - | s (9) | ss (09) |
|
||||||
* | timezone | z | - | - | z (Pacific Standard Time)| - | - |
|
* | timezone | z | - | z (Pacific Standard Time)| - | - |
|
||||||
* | timezone | Z | - | Z (GMT-8:00) | - | - | - |
|
* | timezone | Z | Z (GMT-8:00) | - | - | - |
|
||||||
* | timezone | a | - | a (PM) | - | - | - |
|
* | timezone | a | a (PM) | - | - | - |
|
||||||
*
|
*
|
||||||
* In javascript, only the components specified will be respected (not the ordering,
|
* In javascript, only the components specified will be respected (not the ordering,
|
||||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||||
*
|
*
|
||||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||||
*
|
*
|
||||||
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
|
|
||||||
* applied and the formatted text will have the same day, month and year of the expression.
|
|
||||||
*
|
|
||||||
* WARNINGS:
|
* WARNINGS:
|
||||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||||
* Instead users should treat the date as an immutable object and change the reference when the
|
* Instead users should treat the date as an immutable object and change the reference when the
|
||||||
@ -98,42 +95,22 @@ export class DatePipe implements PipeTransform {
|
|||||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||||
|
|
||||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||||
let date: Date;
|
|
||||||
|
|
||||||
if (isBlank(value)) return null;
|
if (isBlank(value)) return null;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
if (!this.supports(value)) {
|
||||||
value = value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDate(value)) {
|
|
||||||
date = value;
|
|
||||||
} else if (NumberWrapper.isNumeric(value)) {
|
|
||||||
date = new Date(parseFloat(value));
|
|
||||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
|
||||||
/**
|
|
||||||
* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
|
||||||
* before Date creation to avoid time offset and errors in the new Date.
|
|
||||||
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
|
||||||
* date, some browsers (e.g. IE 9) will throw an invalid Date error
|
|
||||||
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
|
||||||
* is applied
|
|
||||||
* Note: ISO months are 0 for January, 1 for February, ...
|
|
||||||
*/
|
|
||||||
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
|
|
||||||
date = new Date(y, m - 1, d);
|
|
||||||
} else {
|
|
||||||
date = new Date(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDate(date)) {
|
|
||||||
throw new InvalidPipeArgumentError(DatePipe, value);
|
throw new InvalidPipeArgumentError(DatePipe, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
if (NumberWrapper.isNumeric(value)) {
|
||||||
|
value = parseFloat(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateFormatter.format(
|
||||||
|
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private supports(obj: any): boolean {
|
||||||
|
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||||
|
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBlank(obj: any): boolean {
|
|
||||||
return obj == null || obj === '';
|
|
||||||
}
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,10 +16,9 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | i18nSelect:mapping`
|
* @howToUse `expression | i18nSelect:mapping`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Where `mapping` is an object that indicates the text that should be displayed
|
* Where:
|
||||||
|
* - `mapping`: is an object that indicates the text that should be displayed
|
||||||
* for different values of the provided `expression`.
|
* for different values of the provided `expression`.
|
||||||
* If none of the keys of the mapping match the value of the `expression`, then the content
|
|
||||||
* of the `other` key is returned when present, otherwise an empty string is returned.
|
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
@ -29,20 +29,12 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
@Pipe({name: 'i18nSelect', pure: true})
|
@Pipe({name: 'i18nSelect', pure: true})
|
||||||
export class I18nSelectPipe implements PipeTransform {
|
export class I18nSelectPipe implements PipeTransform {
|
||||||
transform(value: string, mapping: {[key: string]: string}): string {
|
transform(value: string, mapping: {[key: string]: string}): string {
|
||||||
if (value == null) return '';
|
if (isBlank(value)) return '';
|
||||||
|
|
||||||
if (typeof mapping !== 'object' || typeof value !== 'string') {
|
if (typeof mapping !== 'object' || mapping === null) {
|
||||||
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapping.hasOwnProperty(value)) {
|
return mapping[value] || '';
|
||||||
return mapping[value];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mapping.hasOwnProperty('other')) {
|
|
||||||
return mapping['other'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | lowercase`
|
* @howToUse `expression | lowercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -37,7 +37,7 @@ function formatNumber(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (digits) {
|
if (digits) {
|
||||||
const parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||||
if (parts === null) {
|
if (parts === null) {
|
||||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* @howToUse `expression | uppercase`
|
* @howToUse `expression | uppercase`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('binding to CSS class list', () => {
|
describe('binding to CSS class list', () => {
|
||||||
@ -65,7 +66,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should add and remove classes based on changes to the expression object', async(() => {
|
it('should add and remove classes based on changes to the expression object', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
const objExpr = getComponent().objExpr;
|
let objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
detectChangesAndExpectClassName('foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should add and remove classes based on changes to the expression', async(() => {
|
it('should add and remove classes based on changes to the expression', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||||
const arrExpr = getComponent().arrExpr;
|
let arrExpr = getComponent().arrExpr;
|
||||||
detectChangesAndExpectClassName('foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
arrExpr.push('bar');
|
arrExpr.push('bar');
|
||||||
@ -186,13 +187,6 @@ export function main() {
|
|||||||
getComponent().arrExpr = ['foo bar baz foobar'];
|
getComponent().arrExpr = ['foo bar baz foobar'];
|
||||||
detectChangesAndExpectClassName('foo bar baz foobar');
|
detectChangesAndExpectClassName('foo bar baz foobar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw with descriptive error message when CSS class is not a string', () => {
|
|
||||||
fixture = createTestComponent(`<div [ngClass]="['foo', {}]"></div>`);
|
|
||||||
expect(() => fixture.detectChanges())
|
|
||||||
.toThrowError(
|
|
||||||
/NgClass can only toggle CSS classes expressed as strings, got \[object Object\]/);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expressions evaluating to sets', () => {
|
describe('expressions evaluating to sets', () => {
|
||||||
@ -259,7 +253,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should co-operate with the class attribute', async(() => {
|
it('should co-operate with the class attribute', async(() => {
|
||||||
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
||||||
const objExpr = getComponent().objExpr;
|
let objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName('init foo bar');
|
detectChangesAndExpectClassName('init foo bar');
|
||||||
@ -273,7 +267,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should co-operate with the interpolated class attribute', async(() => {
|
it('should co-operate with the interpolated class attribute', async(() => {
|
||||||
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
||||||
const objExpr = getComponent().objExpr;
|
let objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName(`init foo bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
@ -288,7 +282,7 @@ export function main() {
|
|||||||
it('should co-operate with the class attribute and binding to it', async(() => {
|
it('should co-operate with the class attribute and binding to it', async(() => {
|
||||||
fixture =
|
fixture =
|
||||||
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
||||||
const objExpr = getComponent().objExpr;
|
let objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
objExpr['bar'] = true;
|
objExpr['bar'] = true;
|
||||||
detectChangesAndExpectClassName(`init foo bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
@ -304,7 +298,7 @@ export function main() {
|
|||||||
const template =
|
const template =
|
||||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
const objExpr = getComponent().objExpr;
|
let objExpr = getComponent().objExpr;
|
||||||
|
|
||||||
detectChangesAndExpectClassName('init foo baz');
|
detectChangesAndExpectClassName('init foo baz');
|
||||||
|
|
||||||
@ -322,7 +316,7 @@ export function main() {
|
|||||||
async(() => {
|
async(() => {
|
||||||
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
const cmp = getComponent();
|
let cmp = getComponent();
|
||||||
|
|
||||||
detectChangesAndExpectClassName('init foo');
|
detectChangesAndExpectClassName('init foo');
|
||||||
|
|
||||||
@ -354,4 +348,4 @@ class TestComponent {
|
|||||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
.createComponent(TestComponent);
|
.createComponent(TestComponent);
|
||||||
}
|
}
|
@ -264,7 +264,7 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use a default template if a custom one is null', async(() => {
|
it('should use a default template if a custom one is null', async(() => {
|
||||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
||||||
const cutTemplate =
|
const cutTemplate =
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Attribute, Component, Directive} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -32,153 +32,93 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('switch value changes', () => {
|
describe('switch value changes', () => {
|
||||||
it('should switch amongst when values', () => {
|
it('should switch amongst when values', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
detectChangesAndExpectText('');
|
detectChangesAndExpectText('');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a');
|
detectChangesAndExpectText('when a');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when b');
|
detectChangesAndExpectText('when b');
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should switch amongst when values with fallback to default', () => {
|
it('should switch amongst when values with fallback to default', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||||
'<li template="ngSwitchDefault">when default</li>' +
|
'<li template="ngSwitchDefault">when default</li>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a');
|
detectChangesAndExpectText('when a');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
|
|
||||||
getComponent().switchValue = 'c';
|
getComponent().switchValue = 'c';
|
||||||
detectChangesAndExpectText('when default');
|
detectChangesAndExpectText('when default');
|
||||||
});
|
}));
|
||||||
|
|
||||||
it('should support multiple whens with the same value', () => {
|
it('should support multiple whens with the same value', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when a1;when a2;');
|
detectChangesAndExpectText('when a1;when a2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when b1;when b2;');
|
detectChangesAndExpectText('when b1;when b2;');
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when values changes', () => {
|
describe('when values changes', () => {
|
||||||
it('should switch amongst when values', () => {
|
it('should switch amongst when values', async(() => {
|
||||||
const template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
getComponent().when1 = 'a';
|
getComponent().when1 = 'a';
|
||||||
getComponent().when2 = 'b';
|
getComponent().when2 = 'b';
|
||||||
getComponent().switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
detectChangesAndExpectText('when 1;');
|
detectChangesAndExpectText('when 1;');
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
detectChangesAndExpectText('when 2;');
|
detectChangesAndExpectText('when 2;');
|
||||||
|
|
||||||
getComponent().switchValue = 'c';
|
getComponent().switchValue = 'c';
|
||||||
detectChangesAndExpectText('when default;');
|
detectChangesAndExpectText('when default;');
|
||||||
|
|
||||||
getComponent().when1 = 'c';
|
getComponent().when1 = 'c';
|
||||||
detectChangesAndExpectText('when 1;');
|
detectChangesAndExpectText('when 1;');
|
||||||
|
|
||||||
getComponent().when1 = 'd';
|
getComponent().when1 = 'd';
|
||||||
detectChangesAndExpectText('when default;');
|
detectChangesAndExpectText('when default;');
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
describe('corner cases', () => {
|
|
||||||
|
|
||||||
it('should not create the default case if another case matches', () => {
|
|
||||||
const log: string[] = [];
|
|
||||||
|
|
||||||
@Directive({selector: '[test]'})
|
|
||||||
class TestDirective {
|
|
||||||
constructor(@Attribute('test') test: string) { log.push(test); }
|
|
||||||
}
|
|
||||||
|
|
||||||
const template = '<div [ngSwitch]="switchValue">' +
|
|
||||||
'<div *ngSwitchCase="\'a\'" test="aCase"></div>' +
|
|
||||||
'<div *ngSwitchDefault test="defaultCase"></div>' +
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [TestDirective]});
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
|
||||||
.createComponent(TestComponent);
|
|
||||||
const fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.componentInstance.switchValue = 'a';
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(log).toEqual(['aCase']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the default case if there is no other case', () => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow defaults before cases', () => {
|
|
||||||
const template = '<div>' +
|
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
|
||||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
|
||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
|
||||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
|
||||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
|
||||||
'</ul></div>';
|
|
||||||
|
|
||||||
fixture = createTestComponent(template);
|
|
||||||
detectChangesAndExpectText('when default1;when default2;');
|
|
||||||
|
|
||||||
getComponent().switchValue = 'a';
|
|
||||||
detectChangesAndExpectText('when a1;when a2;');
|
|
||||||
|
|
||||||
getComponent().switchValue = 'b';
|
|
||||||
detectChangesAndExpectText('when b1;when b2;');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,10 @@ export function main() {
|
|||||||
describe('AsyncPipe', () => {
|
describe('AsyncPipe', () => {
|
||||||
|
|
||||||
describe('Observable', () => {
|
describe('Observable', () => {
|
||||||
let emitter: EventEmitter<any>;
|
var emitter: EventEmitter<any>;
|
||||||
let pipe: AsyncPipe;
|
var pipe: AsyncPipe;
|
||||||
let ref: any;
|
var ref: any;
|
||||||
const message = {};
|
var message = {};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
emitter = new EventEmitter();
|
emitter = new EventEmitter();
|
||||||
@ -62,7 +62,7 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
pipe.transform(emitter);
|
pipe.transform(emitter);
|
||||||
|
|
||||||
const newEmitter = new EventEmitter();
|
var newEmitter = new EventEmitter();
|
||||||
expect(pipe.transform(newEmitter)).toBe(null);
|
expect(pipe.transform(newEmitter)).toBe(null);
|
||||||
emitter.emit(message);
|
emitter.emit(message);
|
||||||
|
|
||||||
@ -104,14 +104,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Promise', () => {
|
describe('Promise', () => {
|
||||||
const message = new Object();
|
var message = new Object();
|
||||||
let pipe: AsyncPipe;
|
var pipe: AsyncPipe;
|
||||||
let resolve: (result: any) => void;
|
var resolve: (result: any) => void;
|
||||||
let reject: (error: any) => void;
|
var reject: (error: any) => void;
|
||||||
let promise: Promise<any>;
|
var promise: Promise<any>;
|
||||||
let ref: SpyChangeDetectorRef;
|
var ref: SpyChangeDetectorRef;
|
||||||
// adds longer timers for passing tests in IE
|
// adds longer timers for passing tests in IE
|
||||||
const timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
promise = new Promise((res, rej) => {
|
promise = new Promise((res, rej) => {
|
||||||
@ -154,7 +154,7 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
pipe.transform(promise);
|
pipe.transform(promise);
|
||||||
|
|
||||||
promise = new Promise<any>(() => {});
|
var promise = new Promise<any>(() => {});
|
||||||
expect(pipe.transform(promise)).toBe(null);
|
expect(pipe.transform(promise)).toBe(null);
|
||||||
|
|
||||||
// this should not affect the pipe, so it should return WrappedValue
|
// this should not affect the pipe, so it should return WrappedValue
|
||||||
@ -168,7 +168,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should request a change detection check upon receiving a new value',
|
it('should request a change detection check upon receiving a new value',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
const markForCheck = ref.spy('markForCheck');
|
var markForCheck = ref.spy('markForCheck');
|
||||||
pipe.transform(promise);
|
pipe.transform(promise);
|
||||||
resolve(message);
|
resolve(message);
|
||||||
|
|
||||||
@ -202,14 +202,14 @@ export function main() {
|
|||||||
|
|
||||||
describe('null', () => {
|
describe('null', () => {
|
||||||
it('should return null when given null', () => {
|
it('should return null when given null', () => {
|
||||||
const pipe = new AsyncPipe(null);
|
var pipe = new AsyncPipe(null);
|
||||||
expect(pipe.transform(null)).toEqual(null);
|
expect(pipe.transform(null)).toEqual(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('other types', () => {
|
describe('other types', () => {
|
||||||
it('should throw when given an invalid object', () => {
|
it('should throw when given an invalid object', () => {
|
||||||
const pipe = new AsyncPipe(null);
|
var pipe = new AsyncPipe(null);
|
||||||
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
|
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,18 +8,13 @@
|
|||||||
|
|
||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DatePipe', () => {
|
describe('DatePipe', () => {
|
||||||
let date: Date;
|
var date: Date;
|
||||||
const isoStringWithoutTime = '2015-01-01';
|
var pipe: DatePipe;
|
||||||
let pipe: DatePipe;
|
|
||||||
|
|
||||||
// Check the transformation of a date into a pattern
|
|
||||||
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
|
||||||
expect(pipe.transform(date, pattern)).toEqual(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
||||||
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
||||||
@ -39,9 +34,7 @@ export function main() {
|
|||||||
|
|
||||||
describe('supports', () => {
|
describe('supports', () => {
|
||||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||||
|
|
||||||
it('should support numeric strings',
|
it('should support numeric strings',
|
||||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||||
|
|
||||||
@ -49,143 +42,85 @@ export function main() {
|
|||||||
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
||||||
|
|
||||||
it('should support ISO string',
|
it('should support ISO string',
|
||||||
() => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow());
|
() => { expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow(); });
|
||||||
|
|
||||||
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
it('should not support other objects', () => {
|
||||||
|
expect(() => pipe.transform({})).toThrow();
|
||||||
it('should support ISO string without time',
|
expect(() => pipe.transform('')).toThrow();
|
||||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
});
|
||||||
|
|
||||||
it('should not support other objects',
|
|
||||||
() => { expect(() => pipe.transform({})).toThrowError(); });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should format each component correctly', () => {
|
it('should format each component correctly', () => {
|
||||||
const dateFixtures: any = {
|
expect(pipe.transform(date, 'y')).toEqual('2015');
|
||||||
'y': '2015',
|
expect(pipe.transform(date, 'yy')).toEqual('15');
|
||||||
'yy': '15',
|
expect(pipe.transform(date, 'M')).toEqual('6');
|
||||||
'M': '6',
|
expect(pipe.transform(date, 'MM')).toEqual('06');
|
||||||
'MM': '06',
|
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
||||||
'MMM': 'Jun',
|
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
||||||
'MMMM': 'June',
|
expect(pipe.transform(date, 'd')).toEqual('15');
|
||||||
'd': '15',
|
expect(pipe.transform(date, 'E')).toEqual('Mon');
|
||||||
'dd': '15',
|
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
||||||
'EEE': 'Mon',
|
|
||||||
'EEEE': 'Monday'
|
|
||||||
};
|
|
||||||
|
|
||||||
const isoStringWithoutTimeFixtures: any = {
|
|
||||||
'y': '2015',
|
|
||||||
'yy': '15',
|
|
||||||
'M': '1',
|
|
||||||
'MM': '01',
|
|
||||||
'MMM': 'Jan',
|
|
||||||
'MMMM': 'January',
|
|
||||||
'd': '1',
|
|
||||||
'dd': '01',
|
|
||||||
'EEE': 'Thu',
|
|
||||||
'EEEE': 'Thursday'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
dateFixtures['h'] = '9';
|
expect(pipe.transform(date, 'h')).toEqual('9');
|
||||||
dateFixtures['hh'] = '09';
|
expect(pipe.transform(date, 'hh')).toEqual('09');
|
||||||
dateFixtures['j'] = '9 AM';
|
expect(pipe.transform(date, 'j')).toEqual('9 AM');
|
||||||
isoStringWithoutTimeFixtures['h'] = '12';
|
|
||||||
isoStringWithoutTimeFixtures['hh'] = '12';
|
|
||||||
isoStringWithoutTimeFixtures['j'] = '12 AM';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IE and Edge can't format a date to minutes and seconds without hours
|
// IE and Edge can't format a date to minutes and seconds without hours
|
||||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||||
!browserDetection.supportsNativeIntlApi) {
|
!browserDetection.supportsNativeIntlApi) {
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
dateFixtures['HH'] = '09';
|
expect(pipe.transform(date, 'HH')).toEqual('09');
|
||||||
isoStringWithoutTimeFixtures['HH'] = '00';
|
|
||||||
}
|
}
|
||||||
dateFixtures['E'] = 'M';
|
expect(pipe.transform(date, 'm')).toEqual('3');
|
||||||
dateFixtures['L'] = 'J';
|
expect(pipe.transform(date, 's')).toEqual('1');
|
||||||
dateFixtures['m'] = '3';
|
expect(pipe.transform(date, 'mm')).toEqual('03');
|
||||||
dateFixtures['s'] = '1';
|
expect(pipe.transform(date, 'ss')).toEqual('01');
|
||||||
dateFixtures['mm'] = '03';
|
|
||||||
dateFixtures['ss'] = '01';
|
|
||||||
isoStringWithoutTimeFixtures['m'] = '0';
|
|
||||||
isoStringWithoutTimeFixtures['s'] = '0';
|
|
||||||
isoStringWithoutTimeFixtures['mm'] = '00';
|
|
||||||
isoStringWithoutTimeFixtures['ss'] = '00';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(pipe.transform(date, 'Z')).toBeDefined();
|
expect(pipe.transform(date, 'Z')).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format common multi component patterns', () => {
|
it('should format common multi component patterns', () => {
|
||||||
const dateFixtures: any = {
|
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
|
||||||
'EEE, M/d/y': 'Mon, 6/15/2015',
|
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
|
||||||
'EEE, M/d': 'Mon, 6/15',
|
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
||||||
'MMM d': 'Jun 15',
|
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||||
'dd/MM/yyyy': '15/06/2015',
|
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||||
'MM/dd/yyyy': '06/15/2015',
|
expect(pipe.transform(date, 'yMEd')).toEqual('20156Mon15');
|
||||||
'yMEEEd': '20156Mon15',
|
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
|
||||||
'MEEEd': '6Mon15',
|
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
||||||
'MMMd': 'Jun15',
|
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||||
'yMMMMEEEEd': 'Monday, June 15, 2015'
|
|
||||||
};
|
|
||||||
|
|
||||||
// IE and Edge can't format a date to minutes and seconds without hours
|
// IE and Edge can't format a date to minutes and seconds without hours
|
||||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||||
!browserDetection.supportsNativeIntlApi) {
|
!browserDetection.supportsNativeIntlApi) {
|
||||||
dateFixtures['ms'] = '31';
|
expect(pipe.transform(date, 'ms')).toEqual('31');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
dateFixtures['jm'] = '9:03 AM';
|
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format with pattern aliases', () => {
|
it('should format with pattern aliases', () => {
|
||||||
const dateFixtures: any = {
|
|
||||||
'MM/dd/yyyy': '06/15/2015',
|
|
||||||
'fullDate': 'Monday, June 15, 2015',
|
|
||||||
'longDate': 'June 15, 2015',
|
|
||||||
'mediumDate': 'Jun 15, 2015',
|
|
||||||
'shortDate': '6/15/2015'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
// IE and Edge do not add a coma after the year in these 2 cases
|
// IE and Edge do not add a coma after the year in these 2 cases
|
||||||
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
||||||
browserDetection.supportsNativeIntlApi) {
|
browserDetection.supportsNativeIntlApi) {
|
||||||
dateFixtures['medium'] = 'Jun 15, 2015 9:03:01 AM';
|
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015 9:03:01 AM');
|
||||||
dateFixtures['short'] = '6/15/2015 9:03 AM';
|
expect(pipe.transform(date, 'short')).toEqual('6/15/2015 9:03 AM');
|
||||||
} else {
|
} else {
|
||||||
dateFixtures['medium'] = 'Jun 15, 2015, 9:03:01 AM';
|
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
|
||||||
dateFixtures['short'] = '6/15/2015, 9:03 AM';
|
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||||
|
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
|
||||||
|
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
|
||||||
|
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
|
||||||
|
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
|
||||||
if (!browserDetection.isOldChrome) {
|
if (!browserDetection.isOldChrome) {
|
||||||
dateFixtures['mediumTime'] = '9:03:01 AM';
|
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
|
||||||
dateFixtures['shortTime'] = '9:03 AM';
|
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove bidi control characters',
|
it('should remove bidi control characters',
|
||||||
|
@ -12,10 +12,10 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nPluralPipe', () => {
|
describe('I18nPluralPipe', () => {
|
||||||
let localization: NgLocalization;
|
var localization: NgLocalization;
|
||||||
let pipe: I18nPluralPipe;
|
var pipe: I18nPluralPipe;
|
||||||
|
|
||||||
const mapping = {
|
var mapping = {
|
||||||
'=0': 'No messages.',
|
'=0': 'No messages.',
|
||||||
'=1': 'One message.',
|
'=1': 'One message.',
|
||||||
'many': 'Many messages.',
|
'many': 'Many messages.',
|
||||||
@ -32,27 +32,27 @@ export function main() {
|
|||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return 0 text if value is 0', () => {
|
it('should return 0 text if value is 0', () => {
|
||||||
const val = pipe.transform(0, mapping);
|
var val = pipe.transform(0, mapping);
|
||||||
expect(val).toEqual('No messages.');
|
expect(val).toEqual('No messages.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 1 text if value is 1', () => {
|
it('should return 1 text if value is 1', () => {
|
||||||
const val = pipe.transform(1, mapping);
|
var val = pipe.transform(1, mapping);
|
||||||
expect(val).toEqual('One message.');
|
expect(val).toEqual('One message.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return category messages', () => {
|
it('should return category messages', () => {
|
||||||
const val = pipe.transform(4, mapping);
|
var val = pipe.transform(4, mapping);
|
||||||
expect(val).toEqual('Many messages.');
|
expect(val).toEqual('Many messages.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should interpolate the value into the text where indicated', () => {
|
it('should interpolate the value into the text where indicated', () => {
|
||||||
const val = pipe.transform(6, mapping);
|
var val = pipe.transform(6, mapping);
|
||||||
expect(val).toEqual('There are 6 messages, that is 6.');
|
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use "" if value is undefined', () => {
|
it('should use "" if value is undefined', () => {
|
||||||
const val = pipe.transform(void(0), mapping);
|
var val = pipe.transform(void(0), mapping);
|
||||||
expect(val).toEqual('');
|
expect(val).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,35 +8,40 @@
|
|||||||
|
|
||||||
import {I18nSelectPipe} from '@angular/common';
|
import {I18nSelectPipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nSelectPipe', () => {
|
describe('I18nSelectPipe', () => {
|
||||||
const pipe: I18nSelectPipe = new I18nSelectPipe();
|
var pipe: I18nSelectPipe;
|
||||||
const mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||||
|
|
||||||
|
beforeEach(() => { pipe = new I18nSelectPipe(); });
|
||||||
|
|
||||||
it('should be marked as pure',
|
it('should be marked as pure',
|
||||||
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return the "male" text if value is "male"', () => {
|
it('should return male text if value is male', () => {
|
||||||
const val = pipe.transform('male', mapping);
|
var val = pipe.transform('male', mapping);
|
||||||
expect(val).toEqual('Invite him.');
|
expect(val).toEqual('Invite him.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the "female" text if value is "female"', () => {
|
it('should return female text if value is female', () => {
|
||||||
const val = pipe.transform('female', mapping);
|
var val = pipe.transform('female', mapping);
|
||||||
expect(val).toEqual('Invite her.');
|
expect(val).toEqual('Invite her.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the "other" text if value is neither "male" nor "female"',
|
it('should return "" if value is anything other than male or female', () => {
|
||||||
() => { expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); });
|
var val = pipe.transform('Anything else', mapping);
|
||||||
|
expect(val).toEqual('');
|
||||||
it('should return an empty text if value is null or undefined', () => {
|
|
||||||
expect(pipe.transform(null, mapping)).toEqual('');
|
|
||||||
expect(pipe.transform(void 0, mapping)).toEqual('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on bad arguments',
|
it('should use "" if value is undefined', () => {
|
||||||
|
var val = pipe.transform(void(0), mapping);
|
||||||
|
expect(val).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not support bad arguments',
|
||||||
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('JsonPipe', () => {
|
describe('JsonPipe', () => {
|
||||||
const regNewLine = '\n';
|
var regNewLine = '\n';
|
||||||
let inceptionObj: any;
|
var inceptionObj: any;
|
||||||
let inceptionObjString: string;
|
var inceptionObjString: string;
|
||||||
let pipe: JsonPipe;
|
var pipe: JsonPipe;
|
||||||
|
|
||||||
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ export function main() {
|
|||||||
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
||||||
|
|
||||||
it('should return JSON-formatted string even when normalized', () => {
|
it('should return JSON-formatted string even when normalized', () => {
|
||||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||||
const dream2 = normalize(inceptionObjString);
|
var dream2 = normalize(inceptionObjString);
|
||||||
expect(dream1).toEqual(dream2);
|
expect(dream1).toEqual(dream2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||||
const dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
var dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||||
expect(dream1).toEqual(dream2);
|
expect(dream1).toEqual(dream2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -63,8 +63,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work with mutable objects', async(() => {
|
it('should work with mutable objects', async(() => {
|
||||||
const fixture = TestBed.createComponent(TestComp);
|
let fixture = TestBed.createComponent(TestComp);
|
||||||
const mutable: number[] = [1];
|
let mutable: number[] = [1];
|
||||||
fixture.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
||||||
|
@ -11,9 +11,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('LowerCasePipe', () => {
|
describe('LowerCasePipe', () => {
|
||||||
let upper: string;
|
var upper: string;
|
||||||
let lower: string;
|
var lower: string;
|
||||||
let pipe: LowerCasePipe;
|
var pipe: LowerCasePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
lower = 'something';
|
lower = 'something';
|
||||||
@ -23,14 +23,14 @@ export function main() {
|
|||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return lowercase', () => {
|
it('should return lowercase', () => {
|
||||||
const val = pipe.transform(upper);
|
var val = pipe.transform(upper);
|
||||||
expect(val).toEqual(lower);
|
expect(val).toEqual(lower);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should lowercase when there is a new value', () => {
|
it('should lowercase when there is a new value', () => {
|
||||||
const val = pipe.transform(upper);
|
var val = pipe.transform(upper);
|
||||||
expect(val).toEqual(lower);
|
expect(val).toEqual(lower);
|
||||||
const val2 = pipe.transform('WAT');
|
var val2 = pipe.transform('WAT');
|
||||||
expect(val2).toEqual('wat');
|
expect(val2).toEqual('wat');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
|||||||
export function main() {
|
export function main() {
|
||||||
describe('Number pipes', () => {
|
describe('Number pipes', () => {
|
||||||
describe('DecimalPipe', () => {
|
describe('DecimalPipe', () => {
|
||||||
let pipe: DecimalPipe;
|
var pipe: DecimalPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new DecimalPipe('en-US'); });
|
beforeEach(() => { pipe = new DecimalPipe('en-US'); });
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PercentPipe', () => {
|
describe('PercentPipe', () => {
|
||||||
let pipe: PercentPipe;
|
var pipe: PercentPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new PercentPipe('en-US'); });
|
beforeEach(() => { pipe = new PercentPipe('en-US'); });
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('CurrencyPipe', () => {
|
describe('CurrencyPipe', () => {
|
||||||
let pipe: CurrencyPipe;
|
var pipe: CurrencyPipe;
|
||||||
|
|
||||||
beforeEach(() => { pipe = new CurrencyPipe('en-US'); });
|
beforeEach(() => { pipe = new CurrencyPipe('en-US'); });
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('SlicePipe', () => {
|
describe('SlicePipe', () => {
|
||||||
let list: number[];
|
var list: number[];
|
||||||
let str: string;
|
var str: string;
|
||||||
let pipe: SlicePipe;
|
var pipe: SlicePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
list = [1, 2, 3, 4, 5];
|
list = [1, 2, 3, 4, 5];
|
||||||
@ -93,8 +93,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work with mutable arrays', async(() => {
|
it('should work with mutable arrays', async(() => {
|
||||||
const fixture = TestBed.createComponent(TestComp);
|
let fixture = TestBed.createComponent(TestComp);
|
||||||
const mutable: number[] = [1, 2];
|
let mutable: number[] = [1, 2];
|
||||||
fixture.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('2');
|
expect(fixture.nativeElement).toHaveText('2');
|
||||||
|
@ -11,9 +11,9 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('UpperCasePipe', () => {
|
describe('UpperCasePipe', () => {
|
||||||
let upper: string;
|
var upper: string;
|
||||||
let lower: string;
|
var lower: string;
|
||||||
let pipe: UpperCasePipe;
|
var pipe: UpperCasePipe;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
lower = 'something';
|
lower = 'something';
|
||||||
@ -24,14 +24,14 @@ export function main() {
|
|||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
|
|
||||||
it('should return uppercase', () => {
|
it('should return uppercase', () => {
|
||||||
const val = pipe.transform(lower);
|
var val = pipe.transform(lower);
|
||||||
expect(val).toEqual(upper);
|
expect(val).toEqual(upper);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should uppercase when there is a new value', () => {
|
it('should uppercase when there is a new value', () => {
|
||||||
const val = pipe.transform(lower);
|
var val = pipe.transform(lower);
|
||||||
expect(val).toEqual(upper);
|
expect(val).toEqual(upper);
|
||||||
const val2 = pipe.transform('wat');
|
var val2 = pipe.transform('wat');
|
||||||
expect(val2).toEqual('WAT');
|
expect(val2).toEqual('WAT');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ export class SpyLocation implements Location {
|
|||||||
path(): string { return this._history[this._historyIndex].path; }
|
path(): string { return this._history[this._historyIndex].path; }
|
||||||
|
|
||||||
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||||
const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
var givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||||
const currPath =
|
var currPath =
|
||||||
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
||||||
|
|
||||||
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||||
@ -66,12 +66,12 @@ export class SpyLocation implements Location {
|
|||||||
this._history.push(new LocationState(path, query));
|
this._history.push(new LocationState(path, query));
|
||||||
this._historyIndex = this._history.length - 1;
|
this._historyIndex = this._history.length - 1;
|
||||||
|
|
||||||
const locationState = this._history[this._historyIndex - 1];
|
var locationState = this._history[this._historyIndex - 1];
|
||||||
if (locationState.path == path && locationState.query == query) {
|
if (locationState.path == path && locationState.query == query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push(url);
|
this.urlChanges.push(url);
|
||||||
this._subject.emit({'url': url, 'pop': false});
|
this._subject.emit({'url': url, 'pop': false});
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ export class SpyLocation implements Location {
|
|||||||
replaceState(path: string, query: string = '') {
|
replaceState(path: string, query: string = '') {
|
||||||
path = this.prepareExternalUrl(path);
|
path = this.prepareExternalUrl(path);
|
||||||
|
|
||||||
const history = this._history[this._historyIndex];
|
var history = this._history[this._historyIndex];
|
||||||
if (history.path == path && history.query == query) {
|
if (history.path == path && history.query == query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ export class SpyLocation implements Location {
|
|||||||
history.path = path;
|
history.path = path;
|
||||||
history.query = query;
|
history.query = query;
|
||||||
|
|
||||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push('replace: ' + url);
|
this.urlChanges.push('replace: ' + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,20 +44,20 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||||||
pushState(ctx: any, title: string, path: string, query: string): void {
|
pushState(ctx: any, title: string, path: string, query: string): void {
|
||||||
this.internalTitle = title;
|
this.internalTitle = title;
|
||||||
|
|
||||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.internalPath = url;
|
this.internalPath = url;
|
||||||
|
|
||||||
const externalUrl = this.prepareExternalUrl(url);
|
var externalUrl = this.prepareExternalUrl(url);
|
||||||
this.urlChanges.push(externalUrl);
|
this.urlChanges.push(externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceState(ctx: any, title: string, path: string, query: string): void {
|
replaceState(ctx: any, title: string, path: string, query: string): void {
|
||||||
this.internalTitle = title;
|
this.internalTitle = title;
|
||||||
|
|
||||||
const url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.internalPath = url;
|
this.internalPath = url;
|
||||||
|
|
||||||
const externalUrl = this.prepareExternalUrl(url);
|
var externalUrl = this.prepareExternalUrl(url);
|
||||||
this.urlChanges.push('replace: ' + externalUrl);
|
this.urlChanges.push('replace: ' + externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||||||
back(): void {
|
back(): void {
|
||||||
if (this.urlChanges.length > 0) {
|
if (this.urlChanges.length > 0) {
|
||||||
this.urlChanges.pop();
|
this.urlChanges.pop();
|
||||||
const nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
|
var nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
|
||||||
this.simulatePopState(nextUrl);
|
this.simulatePopState(nextUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ Then you can add an import statement in the `bootstrap` allowing you to bootstra
|
|||||||
generated code:
|
generated code:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
main.module.ts
|
main_module.ts
|
||||||
-------------
|
-------------
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {Component, NgModule, ApplicationRef} from '@angular/core';
|
import {Component, NgModule, ApplicationRef} from '@angular/core';
|
||||||
@ -49,7 +49,7 @@ export class MainModule {
|
|||||||
bootstrap.ts
|
bootstrap.ts
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
import {MainModuleNgFactory} from './main.module.ngfactory';
|
import {MainModuleNgFactory} from './main_module.ngfactory';
|
||||||
import {platformBrowser} from '@angular/platform-browser';
|
import {platformBrowser} from '@angular/platform-browser';
|
||||||
|
|
||||||
platformBrowser().bootstrapModuleFactory(MainModuleNgFactory);
|
platformBrowser().bootstrapModuleFactory(MainModuleNgFactory);
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export {CodeGenerator} from './src/codegen';
|
export {CodeGenerator} from './src/codegen';
|
||||||
export {Extractor} from './src/extractor';
|
|
||||||
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
||||||
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'use-third-party',
|
|
||||||
template: '<third-party-comp [thirdParty]="title"></third-party-comp>' +
|
|
||||||
'<another-third-party-comp></another-third-party-comp>',
|
|
||||||
})
|
|
||||||
export class ComponentUsingThirdParty {
|
|
||||||
title: string = 'from 3rd party';
|
|
||||||
}
|
|
@ -12,10 +12,6 @@
|
|||||||
<source>Welcome</source>
|
<source>Welcome</source>
|
||||||
<target>tervetuloa</target>
|
<target>tervetuloa</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
|
||||||
<source>other-3rdP-component</source>
|
|
||||||
<target>other-3rdP-component</target>
|
|
||||||
</trans-unit>
|
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
@ -9,5 +9,4 @@
|
|||||||
<translationbundle>
|
<translationbundle>
|
||||||
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
|
<translation id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4">käännä teksti</translation>
|
||||||
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
|
<translation id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">tervetuloa</translation>
|
||||||
<translation id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</translation>
|
|
||||||
</translationbundle>
|
</translationbundle>
|
||||||
|
@ -8,15 +8,12 @@
|
|||||||
|
|
||||||
import {ApplicationRef, NgModule} from '@angular/core';
|
import {ApplicationRef, NgModule} from '@angular/core';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import {ServerModule} from '@angular/platform-server';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
import {MdButtonModule} from '@angular2-material/button';
|
import {MdButtonModule} from '@angular2-material/button';
|
||||||
|
|
||||||
import {ThirdpartyModule} from '../third_party_src/module';
|
|
||||||
|
|
||||||
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
import {MultipleComponentsMyComp, NextComp} from './a/multiple_components';
|
||||||
import {AnimateCmp} from './animate';
|
import {AnimateCmp} from './animate';
|
||||||
import {BasicComp} from './basic';
|
import {BasicComp} from './basic';
|
||||||
import {ComponentUsingThirdParty} from './comp_using_3rdp';
|
|
||||||
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
|
||||||
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features';
|
||||||
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures';
|
||||||
@ -25,47 +22,35 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AnimateCmp,
|
|
||||||
BasicComp,
|
|
||||||
CompConsumingEvents,
|
|
||||||
CompForChildQuery,
|
|
||||||
CompUsingPipes,
|
|
||||||
CompUsingRootModuleDirectiveAndPipe,
|
|
||||||
CompWithAnalyzeEntryComponentsProvider,
|
|
||||||
CompWithChildQuery,
|
|
||||||
CompWithDirectiveChild,
|
|
||||||
CompWithEntryComponents,
|
|
||||||
CompWithNgContent,
|
|
||||||
CompWithProviders,
|
|
||||||
CompWithReferences,
|
|
||||||
DirectiveForQuery,
|
|
||||||
DirPublishingEvents,
|
|
||||||
MultipleComponentsMyComp,
|
|
||||||
NextComp,
|
|
||||||
ProjectingComp,
|
|
||||||
SomeDirectiveInRootModule,
|
SomeDirectiveInRootModule,
|
||||||
SomePipeInRootModule,
|
SomePipeInRootModule,
|
||||||
ComponentUsingThirdParty,
|
AnimateCmp,
|
||||||
|
BasicComp,
|
||||||
|
CompForChildQuery,
|
||||||
|
CompWithEntryComponents,
|
||||||
|
CompWithAnalyzeEntryComponentsProvider,
|
||||||
|
ProjectingComp,
|
||||||
|
CompWithChildQuery,
|
||||||
|
CompWithDirectiveChild,
|
||||||
|
CompWithNgContent,
|
||||||
|
CompUsingRootModuleDirectiveAndPipe,
|
||||||
|
CompWithProviders,
|
||||||
|
CompWithReferences,
|
||||||
|
CompUsingPipes,
|
||||||
|
CompConsumingEvents,
|
||||||
|
DirPublishingEvents,
|
||||||
|
MultipleComponentsMyComp,
|
||||||
|
DirectiveForQuery,
|
||||||
|
NextComp,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
ServerModule,
|
BrowserModule, FormsModule, someLibModuleWithProviders(), ModuleUsingCustomElements,
|
||||||
FormsModule,
|
MdButtonModule
|
||||||
MdButtonModule,
|
|
||||||
ModuleUsingCustomElements,
|
|
||||||
someLibModuleWithProviders(),
|
|
||||||
ThirdpartyModule,
|
|
||||||
],
|
],
|
||||||
providers: [SomeService],
|
providers: [SomeService],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AnimateCmp,
|
AnimateCmp, BasicComp, CompWithEntryComponents, CompWithAnalyzeEntryComponentsProvider,
|
||||||
BasicComp,
|
ProjectingComp, CompWithChildQuery, CompUsingRootModuleDirectiveAndPipe, CompWithReferences
|
||||||
CompUsingRootModuleDirectiveAndPipe,
|
|
||||||
CompWithAnalyzeEntryComponentsProvider,
|
|
||||||
CompWithChildQuery,
|
|
||||||
CompWithEntryComponents,
|
|
||||||
CompWithReferences,
|
|
||||||
ProjectingComp,
|
|
||||||
ComponentUsingThirdParty,
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class MainModule {
|
export class MainModule {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import {LowerCasePipe, NgIf} from '@angular/common';
|
import {LowerCasePipe, NgIf} from '@angular/common';
|
||||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SomeService {
|
export class SomeService {
|
||||||
|
@ -19,9 +19,9 @@ describe('template codegen output', () => {
|
|||||||
|
|
||||||
it('should apply the animate states to the element', (done) => {
|
it('should apply the animate states to the element', (done) => {
|
||||||
const compFixture = createComponent(AnimateCmp);
|
const compFixture = createComponent(AnimateCmp);
|
||||||
const debugElement = compFixture.debugElement;
|
var debugElement = compFixture.debugElement;
|
||||||
|
|
||||||
const targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||||
|
|
||||||
compFixture.componentInstance.setAsOpen();
|
compFixture.componentInstance.setAsOpen();
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
@ -45,9 +45,9 @@ describe('template codegen output', () => {
|
|||||||
|
|
||||||
it('should apply the default animate state to the element', (done) => {
|
it('should apply the default animate state to the element', (done) => {
|
||||||
const compFixture = createComponent(AnimateCmp);
|
const compFixture = createComponent(AnimateCmp);
|
||||||
const debugElement = compFixture.debugElement;
|
var debugElement = compFixture.debugElement;
|
||||||
|
|
||||||
const targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||||
|
|
||||||
compFixture.componentInstance.setAsSomethingElse();
|
compFixture.componentInstance.setAsSomethingElse();
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
@ -43,13 +43,13 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to create the basic component', () => {
|
it('should be able to create the basic component', () => {
|
||||||
const compFixture = createComponent(BasicComp);
|
var compFixture = createComponent(BasicComp);
|
||||||
expect(compFixture.componentInstance).toBeTruthy();
|
expect(compFixture.componentInstance).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support ngIf', () => {
|
it('should support ngIf', () => {
|
||||||
const compFixture = createComponent(BasicComp);
|
var compFixture = createComponent(BasicComp);
|
||||||
const debugElement = compFixture.debugElement;
|
var debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children.length).toBe(3);
|
expect(debugElement.children.length).toBe(3);
|
||||||
|
|
||||||
compFixture.componentInstance.ctxBool = true;
|
compFixture.componentInstance.ctxBool = true;
|
||||||
@ -59,8 +59,8 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support ngFor', () => {
|
it('should support ngFor', () => {
|
||||||
const compFixture = createComponent(BasicComp);
|
var compFixture = createComponent(BasicComp);
|
||||||
const debugElement = compFixture.debugElement;
|
var debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children.length).toBe(3);
|
expect(debugElement.children.length).toBe(3);
|
||||||
|
|
||||||
// test NgFor
|
// test NgFor
|
||||||
@ -83,9 +83,11 @@ describe('template codegen output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support i18n for content tags', () => {
|
it('should support i18n for content tags', () => {
|
||||||
const containerElement = createComponent(BasicComp).nativeElement;
|
const compFixture = createComponent(BasicComp);
|
||||||
const pElement = containerElement.children.find((c: any) => c.name == 'p');
|
const debugElement = compFixture.debugElement;
|
||||||
const pText = pElement.children.map((c: any) => c.data).join('').trim();
|
const containerElement = <any>debugElement.nativeElement;
|
||||||
|
const pElement = <any>containerElement.children.find((c: any) => c.name == 'p');
|
||||||
|
const pText = <string>pElement.children.map((c: any) => c.data).join('').trim();
|
||||||
expect(pText).toBe('tervetuloa');
|
expect(pText).toBe('tervetuloa');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -15,8 +15,8 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
it('should support entryComponents in components', () => {
|
it('should support entryComponents in components', () => {
|
||||||
const compFixture = createComponent(CompWithEntryComponents);
|
var compFixture = createComponent(CompWithEntryComponents);
|
||||||
const cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
var cf = compFixture.componentInstance.cfr.resolveComponentFactory(BasicComp);
|
||||||
expect(cf.componentType).toBe(BasicComp);
|
expect(cf.componentType).toBe(BasicComp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<!ELEMENT ex (#PCDATA)>
|
<!ELEMENT ex (#PCDATA)>
|
||||||
]>
|
]>
|
||||||
<messagebundle>
|
<messagebundle>
|
||||||
<msg id="63a85808f03b8181e36a952e0fa38202c2304862">other-3rdP-component</msg>
|
|
||||||
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
<msg id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" desc="desc" meaning="meaning">translate me</msg>
|
||||||
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
<msg id="65cc4ab3b4c438e07c89be2b677d08369fb62da2">Welcome</msg>
|
||||||
</messagebundle>
|
</messagebundle>
|
||||||
@ -44,10 +43,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
|
||||||
<source>other-3rdP-component</source>
|
|
||||||
<target/>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||||
<source>translate me</source>
|
<source>translate me</source>
|
||||||
<target/>
|
<target/>
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
import './init';
|
import './init';
|
||||||
|
|
||||||
import {ComponentUsingThirdParty} from '../src/comp_using_3rdp';
|
|
||||||
import {MainModule} from '../src/module';
|
import {MainModule} from '../src/module';
|
||||||
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
|
||||||
|
|
||||||
@ -16,9 +15,9 @@ import {createComponent, createModule} from './util';
|
|||||||
describe('NgModule', () => {
|
describe('NgModule', () => {
|
||||||
it('should support providers', () => {
|
it('should support providers', () => {
|
||||||
const moduleRef = createModule();
|
const moduleRef = createModule();
|
||||||
expect(moduleRef.instance instanceof MainModule).toEqual(true);
|
expect(moduleRef.instance instanceof MainModule).toBe(true);
|
||||||
expect(moduleRef.injector.get(MainModule) instanceof MainModule).toEqual(true);
|
expect(moduleRef.injector.get(MainModule) instanceof MainModule).toBe(true);
|
||||||
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toEqual(true);
|
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support entryComponents components', () => {
|
it('should support entryComponents components', () => {
|
||||||
@ -27,7 +26,7 @@ describe('NgModule', () => {
|
|||||||
CompUsingRootModuleDirectiveAndPipe);
|
CompUsingRootModuleDirectiveAndPipe);
|
||||||
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
|
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
|
||||||
const compRef = cf.create(moduleRef.injector);
|
const compRef = cf.create(moduleRef.injector);
|
||||||
expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toEqual(true);
|
expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support entryComponents via the ANALYZE_FOR_ENTRY_COMPONENTS provider and function providers in components',
|
it('should support entryComponents via the ANALYZE_FOR_ENTRY_COMPONENTS provider and function providers in components',
|
||||||
@ -43,30 +42,12 @@ describe('NgModule', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('third-party modules', () => {
|
|
||||||
// https://github.com/angular/angular/issues/11889
|
|
||||||
it('should support third party entryComponents components', () => {
|
|
||||||
const fixture = createComponent(ComponentUsingThirdParty);
|
|
||||||
const thirdPComps = fixture.nativeElement.children;
|
|
||||||
expect(thirdPComps[0].children[0].children[0].data).toEqual('3rdP-component');
|
|
||||||
expect(thirdPComps[1].children[0].children[0].data).toEqual('other-3rdP-component');
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://github.com/angular/angular/issues/12428
|
|
||||||
it('should support third party directives', () => {
|
|
||||||
const fixture = createComponent(ComponentUsingThirdParty);
|
|
||||||
const debugElement = fixture.debugElement;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(debugElement.children[0].properties['title']).toEqual('from 3rd party');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support module directives and pipes', () => {
|
it('should support module directives and pipes', () => {
|
||||||
const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe);
|
const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe);
|
||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
|
||||||
const debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children[0].properties['title']).toEqual('transformed someValue');
|
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support module directives and pipes on lib modules', () => {
|
it('should support module directives and pipes on lib modules', () => {
|
||||||
@ -74,10 +55,10 @@ describe('NgModule', () => {
|
|||||||
compFixture.detectChanges();
|
compFixture.detectChanges();
|
||||||
|
|
||||||
const debugElement = compFixture.debugElement;
|
const debugElement = compFixture.debugElement;
|
||||||
expect(debugElement.children[0].properties['title']).toEqual('transformed someValue');
|
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
|
||||||
|
|
||||||
expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toEqual(true);
|
expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toBe(true);
|
||||||
expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule)
|
expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule)
|
||||||
.toEqual(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,10 +13,10 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
it('should support basic content projection', () => {
|
it('should support basic content projection', () => {
|
||||||
const mainCompFixture = createComponent(ProjectingComp);
|
var mainCompFixture = createComponent(ProjectingComp);
|
||||||
|
|
||||||
const debugElement = mainCompFixture.debugElement;
|
var debugElement = mainCompFixture.debugElement;
|
||||||
const compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
var compWithProjection = debugElement.query(By.directive(CompWithNgContent));
|
||||||
expect(compWithProjection.children.length).toBe(1);
|
expect(compWithProjection.children.length).toBe(1);
|
||||||
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
||||||
});
|
});
|
||||||
|
@ -14,18 +14,18 @@ import {createComponent} from './util';
|
|||||||
|
|
||||||
describe('child queries', () => {
|
describe('child queries', () => {
|
||||||
it('should support compiling child queries', () => {
|
it('should support compiling child queries', () => {
|
||||||
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||||
const debugElement = childQueryCompFixture.debugElement;
|
var debugElement = childQueryCompFixture.debugElement;
|
||||||
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||||
expect(childQueryCompFixture.componentInstance.child).toBeDefined();
|
expect(childQueryCompFixture.componentInstance.child).toBeDefined();
|
||||||
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
|
expect(childQueryCompFixture.componentInstance.child instanceof CompForChildQuery).toBe(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support compiling children queries', () => {
|
it('should support compiling children queries', () => {
|
||||||
const childQueryCompFixture = createComponent(CompWithChildQuery);
|
var childQueryCompFixture = createComponent(CompWithChildQuery);
|
||||||
const debugElement = childQueryCompFixture.debugElement;
|
var debugElement = childQueryCompFixture.debugElement;
|
||||||
const compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||||
|
|
||||||
childQueryCompFixture.detectChanges();
|
childQueryCompFixture.detectChanges();
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
This folder emulates consuming precompiled modules and components.
|
|
||||||
It is compiled separately from the other sources under `src`
|
|
||||||
to only generate `*.js` / `*.d.ts` / `*.metadata.json` files,
|
|
||||||
but no `*.ngfactory.ts` files.
|
|
||||||
|
|
||||||
** WARNING **
|
|
||||||
Do not import components/directives from here directly as we want to test that ngc still compiles
|
|
||||||
them when they are not imported.
|
|
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Directive, Input} from '@angular/core';
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: '[thirdParty]',
|
|
||||||
host: {'[title]': 'thirdParty'},
|
|
||||||
})
|
|
||||||
export class ThirdPartyDirective {
|
|
||||||
@Input() thirdParty: string;
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
import {ThirdPartyComponent} from './comp';
|
|
||||||
import {ThirdPartyDirective} from './directive';
|
|
||||||
import {AnotherThirdPartyModule} from './other_module';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
ThirdPartyComponent,
|
|
||||||
ThirdPartyDirective,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
AnotherThirdPartyModule,
|
|
||||||
ThirdPartyComponent,
|
|
||||||
ThirdPartyDirective,
|
|
||||||
],
|
|
||||||
imports: [AnotherThirdPartyModule]
|
|
||||||
})
|
|
||||||
export class ThirdpartyModule {
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Component} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'another-third-party-comp',
|
|
||||||
template: '<div i18n>other-3rdP-component</div>',
|
|
||||||
})
|
|
||||||
export class AnotherThirdpartyComponent {
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {NgModule} from '@angular/core';
|
|
||||||
import {AnotherThirdpartyComponent} from './other_comp';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [AnotherThirdpartyComponent],
|
|
||||||
exports: [AnotherThirdpartyComponent],
|
|
||||||
})
|
|
||||||
export class AnotherThirdPartyModule {
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
// For TypeScript 1.8, we have to lay out generated files
|
|
||||||
// in the same source directory with your code.
|
|
||||||
"genDir": ".",
|
|
||||||
"debug": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"rootDir": "",
|
|
||||||
"declaration": true,
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"baseUrl": ".",
|
|
||||||
"outDir": "../node_modules/third_party"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
// For TypeScript 1.8, we have to lay out generated files
|
|
||||||
// in the same source directory with your code.
|
|
||||||
"genDir": ".",
|
|
||||||
"debug": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"rootDir": "",
|
|
||||||
"declaration": true,
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"baseUrl": "."
|
|
||||||
},
|
|
||||||
|
|
||||||
"files": [
|
|
||||||
"src/module",
|
|
||||||
"src/bootstrap",
|
|
||||||
"test/all_spec",
|
|
||||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
|
||||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
|
||||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
|
||||||
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
|
||||||
]
|
|
||||||
}
|
|
29
modules/@angular/compiler-cli/integrationtest/tsconfig.json
Normal file
29
modules/@angular/compiler-cli/integrationtest/tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
// For TypeScript 1.8, we have to lay out generated files
|
||||||
|
// in the same source directory with your code.
|
||||||
|
"genDir": ".",
|
||||||
|
"debug": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"rootDir": "",
|
||||||
|
"declaration": true,
|
||||||
|
"lib": ["es6", "dom"],
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
|
||||||
|
"files": [
|
||||||
|
"src/module",
|
||||||
|
"src/bootstrap",
|
||||||
|
"test/all_spec",
|
||||||
|
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
||||||
|
]
|
||||||
|
}
|
@ -11,9 +11,8 @@
|
|||||||
* Intended to be used in a build step.
|
* Intended to be used in a build step.
|
||||||
*/
|
*/
|
||||||
import * as compiler from '@angular/compiler';
|
import * as compiler from '@angular/compiler';
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {Directive, NgModule, ViewEncapsulation} from '@angular/core';
|
||||||
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
|
||||||
import {readFileSync} from 'fs';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -23,30 +22,88 @@ import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
|||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
|
const nodeFs = require('fs');
|
||||||
|
|
||||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
|
||||||
const PREAMBLE = `/**
|
const PREAMBLE = `/**
|
||||||
* @fileoverview This file is generated by the Angular 2 template compiler.
|
* This file is generated by the Angular 2 template compiler.
|
||||||
* Do not edit.
|
* Do not edit.
|
||||||
* @suppress {suspiciousCode,uselessCode,missingProperties}
|
|
||||||
*/
|
*/
|
||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export class CodeGeneratorModuleCollector {
|
||||||
|
constructor(
|
||||||
|
private staticReflector: StaticReflector, private reflectorHost: StaticReflectorHost,
|
||||||
|
private program: ts.Program, private options: AngularCompilerOptions) {}
|
||||||
|
|
||||||
|
getModuleSymbols(program: ts.Program): {fileMetas: FileMetadata[], ngModules: StaticSymbol[]} {
|
||||||
|
// Compare with false since the default should be true
|
||||||
|
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
||||||
|
GENERATED_OR_DTS_FILES :
|
||||||
|
GENERATED_FILES;
|
||||||
|
let filePaths = this.program.getSourceFiles()
|
||||||
|
.filter(sf => !skipFileNames.test(sf.fileName))
|
||||||
|
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
||||||
|
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||||
|
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
||||||
|
ngModules.push(...fileMeta.ngModules);
|
||||||
|
return ngModules;
|
||||||
|
}, <StaticSymbol[]>[]);
|
||||||
|
return {fileMetas, ngModules};
|
||||||
|
}
|
||||||
|
|
||||||
|
private readFileMetadata(absSourcePath: string): FileMetadata {
|
||||||
|
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||||
|
const result: FileMetadata = {directives: [], ngModules: [], fileUrl: absSourcePath};
|
||||||
|
if (!moduleMetadata) {
|
||||||
|
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const metadata = moduleMetadata['metadata'];
|
||||||
|
const symbols = metadata && Object.keys(metadata);
|
||||||
|
if (!symbols || !symbols.length) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (const symbol of symbols) {
|
||||||
|
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||||
|
// Ignore symbols that are only included to record error information.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||||
|
const annotations = this.staticReflector.annotations(staticType);
|
||||||
|
annotations.forEach((annotation) => {
|
||||||
|
if (annotation instanceof NgModule) {
|
||||||
|
result.ngModules.push(staticType);
|
||||||
|
} else if (annotation instanceof Directive) {
|
||||||
|
result.directives.push(staticType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class CodeGenerator {
|
export class CodeGenerator {
|
||||||
|
private moduleCollector: CodeGeneratorModuleCollector;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private options: AngularCompilerOptions, private program: ts.Program,
|
private options: AngularCompilerOptions, private program: ts.Program,
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {}
|
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {
|
||||||
|
this.moduleCollector =
|
||||||
|
new CodeGeneratorModuleCollector(staticReflector, reflectorHost, program, options);
|
||||||
|
}
|
||||||
|
|
||||||
// Write codegen in a directory structure matching the sources.
|
// Write codegen in a directory structure matching the sources.
|
||||||
private calculateEmitPath(filePath: string): string {
|
private calculateEmitPath(filePath: string): string {
|
||||||
let root = this.options.basePath;
|
let root = this.options.basePath;
|
||||||
for (const eachRootDir of this.options.rootDirs || []) {
|
for (let eachRootDir of this.options.rootDirs || []) {
|
||||||
if (this.options.trace) {
|
if (this.options.trace) {
|
||||||
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||||
}
|
}
|
||||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||||
root = eachRootDir;
|
root = eachRootDir;
|
||||||
@ -54,28 +111,31 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transplant the codegen path to be inside the `genDir`
|
// transplant the codegen path to be inside the `genDir`
|
||||||
let relativePath: string = path.relative(root, filePath);
|
var relativePath: string = path.relative(root, filePath);
|
||||||
while (relativePath.startsWith('..' + path.sep)) {
|
while (relativePath.startsWith('..' + path.sep)) {
|
||||||
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
||||||
// into `genDir`.
|
// into `genDir`.
|
||||||
relativePath = relativePath.substr(3);
|
relativePath = relativePath.substr(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.join(this.options.genDir, relativePath);
|
return path.join(this.options.genDir, relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen(options: {transitiveModules: boolean}): Promise<any> {
|
codegen(): Promise<any> {
|
||||||
const staticSymbols =
|
const {fileMetas, ngModules} = this.moduleCollector.getModuleSymbols(this.program);
|
||||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
||||||
|
return Promise.all(fileMetas.map(
|
||||||
return this.compiler.compileModules(staticSymbols, options).then(generatedModules => {
|
(fileMeta) =>
|
||||||
generatedModules.forEach(generatedModule => {
|
this.compiler
|
||||||
const sourceFile = this.program.getSourceFile(generatedModule.fileUrl);
|
.compile(
|
||||||
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
fileMeta.fileUrl, analyzedNgModules, fileMeta.directives, fileMeta.ngModules)
|
||||||
this.host.writeFile(
|
.then((generatedModules) => {
|
||||||
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
generatedModules.forEach((generatedModule) => {
|
||||||
});
|
const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
|
||||||
});
|
const emitPath = this.calculateEmitPath(generatedModule.moduleUrl);
|
||||||
|
this.host.writeFile(
|
||||||
|
emitPath, PREAMBLE + generatedModule.source, false, () => {}, [sourceFile]);
|
||||||
|
});
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(
|
static create(
|
||||||
@ -99,7 +159,7 @@ export class CodeGenerator {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`The translation file (${transFile}) locale must be provided. Use the --locale option.`);
|
`The translation file (${transFile}) locale must be provided. Use the --locale option.`);
|
||||||
}
|
}
|
||||||
transContent = readFileSync(transFile, 'utf8');
|
transContent = nodeFs.readFileSync(transFile, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||||
@ -129,56 +189,21 @@ export class CodeGenerator {
|
|||||||
const resolver = new compiler.CompileMetadataResolver(
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
elementSchemaRegistry, normalizer, staticReflector);
|
elementSchemaRegistry, staticReflector);
|
||||||
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
// TODO(vicb): do not pass cliOptions.i18nFormat here
|
||||||
const offlineCompiler = new compiler.OfflineCompiler(
|
const offlineCompiler = new compiler.OfflineCompiler(
|
||||||
resolver, tmplParser, new compiler.StyleCompiler(urlResolver),
|
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
|
||||||
new compiler.ViewCompiler(config, elementSchemaRegistry),
|
new compiler.ViewCompiler(config), new compiler.DirectiveWrapperCompiler(config),
|
||||||
new compiler.DirectiveWrapperCompiler(
|
|
||||||
config, expressionParser, elementSchemaRegistry, console),
|
|
||||||
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
||||||
cliOptions.locale, cliOptions.i18nFormat,
|
cliOptions.locale, cliOptions.i18nFormat);
|
||||||
new compiler.AnimationParser(elementSchemaRegistry));
|
|
||||||
|
|
||||||
return new CodeGenerator(
|
return new CodeGenerator(
|
||||||
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractProgramSymbols(
|
export interface FileMetadata {
|
||||||
program: ts.Program, staticReflector: StaticReflector, reflectorHost: StaticReflectorHost,
|
fileUrl: string;
|
||||||
options: AngularCompilerOptions): StaticSymbol[] {
|
directives: StaticSymbol[];
|
||||||
// Compare with false since the default should be true
|
ngModules: StaticSymbol[];
|
||||||
const skipFileNames =
|
|
||||||
options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
|
||||||
|
|
||||||
const staticSymbols: StaticSymbol[] = [];
|
|
||||||
|
|
||||||
program.getSourceFiles()
|
|
||||||
.filter(sourceFile => !skipFileNames.test(sourceFile.fileName))
|
|
||||||
.forEach(sourceFile => {
|
|
||||||
const absSrcPath = reflectorHost.getCanonicalFileName(sourceFile.fileName);
|
|
||||||
|
|
||||||
const moduleMetadata = staticReflector.getModuleMetadata(absSrcPath);
|
|
||||||
if (!moduleMetadata) {
|
|
||||||
console.warn(`WARNING: no metadata found for ${absSrcPath}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = moduleMetadata['metadata'];
|
|
||||||
|
|
||||||
if (!metadata) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const symbol of Object.keys(metadata)) {
|
|
||||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
|
||||||
// Ignore symbols that are only included to record error information.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
staticSymbols.push(reflectorHost.findDeclaration(absSrcPath, symbol, absSrcPath));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return staticSymbols;
|
|
||||||
}
|
}
|
||||||
|
@ -10,32 +10,27 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract i18n messages from source code
|
* Extract i18n messages from source code
|
||||||
|
*
|
||||||
|
* TODO(vicb): factorize code with the CodeGenerator
|
||||||
*/
|
*/
|
||||||
// Must be imported first, because angular2 decorators throws on load.
|
// Must be imported first, because angular2 decorators throws on load.
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
import * as compiler from '@angular/compiler';
|
import * as compiler from '@angular/compiler';
|
||||||
import * as tsc from '@angular/tsc-wrapped';
|
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
import * as tsc from '@angular/tsc-wrapped';
|
||||||
import {Extractor} from './extractor';
|
import {Console} from './private_import_core';
|
||||||
|
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||||
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
|
import {StaticReflector, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
function extract(
|
function extract(
|
||||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
|
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
|
||||||
program: ts.Program, host: ts.CompilerHost) {
|
program: ts.Program, host: ts.CompilerHost) {
|
||||||
const resourceLoader: compiler.ResourceLoader = {
|
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
||||||
get: (s: string) => {
|
const extractor = Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, htmlParser);
|
||||||
if (!host.fileExists(s)) {
|
|
||||||
// TODO: We should really have a test for error cases like this!
|
|
||||||
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
|
||||||
}
|
|
||||||
return Promise.resolve(host.readFile(s));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const extractor =
|
|
||||||
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
|
|
||||||
|
|
||||||
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
|
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
|
||||||
|
|
||||||
return (bundlePromise).then(messageBundle => {
|
return (bundlePromise).then(messageBundle => {
|
||||||
@ -51,7 +46,6 @@ function extract(
|
|||||||
case 'xliff':
|
case 'xliff':
|
||||||
case 'xlf':
|
case 'xlf':
|
||||||
default:
|
default:
|
||||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
|
||||||
ext = 'xlf';
|
ext = 'xlf';
|
||||||
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
|
||||||
break;
|
break;
|
||||||
@ -62,6 +56,139 @@ function extract(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||||
|
|
||||||
|
export class Extractor {
|
||||||
|
constructor(
|
||||||
|
private program: ts.Program, public host: ts.CompilerHost,
|
||||||
|
private staticReflector: StaticReflector, private messageBundle: compiler.MessageBundle,
|
||||||
|
private reflectorHost: ReflectorHost,
|
||||||
|
private metadataResolver: compiler.CompileMetadataResolver,
|
||||||
|
private directiveNormalizer: compiler.DirectiveNormalizer) {}
|
||||||
|
|
||||||
|
private readFileMetadata(absSourcePath: string): FileMetadata {
|
||||||
|
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||||
|
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
|
||||||
|
if (!moduleMetadata) {
|
||||||
|
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const metadata = moduleMetadata['metadata'];
|
||||||
|
const symbols = metadata && Object.keys(metadata);
|
||||||
|
if (!symbols || !symbols.length) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (const symbol of symbols) {
|
||||||
|
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||||
|
// Ignore symbols that are only included to record error information.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||||
|
const annotations = this.staticReflector.annotations(staticType);
|
||||||
|
annotations.forEach((annotation) => {
|
||||||
|
if (annotation instanceof NgModule) {
|
||||||
|
result.ngModules.push(staticType);
|
||||||
|
} else if (annotation instanceof Component) {
|
||||||
|
result.components.push(staticType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extract(): Promise<compiler.MessageBundle> {
|
||||||
|
const filePaths =
|
||||||
|
this.program.getSourceFiles().map(sf => sf.fileName).filter(f => !GENERATED_FILES.test(f));
|
||||||
|
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||||
|
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
||||||
|
ngModules.push(...fileMeta.ngModules);
|
||||||
|
return ngModules;
|
||||||
|
}, <StaticSymbol[]>[]);
|
||||||
|
const analyzedNgModules = compiler.analyzeModules(ngModules, this.metadataResolver);
|
||||||
|
const errors: compiler.ParseError[] = [];
|
||||||
|
|
||||||
|
let bundlePromise =
|
||||||
|
Promise
|
||||||
|
.all(fileMetas.map((fileMeta) => {
|
||||||
|
const url = fileMeta.fileUrl;
|
||||||
|
return Promise.all(fileMeta.components.map(compType => {
|
||||||
|
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
|
||||||
|
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
|
||||||
|
if (!ngModule) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot determine the module for component ${compMeta.type.name}!`);
|
||||||
|
}
|
||||||
|
return Promise
|
||||||
|
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
||||||
|
dirMeta =>
|
||||||
|
this.directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
||||||
|
.then((normalizedCompWithDirectives) => {
|
||||||
|
const compMeta = normalizedCompWithDirectives[0];
|
||||||
|
const html = compMeta.template.template;
|
||||||
|
const interpolationConfig =
|
||||||
|
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
||||||
|
errors.push(
|
||||||
|
...this.messageBundle.updateFromTemplate(html, url, interpolationConfig));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}))
|
||||||
|
.then(_ => this.messageBundle);
|
||||||
|
|
||||||
|
if (errors.length) {
|
||||||
|
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bundlePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(
|
||||||
|
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
||||||
|
compilerHost: ts.CompilerHost, htmlParser: compiler.I18NHtmlParser,
|
||||||
|
reflectorHostContext?: ReflectorHostContext): Extractor {
|
||||||
|
const resourceLoader: compiler.ResourceLoader = {
|
||||||
|
get: (s: string) => {
|
||||||
|
if (!compilerHost.fileExists(s)) {
|
||||||
|
// TODO: We should really have a test for error cases like this!
|
||||||
|
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||||
|
}
|
||||||
|
return Promise.resolve(compilerHost.readFile(s));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||||
|
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||||
|
const staticReflector = new StaticReflector(reflectorHost);
|
||||||
|
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||||
|
|
||||||
|
const config = new compiler.CompilerConfig({
|
||||||
|
genDebugInfo: options.debug === true,
|
||||||
|
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||||
|
logBindingUpdate: false,
|
||||||
|
useJit: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizer =
|
||||||
|
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
||||||
|
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
||||||
|
const resolver = new compiler.CompileMetadataResolver(
|
||||||
|
new compiler.NgModuleResolver(staticReflector),
|
||||||
|
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||||
|
elementSchemaRegistry, staticReflector);
|
||||||
|
|
||||||
|
// TODO(vicb): implicit tags & attributes
|
||||||
|
let messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
||||||
|
|
||||||
|
return new Extractor(
|
||||||
|
program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver, normalizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileMetadata {
|
||||||
|
fileUrl: string;
|
||||||
|
components: StaticSymbol[];
|
||||||
|
ngModules: StaticSymbol[];
|
||||||
|
}
|
||||||
|
|
||||||
// Entry point
|
// Entry point
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const args = require('minimist')(process.argv.slice(2));
|
const args = require('minimist')(process.argv.slice(2));
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract i18n messages from source code
|
|
||||||
*/
|
|
||||||
// Must be imported first, because angular2 decorators throws on load.
|
|
||||||
import 'reflect-metadata';
|
|
||||||
|
|
||||||
import * as compiler from '@angular/compiler';
|
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
|
||||||
import * as tsc from '@angular/tsc-wrapped';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {extractProgramSymbols} from './codegen';
|
|
||||||
import {ReflectorHost} from './reflector_host';
|
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
|
||||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
|
||||||
|
|
||||||
export class Extractor {
|
|
||||||
constructor(
|
|
||||||
private options: tsc.AngularCompilerOptions, private program: ts.Program,
|
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
|
||||||
private messageBundle: compiler.MessageBundle, private reflectorHost: ReflectorHost,
|
|
||||||
private metadataResolver: compiler.CompileMetadataResolver) {}
|
|
||||||
|
|
||||||
extract(): Promise<compiler.MessageBundle> {
|
|
||||||
const programSymbols: StaticSymbol[] =
|
|
||||||
extractProgramSymbols(this.program, this.staticReflector, this.reflectorHost, this.options);
|
|
||||||
|
|
||||||
const {ngModules, files} = compiler.analyzeAndValidateNgModules(
|
|
||||||
programSymbols, {transitiveModules: true}, this.metadataResolver);
|
|
||||||
return compiler.loadNgModuleDirectives(ngModules).then(() => {
|
|
||||||
const errors: compiler.ParseError[] = [];
|
|
||||||
|
|
||||||
files.forEach(file => {
|
|
||||||
const compMetas: compiler.CompileDirectiveMetadata[] = [];
|
|
||||||
file.directives.forEach(directiveType => {
|
|
||||||
const dirMeta = this.metadataResolver.getDirectiveMetadata(directiveType);
|
|
||||||
if (dirMeta && dirMeta.isComponent) {
|
|
||||||
compMetas.push(dirMeta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
compMetas.forEach(compMeta => {
|
|
||||||
const html = compMeta.template.template;
|
|
||||||
const interpolationConfig =
|
|
||||||
compiler.InterpolationConfig.fromArray(compMeta.template.interpolation);
|
|
||||||
errors.push(
|
|
||||||
...this.messageBundle.updateFromTemplate(html, file.srcUrl, interpolationConfig));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.messageBundle;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static create(
|
|
||||||
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
|
|
||||||
compilerHost: ts.CompilerHost, resourceLoader: compiler.ResourceLoader,
|
|
||||||
reflectorHost?: ReflectorHost): Extractor {
|
|
||||||
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
|
|
||||||
|
|
||||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
|
||||||
if (!reflectorHost) reflectorHost = new ReflectorHost(program, compilerHost, options);
|
|
||||||
const staticReflector = new StaticReflector(reflectorHost);
|
|
||||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
|
||||||
|
|
||||||
const config = new compiler.CompilerConfig({
|
|
||||||
genDebugInfo: options.debug === true,
|
|
||||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
|
||||||
logBindingUpdate: false,
|
|
||||||
useJit: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const normalizer =
|
|
||||||
new compiler.DirectiveNormalizer(resourceLoader, urlResolver, htmlParser, config);
|
|
||||||
const elementSchemaRegistry = new compiler.DomElementSchemaRegistry();
|
|
||||||
const resolver = new compiler.CompileMetadataResolver(
|
|
||||||
new compiler.NgModuleResolver(staticReflector),
|
|
||||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
|
||||||
elementSchemaRegistry, normalizer, staticReflector);
|
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
|
||||||
const messageBundle = new compiler.MessageBundle(htmlParser, [], {});
|
|
||||||
|
|
||||||
return new Extractor(
|
|
||||||
options, program, compilerHost, staticReflector, messageBundle, reflectorHost, resolver);
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,9 +19,7 @@ import {CodeGenerator} from './codegen';
|
|||||||
function codegen(
|
function codegen(
|
||||||
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
|
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.NgcCliOptions, program: ts.Program,
|
||||||
host: ts.CompilerHost) {
|
host: ts.CompilerHost) {
|
||||||
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen({
|
return CodeGenerator.create(ngOptions, cliOptions, program, host).codegen();
|
||||||
transitiveModules: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLI entry point
|
// CLI entry point
|
||||||
|
@ -34,7 +34,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
getCanonicalFileName(fileName: string): string {
|
getCanonicalFileName(fileName: string): string {
|
||||||
if (!fileName) return fileName;
|
if (!fileName) return fileName;
|
||||||
// NB: the rootDirs should have been sorted longest-first
|
// NB: the rootDirs should have been sorted longest-first
|
||||||
for (const dir of this.options.rootDirs || []) {
|
for (let dir of this.options.rootDirs || []) {
|
||||||
if (fileName.indexOf(dir) === 0) {
|
if (fileName.indexOf(dir) === 0) {
|
||||||
fileName = fileName.substring(dir.length);
|
fileName = fileName.substring(dir.length);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
console.log('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||||
}
|
}
|
||||||
return resolved.resolvedFileName;
|
return resolved.resolvedFileName;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||||
|
|
||||||
if (this.options.traceResolution) {
|
if (this.options.traceResolution) {
|
||||||
console.error(
|
console.log(
|
||||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ export class PathMappedReflectorHost extends ReflectorHost {
|
|||||||
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
|
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const importModuleName = importedFile.replace(EXT, '');
|
let importModuleName = importedFile.replace(EXT, '');
|
||||||
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
||||||
let foundRelativeImport: string;
|
let foundRelativeImport: string;
|
||||||
for (let index = parts.length - 1; index >= 0; index--) {
|
for (let index = parts.length - 1; index >= 0; index--) {
|
||||||
|
@ -40,7 +40,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
||||||
|
|
||||||
this.context = context || new NodeReflectorHostContext(compilerHost);
|
this.context = context || new NodeReflectorHostContext(compilerHost);
|
||||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
var genPath: string = path.relative(this.basePath, this.genDir);
|
||||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +67,13 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected normalizeAssetUrl(url: string): string {
|
protected normalizeAssetUrl(url: string): string {
|
||||||
const assetUrl = AssetUrl.parse(url);
|
let assetUrl = AssetUrl.parse(url);
|
||||||
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
const path = assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||||
return this.getCanonicalFileName(path);
|
return this.getCanonicalFileName(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resolveAssetUrl(url: string, containingFile: string): string {
|
protected resolveAssetUrl(url: string, containingFile: string): string {
|
||||||
const assetUrl = this.normalizeAssetUrl(url);
|
let assetUrl = this.normalizeAssetUrl(url);
|
||||||
if (assetUrl) {
|
if (assetUrl) {
|
||||||
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
|
return this.getCanonicalFileName(this.resolve(assetUrl, containingFile));
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
// drop extension
|
// drop extension
|
||||||
importedFile = importedFile.replace(EXT, '');
|
importedFile = importedFile.replace(EXT, '');
|
||||||
|
|
||||||
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
var nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
||||||
const importModule = nodeModulesIndex === -1 ?
|
const importModule = nodeModulesIndex === -1 ?
|
||||||
null :
|
null :
|
||||||
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
||||||
@ -141,7 +141,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private dotRelative(from: string, to: string): string {
|
private dotRelative(from: string, to: string): string {
|
||||||
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
var rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||||
*/
|
*/
|
||||||
private rewriteGenDirPath(filepath: string) {
|
private rewriteGenDirPath(filepath: string) {
|
||||||
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
var nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
||||||
if (nodeModulesIndex !== -1) {
|
if (nodeModulesIndex !== -1) {
|
||||||
// If we are in node_modulse, transplant them into `genDir`.
|
// If we are in node_modulse, transplant them into `genDir`.
|
||||||
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
||||||
@ -172,7 +172,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const assetUrl = this.normalizeAssetUrl(module);
|
let assetUrl = this.normalizeAssetUrl(module);
|
||||||
if (assetUrl) {
|
if (assetUrl) {
|
||||||
module = assetUrl;
|
module = assetUrl;
|
||||||
}
|
}
|
||||||
@ -250,12 +250,6 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
} else {
|
} else {
|
||||||
const sf = this.program.getSourceFile(filePath);
|
const sf = this.program.getSourceFile(filePath);
|
||||||
if (!sf) {
|
if (!sf) {
|
||||||
if (this.context.fileExists(filePath)) {
|
|
||||||
const sourceText = this.context.readFile(filePath);
|
|
||||||
return this.metadataCollector.getMetadata(
|
|
||||||
ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Source file ${filePath} not present in program.`);
|
throw new Error(`Source file ${filePath} not present in program.`);
|
||||||
}
|
}
|
||||||
return this.metadataCollector.getMetadata(sf);
|
return this.metadataCollector.getMetadata(sf);
|
||||||
@ -288,7 +282,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
|||||||
}
|
}
|
||||||
return resolvedModulePath;
|
return resolvedModulePath;
|
||||||
};
|
};
|
||||||
const metadata = this.getResolverMetadata(filePath);
|
let metadata = this.getResolverMetadata(filePath);
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
// If we have metadata for the symbol, this is the original exporting location.
|
// If we have metadata for the symbol, this is the original exporting location.
|
||||||
if (metadata.metadata[symbolName]) {
|
if (metadata.metadata[symbolName]) {
|
||||||
|
@ -25,7 +25,7 @@ export interface StaticReflectorHost {
|
|||||||
* @param modulePath is a string identifier for a module as an absolute path.
|
* @param modulePath is a string identifier for a module as an absolute path.
|
||||||
* @returns the metadata for the given module.
|
* @returns the metadata for the given module.
|
||||||
*/
|
*/
|
||||||
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
|
getMetadataFor(modulePath: string): {[key: string]: any};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||||
@ -72,12 +72,13 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||||
|
|
||||||
importUri(typeOrFunc: StaticSymbol): string {
|
importUri(typeOrFunc: StaticSymbol): string {
|
||||||
const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
var staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||||
return staticSymbol ? staticSymbol.filePath : null;
|
return staticSymbol ? staticSymbol.filePath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
||||||
return this.host.findDeclaration(moduleUrl, name, '');
|
const result = this.host.findDeclaration(moduleUrl, name, '');
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveEnum(enumIdentifier: any, name: string): any {
|
resolveEnum(enumIdentifier: any, name: string): any {
|
||||||
@ -88,7 +89,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
public annotations(type: StaticSymbol): any[] {
|
public annotations(type: StaticSymbol): any[] {
|
||||||
let annotations = this.annotationCache.get(type);
|
let annotations = this.annotationCache.get(type);
|
||||||
if (!annotations) {
|
if (!annotations) {
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
let classMetadata = this.getTypeMetadata(type);
|
||||||
if (classMetadata['decorators']) {
|
if (classMetadata['decorators']) {
|
||||||
annotations = this.simplify(type, classMetadata['decorators']);
|
annotations = this.simplify(type, classMetadata['decorators']);
|
||||||
} else {
|
} else {
|
||||||
@ -102,11 +103,11 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||||
let propMetadata = this.propertyCache.get(type);
|
let propMetadata = this.propertyCache.get(type);
|
||||||
if (!propMetadata) {
|
if (!propMetadata) {
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
let classMetadata = this.getTypeMetadata(type);
|
||||||
const members = classMetadata ? classMetadata['members'] : {};
|
let members = classMetadata ? classMetadata['members'] : {};
|
||||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||||
const prop = (<any[]>propData)
|
let prop = (<any[]>propData)
|
||||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||||
if (prop && prop['decorators']) {
|
if (prop && prop['decorators']) {
|
||||||
return this.simplify(type, prop['decorators']);
|
return this.simplify(type, prop['decorators']);
|
||||||
} else {
|
} else {
|
||||||
@ -125,20 +126,21 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
try {
|
try {
|
||||||
let parameters = this.parameterCache.get(type);
|
let parameters = this.parameterCache.get(type);
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
const classMetadata = this.getTypeMetadata(type);
|
let classMetadata = this.getTypeMetadata(type);
|
||||||
const members = classMetadata ? classMetadata['members'] : null;
|
let members = classMetadata ? classMetadata['members'] : null;
|
||||||
const ctorData = members ? members['__ctor__'] : null;
|
let ctorData = members ? members['__ctor__'] : null;
|
||||||
if (ctorData) {
|
if (ctorData) {
|
||||||
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||||
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
let parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||||
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
let parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||||
|
|
||||||
parameters = [];
|
parameters = [];
|
||||||
parameterTypes.forEach((paramType, index) => {
|
parameterTypes.forEach((paramType, index) => {
|
||||||
const nestedResult: any[] = [];
|
let nestedResult: any[] = [];
|
||||||
if (paramType) {
|
if (paramType) {
|
||||||
nestedResult.push(paramType);
|
nestedResult.push(paramType);
|
||||||
}
|
}
|
||||||
const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
let decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||||
if (decorators) {
|
if (decorators) {
|
||||||
nestedResult.push(...decorators);
|
nestedResult.push(...decorators);
|
||||||
}
|
}
|
||||||
@ -152,7 +154,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
return parameters;
|
return parameters;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,9 +238,9 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public simplify(context: StaticSymbol, value: any): any {
|
public simplify(context: StaticSymbol, value: any): any {
|
||||||
const _this = this;
|
let _this = this;
|
||||||
let scope = BindingScope.empty;
|
let scope = BindingScope.empty;
|
||||||
const calling = new Map<StaticSymbol, boolean>();
|
let calling = new Map<StaticSymbol, boolean>();
|
||||||
|
|
||||||
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
||||||
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
||||||
@ -253,15 +255,16 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||||
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
let result: any = staticSymbol;
|
||||||
const declarationValue =
|
let moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||||
|
let declarationValue =
|
||||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||||
return declarationValue;
|
return declarationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
||||||
if (value && value.__symbolic === 'new' && value.expression) {
|
if (value && value.__symbolic === 'new' && value.expression) {
|
||||||
const target = value.expression;
|
let target = value.expression;
|
||||||
if (target.__symbolic == 'reference') {
|
if (target.__symbolic == 'reference') {
|
||||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
||||||
}
|
}
|
||||||
@ -272,7 +275,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
function simplifyCall(expression: any) {
|
function simplifyCall(expression: any) {
|
||||||
let callContext: {[name: string]: string}|undefined = undefined;
|
let callContext: {[name: string]: string}|undefined = undefined;
|
||||||
if (expression['__symbolic'] == 'call') {
|
if (expression['__symbolic'] == 'call') {
|
||||||
const target = expression['expression'];
|
let target = expression['expression'];
|
||||||
let functionSymbol: StaticSymbol;
|
let functionSymbol: StaticSymbol;
|
||||||
let targetFunction: any;
|
let targetFunction: any;
|
||||||
if (target) {
|
if (target) {
|
||||||
@ -315,7 +318,7 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
for (let i = 0; i < parameters.length; i++) {
|
for (let i = 0; i < parameters.length; i++) {
|
||||||
functionScope.define(parameters[i], args[i]);
|
functionScope.define(parameters[i], args[i]);
|
||||||
}
|
}
|
||||||
const oldScope = scope;
|
let oldScope = scope;
|
||||||
let result: any;
|
let result: any;
|
||||||
try {
|
try {
|
||||||
scope = functionScope.done();
|
scope = functionScope.done();
|
||||||
@ -346,19 +349,19 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
if (expression instanceof Array) {
|
if (expression instanceof Array) {
|
||||||
const result: any[] = [];
|
let result: any[] = [];
|
||||||
for (const item of (<any>expression)) {
|
for (let item of (<any>expression)) {
|
||||||
// Check for a spread expression
|
// Check for a spread expression
|
||||||
if (item && item.__symbolic === 'spread') {
|
if (item && item.__symbolic === 'spread') {
|
||||||
const spreadArray = simplify(item.expression);
|
let spreadArray = simplify(item.expression);
|
||||||
if (Array.isArray(spreadArray)) {
|
if (Array.isArray(spreadArray)) {
|
||||||
for (const spreadItem of spreadArray) {
|
for (let spreadItem of spreadArray) {
|
||||||
result.push(spreadItem);
|
result.push(spreadItem);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const value = simplify(item);
|
let value = simplify(item);
|
||||||
if (shouldIgnore(value)) {
|
if (shouldIgnore(value)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -465,8 +468,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
return null;
|
return null;
|
||||||
case 'reference':
|
case 'reference':
|
||||||
if (!expression.module) {
|
if (!expression.module) {
|
||||||
const name: string = expression['name'];
|
let name: string = expression['name'];
|
||||||
const localValue = scope.resolve(name);
|
let localValue = scope.resolve(name);
|
||||||
if (localValue != BindingScope.missing) {
|
if (localValue != BindingScope.missing) {
|
||||||
return localValue;
|
return localValue;
|
||||||
}
|
}
|
||||||
@ -515,8 +518,6 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (expression['line']) {
|
if (expression['line']) {
|
||||||
message =
|
message =
|
||||||
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
|
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
|
||||||
throw positionalError(
|
|
||||||
message, context.filePath, expression['line'], expression['character']);
|
|
||||||
}
|
}
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
@ -530,15 +531,11 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
try {
|
try {
|
||||||
return simplify(value);
|
return simplify(value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`;
|
throw new Error(`${e.message}, resolving symbol ${context.name} in ${context.filePath}`);
|
||||||
if (e.fileName) {
|
|
||||||
throw positionalError(message, e.fileName, e.line, e.column);
|
|
||||||
}
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = simplifyInContext(context, value, 0);
|
let result = simplifyInContext(context, value, 0);
|
||||||
if (shouldIgnore(result)) {
|
if (shouldIgnore(result)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -553,7 +550,8 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
moduleMetadata = this.host.getMetadataFor(module);
|
moduleMetadata = this.host.getMetadataFor(module);
|
||||||
if (Array.isArray(moduleMetadata)) {
|
if (Array.isArray(moduleMetadata)) {
|
||||||
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
|
moduleMetadata = (<Array<any>>moduleMetadata)
|
||||||
|
.find(element => element.version === SUPPORTED_SCHEMA_VERSION) ||
|
||||||
moduleMetadata[0];
|
moduleMetadata[0];
|
||||||
}
|
}
|
||||||
if (!moduleMetadata) {
|
if (!moduleMetadata) {
|
||||||
@ -570,8 +568,12 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||||
const moduleMetadata = this.getModuleMetadata(type.filePath);
|
let moduleMetadata = this.getModuleMetadata(type.filePath);
|
||||||
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'};
|
let result = moduleMetadata['metadata'][type.name];
|
||||||
|
if (!result) {
|
||||||
|
result = {__symbolic: 'class'};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,9 +613,9 @@ function produceErrorMessage(error: any): string {
|
|||||||
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
||||||
{[key: string]: any} {
|
{[key: string]: any} {
|
||||||
if (!input) return {};
|
if (!input) return {};
|
||||||
const result: {[key: string]: any} = {};
|
var result: {[key: string]: any} = {};
|
||||||
Object.keys(input).forEach((key) => {
|
Object.keys(input).forEach((key) => {
|
||||||
const value = transform(input[key], key);
|
let value = transform(input[key], key);
|
||||||
if (!shouldIgnore(value)) {
|
if (!shouldIgnore(value)) {
|
||||||
result[key] = value;
|
result[key] = value;
|
||||||
}
|
}
|
||||||
@ -636,7 +638,8 @@ abstract class BindingScope {
|
|||||||
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
||||||
|
|
||||||
public static build(): BindingScopeBuilder {
|
public static build(): BindingScopeBuilder {
|
||||||
const current = new Map<string, any>();
|
let current = new Map<string, any>();
|
||||||
|
let parent: BindingScope = undefined;
|
||||||
return {
|
return {
|
||||||
define: function(name, value) {
|
define: function(name, value) {
|
||||||
current.set(name, value);
|
current.set(name, value);
|
||||||
@ -664,11 +667,3 @@ function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
|||||||
function shouldIgnore(value: any): boolean {
|
function shouldIgnore(value: any): boolean {
|
||||||
return value && value.__symbolic == 'ignore';
|
return value && value.__symbolic == 'ignore';
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
|
||||||
const result = new Error(message);
|
|
||||||
(result as any).fileName = fileName;
|
|
||||||
(result as any).line = line;
|
|
||||||
(result as any).column = column;
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
directoryExists(path: string): boolean { return typeof this.getEntry(path) === 'object'; }
|
directoryExists(path: string): boolean { return typeof this.getEntry(path) === 'object'; }
|
||||||
|
|
||||||
readFile(fileName: string): string|undefined {
|
readFile(fileName: string): string|undefined {
|
||||||
const data = this.getEntry(fileName);
|
let data = this.getEntry(fileName);
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -29,9 +29,9 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeFile(fileName: string, data: string): void {
|
writeFile(fileName: string, data: string): void {
|
||||||
const parts = fileName.split('/');
|
let parts = fileName.split('/');
|
||||||
const name = parts.pop();
|
let name = parts.pop();
|
||||||
const entry = this.getEntry(parts);
|
let entry = this.getEntry(parts);
|
||||||
if (entry && typeof entry !== 'string') {
|
if (entry && typeof entry !== 'string') {
|
||||||
entry[name] = data;
|
entry[name] = data;
|
||||||
}
|
}
|
||||||
@ -48,11 +48,11 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
parts = normalize(parts);
|
parts = normalize(parts);
|
||||||
let current = this.files;
|
let current = this.files;
|
||||||
while (parts.length) {
|
while (parts.length) {
|
||||||
const part = parts.shift();
|
let part = parts.shift();
|
||||||
if (typeof current === 'string') {
|
if (typeof current === 'string') {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const next = (<Directory>current)[part];
|
let next = (<Directory>current)[part];
|
||||||
if (next === undefined) {
|
if (next === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -72,9 +72,9 @@ export class MockContext implements ReflectorHostContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalize(parts: string[]): string[] {
|
function normalize(parts: string[]): string[] {
|
||||||
const result: string[] = [];
|
let result: string[] = [];
|
||||||
while (parts.length) {
|
while (parts.length) {
|
||||||
const part = parts.shift();
|
let part = parts.shift();
|
||||||
switch (part) {
|
switch (part) {
|
||||||
case '.':
|
case '.':
|
||||||
break;
|
break;
|
||||||
@ -102,7 +102,7 @@ export class MockCompilerHost implements ts.CompilerHost {
|
|||||||
getSourceFile(
|
getSourceFile(
|
||||||
fileName: string, languageVersion: ts.ScriptTarget,
|
fileName: string, languageVersion: ts.ScriptTarget,
|
||||||
onError?: (message: string) => void): ts.SourceFile {
|
onError?: (message: string) => void): ts.SourceFile {
|
||||||
const sourceText = this.context.readFile(fileName);
|
let sourceText = this.context.readFile(fileName);
|
||||||
if (sourceText) {
|
if (sourceText) {
|
||||||
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
||||||
} else {
|
} else {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user