Compare commits

...

64 Commits

Author SHA1 Message Date
ab41920e30 fix(compiler): avoid a crash in ngc-wrapped.
`ng.performCompilation` can return an `undefined` program, which is not handled by ngc-wrapped.

Avoid crashing by checking for the error return and returning the diagnostics.
2018-04-20 14:53:32 +02:00
ca776c59dd fix(compiler): handle undefined annotation metadata (#23349)
In certain cases seen in production, simplify() can returned
undefined when simplifying decorator metadata. This has proven tricky
to reproduce in an isolated test, but the fix is simple and low-risk:
don't attempt to spread an undefined set of annotations in the first
place.

PR Close #23349
2018-04-19 18:57:22 -07:00
f2563ca800 ci(compiler): replace chuckjaz with alxhub as compiler owner (#23456)
PR Close #23456
2018-04-19 16:32:31 -07:00
9757347e71 feat(ivy): add an IVY local the compiler which avoids analyzeModule (#23441)
closes #23289

Based on a spike by @chukjaz

PR Close #23441
2018-04-19 16:32:09 -07:00
a19e018439 refactor(ivy): remove the backpatch compiler (#23441)
PR Close #23441
2018-04-19 16:32:09 -07:00
6ff164be0e refactor(compiler): misc minor (#23441)
PR Close #23441
2018-04-19 16:32:09 -07:00
84f024309a refactor(ivy): misc cleanup (#23441)
PR Close #23441
2018-04-19 16:32:09 -07:00
c6b206ee4b feat(compiler): support // ... and // TODO in mock compiler expectations (#23441)
PR Close #23441
2018-04-19 16:32:09 -07:00
1d1e75ee2b Revert "fix(compiler): Pretty print object instead of [Object object] (#22689)" (#23442)
This reverts commit 8555a3a3cd.

Reverted because of https://github.com/angular/angular/issues/23440

PR Close #23442
2018-04-19 14:51:58 -07:00
acf6781ccc test(core): add a symbols test for renderer2 code (#23436)
PR Close #23436
2018-04-18 14:49:29 -07:00
fd48e53986 docs(aio): add front page campaign for the ng-conf live stream (#23391)
PR Close #23391
2018-04-17 14:13:43 -07:00
fe312ccb4c docs(aio): Cleanup examples with edits from Igor/George (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
764f471dc0 build(aio): turn on webpack's stats.json generation for debugging purposes (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
8b02c0e769 build(aio): add @angular/language-service (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
5a2ee7a6f5 docs(aio): Bump shared yarn.lock file for examples (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
529d4fc9ee docs(aio): Bump shared dependencies to RC5 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
fac7dde5b1 docs(aio): Fix failing upgrade-module tests (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
1f005908a4 docs(aio): Fix failing boilerplate tests (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
2278fe8f0e docs(aio): Upgrade examples to Angular 6 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
aad3444a58 test(aio): fix failing tests (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
44377adbcc docs(aio): update yarn test command in README.md (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
b28b3acb83 build(aio): update to @angular/material@6.0.0-rc.11 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
7493435911 test(aio): move reflect-metadata polyfills to test.ts (#23234)
This resolves https://github.com/angular/angular-cli/issues/10333 and nicely cleans up the code.

PR Close #23234
2018-04-17 14:09:02 -07:00
937f7cea37 build(aio): update to angular/core@6.0.0-rc.5 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
7d1990e4d1 style(aio): lint fixes for examples (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
76f8ae31ad test(aio): fix tests and update testing infra (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
103846a51d build(aio): update tslint and codelyzer (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
0a536af093 build(aio): fix deployment script (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
4f29287399 build(aio): upgrade @angular/cli to 6.0.0-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
62e6c1f43a build(aio): upgrade @angular/* to 6.0.0-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
c3c513ed9e build(aio): remove redundant flags from cli commands (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
ed495bc9f1 build(aio): switch to webpack-cli for IE polyfills (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
a3de5f8f20 build(aio): upgrade rxjs to 6.0.0-turbo-rc.4 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
2491b7249a ci: chown bazel-built packages when running integration tests (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
a851ba3781 build: update to rxjs@6.0.0-uncanny-rc.7 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
0468a649af build: remove a postinstall-patch to fix rxjs (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
47d3acdc49 build(aio): reorder entries in package.json (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
acbfb9eb4d build: fix angular.json that was missing keys due to cli bugs (#23234)
https://github.com/angular/angular-cli/issues/10225
https://github.com/angular/angular-cli/issues/10226

PR Close #23234
2018-04-17 14:09:02 -07:00
d35f84a167 build(aio): update to @angular/material@6.0.0-rc.1 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
87e9f333d4 build(aio): update to @angular/material@5.2.4 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
08fc4f3ad8 build: update to rxjs@6.0.0-tactical-rc.1 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
c6c79ab5dc test: simplify config for cli-hello-world (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
6837491f08 test: update cli-hello-world to cli@6.0.0-rc.2 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
fc5af69fb2 build(aio): update to @angular/cli@6.0.0-rc.2 + project layout update (#23234)
project layout was updated using:
yarn ng update @angular/cli --migrate-only --from=1.7.3

PR Close #23234
2018-04-17 14:09:02 -07:00
81ccb718b1 build(aio): upgrade to @angular/*@6.0.0-rc.3 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
0c56dfadef build(aio): upgrade to @angular/*@6.0.0-rc.2 (#23234)
PR Close #23234
2018-04-17 14:09:02 -07:00
7be7abdebd refactor(animations): use a const enum to avoid compilation side effects (#23402)
This patch is in response to #23401 where a non-const enum was being
compiled as an empty object when used in an animation player when
`ng build --prod` was being processed. This patch is a immediate fix
for the issue and #23400 tracks it.

Closes #23401

PR Close #23402
2018-04-17 14:03:01 -07:00
5a1ddee88c refactor(ivy): speed up bound text nodes (#23386)
PR Close #23386
2018-04-17 13:49:19 -07:00
99f8e10809 ci(aio): fix aio-monitoring tests (#23390)
Previously, we were running the e2e tests from master against
`https://angular.io` (deployed from the stable branch). Often the e2e
tests from master do not apply to the stable branch, since the app has
deviated slightly.

This commit fixes this by stop running the full e2e tests against the
deployed versions, but a smaller set of "smoke tests", which check basic
functionality that is less likely to change between versions.

PR Close #23390
2018-04-17 13:45:38 -07:00
d665d9a18c refactor(aio): rename directory (tests/deployment-config --> tests/deployment) (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
8b2101be9f refactor(aio): rename spec file (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
0d56cee9e1 refactor(aio): rename yarn script (deployment-config-test --> redirects-test) (#23390)
PR Close #23390
2018-04-17 13:45:38 -07:00
7f612fc828 fix(ivy): generate bind calls for property bindings (#23403)
PR Close #23403
2018-04-17 13:44:48 -07:00
010a4efa8c docs(aio): Fixed typo in 'comparing observables -> Observabled compared (#23407)
to arrays' section.

PR Close #23407
2018-04-17 13:43:45 -07:00
c3280b2c2f docs(aio): change HeroService to MessagesComponent (#23397)
Someone probably forgot to change this when coping the sentence from a previous section.

PR Close #23397
2018-04-16 09:01:58 -07:00
d11b249d36 docs(aio): change HeroDetailsComponent to HeroDetailComponent (#23397)
For the sake of consistency, change `HeroDetailsComponent` to `HeroDetailComponent`.

PR Close #23397
2018-04-16 09:01:58 -07:00
9c29127723 docs: document how to enable service worker using ng add command (#23348)
PR Close #23348
2018-04-16 08:57:13 -07:00
43615604d1 docs: add changelog for the v4.4.7 release 2018-04-16 03:09:48 -06:00
d9792309ec docs: release notes for the 5.2.10 release 2018-04-16 01:44:59 -06:00
fd1c39ce42 docs: update lifecycle hooks section in cheatsheet (#23320)
PR Close #23320
2018-04-15 23:44:44 -07:00
896811df64 docs(aio): add missing word in the Component metadata section (#23384)
PR Close #23384
2018-04-15 23:36:55 -07:00
fb59b2dd97 fix(service-worker): add badge to NOTIFICATION_OPTION_NAMES (#23241)
Add badge to NOTIFICATION_OPTION_NAMES to support custom notification badge/icon.
Fixes #23196
PR Close #23241
2018-04-15 23:23:37 -07:00
b64a276d4b refactor(ivy): make return value of define(Component|Directive|Pipe|Injector|Injectable) private (#23371) (#23383)
Ivy definition looks something like this:

```
class MyService {
  static ngInjectableDef = defineInjectable({
    …
  });
}
```

Here the argument to `defineInjectable` is well known public contract which needs
to be honored in backward compatible way between versions. The type of the
return value of `defineInjectable` on the other hand is private and can change
shape drastically between versions without effecting backwards compatibility of
libraries publish to NPM. To our users it is effectively an opaque token.
For this reson why declare the return value of `defineInjectable` as `never`.

PR Close #23383
2018-04-14 20:40:14 -07:00
815ae29b83 build: add '@angular/elements' the framework package group 2018-04-14 03:48:32 -07:00
190 changed files with 10147 additions and 3456 deletions

View File

@ -162,7 +162,7 @@ groups:
files:
- "packages/compiler/*"
users:
- chuckjaz #primary
- alxhub #primary
- vicb
- mhevery
- IgorMinar #fallback

View File

@ -1,5 +1,36 @@
<a name="5.2.10"></a>
## [5.2.10](https://github.com/angular/angular/compare/5.2.9...5.2.10) (2018-04-16)
### Bug Fixes
* **animations:** avoid animation insertions during router back/refresh ([#21977](https://github.com/angular/angular/issues/21977)) ([641cc49](https://github.com/angular/angular/commit/641cc49)), closes [#19712](https://github.com/angular/angular/issues/19712)
* **common:** properly take className changes into account ([#21937](https://github.com/angular/angular/issues/21937)) ([54e9108](https://github.com/angular/angular/commit/54e9108)), closes [#21932](https://github.com/angular/angular/issues/21932)
* **compiler:** fix support for html-like text in translatable attributes ([#23053](https://github.com/angular/angular/issues/23053)) ([4f7c369](https://github.com/angular/angular/commit/4f7c369))
* **compiler-cli:** emit correct css string escape sequences ([#22776](https://github.com/angular/angular/issues/22776)) ([db0afa9](https://github.com/angular/angular/commit/db0afa9))
* **forms:** improve error message for invalid value accessors ([#22731](https://github.com/angular/angular/issues/22731)) ([dd61595](https://github.com/angular/angular/commit/dd61595))
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([7b23983](https://github.com/angular/angular/commit/7b23983)), closes [#23196](https://github.com/angular/angular/issues/23196)
* **service-worker:** do not enter degraded mode when offline ([#22883](https://github.com/angular/angular/issues/22883)) ([ae9c25f](https://github.com/angular/angular/commit/ae9c25f)), closes [#21636](https://github.com/angular/angular/issues/21636)
* **service-worker:** fix LruList bugs ([#22769](https://github.com/angular/angular/issues/22769)) ([65f8943](https://github.com/angular/angular/commit/65f8943)), closes [#22218](https://github.com/angular/angular/issues/22218) [#22768](https://github.com/angular/angular/issues/22768)
* **service-worker:** ignore invalid `only-if-cached` requests ([#22883](https://github.com/angular/angular/issues/22883)) ([0d4fe38](https://github.com/angular/angular/commit/0d4fe38)), closes [#22362](https://github.com/angular/angular/issues/22362)
* **upgrade:** correctly handle downgraded `OnPush` components ([#22209](https://github.com/angular/angular/issues/22209)) ([f43fba6](https://github.com/angular/angular/commit/f43fba6)), closes [#14286](https://github.com/angular/angular/issues/14286)
* **upgrade:** propagate return value of resumeBootstrap ([#22754](https://github.com/angular/angular/issues/22754)) ([ae76eec](https://github.com/angular/angular/commit/ae76eec)), closes [#22723](https://github.com/angular/angular/issues/22723)
* **upgrade:** two-way binding and listening for event ([#22772](https://github.com/angular/angular/issues/22772)) ([5391f96](https://github.com/angular/angular/commit/5391f96)), closes [#22734](https://github.com/angular/angular/issues/22734)
<a name="4.4.7"></a>
## [4.4.7](https://github.com/angular/angular/compare/4.4.6...4.4.7) (2018-04-16)
### Bug Fixes
* **core:** use appropriate inert document strategy for Firefox & Safari ([#22077](https://github.com/angular/angular/issues/22077)) ([2c5cf19](https://github.com/angular/angular/commit/2c5cf19))
<a name="6.0.0-rc.5"></a>
# [6.0.0-rc.5](https://github.com/angular/angular/compare/6.0.0-rc.4...6.0.0-rc.5) (2018-04-14)
## [6.0.0-rc.5](https://github.com/angular/angular/compare/6.0.0-rc.4...6.0.0-rc.5) (2018-04-14)
### Bug Fixes
@ -18,7 +49,7 @@
<a name="6.0.0-rc.4"></a>
# [6.0.0-rc.4](https://github.com/angular/angular/compare/6.0.0-rc.3...6.0.0-rc.4) (2018-04-12)
## [6.0.0-rc.4](https://github.com/angular/angular/compare/6.0.0-rc.3...6.0.0-rc.4) (2018-04-12)
### Bug Fixes
@ -39,7 +70,7 @@
<a name="6.0.0-rc.3"></a>
# [6.0.0-rc.3](https://github.com/angular/angular/compare/6.0.0-rc.2...6.0.0-rc.3) (2018-04-06)
## [6.0.0-rc.3](https://github.com/angular/angular/compare/6.0.0-rc.2...6.0.0-rc.3) (2018-04-06)
### Bug Fixes
@ -50,7 +81,7 @@
<a name="6.0.0-rc.2"></a>
# [6.0.0-rc.2](https://github.com/angular/angular/compare/6.0.0-rc.1...6.0.0-rc.2) (2018-04-05)
## [6.0.0-rc.2](https://github.com/angular/angular/compare/6.0.0-rc.1...6.0.0-rc.2) (2018-04-05)
### Bug Fixes
@ -71,7 +102,7 @@
<a name="6.0.0-rc.1"></a>
# [6.0.0-rc.1](https://github.com/angular/angular/compare/6.0.0-rc.0...6.0.0-rc.1) (2018-03-30)
## [6.0.0-rc.1](https://github.com/angular/angular/compare/6.0.0-rc.0...6.0.0-rc.1) (2018-03-30)
### Bug Fixes
@ -131,7 +162,7 @@ possible, and ideally also a include a good reproduction of the problem.
<a name="6.0.0-beta.8"></a>
# [6.0.0-beta.8](https://github.com/angular/angular/compare/6.0.0-beta.7...6.0.0-beta.8) (2018-03-16)
## [6.0.0-beta.8](https://github.com/angular/angular/compare/6.0.0-beta.7...6.0.0-beta.8) (2018-03-16)
### Bug Fixes

View File

@ -1,73 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "site"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"generated",
"app/search/search-worker.js",
"favicon.ico",
"pwa-manifest.json",
"google385281288605d160.html",
{ "glob": "custom-elements.min.js", "input": "../node_modules/@webcomponents/custom-elements", "output": "./assets/js" },
{ "glob": "native-shim.js", "input": "../node_modules/@webcomponents/custom-elements/src", "output": "./assets/js" }
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "aio",
"serviceWorker": false,
"styles": [
"styles.scss"
],
"scripts": [
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"next": "environments/environment.next.ts",
"stable": "environments/environment.stable.ts",
"archive": "environments/environment.archive.ts"
}
}
],
"e2e": {
"protractor": {
"config": "tests/e2e/protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json"
},
{
"project": "src/tsconfig.spec.json"
},
{
"project": "tests/e2e/tsconfig.e2e.json"
}
],
"test": {
"karma": {
"config": "src/karma.conf.js"
}
},
"defaults": {
"styleExt": "scss",
"component": {
"inlineStyle": true
},
"build": {
"namedChunks": true
}
},
"packageManager": "yarn"
}

View File

@ -22,7 +22,8 @@ Here are the most important tasks you might need to use:
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
* `yarn lint` - check that the doc-viewer code follows our style rules.
* `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
* `yarn test` - run all the unit tests once.
* `yarn test --watch` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
* `yarn e2e` - run all the e2e tests for the doc-viewer.
* `yarn docs` - generate all the docs from the source files.

235
aio/angular.json Normal file
View File

@ -0,0 +1,235 @@
{
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
"version": 1,
"cli": {
"packageManager": "yarn"
},
"newProjectRoot": "projects",
"projects": {
"site": {
"root": "",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"aot": true,
"optimization": true,
"buildOptimizer": true,
"outputHashing": "all",
"sourceMap": true,
"statsJson": true,
"extractCss": true,
"extractLicenses": true,
"namedChunks": true,
"vendorChunk": false,
"polyfills": "src/polyfills.ts",
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "src/generated",
"output": "/generated"
},
{
"glob": "app/search/search-worker.js",
"input": "src",
"output": "/"
},
{
"glob": "favicon.ico",
"input": "src",
"output": "/"
},
{
"glob": "pwa-manifest.json",
"input": "src",
"output": "/"
},
{
"glob": "google385281288605d160.html",
"input": "src",
"output": "/"
},
{
"glob": "custom-elements.min.js",
"input": "node_modules/@webcomponents/custom-elements",
"output": "/assets/js"
},
{
"glob": "native-shim.js",
"input": "node_modules/@webcomponents/custom-elements/src",
"output": "/assets/js"
}
],
"styles": [
{
"input": "src/styles.scss"
}
],
"scripts": [],
},
"configurations": {
"next": {
"fileReplacements": [
{
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.next.ts"
}
]
},
"stable": {
"fileReplacements": [
{
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.stable.ts"
}
]
},
"archive": {
"fileReplacements": [
{
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.archive.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "site:build"
},
"configurations": {
"next": {
"browserTarget": "site:build:next"
},
"stable": {
"browserTarget": "site:build:stable"
},
"archive": {
"browserTarget": "site:build:archive"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "site:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "src/karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
{
"input": "src/styles.scss"
}
],
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "src/generated",
"output": "/generated"
},
{
"glob": "app/search/search-worker.js",
"input": "src",
"output": "/"
},
{
"glob": "favicon.ico",
"input": "src",
"output": "/"
},
{
"glob": "pwa-manifest.json",
"input": "src",
"output": "/"
},
{
"glob": "google385281288605d160.html",
"input": "src",
"output": "/"
},
{
"glob": "custom-elements.min.js",
"input": "node_modules/@webcomponents/custom-elements",
"output": "/assets/js"
},
{
"glob": "native-shim.js",
"input": "node_modules/@webcomponents/custom-elements/src",
"output": "/assets/js"
}
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": []
}
}
}
},
"site-e2e": {
"root": "",
"projectType": "application",
"cli": {},
"schematics": {},
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "tests/e2e/protractor.conf.js",
"devServerTarget": "site:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tests/e2e/tsconfig.e2e.json"
],
"exclude": []
}
}
}
}
},
"schematics": {
"@schematics/angular:component": {
"inlineStyle": true,
"prefix": "aio",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "aio"
}
}
}

View File

@ -13,18 +13,20 @@
**/src/tsconfig.app.json
**/src/tsconfig.spec.json
**/src/typings.d.ts
**/e2e/app.po.ts
**/e2e/src/app.po.ts
**/e2e/tsconfig.e2e.json
**/src/karma.conf.js
**/.angular-cli.json
**/.editorconfig
**/angular.json
**/tsconfig.json
**/bs-config.e2e.json
**/bs-config.json
**/package.json
**/tslint.json
**/karma.conf.js
**/karma-test-shim.js
**/browser-test-shim.js
**/browserslist
**/node_modules
# built files

View File

@ -50,7 +50,7 @@ const templateC = `
})
// #docregion carol-class
export class CarolComponent {
name= 'Carol';
name = 'Carol';
// #docregion carol-ctor
constructor( @Optional() public parent: Parent ) { }
// #enddocregion carol-ctor
@ -63,7 +63,7 @@ export class CarolComponent {
template: templateC
})
export class ChrisComponent {
name= 'Chris';
name = 'Chris';
constructor( @Optional() public parent: Parent ) { }
}
@ -116,7 +116,7 @@ export class BarryComponent implements Parent {
providers: [ provideParent(BobComponent) ]
})
export class BobComponent implements Parent {
name= 'Bob';
name = 'Bob';
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
}
@ -128,7 +128,7 @@ export class BobComponent implements Parent {
// #enddocregion beth-providers
})
export class BethComponent implements Parent {
name= 'Beth';
name = 'Beth';
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
}
@ -157,7 +157,7 @@ export class BethComponent implements Parent {
export class AlexComponent extends Base
// #enddocregion alex-class-signature
{
name= 'Alex';
name = 'Alex';
}
// #enddocregion alex, alex-1
@ -182,7 +182,7 @@ export class AlexComponent extends Base
export class AliceComponent implements Parent
// #enddocregion alice-class-signature
{
name= 'Alice';
name = 'Alice';
}
// #enddocregion alice

View File

@ -187,7 +187,7 @@ describe('Dependency Injection Tests', function () {
let heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0);
let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number){
let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) {
return elem.getText().then(function(text: string) {
return /secret/.test(text);
});

View File

@ -19,7 +19,7 @@ import { AdComponent } from './ad.component';
// #docregion class
export class AdBannerComponent implements OnInit, OnDestroy {
@Input() ads: AdItem[];
currentAdIndex: number = -1;
currentAdIndex = -1;
@ViewChild(AdDirective) adHost: AdDirective;
interval: any;

View File

@ -6,8 +6,8 @@ import { AdComponent } from './ad.component';
@Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
<h4>{{data.headline}}</h4>
{{data.body}}
</div>
`

View File

@ -8,7 +8,7 @@ import { AdComponent } from './ad.component';
<div class="hero-profile">
<h3>Featured Hero Profile</h3>
<h4>{{data.name}}</h4>
<p>{{data.bio}}</p>
<strong>Hire this hero today!</strong>

View File

@ -21,7 +21,7 @@ describe('Dynamic Form', function () {
element(by.css('select option[value="solid"]')).click();
let saveButton = element.all(by.css('button')).get(0);
saveButton.click().then(function(){
saveButton.click().then(function() {
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
});
});

View File

@ -1,5 +1,5 @@
// #docregion
export class QuestionBase<T>{
export class QuestionBase<T> {
value: T;
key: string;
label: string;

View File

@ -88,7 +88,8 @@ describe('Router', () => {
await crisisCenterEdit(2, true);
});
it('can cancel changed crisis details', async () => {
// TODO: Figure out why this test is failing now
xit('can cancel changed crisis details', async () => {
const page = getPageStruct();
await page.crisisHref.click();
await crisisCenterEdit(3, false);

View File

@ -44,10 +44,7 @@ class Hero {
}
}
// TODO(i): temorarily disable these tests because angular-in-memory-web-api is not compatible with rxjs v6 yet
// and we don't have the backwards compatibility package yet.
// Reenable after rxjs v6 compatibility package is out or angular-in-memory-web-api is compatible with rxjs v6
xdescribe('Tutorial part 6', () => {
describe('Tutorial part 6', () => {
beforeAll(() => browser.get(''));

View File

@ -36,10 +36,6 @@
'rxjs/operators': {main: 'index.js', defaultExtension: 'js' },
'rxjs/testing': {main: 'index.js', defaultExtension: 'js' },
'rxjs/websocket': {main: 'index.js', defaultExtension: 'js' },
'rxjs/ajax': {main: 'index.js', defaultExtension: 'js' },
'rxjs/operators': {main: 'index.js', defaultExtension: 'js' },
'rxjs/testing': {main: 'index.js', defaultExtension: 'js' },
'rxjs/websocket': {main: 'index.js', defaultExtension: 'js' },
'rxjs': { main: 'index.js', defaultExtension: 'js' },
}
});

View File

@ -22,7 +22,7 @@ Angular creates, updates, and destroys components as the user moves through the
<img src="generated/images/guide/architecture/metadata.png" alt="Metadata" class="left">
The `@Component` decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that `HeroListComponent` is just a class, with no special Angular notation or syntax at all. It's not a component until mark it as one with the `@Component` decorator.
The `@Component` decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that `HeroListComponent` is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the `@Component` decorator.
The metadata for a component tells Angular where to get the major building blocks it needs to create and present the component and its view. In particular, it associates a _template_ with the component, either directly with inline code, or by reference. Together, the component and its template describe a _view_.
@ -76,7 +76,7 @@ This template uses typical HTML elements like `<h2>` and `<p>`, and also includ
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
Angular supports *two-way data binding*, a mechanism for coordinating parts of a template with parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
Angular supports *two-way data binding*, a mechanism for coordinating parts of a template with parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
The following diagram shows the four forms of data binding markup. Each form has a direction&mdash;to the DOM, from the DOM, or in both directions.

View File

@ -311,11 +311,11 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewInit()</b> { ... }</code></td>
<td><p>Called after <code>ngAfterContentInit</code> when the component's view has been initialized. Applies to components only.</p>
<td><p>Called after <code>ngAfterContentInit</code> when the component's views and child views / the view that a directive is in has been initialized.</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewChecked()</b> { ... }</code></td>
<td><p>Called after every check of the component's view. Applies to components only.</p>
<td><p>Called after every check of the component's views and child views / the view that a directive is in.</p>
</td>
</tr><tr>
<td><code><b>ngOnDestroy()</b> { ... }</code></td>

View File

@ -249,7 +249,7 @@ An observable produces values over time. An array is created as a static set of
<pre>➞5</pre>
</td>
<td>
<pre>arr.find((v) => v>10)</pre>
<pre>arr.find((v) => v>3)</pre>
<pre>5</pre>
</td>
</tr>
@ -273,8 +273,8 @@ An observable produces values over time. An array is created as a static set of
1
2
3
4
5</pre>
5
7</pre>
</td>
<td>
<pre>arr.forEach((v) => {
@ -283,8 +283,8 @@ An observable produces values over time. An array is created as a static set of
1
2
3
4
5</pre>
5
7</pre>
</td>
</tr>
<tr>

View File

@ -61,7 +61,7 @@ The running application displays three heroes:
E2E test that all children were instantiated and displayed as expected:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="parent-to-child" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="parent-to-child" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>
@ -105,7 +105,7 @@ Here's the `NameParentComponent` demonstrating name variations including a name
E2E tests of input property setter with empty and non-empty names:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="parent-to-child-setter" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="parent-to-child-setter" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>
@ -164,7 +164,7 @@ Test that ***both*** input properties are set initially and that button clicks t
the expected `ngOnChanges` calls and values:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="parent-to-child-onchanges" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="parent-to-child-onchanges" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>
@ -217,7 +217,7 @@ and the method processes it:
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="child-to-parent" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="child-to-parent" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>
@ -284,7 +284,7 @@ match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="countdown-timer-tests" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="countdown-timer-tests" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>
@ -433,7 +433,7 @@ Tests click buttons of both the parent `MissionControlComponent` and the `Astron
and verify that the history meets expectations:
<code-example path="component-interaction/e2e/app.e2e-spec.ts" region="bidirectional-service" title="component-interaction/e2e/app.e2e-spec.ts">
<code-example path="component-interaction/e2e/src/app.e2e-spec.ts" region="bidirectional-service" title="component-interaction/e2e/src/app.e2e-spec.ts">
</code-example>

View File

@ -1,87 +1,35 @@
# Getting started with service workers
This document explains how to enable Angular service worker support in your CLI projects. It then uses a simple example to show you a service worker in action, demonstrating loading and basic caching.
#### Prerequisites
A basic understanding of the following:
* [Introduction to Angular service workers](guide/service-worker-intro).
* Angular v6, including Angular CLI v6.
<hr />
Beginning in Angular 5.0.0, you can easily enable Angular service worker support in any CLI project. This document explains how to enable Angular service worker support in new and existing projects. It then uses a simple example to show you a service worker in action, demonstrating loading and basic caching.
## Adding a service worker to your project
## Adding a service worker to a new application
If you're generating a new CLI project, you can use the CLI to set up the Angular service worker as part of creating the project. To do so, add the `--service-worker` flag to the `ng new` command:
To set up the Angular service worker in your project, use the CLI command `ng add @angular/pwa`. It takes care of configuring your app to use service workers by adding the `service-worker` package along
with setting up the necessary support files.
```sh
ng new my-project --service-worker
ng add @angular/pwa --project *project-name*
```
The `--service-worker` flag takes care of configuring your app to
use service workers by adding the `service-worker` package along
with setting up the necessary files to support service workers.
For information on the details, see the following section
which covers the process in detail as it shows you how to add a
service worker manually to an existing app.
The above command completes the following actions:
1. Adds the `@angular/service-worker` package.
2. Enables service worker build support in the CLI.
3. Imports and registers the service worker in the app module.
4. Creates the service worker configuration file called `ngsw-config.json` which specifies the caching behaviors and other settings.
## Adding a service worker to an existing app
To add a service worker to an existing app:
1. Add the service worker package.
2. Enable service worker build support in the CLI.
3. Import and register the service worker.
4. Create the service worker configuration file, which specifies the caching behaviors and other settings.
5. Build the project.
### Step 1: Add the service worker package
Add the package `@angular/service-worker`, using the yarn utility as shown here:
```sh
yarn add @angular/service-worker
```
### Step 2: Enable service worker build support in the CLI
To enable the Angular service worker, the CLI must generate an Angular service worker manifest at build time. To cause the CLI to generate the manifest for an existing project, set the `serviceWorker` flag to `true` in the project's `.angular-cli.json` file as shown here:
```sh
ng set apps.0.serviceWorker=true
```
### Step 3: Import and register the service worker
To import and register the Angular service worker:
At the top of the root module, `src/app/app.module.ts`, import `ServiceWorkerModule` and `environment`.
<code-example path="service-worker-getting-started/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts" region="sw-import"> </code-example>
Add `ServiceWorkerModule` to the `@NgModule` `imports` array. Use the `register()` helper to take care of registering the service worker, taking care to disable the service worker when not running in production mode.
<code-example path="service-worker-getting-started/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts" region="sw-module"> </code-example>
The file `ngsw-worker.js` is the name of the prebuilt service worker script, which the CLI copies into `dist/` to deploy along with your server.
### Step 4: Create the configuration file, `ngsw-config.json`
The Angular CLI needs a service worker configuration file, called `ngsw-config.json`. The configuration file controls how the service worker caches files and data
resources.
You can begin with the boilerplate version from the CLI, which configures sensible defaults for most applications.
Alternately, save the following as `src/ngsw-config.json`:
<code-example path="service-worker-getting-started/src/ngsw-config.json" linenums="false" title="src/ngsw-config.json"> </code-example>
### Step 5: Build the project
Finally, build the project:
Now, build the project:
```sh
ng build --prod
@ -92,17 +40,18 @@ The CLI project is now set up to use the Angular service worker.
## Service worker in action: a tour
This section demonstrates a service worker in action,
using an example application.
This section demonstrates a service worker in action,
using an example application.
### Serving with `http-server`
Because `ng serve` does not work with service workers, you must use a separate HTTP server to test your project locally. You can use any HTTP server. The example below uses the [http-server](https://www.npmjs.com/package/http-server) package from npm. To reduce the possibility of conflicts, test on a dedicated port.
To serve the app, start `http-server` and specify the directory containing your web files and the port:
To serve with `http-server`, change to the directory containing your web files and start the web server:
```sh
http-server dist -p 8080
cd dist
http-server -p 8080
```
### Initial load
@ -113,7 +62,7 @@ With the server running, you can point your browser at http://localhost:8080/. Y
### Simulating a network issue
To simulate a network issue, disable network interaction for your application. In Chrome:
To simulate a network issue, disable network interaction for your application. In Chrome:
1. Select **Tools** > **Developer Tools** (from the Chrome menu located at the top right corner).
2. Go to the **Network tab**.
@ -125,9 +74,9 @@ To simulate a network issue, disable network interaction for your application. I
Now the app has no access to network interaction.
For applications that do not use the Angular service worker, refreshing now would display Chrome's Internet disconnected page that says "There is no Internet connection".
For applications that do not use the Angular service worker, refreshing now would display Chrome's Internet disconnected page that says "There is no Internet connection".
With the addition of an Angular service worker, the application behavior changes. On a refresh, the page loads normally.
With the addition of an Angular service worker, the application behavior changes. On a refresh, the page loads normally.
If you look at the Network tab, you can verify that the service worker is active.
@ -149,12 +98,12 @@ Notice that all of the files the browser needs to render this application are ca
### Making changes to your application
Now that you've seen how service workers cache your application, the
next step is understanding how updates work.
Now that you've seen how service workers cache your application, the
next step is understanding how updates work.
1. If you're testing in an incognito window, open a second blank tab. This will keep the incognito and the cache state alive during your test.
2. Close the application tab, but not the window. This should also close the Developer Tools.
2. Close the application tab, but not the window. This should also close the Developer Tools.
3. Shut down `http-server`.
@ -168,7 +117,8 @@ next step is understanding how updates work.
```sh
ng build --prod
http-server dist -p 8080
cd dist
http-server -p 8080
```
### Updating your application in the browser

View File

@ -25,17 +25,12 @@ The Angular service worker's behavior follows that design goal:
* Updates happen in the background, relatively quickly after changes are published. The previous version of the application is served until an update is installed and ready.
* The service worker conserves bandwidth when possible. Resources are only downloaded if they've changed.
To support these behaviors, the Angular service worker loads a *manifest* file from the server. The manifest describes the resources to cache and includes hashes of every file's contents. When an update to the application is deployed, the contents of the manifest change, informing the service worker that a new version of the application should be downloaded and cached. This manifest is generated from a user-provided configuration file called `ngsw-config.json`, by using a build tool such as the Angular CLI.
To support these behaviors, the Angular service worker loads a *manifest* file from the server. The manifest describes the resources to cache and includes hashes of every file's contents. When an update to the application is deployed, the contents of the manifest change, informing the service worker that a new version of the application should be downloaded and cached. This manifest is generated from a CLI-generated configuration file called `ngsw-config.json`.
Installing the Angular service worker is as simple as including an `NgModule`. In addition to registering the Angular service worker with the browser, this also makes a few services available for injection which interact with the service worker and can be used to control it. For example, an application can ask to be notified when a new update becomes available, or an application can ask the service worker to check the server for available updates.
## Prerequisites
To use Angular service workers, you must have the following Angular and CLI versions:
* Angular 5.0.0 or later.
* Angular CLI 1.6.0 or later.
Your application must run in a web browser that supports service workers. Currently, the latest versions of Chrome and Firefox are supported. To learn about other browsers that are service worker ready, see the [Can I Use](http://caniuse.com/#feat=serviceworkers) page.
## Related resources

View File

@ -1,9 +1,16 @@
[
{
"startDate": "2018-02-14",
"endDate": "2018-04-22",
"endDate": "2018-04-18",
"message": "Join us for ng-conf<br/>Apr 18th-20th, 2018",
"imageUrl": "generated/images/marketing/home/ng-conf.png",
"linkUrl": "http://ng-conf.org/"
},
{
"startDate": "2018-04-18",
"endDate": "2018-04-22",
"message": "Watch ng-conf live stream <br/>Apr 18th-20th, 2018",
"imageUrl": "generated/images/marketing/home/ng-conf.png",
"linkUrl": "https://www.ng-conf.org/livestream/"
}
]

View File

@ -5,10 +5,10 @@ At the moment, the `HeroesComponent` displays both the list of heroes and the se
Keeping all features in one component as the application grows will not be maintainable.
You'll want to split up large components into smaller sub-components, each focused on a specific task or workflow.
In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable `HeroDetailsComponent`.
In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable `HeroDetailComponent`.
The `HeroesComponent` will only present the list of heroes.
The `HeroDetailsComponent` will present details of a selected hero.
The `HeroDetailComponent` will present details of a selected hero.
## Make the `HeroDetailComponent`

View File

@ -348,7 +348,7 @@ Open `MessagesComponent` and import the `MessageService`.
Modify the constructor with a parameter that declares a **public** `messageService` property.
Angular will inject the singleton `MessageService` into that property
when it creates the `HeroService`.
when it creates the `MessagesComponent`.
<code-example
path="toh-pt4/src/app/messages/messages.component.ts" region="ctor">

View File

@ -12,7 +12,7 @@
"aio-use-npm": "node tools/ng-packages-installer restore .",
"aio-check-local": "node tools/ng-packages-installer check .",
"ng": "yarn check-env && ng",
"start": "yarn check-env && ng serve --aot",
"start": "yarn check-env && ng serve",
"prebuild": "yarn setup",
"build": "yarn ~~build",
"prebuild-local": "yarn setup-local",
@ -21,7 +21,6 @@
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn update-webdriver",
"e2e": "ng e2e --no-webdriver-update",
"e2e-prod": "yarn e2e --environment=dev --target=production",
"presetup": "yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
"setup": "yarn aio-use-npm && yarn example-use-npm",
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn docs",
@ -45,7 +44,7 @@
"docs-watch": "node tools/transforms/authors-package/watchr.js",
"docs-lint": "eslint --ignore-path=\"tools/transforms/.eslintignore\" tools/transforms",
"docs-test": "node tools/transforms/test.js",
"deployment-config-test": "jasmine-ts tests/deployment-config/unit/**/*.spec.ts",
"redirects-test": "jasmine-ts tests/deployment/unit/**/*.spec.ts",
"firebase-utils-test": "jasmine-ts tools/firebase-test-utils/*.spec.ts",
"tools-lint": "tslint -c \"tools/tslint.json\" \"tools/firebase-test-utils/**/*.ts\"",
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js && yarn firebase-utils-test",
@ -58,10 +57,10 @@
"generate-zips": "node ./tools/example-zipper/generateZips",
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js",
"build-ie-polyfills": "yarn webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG",
"~~check-env": "node scripts/check-environment",
"~~build": "ng build --target=production --environment=stable -sm",
"~~build": "ng build --configuration=stable",
"post~~build": "yarn sw-manifest && yarn sw-copy"
},
"engines": {
@ -70,34 +69,36 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^6.0.0-rc.1",
"@angular/cdk": "^5.0.0-rc.1",
"@angular/common": "^6.0.0-rc.1",
"@angular/compiler": "^6.0.0-rc.1",
"@angular/core": "^6.0.0-rc.1",
"@angular/elements": "^6.0.0-rc.1",
"@angular/forms": "^6.0.0-rc.1",
"@angular/http": "^6.0.0-rc.1",
"@angular/material": "^5.0.0-rc.1",
"@angular/platform-browser": "^6.0.0-rc.1",
"@angular/platform-browser-dynamic": "^6.0.0-rc.1",
"@angular/platform-server": "^6.0.0-rc.1",
"@angular/router": "^6.0.0-rc.1",
"@angular/animations": "6.0.0-rc.5",
"@angular/cdk": "6.0.0-rc.11",
"@angular/common": "6.0.0-rc.5",
"@angular/core": "6.0.0-rc.5",
"@angular/elements": "6.0.0-rc.5",
"@angular/forms": "6.0.0-rc.5",
"@angular/http": "6.0.0-rc.5",
"@angular/material": "6.0.0-rc.11",
"@angular/platform-browser": "6.0.0-rc.5",
"@angular/platform-browser-dynamic": "6.0.0-rc.5",
"@angular/platform-server": "6.0.0-rc.5",
"@angular/router": "6.0.0-rc.5",
"@angular/service-worker": "^1.0.0-beta.16",
"@webcomponents/custom-elements": "^1.0.8",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
"jasmine": "^2.6.0",
"ng-pwa-tools": "^0.0.10",
"rxjs": "6.0.0-rc.0",
"rxjs-compat": "6.0.0-rc.0",
"rxjs": "6.0.0-uncanny-rc.7",
"rxjs-compat": "6.0.0-uncanny-rc.7",
"tslib": "^1.9.0",
"web-animations-js": "^2.2.5",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular/cli": "^1.7.3",
"@angular/compiler-cli": "^6.0.0-rc.1",
"@angular-devkit/build-angular": "^0.5.6",
"@angular/cli": "^6.0.0-rc.4",
"@angular/compiler": "6.0.0-rc.5",
"@angular/compiler-cli": "6.0.0-rc.5",
"@angular/language-service": "6.0.0-rc.5",
"@types/jasmine": "^2.5.52",
"@types/jasminewd2": "^2.0.3",
"@types/node": "~6.0.60",
@ -105,7 +106,7 @@
"canonical-path": "^0.0.2",
"chalk": "^2.1.0",
"cjson": "^0.5.0",
"codelyzer": "~2.0.0",
"codelyzer": "~4.2.1",
"concurrently": "^3.4.0",
"cross-spawn": "^5.1.0",
"css-selector-parser": "^1.3.0",
@ -146,16 +147,17 @@
"shelljs": "^0.7.7",
"tree-kill": "^1.1.0",
"ts-node": "^3.3.0",
"tslint": "~4.5.0",
"tslint": "~5.9.1",
"typescript": "~2.7.2",
"uglify-js": "^3.0.15",
"unist-util-filter": "^0.2.1",
"unist-util-source": "^1.0.1",
"unist-util-visit": "^1.1.1",
"unist-util-visit-parents": "^1.1.1",
"vrsource-tslint-rules": "^4.0.1",
"vrsource-tslint-rules": "^5.8.2",
"watchr": "^3.0.1",
"webpack-cli": "^2.0.14",
"xregexp": "^4.0.0",
"yargs": "^7.0.2"
}
}
}

View File

@ -2,10 +2,10 @@
"aio": {
"master": {
"uncompressed": {
"inline": 1971,
"main": 565539,
"polyfills": 38514,
"prettify": 14886
"runtime": 2689,
"main": 478529,
"polyfills": 38453,
"prettify": 14913
}
}
}

View File

@ -20,7 +20,7 @@ fi
if [[ $TRAVIS_BRANCH == master ]]; then
readonly deployEnv=next
elif [[ $TRAVIS_BRANCH == $STABLE_BRANCH ]]; then
readonly deployEnv=stable
readonly deployEnv=stable
else
# Extract the major versions from the branches, e.g. the 4 from 4.3.x
readonly majorVersion=${TRAVIS_BRANCH%%.*}
@ -87,7 +87,7 @@ fi
cd "`dirname $0`/.."
# Build the app
yarn build --env=$deployEnv
yarn build --configuration=$deployEnv
# Include any mode-specific files
cp -rf src/extra-files/$deployEnv/. dist/

View File

@ -5,12 +5,11 @@ set +x -eu -o pipefail
readonly thisDir="$(cd $(dirname ${BASH_SOURCE[0]}); pwd)"
readonly aioDir="$(realpath $thisDir/..)"
readonly appPtorConf="$aioDir/tests/e2e/protractor.conf.js"
readonly cfgPtorConf="$aioDir/tests/deployment-config/e2e/protractor.conf.js"
readonly protractorConf="$aioDir/tests/deployment/e2e/protractor.conf.js"
readonly minPwaScore="95"
readonly urls=(
"https://angular.io/"
"https://next.angular.io"
"https://next.angular.io/"
)
cd "$aioDir"
@ -24,11 +23,8 @@ set +x -eu -o pipefail
for url in "${urls[@]}"; do
echo -e "\nChecking '$url'...\n-----"
# Run e2e tests.
yarn protractor "$appPtorConf" --baseUrl "$url"
# Run deployment config tests.
yarn protractor "$cfgPtorConf" --baseUrl "$url"
# Run basic e2e and deployment config tests.
yarn protractor "$protractorConf" --baseUrl "$url"
# Run PWA-score tests.
yarn test-pwa-score "$url" "$minPwaScore"

View File

@ -34,7 +34,7 @@
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide}" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode" (open)="updateHostClasses()" (close)="updateHostClasses()">
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide}" #sidenav class="sidenav" [mode]="mode" [opened]="isOpened" (openedChange)="updateHostClasses()">
<aio-nav-menu *ngIf="!isSideBySide" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="isSideBySide"></aio-nav-menu>

View File

@ -6,8 +6,8 @@ import { HttpClient } from '@angular/common/http';
import { MatProgressBar, MatSidenav } from '@angular/material';
import { By } from '@angular/platform-browser';
import { Observable, timer } from 'rxjs';
import { mapTo } from 'rxjs/operators';
import { timer } from 'rxjs';
import { first, mapTo } from 'rxjs/operators';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
@ -64,13 +64,13 @@ describe('AppComponent', () => {
const de = fixture.debugElement;
const docViewerDe = de.query(By.css('aio-doc-viewer'));
documentService = de.injector.get(DocumentService) as DocumentService;
documentService = de.injector.get<DocumentService>(DocumentService);
docViewer = docViewerDe.nativeElement;
docViewerComponent = docViewerDe.componentInstance;
hamburger = de.query(By.css('.hamburger')).nativeElement;
locationService = de.injector.get(LocationService) as any;
locationService = de.injector.get<any>(LocationService);
sidenav = de.query(By.directive(MatSidenav)).componentInstance;
tocService = de.injector.get(TocService);
tocService = de.injector.get<TocService>(TocService);
return waitForDoc && awaitDocRendered();
};
@ -463,7 +463,7 @@ describe('AppComponent', () => {
let scrollToTopSpy: jasmine.Spy;
beforeEach(() => {
scrollService = fixture.debugElement.injector.get(ScrollService);
scrollService = fixture.debugElement.injector.get<ScrollService>(ScrollService);
scrollSpy = spyOn(scrollService, 'scroll');
scrollToTopSpy = spyOn(scrollService, 'scrollToTop');
});
@ -1114,20 +1114,23 @@ describe('AppComponent', () => {
checkHostClass('sidenav', 'open');
sidenav.close();
await waitForEmit(sidenav.onClose);
await waitForSidenavOpenedChange();
fixture.detectChanges();
checkHostClass('sidenav', 'closed');
sidenav.open();
await waitForEmit(sidenav.onOpen);
await waitForSidenavOpenedChange();
fixture.detectChanges();
checkHostClass('sidenav', 'open');
function waitForEmit(emitter: Observable<void>): Promise<void> {
return new Promise(resolve => {
emitter.subscribe(resolve);
fixture.detectChanges();
});
async function waitForSidenavOpenedChange() {
const promise = new Promise(resolve => sidenav.openedChange.pipe(first()).subscribe(resolve));
await Promise.resolve(); // Wait for `MatSidenav.openedChange.emit()` to be called.
jasmine.clock().tick(0); // Notify `MatSidenav.openedChange` observers.
// (It is an async `EventEmitter`, thus uses `setTimeout()`.)
await promise;
}
});

View File

@ -97,11 +97,11 @@ describe('ApiListComponent', () => {
});
});
describe('initial critera from location', () => {
describe('initial criteria from location', () => {
let locationService: TestLocationService;
beforeEach(() => {
locationService = <any> fixture.componentRef.injector.get(LocationService);
locationService = fixture.componentRef.injector.get<any>(LocationService);
});
function expectOneItem(name: string, section: string, type: string, stability: string) {
@ -110,7 +110,7 @@ describe('ApiListComponent', () => {
component.filteredSections.subscribe(filtered => {
expect(filtered.length).toBe(1, 'sections');
expect(filtered[0].name).toBe(section, 'section name');
const items = filtered[0].items.filter(item => item.show);
const items = filtered[0].items.filter(i => i.show);
expect(items.length).toBe(1, 'items');
const item = items[0];
@ -168,7 +168,7 @@ describe('ApiListComponent', () => {
let locationService: TestLocationService;
beforeEach(() => {
locationService = <any> fixture.componentRef.injector.get(LocationService);
locationService = fixture.componentRef.injector.get<any>(LocationService);
});
it('should have query', () => {

View File

@ -16,9 +16,9 @@ import { ApiSection, ApiService } from './api.service';
import { Option } from 'app/shared/select/select.component';
class SearchCriteria {
query? = '';
status? = 'all';
type? = 'all';
query ? = '';
status ? = 'all';
type ? = 'all';
}
@Component({

View File

@ -21,7 +21,7 @@ describe('ApiService', () => {
]
});
service = injector.get(ApiService);
service = injector.get<ApiService>(ApiService);
httpMock = injector.get(HttpTestingController);
});

View File

@ -15,18 +15,18 @@ import { CodeComponent } from './code.component';
*/
@Component({
selector: 'code-example',
template: `
template: `
<!-- Content projection is used to get the content HTML provided to this component -->
<div #content style="display: none"><ng-content></ng-content></div>
<header *ngIf="title">{{title}}</header>
<aio-code [ngClass]="classes"
[language]="language"
[linenums]="linenums"
[path]="path"
[region]="region"
[hideCopy]="hidecopy"
<aio-code [ngClass]="classes"
[language]="language"
[linenums]="linenums"
[path]="path"
[region]="region"
[hideCopy]="hidecopy"
[title]="title">
</aio-code>
`,

View File

@ -87,7 +87,7 @@ describe('CodeTabsComponent', () => {
region="region-B"
title="title-B">
Code example 2
</code-pane>
</code-pane>
</code-tabs>
`
})

View File

@ -24,7 +24,7 @@ export interface TabInfo {
template: `
<!-- Use content projection so that the provided HTML's code-panes can be split into tabs -->
<div #content style="display: none"><ng-content></ng-content></div>
<mat-tab-group class="code-tab-group" disableRipple>
<mat-tab style="overflow-y: hidden;" *ngFor="let tab of tabs">
<ng-template mat-tab-label>
@ -37,7 +37,7 @@ export interface TabInfo {
[region]="tab.region"
[title]="tab.title">
</aio-code>
</mat-tab>
</mat-tab>
</mat-tab-group>
`,
})

View File

@ -19,7 +19,7 @@ describe('ContributorService', () => {
]
});
contribService = injector.get(ContributorService);
contribService = injector.get<ContributorService>(ContributorService);
httpMock = injector.get(HttpTestingController);
});

View File

@ -11,5 +11,5 @@ export class Contributor {
website?: string;
twitter?: string;
bio?: string;
isFlipped? = false;
isFlipped ? = false;
}

View File

@ -366,13 +366,18 @@ describe('TocComponent', () => {
let parentScrollTop: number;
beforeEach(() => {
const hostElem = fixture.nativeElement;
const firstItem = page.listItems[0].nativeElement;
const offsetParent = firstItem.offsetParent;
offsetParent.style.maxHeight = `${offsetParent.clientHeight - firstItem.clientHeight}px`;
Object.defineProperty(offsetParent, 'scrollTop', {
Object.assign(hostElem.style, {
display: 'block',
maxHeight: `${hostElem.clientHeight - firstItem.clientHeight}px`,
overflow: 'auto',
position: 'relative',
});
Object.defineProperty(hostElem, 'scrollTop', {
get: () => parentScrollTop,
set: v => parentScrollTop = v
set: v => parentScrollTop = v,
});
parentScrollTop = 0;
@ -461,7 +466,7 @@ class TestTocService {
activeItemIndex = new BehaviorSubject<number | null>(null);
setActiveIndex(index: number|null) {
this.activeItemIndex.next(index);
if (asap.scheduled) {
if (asap.scheduled !== undefined) {
asap.flush();
}
}

View File

@ -90,5 +90,5 @@ export class TocComponent implements OnInit, AfterViewInit, OnDestroy {
}
function count<T>(array: T[], fn: (item: T) => boolean) {
return array.reduce((count, item) => fn(item) ? count + 1 : count, 0);
return array.reduce((result, item) => fn(item) ? result + 1 : result, 0);
}

View File

@ -5,12 +5,13 @@ describe('CustomIconRegistry', () => {
it('should get the SVG element for a preloaded icon from the cache', () => {
const mockHttp: any = {};
const mockSanitizer: any = {};
const mockDocument: any = {};
const svgSrc = '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
'viewBox="0 0 24 24"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></svg>';
const svgIcons: SvgIconInfo[] = [
{ name: 'test_icon', svgSource: svgSrc }
];
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, svgIcons);
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
let svgElement: SVGElement|undefined;
registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el);
expect(svgElement).toEqual(createSvg(svgSrc));
@ -19,6 +20,7 @@ describe('CustomIconRegistry', () => {
it('should call through to the MdIconRegistry if the icon name is not in the preloaded cache', () => {
const mockHttp: any = {};
const mockSanitizer: any = {};
const mockDocument: any = {};
const svgSrc = '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
'viewBox="0 0 24 24"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></svg>';
const svgIcons: SvgIconInfo[] = [
@ -26,7 +28,7 @@ describe('CustomIconRegistry', () => {
];
spyOn(MatIconRegistry.prototype, 'getNamedSvgIcon');
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, svgIcons);
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
registry.getNamedSvgIcon('other_icon');
expect(MatIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', undefined);

View File

@ -1,4 +1,5 @@
import { InjectionToken, Inject, Injectable } from '@angular/core';
import { InjectionToken, Inject, Injectable, Optional } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { of } from 'rxjs';
import { MatIconRegistry } from '@angular/material/icon';
import { HttpClient } from '@angular/common/http';
@ -35,8 +36,9 @@ interface SvgIconMap {
export class CustomIconRegistry extends MatIconRegistry {
private preloadedSvgElements: SvgIconMap = {};
constructor(http: HttpClient, sanitizer: DomSanitizer, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
super(http, sanitizer);
constructor(http: HttpClient, sanitizer: DomSanitizer, @Optional() @Inject(DOCUMENT) document,
@Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
super(http, sanitizer, document);
this.loadSvgElements(svgIcons);
}

View File

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';
import { DOCUMENT } from '@angular/common';
import { fromEvent, Observable, ReplaySubject, Subject } from 'rxjs';
import { auditTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';

View File

@ -1,5 +1,6 @@
import { DOCUMENT } from '@angular/common';
import { ReflectiveInjector } from '@angular/core';
import { DOCUMENT, DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Subject } from 'rxjs';
import { ScrollItem, ScrollSpyInfo, ScrollSpyService } from 'app/shared/scroll-spy.service';
@ -33,8 +34,8 @@ describe('TocService', () => {
describe('tocList', () => {
it('should emit the latest value to new subscribers', () => {
const expectedValue1 = tocItem('Heading A');
const expectedValue2 = tocItem('Heading B');
const expectedValue1 = createTocItem('Heading A');
const expectedValue2 = createTocItem('Heading B');
let value1: TocItem[]|undefined;
let value2: TocItem[]|undefined;
@ -48,8 +49,8 @@ describe('TocService', () => {
});
it('should emit the same values to all subscribers', () => {
const expectedValue1 = tocItem('Heading A');
const expectedValue2 = tocItem('Heading B');
const expectedValue1 = createTocItem('Heading A');
const expectedValue2 = createTocItem('Heading B');
const emittedValues: TocItem[][] = [];
tocService.tocList.subscribe(v => emittedValues.push(v));
@ -149,8 +150,8 @@ describe('TocService', () => {
describe('should clear tocList', () => {
beforeEach(() => {
// Start w/ dummy data from previous usage
const expectedValue1 = tocItem('Heading A');
const expectedValue2 = tocItem('Heading B');
const expectedValue1 = createTocItem('Heading A');
const expectedValue2 = createTocItem('Heading B');
tocService.tocList.next([expectedValue1, expectedValue2]);
expect(lastTocList).not.toEqual([]);
});
@ -369,7 +370,7 @@ class MockScrollSpyService {
}
}
function tocItem(title: string, level = 'h2', href = '', content = title) {
function createTocItem(title: string, level = 'h2', href = '', content = title) {
return { title, href, level, content };
}

View File

@ -1,5 +1,6 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { DOCUMENT, DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { ReplaySubject } from 'rxjs';
import { ScrollSpyInfo, ScrollSpyService } from 'app/shared/scroll-spy.service';

View File

@ -4,12 +4,10 @@
// The list of which env maps to which file can be found in `angular-cli.json`.
// Reflect.metadata polyfill is only needed in the JIT/dev mode.
//
// In order to load these polyfills early enough (before app code), polyfill.ts imports this file to
// to change the order in the final bundle.
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
// Reflect.metadata polyfill is only needed in the JIT, which use use only for tests
// to make the unit tests work we load these polyfills in tests.ts instead
// import 'core-js/es6/reflect';
// import 'core-js/es7/reflect';
export const environment = {

View File

@ -1,22 +1,23 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
require('@angular-devkit/build-angular/plugins/karma')
],
client:{
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
dir: require('path').join(__dirname, 'coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
angularCli: {

View File

@ -27,9 +27,6 @@
* and executed before the rest of the application files are executed.
*/
/** HACK: force import of environment.ts/environment.prod.ts to load env specific polyfills */
import './environments/environment';
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.

View File

@ -3,25 +3,22 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare const __karma__: any;
declare const require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// Reflect.metadata polyfill is only needed in the JIT mode which we use only for unit tests
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();

View File

@ -3,19 +3,18 @@
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"target": "es5",
"baseUrl": "",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts"
"test.ts",
"polyfills.ts"
],
"include": [
"testing/**/*.ts",
"**/*.spec.ts",
"**/*.d.ts"
]
}
}

17
aio/src/tslint.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"aio",
"camelCase"
],
"component-selector": [
true,
"element",
"aio",
"kebab-case"
]
}
}

View File

@ -1,50 +0,0 @@
import { browser } from 'protractor';
describe(browser.baseUrl, () => {
const sitemapUrls = browser.params.sitemapUrls;
const legacyUrls = browser.params.legacyUrls;
const goTo = async url => {
// Go to the specified URL and then unregister the ServiceWorker
// to ensure subsequent requests are passed through to the server.
await browser.get(url);
await browser.executeAsyncScript(cb => navigator.serviceWorker
.getRegistrations()
.then(regs => Promise.all(regs.map(reg => reg.unregister())))
.then(cb));
};
beforeAll(async done => {
// Make an initial request to unregister the ServiceWorker.
await goTo(browser.baseUrl);
done();
});
beforeEach(() => browser.waitForAngularEnabled(false));
afterEach(() => browser.waitForAngularEnabled(true));
describe('(with sitemap URLs)', () => {
sitemapUrls.forEach((url, i) => {
it(`should not redirect '${url}' (${i + 1}/${sitemapUrls.length})`, async () => {
await goTo(url);
const expectedUrl = browser.baseUrl + url;
const actualUrl = (await browser.getCurrentUrl()).replace(/\?.*$/, '');
expect(actualUrl).toBe(expectedUrl);
});
});
});
describe('(with legacy URLs)', () => {
legacyUrls.forEach(([fromUrl, toUrl], i) => {
it(`should redirect '${fromUrl}' to '${toUrl}' (${i + 1}/${legacyUrls.length})`, async () => {
await goTo(fromUrl);
const expectedUrl = (/^http/.test(toUrl) ? '' : browser.baseUrl.replace(/\/$/, '')) + toUrl;
const actualUrl = (await browser.getCurrentUrl()).replace(/\?.*$/, '');
expect(actualUrl).toBe(expectedUrl);
});
});
});
});

View File

@ -0,0 +1,37 @@
import { browser } from 'protractor';
import { SitePage } from './site.po';
describe(browser.baseUrl, () => {
const page = new SitePage();
beforeAll(done => page.init().then(done));
beforeEach(() => browser.waitForAngularEnabled(false));
afterEach(() => browser.waitForAngularEnabled(true));
describe('(with sitemap URLs)', () => {
page.sitemapUrls.forEach((url, i) => {
it(`should not redirect '${url}' (${i + 1}/${page.sitemapUrls.length})`, async () => {
await page.goTo(url);
const expectedUrl = browser.baseUrl + url;
const actualUrl = (await browser.getCurrentUrl()).replace(/\?.*$/, '');
expect(actualUrl).toBe(expectedUrl);
});
});
});
describe('(with legacy URLs)', () => {
page.legacyUrls.forEach(([fromUrl, toUrl], i) => {
it(`should redirect '${fromUrl}' to '${toUrl}' (${i + 1}/${page.legacyUrls.length})`, async () => {
await page.goTo(fromUrl);
const expectedUrl = (/^http/.test(toUrl) ? '' : browser.baseUrl.replace(/\/$/, '')) + toUrl;
const actualUrl = (await browser.getCurrentUrl()).replace(/\?.*$/, '');
expect(actualUrl).toBe(expectedUrl);
});
});
});
});

View File

@ -0,0 +1,60 @@
import { browser, by, element, ExpectedConditions } from 'protractor';
export class SitePage {
/** All URLs found in the app's `sitemap.xml` (i.e. valid URLs tha should not be redirected). */
sitemapUrls: string[] = browser.params.sitemapUrls;
/** A list of legacy URLs that should be redirected to new URLs (in the form `[fromUrl, toUrl]`). */
legacyUrls: string[][] = browser.params.legacyUrls;
/**
* Enter a query into the search field.
*/
async enterSearch(query: string) {
const searchInput = element(by.css('input[type=search]'));
await searchInput.clear();
await searchInput.sendKeys(query);
}
/**
* Get the text content of the `aio-doc-viewer` element (in lowercase).
*/
async getDocViewerText() {
const docViewer = element(by.css('aio-doc-viewer'));
const text = await docViewer.getText();
return text.toLowerCase();
}
/**
* Get a list of text contents for all search result items found on the page.
*/
async getSearchResults() {
const results = element.all(by.css('.search-results li'));
await browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return await results.map<string>(link => link!.getText());
}
/**
* Navigate to a URL, disable animations, unregister the ServiceWorker, and wait for Angular.
* (The SW is unregistered to ensure that subsequent requests are passed through to the server.)
*/
async goTo(url: string) {
const unregisterServiceWorker = (cb: () => void) => navigator.serviceWorker
.getRegistrations()
.then(regs => Promise.all(regs.map(reg => reg.unregister())))
.then(cb);
await browser.get(url || browser.baseUrl);
await browser.executeScript('document.body.classList.add(\'no-animations\')');
await browser.executeAsyncScript(unregisterServiceWorker);
await browser.waitForAngular();
};
/**
* Initialize the page object and get it ready for further requests.
*/
async init() {
// Make an initial request to unregister the ServiceWorker.
await this.goTo('');
}
}

View File

@ -0,0 +1,103 @@
import { browser } from 'protractor';
import { SitePage } from './site.po';
describe(browser.baseUrl, () => {
const page = new SitePage();
beforeAll(done => page.init().then(done));
beforeEach(() => browser.waitForAngularEnabled(false));
afterEach(() => browser.waitForAngularEnabled(true));
describe('(smoke tests)', () => {
it('should show the home page', () => {
page.goTo('');
const text = page.getDocViewerText();
expect(text).toContain('one framework');
expect(text).toContain('mobile & desktop');
});
describe('(marketing pages)', () => {
const textPerUrl = {
features: 'features & benefits',
docs: 'what is angular?',
events: 'events',
resources: 'explore angular resources',
};
Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => {
page.goTo(url);
expect(page.getDocViewerText()).toContain(textPerUrl[url]);
});
});
});
describe('(docs pages)', () => {
const textPerUrl = {
api: 'api list',
'guide/architecture': 'architecture',
'guide/http': 'httpclient',
'guide/quickstart': 'quickstart',
'guide/security': 'security',
tutorial: 'tutorial',
};
Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => {
page.goTo(url);
expect(page.getDocViewerText()).toContain(textPerUrl[url]);
});
});
});
describe('(api docs pages)', () => {
const textPerUrl = {
/* Class */ 'api/core/Injector': 'class injector',
/* Const */ 'api/forms/NG_VALIDATORS': 'const ng_validators',
/* Decorator */ 'api/core/Component': '@component',
/* Directive */ 'api/common/NgIf': 'class ngif',
/* Enum */ 'api/core/ChangeDetectionStrategy': 'enum changedetectionstrategy',
/* Function */ 'api/animations/animate': 'animate(',
/* Interface */ 'api/core/OnDestroy': 'interface ondestroy',
/* Pipe */ 'api/common/JsonPipe': '| json',
/* Type-Alias */ 'api/common/http/HttpEvent': 'type httpevent',
};
Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => {
page.goTo(url);
expect(page.getDocViewerText()).toContain(textPerUrl[url]);
});
});
});
describe('(search results)', () => {
beforeEach(() => page.goTo(''));
it('should find pages when searching by a partial word in the title', () => {
page.enterSearch('ngCont');
expect(page.getSearchResults()).toContain('NgControl');
});
it('should find API docs when searching for an instance member name', () => {
page.enterSearch('writeValue');
expect(page.getSearchResults()).toContain('ControlValueAccessor');
});
it('should find API docs when searching for a static member name', () => {
page.enterSearch('compose');
expect(page.getSearchResults()).toContain('Validators');
});
});
it('should show relevant results on 404', () => {
page.goTo('http/router');
const results = page.getSearchResults();
expect(results).toContain('HttpClient');
expect(results).toContain('Router');
});
});
});

View File

@ -39,14 +39,14 @@ describe('site App', function() {
navItemHeadings.each(heading => testNavItemHeading(heading!, 1));
// Helpers
function expectToBeCollapsed(element: ElementFinder) {
expect(element.getAttribute('class')).toMatch(/\bcollapsed\b/);
expect(element.getAttribute('class')).not.toMatch(/\bexpanded\b/);
function expectToBeCollapsed(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).not.toMatch(/\bexpanded\b/);
}
function expectToBeExpanded(element: ElementFinder) {
expect(element.getAttribute('class')).not.toMatch(/\bcollapsed\b/);
expect(element.getAttribute('class')).toMatch(/\bexpanded\b/);
function expectToBeExpanded(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).not.toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).toMatch(/\bexpanded\b/);
}
function testNavItemHeading(heading: ElementFinder, level: number) {

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