Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
84fc1a3663 | |||
1c40be26c6 | |||
2c5cf19c6d | |||
0dacf6d5f1 | |||
e9f1d44015 | |||
1d9024ee9a | |||
6a6164ab4f | |||
7231f5e26a | |||
86415223cb | |||
269f5acc54 | |||
6e6c866de9 | |||
732ed92cb7 | |||
53a807ae09 | |||
3342a8253b | |||
630c19f52d | |||
af8c2fa4be | |||
0789601dd6 | |||
ce0ac46e42 | |||
b531d87580 | |||
23a2154817 | |||
76d2496f24 | |||
b85cb410f1 | |||
1be22df0df | |||
a805839d38 | |||
3ac61a7550 | |||
57ea33bc5c | |||
4891649d68 | |||
93aba1bb1c | |||
f983a6c615 | |||
18f1b016e5 | |||
591dcc26af | |||
4acd322128 | |||
32a814bdfa | |||
912068e71c | |||
df8e57dc5d | |||
f27f6e498f | |||
01bfbcb84a | |||
a15abbb324 | |||
28c29d560e | |||
65ca7fd4aa | |||
3e3f918bb3 | |||
fc1dcffbdd | |||
ee3c681f98 | |||
6225fedcb8 | |||
2905069559 | |||
47202dd747 | |||
fb130c4eae | |||
734378c90b | |||
3959b7ef28 | |||
e292548523 | |||
c2506a78a1 | |||
34f70c6de2 | |||
3d9c2a6352 | |||
3232125650 | |||
c9f8718d2a | |||
f89ac92e5e | |||
f82efcf942 | |||
dc22f4dc69 | |||
173ccf03ab | |||
1d5c3c1c9b | |||
e4dac421db | |||
c639cdd3b3 | |||
5bcfa7cdfe | |||
d8fd892e71 | |||
074a997302 | |||
62616f541a | |||
156442f80d | |||
37112f549a | |||
cf4b4d53ba | |||
d45e3aa433 | |||
ccc25ee901 | |||
3052063c05 | |||
e107322f5c | |||
f0774254de | |||
6c66031c4a | |||
8f668807cf | |||
f98eb35179 | |||
cc8ae32503 | |||
95f3b1dbe6 | |||
6b96b069bf | |||
ecfe85b06c | |||
5439d4cd49 | |||
df91fd032d | |||
2d5ef15e08 | |||
544a7ad0a5 | |||
ec2c1bec4a | |||
e064d2607d | |||
d489ad595d | |||
0a3753bcce | |||
7fc2dceaf5 | |||
c3b39bac52 | |||
2cd88bfb0f | |||
0cefb0b79b | |||
55a7443974 | |||
f9ebaf1b90 | |||
bc81fbdd27 | |||
c7aa8a132d | |||
5c99b01512 | |||
992ba33a28 | |||
17e7c58981 | |||
b8f15d2b77 | |||
5f9a10aab9 | |||
04bc5257a6 | |||
5bca58e748 | |||
526a67f8f4 | |||
53a27e07b1 | |||
ba0fb1e055 | |||
ed2c8aa6f8 | |||
9226760120 | |||
b9ee8b46a0 | |||
e7eb0b8b7c | |||
ae52851458 | |||
e63cf3b89e | |||
9624fda082 | |||
e19b6a8f38 | |||
2aa6b54201 | |||
fe8550d278 | |||
e2e8ba6ffa | |||
60cdf9dc38 | |||
0371538d10 | |||
351331a81b | |||
0a54713f8a |
@ -11,7 +11,7 @@
|
||||
anchor_1: &job_defaults
|
||||
working_directory: ~/ng
|
||||
docker:
|
||||
- image: angular/ngcontainer
|
||||
- image: angular/ngcontainer:0.0.2
|
||||
|
||||
# After checkout, rebase on top of master.
|
||||
# Similar to travis behavior, but not quite the same.
|
||||
@ -27,10 +27,9 @@ jobs:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
- run: npm install
|
||||
- run: npm run postinstall
|
||||
- run: yarn install --freeze-lockfile --non-interactive
|
||||
- run: ./node_modules/.bin/gulp lint
|
||||
|
||||
build:
|
||||
@ -39,12 +38,12 @@ jobs:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel build ...
|
||||
- save_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- "node_modules"
|
||||
|
||||
|
@ -104,7 +104,7 @@ groups:
|
||||
animations:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/animation/*"
|
||||
- "packages/animations/*"
|
||||
- "packages/platform-browser/animations/*"
|
||||
users:
|
||||
- matsko #primary
|
||||
|
@ -1,12 +1,10 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
# force trusty as Google Chrome addon is not supported on Precise
|
||||
dist: trusty
|
||||
node_js:
|
||||
- '6.9.5'
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
@ -50,7 +48,8 @@ env:
|
||||
- CI_MODE=e2e_2
|
||||
- CI_MODE=js
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
# deactivated, see #19768
|
||||
# - CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
- CI_MODE=aio_tools_test
|
||||
|
73
CHANGELOG.md
73
CHANGELOG.md
@ -1,3 +1,76 @@
|
||||
<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="4.4.6"></a>
|
||||
## [4.4.6](https://github.com/angular/angular/compare/4.4.5...4.4.6) (2017-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** properly support boolean-based transitions and state changes ([#19672](https://github.com/angular/angular/issues/19672)) ([f983a6c](https://github.com/angular/angular/commit/f983a6c)), closes [#9396](https://github.com/angular/angular/issues/9396) [#12337](https://github.com/angular/angular/issues/12337)
|
||||
* **common:** attempt to JSON.parse errors for JSON responses ([#19773](https://github.com/angular/angular/issues/19773)) ([269f5ac](https://github.com/angular/angular/commit/269f5ac))
|
||||
* **router:** RouterLinkActive should update its state right after checking the children ([53a807a](https://github.com/angular/angular/commit/53a807a)), closes [#18983](https://github.com/angular/angular/issues/18983)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **animations:** reduce size of bundle by removing AST classes ([#19673](https://github.com/angular/angular/issues/19673)) ([76d2496](https://github.com/angular/angular/commit/76d2496))
|
||||
|
||||
|
||||
|
||||
<a name="4.4.5"></a>
|
||||
## [4.4.5](https://github.com/angular/angular/compare/4.4.4...4.4.5) (2017-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** `TestBed.overrideProvider` should keep imported `NgModule`s eager ([#19624](https://github.com/angular/angular/issues/19624)) ([734378c](https://github.com/angular/angular/commit/734378c))
|
||||
* **compiler:** correctly instantiate eager providers that are used via `Injector.get` ([#19558](https://github.com/angular/angular/issues/19558)) ([e292548](https://github.com/angular/angular/commit/e292548)), closes [#15501](https://github.com/angular/angular/issues/15501)
|
||||
* **compiler:** disallow references for select and index evaluation ([95f3b1d](https://github.com/angular/angular/commit/95f3b1d))
|
||||
* **core:** make dynamic & inline code checking behave the same ([#19189](https://github.com/angular/angular/issues/19189)) ([6c66031](https://github.com/angular/angular/commit/6c66031))
|
||||
* **platform-browser:** support customEqualityTesters when overriding Jasmine toEqual ([cc8ae32](https://github.com/angular/angular/commit/cc8ae32))
|
||||
* **tsc-wrapped:** don't rewrite imports when annotating for closure ([#19579](https://github.com/angular/angular/issues/19579)) ([c9f8718](https://github.com/angular/angular/commit/c9f8718))
|
||||
|
||||
|
||||
|
||||
<a name="4.4.4"></a>
|
||||
## [4.4.4](https://github.com/angular/angular/compare/4.4.3...4.4.4) (2017-09-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** support negative query limit value ([#19419](https://github.com/angular/angular/issues/19419)) ([bc81fbd](https://github.com/angular/angular/commit/bc81fbd)), closes [#19232](https://github.com/angular/angular/issues/19232)
|
||||
* **compiler:** correctly map error message locations ([#19424](https://github.com/angular/angular/issues/19424)) ([c3b39ba](https://github.com/angular/angular/commit/c3b39ba))
|
||||
* **compiler:** do not consider a reference with members as a reference ([#19466](https://github.com/angular/angular/issues/19466)) ([7fc2dce](https://github.com/angular/angular/commit/7fc2dce))
|
||||
* **compiler:** skip when trimming / removing whitespaces ([#19310](https://github.com/angular/angular/issues/19310)) ([c7aa8a1](https://github.com/angular/angular/commit/c7aa8a1)), closes [#19304](https://github.com/angular/angular/issues/19304)
|
||||
* **tsc-wrapped:** add metadata for `type` declarations ([#19040](https://github.com/angular/angular/issues/19040)) ([ae52851](https://github.com/angular/angular/commit/ae52851))
|
||||
|
||||
|
||||
<a name="4.4.3"></a>
|
||||
## [4.4.3](https://github.com/angular/angular/compare/4.4.2...4.4.3) (2017-09-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tsc-wrapped:** deduplicate metadata only when the module is the same ([#19261](https://github.com/angular/angular/issues/19261)) ([0371538](https://github.com/angular/angular/commit/0371538)), closes [#19219](https://github.com/angular/angular/issues/19219)
|
||||
|
||||
|
||||
<a name="4.4.2"></a>
|
||||
## [4.4.2](https://github.com/angular/angular/compare/4.4.1...4.4.2) (2017-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **platform-server**: fix for packaging issues [#19250](https://github.com/angular/angular/issues/19250)
|
||||
|
||||
|
||||
<a name="4.4.1"></a>
|
||||
# [4.4.1](https://github.com/angular/angular/compare/4.3.6...4.4.1) (2017-09-15)
|
||||
|
||||
|
@ -13,7 +13,11 @@ You should run all these tasks from the `angular/aio` folder.
|
||||
Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn` - install all the dependencies.
|
||||
* `yarn setup` - Install all the dependencies, boilerplate, plunkers, zips and runs dgeni on the docs.
|
||||
* `yarn setup` - install all the dependencies, boilerplate, plunkers, zips and run dgeni on the docs.
|
||||
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
|
||||
|
||||
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
||||
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
||||
|
||||
* `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.
|
||||
@ -26,33 +30,33 @@ Here are the most important tasks you might need to use:
|
||||
* `yarn docs-lint` - check that the doc gen code follows our style rules.
|
||||
* `yarn docs-test` - run the unit tests for the doc generation code.
|
||||
|
||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `-- --local` to use your local version of Angular contained in the "dist" folder.
|
||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
|
||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||
* `yarn generate-plunkers` - generate the plunker files that are used by the `live-example` tags in the docs.
|
||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||
|
||||
* `yarn example-e2e` - run all e2e tests for examples
|
||||
- `yarn example-e2e -- --setup` - force webdriver update & other setup, then run tests
|
||||
- `yarn example-e2e -- --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||
- `yarn example-e2e -- --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||
- `yarn example-e2e --setup` - force webdriver update & other setup, then run tests
|
||||
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||
|
||||
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
||||
|
||||
## Using ServiceWorker locally
|
||||
|
||||
Since abb36e3cb, running `yarn start -- --prod` will no longer set up the ServiceWorker, which
|
||||
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
|
||||
would require manually running `yarn sw-manifest` and `yarn sw-copy` (something that is not possible
|
||||
with webpack serving the files from memory).
|
||||
|
||||
If you want to test ServiceWorker locally, you can use `yarn build` and serve the files in `dist/`
|
||||
with `yarn http-server -- dist -p 4200`.
|
||||
with `yarn http-server dist -p 4200`.
|
||||
|
||||
For more details see #16745.
|
||||
|
||||
|
||||
## Guide to authoring
|
||||
|
||||
There are two types of content in the documentatation:
|
||||
There are two types of content in the documentation:
|
||||
|
||||
* **API docs**: descriptions of the modules, classes, interfaces, decorators, etc that make up the Angular platform.
|
||||
API docs are generated directly from the source code.
|
||||
@ -107,8 +111,16 @@ yarn start
|
||||
yarn docs-watch
|
||||
```
|
||||
|
||||
>Alternatively, try the consolidated `serve-and-sync` command that builds, watches and serves in the same terminal window
|
||||
```bash
|
||||
yarn serve-and-sync
|
||||
```
|
||||
|
||||
* Open a browser at https://localhost:4200/ and navigate to the document on which you want to work.
|
||||
You can automatically open the browser by using `yarn start -- -o` in the first terminal.
|
||||
You can automatically open the browser by using `yarn start -o` in the first terminal.
|
||||
|
||||
* Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
|
||||
be regenerated, the app will rebuild and the page will reload.
|
||||
|
||||
* If you get a build error complaining about examples or any other odd behavior, be sure to consult
|
||||
the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
|
||||
|
@ -156,7 +156,7 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
||||
# Set up the Node.js scripts
|
||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||
RUN yarn install --production
|
||||
RUN yarn install --production --freeze-lockfile
|
||||
|
||||
|
||||
# Set up health check
|
||||
|
@ -6,7 +6,7 @@ server=8.8.4.4
|
||||
# Listen for DHCP and DNS requests only on this address.
|
||||
listen-address=127.0.0.1
|
||||
|
||||
# Force an IP addres for these domains.
|
||||
# Force an IP address for these domains.
|
||||
address=/{{$AIO_NGINX_HOSTNAME}}/127.0.0.1
|
||||
address=/{{$AIO_UPLOAD_HOSTNAME}}/127.0.0.1
|
||||
address=/{{$TEST_AIO_NGINX_HOSTNAME}}/127.0.0.1
|
||||
|
@ -13,10 +13,8 @@ const AIO_REPO_SLUG = getEnvVar('AIO_REPO_SLUG');
|
||||
const AIO_TRUSTED_PR_LABEL = getEnvVar('AIO_TRUSTED_PR_LABEL');
|
||||
const AIO_UPLOAD_HOSTNAME = getEnvVar('AIO_UPLOAD_HOSTNAME');
|
||||
const AIO_UPLOAD_PORT = +getEnvVar('AIO_UPLOAD_PORT');
|
||||
const AIO_WWW_USER = getEnvVar('AIO_WWW_USER');
|
||||
|
||||
// Run
|
||||
process.setuid(AIO_WWW_USER); // TODO(gkalpak): Find more suitable way to run as `www-data`.
|
||||
_main();
|
||||
|
||||
// Functions
|
||||
|
@ -18,7 +18,7 @@ const TEST_AIO_UPLOAD_PORT = +getEnvVar('TEST_AIO_UPLOAD_PORT');
|
||||
const WWW_USER = getEnvVar('AIO_WWW_USER');
|
||||
|
||||
// Interfaces - Types
|
||||
export interface CmdResult { success: boolean; err: Error; stdout: string; stderr: string; }
|
||||
export interface CmdResult { success: boolean; err: Error | null; stdout: string; stderr: string; }
|
||||
export interface FileSpecs { content?: string; size?: number; }
|
||||
|
||||
export type CleanUpFn = () => void;
|
||||
@ -143,7 +143,7 @@ class Helper {
|
||||
statusText = status[1];
|
||||
} else {
|
||||
statusCode = status;
|
||||
statusText = http.STATUS_CODES[statusCode];
|
||||
statusText = http.STATUS_CODES[statusCode] || 'UNKNOWN_STATUS_CODE';
|
||||
}
|
||||
|
||||
return (result: CmdResult) => {
|
||||
@ -196,7 +196,7 @@ class Helper {
|
||||
}
|
||||
|
||||
// Methods - Protected
|
||||
protected createCleanUpFn(fn: Function): CleanUpFn {
|
||||
protected createCleanUpFn(fn: () => void): CleanUpFn {
|
||||
const cleanUpFn = () => {
|
||||
const idx = this.cleanUpFns.indexOf(cleanUpFn);
|
||||
if (idx !== -1) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
"scripts": {
|
||||
"prebuild": "yarn clean-dist",
|
||||
"build": "tsc",
|
||||
"build-watch": "yarn tsc -- --watch",
|
||||
"build-watch": "yarn tsc --watch",
|
||||
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
||||
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
|
||||
"lint": "tslint --project tsconfig.json",
|
||||
@ -20,26 +20,26 @@
|
||||
"test-watch": "nodemon --exec \"yarn ~~test-only\" --watch dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.17.2",
|
||||
"express": "^4.14.1",
|
||||
"jasmine": "^2.5.3",
|
||||
"jsonwebtoken": "^7.3.0",
|
||||
"shelljs": "^0.7.6"
|
||||
"body-parser": "^1.18.2",
|
||||
"express": "^4.15.4",
|
||||
"jasmine": "^2.8.0",
|
||||
"jsonwebtoken": "^8.0.1",
|
||||
"shelljs": "^0.7.8",
|
||||
"tslib": "^1.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/body-parser": "^1.16.4",
|
||||
"@types/express": "^4.0.35",
|
||||
"@types/jasmine": "^2.5.43",
|
||||
"@types/jsonwebtoken": "^7.2.0",
|
||||
"@types/node": "^7.0.5",
|
||||
"@types/shelljs": "^0.7.0",
|
||||
"@types/supertest": "^2.0.0",
|
||||
"concurrently": "^3.3.0",
|
||||
"eslint": "^3.15.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
"nodemon": "^1.11.0",
|
||||
"@types/body-parser": "^1.16.5",
|
||||
"@types/express": "^4.0.37",
|
||||
"@types/jasmine": "^2.6.0",
|
||||
"@types/jsonwebtoken": "^7.2.3",
|
||||
"@types/node": "^8.0.30",
|
||||
"@types/shelljs": "^0.7.4",
|
||||
"@types/supertest": "^2.0.3",
|
||||
"concurrently": "^3.5.0",
|
||||
"nodemon": "^1.12.1",
|
||||
"supertest": "^3.0.0",
|
||||
"tslint": "^4.4.2",
|
||||
"typescript": "^2.1.6"
|
||||
"tslint": "^5.7.0",
|
||||
"tslint-jasmine-noSkipOrFocus": "^1.0.8",
|
||||
"typescript": "^2.5.2"
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ describe('BuildCleaner', () => {
|
||||
let cleanerGetExistingBuildNumbersSpy: jasmine.Spy;
|
||||
let cleanerGetOpenPrNumbersSpy: jasmine.Spy;
|
||||
let cleanerRemoveUnnecessaryBuildsSpy: jasmine.Spy;
|
||||
let existingBuildsDeferred: {resolve: Function, reject: Function};
|
||||
let openPrsDeferred: {resolve: Function, reject: Function};
|
||||
let existingBuildsDeferred: {resolve: (v?: any) => void, reject: (e?: any) => void};
|
||||
let openPrsDeferred: {resolve: (v?: any) => void, reject: (e?: any) => void};
|
||||
let promise: Promise<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -195,7 +195,7 @@ describe('BuildCleaner', () => {
|
||||
|
||||
|
||||
describe('getOpenPrNumbers()', () => {
|
||||
let prDeferred: {resolve: Function, reject: Function};
|
||||
let prDeferred: {resolve: (v: any) => void, reject: (v: any) => void};
|
||||
let promise: Promise<number[]>;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -277,7 +277,10 @@ describe('BuildCleaner', () => {
|
||||
|
||||
it('should catch errors and log them', () => {
|
||||
const consoleErrorSpy = spyOn(console, 'error');
|
||||
shellRmSpy.and.callFake(() => { throw 'Test'; });
|
||||
shellRmSpy.and.callFake(() => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
|
||||
(cleaner as any).removeDir('/foo/bar');
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe('GithubApi', () => {
|
||||
|
||||
|
||||
it('should not pass data to \'request()\'', () => {
|
||||
(api.get as Function)('foo', {}, {});
|
||||
(api.get as any)('foo', {}, {});
|
||||
|
||||
expect(apiRequestSpy).toHaveBeenCalled();
|
||||
expect(apiRequestSpy.calls.argsFor(0)[2]).toBeUndefined();
|
||||
@ -144,7 +144,7 @@ describe('GithubApi', () => {
|
||||
|
||||
|
||||
describe('getPaginated()', () => {
|
||||
let deferreds: {resolve: Function, reject: Function}[];
|
||||
let deferreds: {resolve: (v: any) => void, reject: (v: any) => void}[];
|
||||
|
||||
beforeEach(() => {
|
||||
deferreds = [];
|
||||
@ -292,7 +292,7 @@ describe('GithubApi', () => {
|
||||
|
||||
|
||||
describe('onResponse', () => {
|
||||
let promise: Promise<Object>;
|
||||
let promise: Promise<object>;
|
||||
let respond: (statusCode: number) => IncomingMessage;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -22,7 +22,7 @@ describe('GithubPullRequests', () => {
|
||||
|
||||
describe('addComment()', () => {
|
||||
let prs: GithubPullRequests;
|
||||
let deferred: {resolve: Function, reject: Function};
|
||||
let deferred: {resolve: (v: any) => void, reject: (v: any) => void};
|
||||
|
||||
beforeEach(() => {
|
||||
prs = new GithubPullRequests('12345', 'foo/bar');
|
||||
|
@ -223,6 +223,7 @@ describe('BuildCreator', () => {
|
||||
|
||||
|
||||
it('should reject with an UploadError', done => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expectToBeUploadError(err, 500, `Error while uploading to directory: ${shaDir}\nTest`);
|
||||
@ -407,6 +408,7 @@ describe('BuildCreator', () => {
|
||||
|
||||
|
||||
it('should reject with an UploadError', done => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
||||
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||
expectToBeUploadError(err, 500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||
@ -434,11 +436,11 @@ describe('BuildCreator', () => {
|
||||
|
||||
describe('exists()', () => {
|
||||
let fsAccessSpy: jasmine.Spy;
|
||||
let fsAccessCbs: Function[];
|
||||
let fsAccessCbs: ((v?: any) => void)[];
|
||||
|
||||
beforeEach(() => {
|
||||
fsAccessCbs = [];
|
||||
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: Function) => fsAccessCbs.push(cb));
|
||||
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb));
|
||||
});
|
||||
|
||||
|
||||
@ -482,7 +484,7 @@ describe('BuildCreator', () => {
|
||||
let shellChmodSpy: jasmine.Spy;
|
||||
let shellRmSpy: jasmine.Spy;
|
||||
let cpExecSpy: jasmine.Spy;
|
||||
let cpExecCbs: Function[];
|
||||
let cpExecCbs: ((...args: any[]) => void)[];
|
||||
|
||||
beforeEach(() => {
|
||||
cpExecCbs = [];
|
||||
@ -490,7 +492,7 @@ describe('BuildCreator', () => {
|
||||
consoleWarnSpy = spyOn(console, 'warn');
|
||||
shellChmodSpy = spyOn(shell, 'chmod');
|
||||
shellRmSpy = spyOn(shell, 'rm');
|
||||
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: Function) => cpExecCbs.push(cb));
|
||||
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
||||
});
|
||||
|
||||
|
||||
@ -556,7 +558,11 @@ describe('BuildCreator', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
shellChmodSpy.and.callFake(() => { throw 'Test'; });
|
||||
shellChmodSpy.and.callFake(() => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
|
||||
cpExecCbs[0]();
|
||||
});
|
||||
|
||||
@ -569,7 +575,11 @@ describe('BuildCreator', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
shellRmSpy.and.callFake(() => { throw 'Test'; });
|
||||
shellRmSpy.and.callFake(() => {
|
||||
// tslint:disable-next-line: no-string-throw
|
||||
throw 'Test';
|
||||
});
|
||||
|
||||
cpExecCbs[0]();
|
||||
});
|
||||
|
||||
|
@ -116,7 +116,7 @@ describe('uploadServerFactory', () => {
|
||||
|
||||
it('should log the server address info on \'listening\'', () => {
|
||||
const consoleInfoSpy = spyOn(console, 'info');
|
||||
const server = createUploadServer('builds/dir');
|
||||
const server = createUploadServer();
|
||||
server.address = () => ({address: 'foo', family: '', port: 1337});
|
||||
|
||||
expect(consoleInfoSpy).not.toHaveBeenCalled();
|
||||
|
@ -1,25 +1,66 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"alwaysStrict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSourceMap": true,
|
||||
/* Basic Options */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": [
|
||||
"es2016"
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"outDir": "dist",
|
||||
"pretty": true,
|
||||
"rootDir": ".",
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"target": "es5",
|
||||
"es2015",
|
||||
"es2016.array.include"
|
||||
], /* Specify library files to be included in the compilation: */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
"importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
]
|
||||
], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Other */
|
||||
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
||||
"newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix). */
|
||||
"pretty": true, /* Stylize errors and messages using color and context. */
|
||||
"skipLibCheck": true /* Skip type checking of all declaration files (*.d.ts). */
|
||||
},
|
||||
"include": [
|
||||
"lib/**/*",
|
||||
|
@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"array-type": [true, "array"],
|
||||
"arrow-parens": [true, "ban-single-arg-parens"],
|
||||
@ -7,9 +11,14 @@
|
||||
"max-classes-per-file": [true, 4],
|
||||
"no-consecutive-blank-lines": [true, 2],
|
||||
"no-console": [false],
|
||||
"no-focused-test": true,
|
||||
"no-namespace": [true, "allow-declarations"],
|
||||
"no-skipped-test": true,
|
||||
"no-string-literal": false,
|
||||
"quotemark": [true, "single"],
|
||||
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"]
|
||||
}
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-jasmine-noSkipOrFocus"
|
||||
]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,10 +6,9 @@ export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null || ec
|
||||
export AIO_PREVIEW_DEPLOYMENT_TOKEN=$(head -c -1 /aio-secrets/PREVIEW_DEPLOYMENT_TOKEN 2>/dev/null || echo "MISSING_PREVIEW_DEPLOYMENT_TOKEN")
|
||||
|
||||
# Start the upload-server instance
|
||||
# TODO(gkalpak): Ideally, the upload server should be run as a non-privileged user.
|
||||
# (Currently, there doesn't seem to be a straight forward way.)
|
||||
action=$([ "$1" == "stop" ] && echo "stop" || echo "start")
|
||||
pm2 $action $AIO_SCRIPTS_JS_DIR/dist/lib/upload-server \
|
||||
--uid $AIO_WWW_USER \
|
||||
--log /var/log/aio/upload-server-prod.log \
|
||||
--name aio-upload-server-prod \
|
||||
${@:2}
|
||||
|
@ -15,13 +15,12 @@ export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/TEST_GITHUB_TOKEN 2>/dev/null
|
||||
export AIO_PREVIEW_DEPLOYMENT_TOKEN=$(head -c -1 /aio-secrets/TEST_PREVIEW_DEPLOYMENT_TOKEN 2>/dev/null || echo "TEST_PREVIEW_DEPLOYMENT_TOKEN")
|
||||
|
||||
# Start the upload-server instance
|
||||
# TODO(gkalpak): Ideally, the upload server should be run as a non-privileged user.
|
||||
# (Currently, there doesn't seem to be a straight forward way.)
|
||||
appName=aio-upload-server-test
|
||||
if [[ "$1" == "stop" ]]; then
|
||||
pm2 delete $appName
|
||||
else
|
||||
pm2 start $AIO_SCRIPTS_JS_DIR/dist/lib/verify-setup/start-test-upload-server.js \
|
||||
--uid $AIO_WWW_USER \
|
||||
--log /var/log/aio/upload-server-test.log \
|
||||
--name $appName \
|
||||
--no-autorestart \
|
||||
|
@ -9,7 +9,7 @@ VM host to update the preview server based on changes in the source code.
|
||||
The script will pull the latest changes from the origin's master branch and examine if there have
|
||||
been any changes in files inside the preview server source code directory (see below). If there are,
|
||||
it will create a new image and verify that is works as expected. Finally, it will stop and remove
|
||||
the old docker container and image, create and new container based on the new image and start it.
|
||||
the old docker container and image, create a new container based on the new image and start it.
|
||||
|
||||
The script assumes that the preview server source code is in the repository's
|
||||
`aio/aio-builds-setup/` directory and expects the following inputs:
|
||||
|
@ -9,7 +9,7 @@ readonly defaultImageNameAndTag="aio-builds:latest"
|
||||
# (Necessary, because only `scripts-js/dist/` is copied to the docker image.)
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install
|
||||
yarn install --freeze-lockfile --non-interactive
|
||||
yarn build
|
||||
)
|
||||
|
||||
|
@ -7,6 +7,6 @@ source "`dirname $0`/_env.sh"
|
||||
# Test `scripts-js/`
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install
|
||||
yarn install --freeze-lockfile --non-interactive
|
||||
yarn test
|
||||
)
|
||||
|
@ -335,17 +335,17 @@ describe('Animation Tests', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function getBoundingClientWidth(el: ElementFinder): promise.Promise<number> {
|
||||
function getBoundingClientWidth(el: ElementFinder) {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].getBoundingClientRect().width',
|
||||
el.getWebElement()
|
||||
);
|
||||
) as PromiseLike<number>;
|
||||
}
|
||||
|
||||
function getOffsetWidth(el: ElementFinder): promise.Promise<number> {
|
||||
function getOffsetWidth(el: ElementFinder) {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].offsetWidth',
|
||||
el.getWebElement()
|
||||
);
|
||||
) as PromiseLike<number>;
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
// #docregion animations-module
|
||||
// #docplaster
|
||||
import { NgModule } from '@angular/core';
|
||||
// #docregion animations-module
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
// #enddocregion animations-module
|
||||
@ -15,11 +16,12 @@ import { HeroListAutoComponent } from './hero-list-auto.component';
|
||||
import { HeroListGroupsComponent } from './hero-list-groups.component';
|
||||
import { HeroListMultistepComponent } from './hero-list-multistep.component';
|
||||
import { HeroListTimingsComponent } from './hero-list-timings.component';
|
||||
// #docregion animations-module
|
||||
|
||||
// #docregion animation-module
|
||||
@NgModule({
|
||||
imports: [ BrowserModule, BrowserAnimationsModule ],
|
||||
// #enddocregion animation-module
|
||||
// ... more stuff ...
|
||||
// #enddocregion animations-module
|
||||
declarations: [
|
||||
HeroTeamBuilderComponent,
|
||||
HeroListBasicComponent,
|
||||
@ -34,5 +36,8 @@ import { HeroListTimingsComponent } from './hero-list-timings.component';
|
||||
HeroListGroupsComponent
|
||||
],
|
||||
bootstrap: [ HeroTeamBuilderComponent ]
|
||||
// #docregion animations-module
|
||||
})
|
||||
export class AppModule { }
|
||||
// #enddocregion animations-module
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
transition
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-auto',
|
||||
@ -43,5 +43,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListAutoComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from '@angular/animations';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-basic',
|
||||
@ -66,5 +66,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListBasicComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from '@angular/animations';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-combined-transitions',
|
||||
@ -55,5 +55,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListCombinedTransitionsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
transition
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-enter-leave-states',
|
||||
@ -59,5 +59,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListEnterLeaveStatesComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
transition
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-enter-leave',
|
||||
@ -47,5 +47,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListEnterLeaveComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
group
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-groups',
|
||||
@ -76,5 +76,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListGroupsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
} from '@angular/animations';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-inline-styles',
|
||||
@ -56,5 +56,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListInlineStylesComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
AnimationEvent
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-multistep',
|
||||
@ -59,7 +59,7 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListMultistepComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
|
||||
animationStarted(event: AnimationEvent) {
|
||||
console.warn('Animation started: ', event);
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
transition
|
||||
} from '@angular/animations';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-timings',
|
||||
@ -54,5 +54,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListTimingsComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from '@angular/animations';
|
||||
// #enddocregion imports
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-list-twoway',
|
||||
@ -54,5 +54,5 @@ import { Heroes } from './hero.service';
|
||||
// #enddocregion animationdef
|
||||
})
|
||||
export class HeroListTwowayComponent {
|
||||
@Input() heroes: Heroes;
|
||||
@Input() heroes: Hero[];
|
||||
}
|
||||
|
@ -1,40 +1,41 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Heroes } from './hero.service';
|
||||
import { Hero, HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-team-builder',
|
||||
template: `
|
||||
<div class="buttons">
|
||||
<button [disabled]="!heroes.canAdd()" (click)="heroes.addInactive()">Add inactive hero</button>
|
||||
<button [disabled]="!heroes.canAdd()" (click)="heroes.addActive()">Add active hero</button>
|
||||
<button [disabled]="!heroes.canRemove()" (click)="heroes.remove()">Remove hero</button>
|
||||
<button [disabled]="!heroService.canAdd()" (click)="heroService.addInactive()">Add inactive hero</button>
|
||||
<button [disabled]="!heroService.canAdd()" (click)="heroService.addActive()">Add active hero</button>
|
||||
<button [disabled]="!heroService.canRemove()" (click)="heroService.remove()">Remove hero</button>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h4>Basic State</h4>
|
||||
<p>Switch between active/inactive on click.</p>
|
||||
<hero-list-basic [heroes]=heroes></hero-list-basic>
|
||||
<hero-list-basic [heroes]="heroes"></hero-list-basic>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Styles inline in transitions</h4>
|
||||
<p>Animated effect on click, no persistend end styles.</p>
|
||||
<hero-list-inline-styles [heroes]=heroes></hero-list-inline-styles>
|
||||
<p>Animated effect on click, no persistent end styles.</p>
|
||||
<hero-list-inline-styles [heroes]="heroes"></hero-list-inline-styles>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Combined transition syntax</h4>
|
||||
<p>Switch between active/inactive on click. Define just one transition used in both directions.</p>
|
||||
<hero-list-combined-transitions [heroes]=heroes></hero-list-combined-transitions>
|
||||
<hero-list-combined-transitions [heroes]="heroes"></hero-list-combined-transitions>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Two-way transition syntax</h4>
|
||||
<p>Switch between active/inactive on click. Define just one transition used in both directions using the <=> syntax.</p>
|
||||
<hero-list-twoway [heroes]=heroes></hero-list-twoway>
|
||||
<hero-list-twoway [heroes]="heroes"></hero-list-twoway>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Enter & Leave</h4>
|
||||
<p>Enter and leave animations using the void state.</p>
|
||||
<hero-list-enter-leave [heroes]=heroes></hero-list-enter-leave>
|
||||
<hero-list-enter-leave [heroes]="heroes"></hero-list-enter-leave>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
@ -44,27 +45,27 @@ import { Heroes } from './hero.service';
|
||||
Enter and leave animations combined with active/inactive state animations.
|
||||
Different enter and leave transitions depending on state.
|
||||
</p>
|
||||
<hero-list-enter-leave-states [heroes]=heroes></hero-list-enter-leave-states>
|
||||
<hero-list-enter-leave-states [heroes]="heroes"></hero-list-enter-leave-states>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Auto Style Calc</h4>
|
||||
<p>Leave animation from the current computed height using the auto-style value *.</p>
|
||||
<hero-list-auto [heroes]=heroes></hero-list-auto>
|
||||
<hero-list-auto [heroes]="heroes"></hero-list-auto>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Different Timings</h4>
|
||||
<p>Enter and leave animations with different easings, ease-in for enter, ease-out for leave.</p>
|
||||
<hero-list-timings [heroes]=heroes></hero-list-timings>
|
||||
<hero-list-timings [heroes]="heroes"></hero-list-timings>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Multiple Keyframes</h4>
|
||||
<p>Enter and leave animations with three keyframes in each, to give the transition some bounce.</p>
|
||||
<hero-list-multistep [heroes]=heroes></hero-list-multistep>
|
||||
<hero-list-multistep [heroes]="heroes"></hero-list-multistep>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h4>Parallel Groups</h4>
|
||||
<p>Enter and leave animations with multiple properties animated in parallel with different timings.</p>
|
||||
<hero-list-groups [heroes]=heroes></hero-list-groups>
|
||||
<hero-list-groups [heroes]="heroes"></hero-list-groups>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@ -87,8 +88,12 @@ import { Heroes } from './hero.service';
|
||||
min-height: 6em;
|
||||
}
|
||||
`],
|
||||
providers: [Heroes]
|
||||
providers: [HeroService]
|
||||
})
|
||||
export class HeroTeamBuilderComponent {
|
||||
constructor(private heroes: Heroes) { }
|
||||
heroes: Hero[];
|
||||
|
||||
constructor(private heroService: HeroService) {
|
||||
this.heroes = heroService.heroes;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
class Hero {
|
||||
constructor(public name: string,
|
||||
public state = 'inactive') {
|
||||
}
|
||||
// #docregion hero
|
||||
export class Hero {
|
||||
constructor(public name: string, public state = 'inactive') { }
|
||||
|
||||
toggleState() {
|
||||
this.state = (this.state === 'active' ? 'inactive' : 'active');
|
||||
this.state = this.state === 'active' ? 'inactive' : 'active';
|
||||
}
|
||||
}
|
||||
// #enddocregion hero
|
||||
|
||||
let ALL_HEROES = [
|
||||
const ALL_HEROES = [
|
||||
'Windstorm',
|
||||
'RubberMan',
|
||||
'Bombasto',
|
||||
@ -25,36 +25,30 @@ let ALL_HEROES = [
|
||||
].map(name => new Hero(name));
|
||||
|
||||
@Injectable()
|
||||
export class Heroes implements Iterable<Hero> {
|
||||
export class HeroService {
|
||||
|
||||
currentHeroes: Hero[] = [];
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.currentHeroes.values();
|
||||
}
|
||||
heroes: Hero[] = [];
|
||||
|
||||
canAdd() {
|
||||
return this.currentHeroes.length < ALL_HEROES.length;
|
||||
return this.heroes.length < ALL_HEROES.length;
|
||||
}
|
||||
|
||||
canRemove() {
|
||||
return this.currentHeroes.length > 0;
|
||||
return this.heroes.length > 0;
|
||||
}
|
||||
|
||||
addActive() {
|
||||
let hero = ALL_HEROES[this.currentHeroes.length];
|
||||
hero.state = 'active';
|
||||
this.currentHeroes.push(hero);
|
||||
addActive(active = true) {
|
||||
let hero = ALL_HEROES[this.heroes.length];
|
||||
hero.state = active ? 'active' : 'inactive';
|
||||
this.heroes.push(hero);
|
||||
}
|
||||
|
||||
addInactive() {
|
||||
let hero = ALL_HEROES[this.currentHeroes.length];
|
||||
hero.state = 'inactive';
|
||||
this.currentHeroes.push(hero);
|
||||
this.addActive(false);
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.currentHeroes.splice(this.currentHeroes.length - 1, 1);
|
||||
this.heroes.length -= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ describe('Component Style Tests', function () {
|
||||
let componentH1 = element(by.css('hero-app > h1'));
|
||||
let externalH1 = element(by.css('body > h1'));
|
||||
|
||||
expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
|
||||
expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
|
||||
// Note: sometimes webdriver returns the fontWeight as "normal",
|
||||
// othertimes as "400", both of which are equal in CSS terms.
|
||||
expect(componentH1.getCssValue('fontWeight')).toMatch(/normal|400/);
|
||||
expect(externalH1.getCssValue('fontWeight')).not.toMatch(/normal|400/);
|
||||
});
|
||||
|
||||
|
||||
|
@ -7,3 +7,4 @@
|
||||
<p>We're sorry. The page you are looking for cannot be found.</p>
|
||||
</div>
|
||||
</div>
|
||||
<aio-file-not-found-search></aio-file-not-found-search>
|
||||
|
@ -150,11 +150,6 @@ The following table lists some of the key AngularJS template features with their
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
|
||||
## Template directives
|
||||
AngularJS provides more than seventy built-in directives for templates.
|
||||
Many of them aren't needed in Angular because of its more capable and expressive binding system.
|
||||
@ -721,10 +716,6 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a filters-pipes}
|
||||
|
||||
|
||||
@ -992,9 +983,6 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a controllers-components}
|
||||
|
||||
|
||||
@ -1215,11 +1203,6 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a style-sheets}
|
||||
|
||||
|
||||
@ -1297,6 +1280,3 @@ also encapsulate a style sheet within a specific component.
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
@ -28,19 +28,37 @@ The examples in this page are available as a <live-example></live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
## Setup
|
||||
|
||||
Before you can add animations to your application, you need
|
||||
to import a few animation-specific modules and functions to the root application module.
|
||||
|
||||
<code-example path="animations/src/app/app.module.ts" region="animations-module" title="app.module.ts (animation module import excerpt)" linenums="false"></code-example>
|
||||
|
||||
#### Example basics
|
||||
|
||||
The animations examples in this guide animate a list of heroes.
|
||||
|
||||
A `Hero` class has a `name` property, a `state` property that indicates if the hero is active or not,
|
||||
and a `toggleState()` method to switch between the states.
|
||||
|
||||
<code-example path="animations/src/app/hero.service.ts" region="hero" title="hero.service.ts (Hero class)" linenums="false"></code-example>
|
||||
|
||||
Across the top of the screen (`app.hero-team-builder.component.ts`)
|
||||
are a series of buttons that add and remove heroes from the list (via the `HeroService`).
|
||||
The buttons trigger changes to the list that all of the example components see at the same time.
|
||||
|
||||
{@a example-transitioning-between-states}
|
||||
|
||||
## Quickstart example: Transitioning between two states
|
||||
## Transitioning between two states
|
||||
|
||||
<img src="generated/images/guide/animations/animation_basic_click.gif" alt="A simple transition animation" class="right">
|
||||
|
||||
You can build a simple animation that transitions an element between two states
|
||||
driven by a model attribute.
|
||||
|
||||
Animations are defined inside `@Component` metadata. Before you can add animations, you need
|
||||
to import a few animation-specific imports and functions:
|
||||
|
||||
<code-example path="animations/src/app/app.module.ts" region="animations-module" title="app.module.ts (@NgModule imports excerpt)" linenums="false"></code-example>
|
||||
Animations can be defined inside `@Component` metadata.
|
||||
|
||||
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" title="hero-list-basic.component.ts" linenums="false"></code-example>
|
||||
|
||||
|
@ -25,7 +25,7 @@ Here are a few essential commands for guide page authors.
|
||||
|
||||
1. `yarn setup` — installs packages; builds docs, plunkers, and zips.
|
||||
|
||||
1. `yarn docs-watch -- --watch-only` — watches for saved content changes and refreshes the browser. The (optional) `--watch-only` flag skips the initial docs rebuild.
|
||||
1. `yarn docs-watch --watch-only` — watches for saved content changes and refreshes the browser. The (optional) `--watch-only` flag skips the initial docs rebuild.
|
||||
|
||||
1. `yarn start` — starts the doc viewer application so you can see your local changes in the browser.
|
||||
|
||||
@ -360,6 +360,23 @@ located in the `content/examples/docs-style-guide` directory.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
The doc tooling reports an error if the file identified in the path does not exist **or is _git_-ignored**.
|
||||
|
||||
Most `.js` files are _git_-ignored.
|
||||
If you want to include an ignored code file in your project and display it in a guide you must _un-ignore_ it.
|
||||
|
||||
The preferred way to un-ignore a file is to update the `content/examples/.gitignore` like this:
|
||||
|
||||
<code-example title="content/examples/.gitignore">
|
||||
# my-guide
|
||||
!my-guide/src/something.js
|
||||
!my-guide/more-javascript*.js
|
||||
</code-example>
|
||||
|
||||
</div>
|
||||
|
||||
#### Code-example attributes
|
||||
|
||||
You control the _code-example_ output by setting one or more of its attributes:
|
||||
|
@ -139,6 +139,10 @@ null if the control value is valid _or_ a validation error object.
|
||||
The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
|
||||
and whose value is an arbitrary dictionary of values that you could insert into an error message, `{name}`.
|
||||
|
||||
Custom async validators are similar to sync validators, but they must instead return a Promise or Observable
|
||||
that later emits null or a validation error object. In the case of an Observable, the Observable must complete,
|
||||
at which point the form uses the last value emitted for validation.
|
||||
|
||||
### Adding to reactive forms
|
||||
|
||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
||||
|
@ -117,7 +117,7 @@ Now the extractor tool and compiler will generate a translation unit with _your
|
||||
<code-example path="i18n/src/locale/messages.es.xlf.html" region="custom-id" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Here is the `i18n` attribute with a _definition_, followed by the custom `id`:
|
||||
Here is the `i18n` attribute with a _description_, followed by the custom `id`:
|
||||
|
||||
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-id' title='app/app.component.html' linenums="false">
|
||||
</code-example>
|
||||
|
@ -83,4 +83,3 @@ you'll have to provide a different `Title` service that understands
|
||||
the concept of a "document title" for that specific platform.
|
||||
Ideally, the application itself neither knows nor cares about the runtime environment.
|
||||
|
||||
[Back to top](guide/set-document-title#top)
|
||||
|
@ -316,10 +316,13 @@ which `NgFor` has initialized with the hero for the current iteration.
|
||||
* The [API guide](api/common/NgFor "API: NgFor")
|
||||
describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
* `NgFor` is implemented by the `NgForOf` directive. Read more about additional `NgForOf` directive properties and context properties [NgForOf API reference](api/common/NgForOf).
|
||||
|
||||
|
||||
These microsyntax mechanisms are available to you when you write your own structural directives.
|
||||
Studying the
|
||||
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
|
||||
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
|
||||
and [`NgForOf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgForOf")
|
||||
is a great way to learn more.
|
||||
|
||||
|
||||
|
@ -4502,8 +4502,8 @@ helps instantly identify which members of the component serve which purpose.
|
||||
|
||||
**Why?** The property associated with `@HostBinding` or the method associated with `@HostListener`
|
||||
can be modified only in a single place—in the directive's class.
|
||||
If you use the `host` metadata property, you must modify both the property declaration inside the controller,
|
||||
and the metadata associated with the directive.
|
||||
If you use the `host` metadata property, you must modify both the property/method declaration in the
|
||||
directive's class and the metadata in the decorator associated with the directive.
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -37,7 +37,6 @@ You can extend the HTML vocabulary of your templates with components and directi
|
||||
In the following sections, you'll learn how to get and set DOM (Document Object Model) values dynamically through data binding.
|
||||
|
||||
Begin with the first form of data binding—interpolation—to see how much richer template HTML can be.
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -81,7 +80,7 @@ Though this is not exactly true. Interpolation is a special syntax that Angular
|
||||
|
||||
But first, let's take a closer look at template expressions and statements.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -143,11 +142,10 @@ The `hero` in `{{hero.name}}`
|
||||
refers to the template input variable, not the component's property.
|
||||
|
||||
Template expressions cannot refer to anything in
|
||||
the global namespace. They can't refer to `window` or `document`. They
|
||||
the global namespace (except `undefined`). They can't refer to `window` or `document`. They
|
||||
can't call `console.log` or `Math.max`. They are restricted to referencing
|
||||
members of the expression context.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
{@a no-side-effects}
|
||||
|
||||
@ -204,7 +202,7 @@ Dependent values should not change during a single turn of the event loop.
|
||||
If an idempotent expression returns a string or a number, it returns the same string or number
|
||||
when called twice in a row. If the expression returns an object (including an `array`),
|
||||
it returns the same object *reference* when called twice in a row.
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -276,7 +274,6 @@ A method call or simple property assignment should be the norm.
|
||||
Now that you have a feel for template expressions and statements,
|
||||
you're ready to learn about the varieties of data binding syntax beyond interpolation.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -585,7 +582,6 @@ The following table summarizes:
|
||||
</table>
|
||||
|
||||
With this broad view in mind, you're ready to look at binding types in detail.
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -773,7 +769,6 @@ content harmlessly.
|
||||
<img src='generated/images/guide/template-syntax/evil-title.png' alt="evil title made safe">
|
||||
</figure>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -847,7 +842,6 @@ is to set ARIA attributes, as in this example:
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="attrib-binding-aria" title="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -885,7 +879,6 @@ the [NgClass directive](guide/template-syntax#ngClass) is usually preferred when
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -921,8 +914,6 @@ Note that a _style property_ name can be written in either
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a event-binding}
|
||||
@ -1053,7 +1044,6 @@ Deleting the hero updates the model, perhaps triggering other changes
|
||||
including queries and saves to a remote server.
|
||||
These changes percolate through the system and are ultimately displayed in this and other views.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1118,7 +1108,6 @@ However, no native HTML element follows the `x` value and `xChange` event patter
|
||||
|
||||
Fortunately, the Angular [_NgModel_](guide/template-syntax#ngModel) directive is a bridge that enables two-way binding to form elements.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1162,7 +1151,7 @@ This section is an introduction to the most commonly used attribute directives:
|
||||
* [`NgClass`](guide/template-syntax#ngClass) - add and remove a set of CSS classes
|
||||
* [`NgStyle`](guide/template-syntax#ngStyle) - add and remove a set of HTML styles
|
||||
* [`NgModel`](guide/template-syntax#ngModel) - two-way data binding to an HTML form element
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1203,8 +1192,6 @@ It's up to you to call `setCurrentClassess()`, both initially and when the depen
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a ngStyle}
|
||||
@ -1241,7 +1228,6 @@ It's up to you to call `setCurrentStyles()`, both initially and when the depende
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1335,8 +1321,6 @@ Here are all variations in action, including the uppercase version:
|
||||
<img src='generated/images/guide/template-syntax/ng-model-anim.gif' alt="NgModel variations">
|
||||
</figure>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a structural-directives}
|
||||
@ -1431,7 +1415,6 @@ described below.
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1511,8 +1494,8 @@ The next example captures the `index` in a variable named `i` and displays it wi
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
Learn about the other `NgFor` context values such as `last`, `even`,
|
||||
and `odd` in the [NgFor API reference](api/common/NgFor).
|
||||
`NgFor` is implemented by the `NgForOf` directive. Read more about the other `NgForOf` context values such as `last`, `even`,
|
||||
and `odd` in the [NgForOf API reference](api/common/NgForOf).
|
||||
|
||||
</div>
|
||||
|
||||
@ -1553,7 +1536,6 @@ Here is an illustration of the _trackBy_ effect.
|
||||
<img src="generated/images/guide/template-syntax/ng-for-track-by-anim.gif" alt="trackBy">
|
||||
</figure>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1601,8 +1583,6 @@ For example, you could replace the `<confused-hero>` switch case with the follow
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="NgSwitch-div" title="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a template-reference-variable}
|
||||
@ -1673,7 +1653,6 @@ This example declares the `fax` variable as `ref-fax` instead of `#fax`.
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="ref-fax" title="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1810,7 +1789,6 @@ the directive property name on the *left* and the public alias on the *right*:
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1861,7 +1839,6 @@ The generated output would look something like this
|
||||
"rate": 325 }
|
||||
</code-example>
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1935,7 +1912,6 @@ The display is blank, but the app keeps rolling without errors.
|
||||
|
||||
It works perfectly with long property paths such as `a?.b?.c?.d`.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -18,7 +18,7 @@ The sample application and all tests in this guide are available as live example
|
||||
* <live-example name="setup" plnkr="quickstart-specs" embedded-style>The QuickStart seed's AppComponent spec</live-example>.
|
||||
* <live-example embedded-style>The sample application to be tested</live-example>.
|
||||
* <live-example plnkr="app-specs" embedded-style>All specs that test the sample application</live-example>.
|
||||
* <live-example plnkr="bag-specs" embedded-style>A grab bag of additional specs</live-example>.<a href="#top" class='to-top'>Back to top</a>
|
||||
* <live-example plnkr="bag-specs" embedded-style>A grab bag of additional specs</live-example>.
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -199,7 +199,6 @@ A comprehensive review of the Angular testing utilities appears [later in this g
|
||||
|
||||
But first you should write a dummy test to verify that your test environment is set up properly
|
||||
and to lock in a few basic testing skills.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -365,7 +364,6 @@ Debug specs in the browser in the same way that you debug an application.
|
||||
|
||||
You can also try this test as a <live-example plnkr="1st-specs" title="First spec" embedded-style></live-example> in plunker.
|
||||
All of the tests in this guide are available as [live examples](guide/testing#live-examples "Live examples of these tests").
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -631,7 +629,7 @@ There is no harm in calling `detectChanges()` more often than is strictly necess
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -800,7 +798,7 @@ The tests in this guide only call `compileComponents` when necessary.
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -966,7 +964,6 @@ In a spec with multiple expectations, it can help clarify what went wrong and wh
|
||||
The remaining tests confirm the logic of the component when the service returns different values.
|
||||
The second test validates the effect of changing the user name.
|
||||
The third test checks that the component displays the proper message when there is no logged-in user.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1202,7 +1199,7 @@ and `fakeAsync`, is a viable and occasionally necessary technique.
|
||||
For example, you can't call `async` or `fakeAsync` when testing
|
||||
code that involves the `intervalTimer`, as is common when
|
||||
testing async `Observable` methods.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1482,7 +1479,7 @@ The tests themselves are almost identical to the stand-alone version:
|
||||
|
||||
Only the selected event test differs. It confirms that the selected `DashboardHeroComponent` hero
|
||||
really does find its way up through the event binding to the host component.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1782,7 +1779,6 @@ Inspect and download _all_ of the guide's application test code with this <live-
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1848,7 +1844,7 @@ Here are a few more `HeroDetailComponent` tests to drive the point home.
|
||||
|
||||
</code-example>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -1945,8 +1941,6 @@ especially when the feature module is small and mostly self-contained, as featur
|
||||
|
||||
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
@ -2104,7 +2098,6 @@ The `TestBed` offers similar `overrideDirective`, `overrideModule`, and `overrid
|
||||
for digging into and replacing parts of these other classes.
|
||||
|
||||
Explore the options and combinations on your own.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -2272,8 +2265,6 @@ tests with the `RouterTestingModule`.
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
@ -2327,7 +2318,6 @@ such as misspelled or misused components and directives.
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -2427,7 +2417,7 @@ The test for the default color uses the injector of the second `<h2>` to get its
|
||||
and its `defaultColor`.
|
||||
|
||||
* `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -2655,7 +2645,6 @@ may require meticulous preparation with the Angular testing utilities.
|
||||
On the other hand, isolated unit tests can't confirm that the `ButtonComp` is
|
||||
properly bound to its template or even data bound at all.
|
||||
Use Angular tests for that.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -3642,8 +3631,6 @@ The Angular `By` class has three static methods for common predicates:
|
||||
|
||||
</code-example>
|
||||
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<div class='l' class='hr'>
|
||||
|
||||
</div>
|
||||
@ -3792,7 +3779,6 @@ The sample tests are written to run in Jasmine and karma.
|
||||
The two "fast path" setups added the appropriate Jasmine and karma npm packages to the
|
||||
`devDependencies` section of the `package.json`.
|
||||
They're installed when you run `npm install`.
|
||||
<a href="#top" class='to-top'>Back to top</a>
|
||||
|
||||
<div class='l' class='hr'>
|
||||
|
||||
|
BIN
aio/content/images/marketing/home/angular-connect.png
Normal file
BIN
aio/content/images/marketing/home/angular-connect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
@ -13,18 +13,6 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ngJapan -->
|
||||
<tr>
|
||||
<th><a href="http://ngjapan.org/" title="ng-Japan">ng-Japan</a></th>
|
||||
<td>Tokyo, Japan</td>
|
||||
<td>June 17, 2017</td>
|
||||
</tr>
|
||||
<!-- AngularMix -->
|
||||
<tr>
|
||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
||||
<td>Universal Studios, Orlando, Florida</td>
|
||||
<td>October 8, 2017</td>
|
||||
</tr>
|
||||
<!-- ReactiveConf -->
|
||||
<tr>
|
||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
||||
@ -35,7 +23,7 @@
|
||||
<tr>
|
||||
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
|
||||
<td>London, United Kingdom</td>
|
||||
<td>November 07, 2017</td>
|
||||
<td>November 7-8, 2017</td>
|
||||
</tr>
|
||||
<!-- ngAtlanta-->
|
||||
<tr>
|
||||
@ -43,6 +31,30 @@
|
||||
<td>Atlanta, Georgia</td>
|
||||
<td>January 30, 2018</td>
|
||||
</tr>
|
||||
<!-- ngVikings-->
|
||||
<tr>
|
||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
||||
<td>Helsinki, Finland</td>
|
||||
<td>March 1-2, 2018</td>
|
||||
</tr>
|
||||
<!-- ngconf 2018-->
|
||||
<tr>
|
||||
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
||||
<td>Salt Lake City, UT</td>
|
||||
<td>April 18-20, 2018</td>
|
||||
</tr>
|
||||
<!-- WeRDevs-->
|
||||
<tr>
|
||||
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
|
||||
<td>Vienna</td>
|
||||
<td>May 16-18, 2018</td>
|
||||
</tr>
|
||||
<!-- AngularConnect-->
|
||||
<tr>
|
||||
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
|
||||
<td>London, United Kingdom</td>
|
||||
<td>November 5-7, 2018</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
@ -30,9 +30,9 @@
|
||||
<!--Announcement Bar-->
|
||||
<div class="homepage-container">
|
||||
<div class="announcement-bar">
|
||||
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
|
||||
<p>Join us at our newest event, October 2017</p>
|
||||
<a class="button" href="https://angularmix.com/">Learn More</a>
|
||||
<img src="generated/images/marketing/home/angular-connect.png">
|
||||
<p>Join us in London for AngularConnect<br>November 7-8, 2017</p>
|
||||
<a class="button" href="https://angularconnect.com/">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -317,7 +317,7 @@
|
||||
"desc": "Material Design components for Angular",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "Angular Material 2",
|
||||
"title": "Angular Material",
|
||||
"url": "https://github.com/angular/material2"
|
||||
},
|
||||
"aggrid": {
|
||||
|
@ -87,7 +87,7 @@ Here's the app in action:
|
||||
|
||||
|
||||
|
||||
## Up next
|
||||
## Next step
|
||||
|
||||
You'll build the Tour of Heroes app, step by step.
|
||||
Each step is motivated with a requirement that you've likely
|
||||
|
@ -263,7 +263,7 @@ You can edit the hero's name and see the changes reflected immediately in the `<
|
||||
|
||||
|
||||
|
||||
## The road you've travelled
|
||||
## Summary
|
||||
|
||||
Take stock of what you've built.
|
||||
|
||||
@ -286,7 +286,7 @@ Here's the complete `app.component.ts` as it stands now:
|
||||
|
||||
|
||||
|
||||
## The road ahead
|
||||
## Next step
|
||||
In the [next tutorial page](tutorial/toh-pt2 "Master/Detail"), you'll build on the Tour of Heroes app to display a list of heroes.
|
||||
You'll also allow the user to select heroes and display their details.
|
||||
You'll learn more about how to retrieve lists and bind them to the template.
|
||||
|
@ -450,7 +450,7 @@ Here's the complete `app.component.ts` as of now:
|
||||
|
||||
|
||||
|
||||
## The road you've travelled
|
||||
## Summary
|
||||
|
||||
Here's what you achieved in this page:
|
||||
|
||||
@ -460,7 +460,7 @@ Here's what you achieved in this page:
|
||||
|
||||
Your app should look like this <live-example></live-example>.
|
||||
|
||||
## The road ahead
|
||||
## Next step
|
||||
You've expanded the Tour of Heroes app, but it's far from complete.
|
||||
An app shouldn't be one monolithic component.
|
||||
In the [next page](tutorial/toh-pt3 "Multiple Components"), you'll split the app into subcomponents and make them work together.
|
||||
|
@ -443,7 +443,7 @@ Here are the code files discussed in this page.
|
||||
|
||||
|
||||
|
||||
## The road you’ve travelled
|
||||
## Summary
|
||||
|
||||
Here's what you achieved in this page:
|
||||
|
||||
@ -457,7 +457,7 @@ Your app should look like this <live-example></live-example>.
|
||||
|
||||
|
||||
|
||||
## The road ahead
|
||||
## Next step
|
||||
The Tour of Heroes app is more reusable with shared components,
|
||||
but its (mock) data is still hard coded within the `AppComponent`.
|
||||
That's not sustainable.
|
||||
|
@ -593,7 +593,7 @@ Here are the code files discussed in this page.
|
||||
|
||||
|
||||
|
||||
## The road you've travelled
|
||||
## Summary
|
||||
Here's what you achieved in this page:
|
||||
|
||||
* You created a service class that can be shared by many components.
|
||||
@ -604,7 +604,7 @@ Here's what you achieved in this page:
|
||||
|
||||
Your app should look like this <live-example></live-example>.
|
||||
|
||||
## The road ahead
|
||||
## Next step
|
||||
The Tour of Heroes has become more reusable using shared components and services.
|
||||
The next goal is to create a dashboard, add menu links that route between the views, and format data in a template.
|
||||
As the app evolves, you'll discover how to design it to make it easier to grow and maintain.
|
||||
|
@ -1207,7 +1207,7 @@ Here's an excerpt:
|
||||
|
||||
|
||||
Create the file <code>styles.css</code>.
|
||||
Ensure that the file contains the [master styles provided here](https://raw.githubusercontent.com/angular/angular/master/aio/tools/examples/shared/boilerplate/src/styles.css).
|
||||
Ensure that the file contains the [master styles provided here](https://raw.githubusercontent.com/angular/angular/master/aio/tools/examples/shared/boilerplate/common/src/styles.css).
|
||||
Also edit <code>index.html</code> to refer to this stylesheet.
|
||||
|
||||
|
||||
@ -1356,7 +1356,7 @@ Verify that you have the following structure:
|
||||
|
||||
|
||||
|
||||
## The road you’ve travelled
|
||||
## Summary
|
||||
Here's what you achieved in this page:
|
||||
|
||||
* You added the Angular router to navigate among different components.
|
||||
@ -1368,7 +1368,7 @@ Here's what you achieved in this page:
|
||||
|
||||
Your app should look like this <live-example></live-example>.
|
||||
|
||||
### The road ahead
|
||||
### Next step
|
||||
|
||||
You have much of the foundation you need to build an app.
|
||||
You're still missing a key piece: remote data access.
|
||||
|
@ -518,7 +518,7 @@ Verify that you have the following structure:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Home Stretch
|
||||
## Summary
|
||||
|
||||
You're at the end of your journey, and you've accomplished a lot.
|
||||
* You added the necessary dependencies to use HTTP in the app.
|
||||
|
44
aio/e2e/api.e2e-spec.ts
Normal file
44
aio/e2e/api.e2e-spec.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { ApiPage } from './api.po';
|
||||
|
||||
describe('Api pages', function() {
|
||||
it('should show direct subclasses of a class', () => {
|
||||
const page = new ApiPage('api/forms/AbstractControlDirective');
|
||||
// We must use `as any` (here and below) because of broken typings for jasmine
|
||||
expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl'] as any);
|
||||
});
|
||||
|
||||
it('should show direct and indirect subclasses of a class', () => {
|
||||
const page = new ApiPage('api/forms/AbstractControlDirective');
|
||||
expect(page.getDescendants('class')).toEqual(['ControlContainer', 'AbstractFormGroupDirective', 'NgControl'] as any);
|
||||
});
|
||||
|
||||
it('should show child interfaces that extend an interface', () => {
|
||||
const page = new ApiPage('api/forms/Validator');
|
||||
expect(page.getDescendants('interface')).toEqual(['AsyncValidator'] as any);
|
||||
});
|
||||
|
||||
it('should show classes that implement an interface', () => {
|
||||
const page = new ApiPage('api/animations/AnimationPlayer');
|
||||
expect(page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer'] as any);
|
||||
});
|
||||
|
||||
it('should show type params of type-aliases', () => {
|
||||
const page = new ApiPage('api/common/http/HttpEvent');
|
||||
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
|
||||
});
|
||||
|
||||
it('should show readonly properties as getters', () => {
|
||||
const page = new ApiPage('api/common/http/HttpRequest');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T|null');
|
||||
});
|
||||
|
||||
it('should not show parenthesis for getters', () => {
|
||||
const page = new ApiPage('api/core/NgModuleRef');
|
||||
expect(page.getOverview('class').getText()).toContain('get injector: Injector');
|
||||
});
|
||||
|
||||
it('should show both type and initializer if set', () => {
|
||||
const page = new ApiPage('api/common/HashLocationStrategy');
|
||||
expect(page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string');
|
||||
});
|
||||
});
|
34
aio/e2e/api.po.ts
Normal file
34
aio/e2e/api.po.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { element, by } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
export class ApiPage extends SitePage {
|
||||
constructor(url: string) {
|
||||
super();
|
||||
this.navigateTo(url);
|
||||
}
|
||||
|
||||
getDescendants(docType: string, onlyDirect = false) {
|
||||
// This selector is horrible because we have potentially recursive HTML lists
|
||||
//
|
||||
// ul
|
||||
// li
|
||||
// code
|
||||
// ul
|
||||
// li
|
||||
// code
|
||||
// ul
|
||||
// li
|
||||
// code
|
||||
// li
|
||||
// code
|
||||
//
|
||||
// and we want to be able to pull out the code elements from only the first level
|
||||
// if `onlyDirect` is set to `true`.
|
||||
const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} li > :not(ul) code`;
|
||||
return element.all(by.css(selector)).map<string>(item => item.getText());
|
||||
}
|
||||
|
||||
getOverview(docType) {
|
||||
return element(by.css(`.${docType}-overview`));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { browser, element, by, promise } from 'protractor';
|
||||
import { element, by } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('site App', function() {
|
||||
@ -41,20 +41,20 @@ describe('site App', function() {
|
||||
|
||||
describe('scrolling to the top', () => {
|
||||
it('should scroll to the top when navigating to another page', () => {
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.scrollToBottom();
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
|
||||
|
||||
page.navigateTo('guide/api');
|
||||
page.navigateTo('api');
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
|
||||
});
|
||||
|
||||
it('should scroll to the top when navigating to the same page', () => {
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.scrollToBottom();
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
|
||||
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
|
||||
});
|
||||
});
|
||||
@ -66,7 +66,9 @@ describe('site App', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('google analytics', () => {
|
||||
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
|
||||
// once it is no more flaky.
|
||||
xdescribe('google analytics', () => {
|
||||
beforeEach(done => page.gaReady.then(done));
|
||||
|
||||
it('should call ga', done => {
|
||||
@ -100,4 +102,12 @@ describe('site App', function() {
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('ControlValueAccessor');
|
||||
});
|
||||
});
|
||||
|
||||
describe('404 page', () => {
|
||||
it('should search the index for words found in the url', () => {
|
||||
page.navigateTo('http/router');
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('Http');
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('Router');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -30,8 +30,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
browsers: ['CustomChrome'],
|
||||
browserNoActivityTimeout: 60000,
|
||||
singleRun: false
|
||||
singleRun: false,
|
||||
customLaunchers: {
|
||||
CustomChrome: {
|
||||
base: 'Chrome',
|
||||
flags: process.env.TRAVIS && ['--no-sandbox']
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -6,33 +6,45 @@
|
||||
"author": "Angular",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug --ignore-packages @angular/service-worker",
|
||||
"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",
|
||||
"prebuild": "yarn check-env && yarn setup",
|
||||
"build": "ng build --target=production --environment=stable -sm --build-optimizer",
|
||||
"postbuild": "yarn sw-manifest && yarn sw-copy",
|
||||
"prebuild": "yarn setup",
|
||||
"build": "yarn ~~build",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn ~~update-webdriver",
|
||||
"e2e": "ng e2e --no-webdriver-update",
|
||||
"setup": "yarn && yarn build-ie-polyfills && yarn boilerplate:remove && yarn boilerplate:add && yarn generate-plunkers && yarn generate-zips && yarn docs",
|
||||
"pretest-pwa-score-local": "yarn build",
|
||||
"test-pwa-score-local": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"",
|
||||
"presetup": "yarn install --freeze-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 generate-plunkers && yarn generate-zips && yarn docs",
|
||||
"presetup-local": "yarn presetup",
|
||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||
"postsetup-local": "yarn postsetup",
|
||||
"pretest-pwa-score-localhost": "yarn build",
|
||||
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score http://localhost:4200 90\"",
|
||||
"test-pwa-score": "node scripts/test-pwa-score",
|
||||
"example-e2e": "node ./tools/examples/run-example-e2e",
|
||||
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
|
||||
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
|
||||
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared",
|
||||
"example-use-npm": "node tools/ng-packages-installer restore ./tools/examples/shared",
|
||||
"example-check-local": "node tools/ng-packages-installer check ./tools/examples/shared",
|
||||
"deploy-preview": "scripts/deploy-preview.sh",
|
||||
"deploy-production": "scripts/deploy-to-firebase.sh",
|
||||
"check-env": "node scripts/check-environment",
|
||||
"check-env": "yarn ~~check-env",
|
||||
"postcheck-env": "yarn aio-check-local",
|
||||
"payload-size": "scripts/payload.sh",
|
||||
"predocs": "rimraf src/generated/{docs,*.json}",
|
||||
"docs": "dgeni ./tools/transforms/angular.io-package",
|
||||
"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",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js",
|
||||
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch\" \"yarn start\"",
|
||||
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false",
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
|
||||
"boilerplate:test": "node tools/examples/test.js",
|
||||
@ -41,11 +53,15 @@
|
||||
"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/",
|
||||
"postinstall": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
|
||||
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js"
|
||||
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js",
|
||||
"~~check-env": "node scripts/check-environment",
|
||||
"~~build": "ng build --target=production --environment=stable -sm --build-optimizer",
|
||||
"post~~build": "yarn sw-manifest && yarn sw-copy",
|
||||
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.5 <7.0.0",
|
||||
"yarn": ">=0.21.3 <1.0.0"
|
||||
"yarn": ">=1.0.2 <2.0.0"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@ -66,7 +82,7 @@
|
||||
"core-js": "^2.4.1",
|
||||
"jasmine": "^2.6.0",
|
||||
"ng-pwa-tools": "^0.0.10",
|
||||
"rxjs": "^5.2.0",
|
||||
"rxjs": "^5.4.3",
|
||||
"tslib": "^1.7.1",
|
||||
"web-animations-js": "^2.2.5",
|
||||
"zone.js": "^0.8.16"
|
||||
@ -78,11 +94,12 @@
|
||||
"@types/node": "~6.0.60",
|
||||
"archiver": "^1.3.0",
|
||||
"canonical-path": "^0.0.2",
|
||||
"chalk": "^2.1.0",
|
||||
"codelyzer": "~2.0.0",
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.21.2",
|
||||
"dgeni-packages": "0.22.0",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
@ -117,7 +134,7 @@
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.7",
|
||||
"tree-kill": "^1.1.0",
|
||||
"ts-node": "~2.0.0",
|
||||
"ts-node": "^3.3.0",
|
||||
"tslint": "~4.5.0",
|
||||
"typescript": "2.3.2",
|
||||
"uglify-js": "^3.0.15",
|
||||
|
@ -12,7 +12,8 @@ exports.config = {
|
||||
browserName: 'chrome',
|
||||
// For Travis
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN
|
||||
binary: process.env.CHROME_BIN,
|
||||
args: ['--no-sandbox']
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
|
@ -3,7 +3,7 @@
|
||||
set -u -e -o pipefail
|
||||
|
||||
declare -A limitUncompressed
|
||||
limitUncompressed=(["inline"]=1600 ["main"]=525000 ["polyfills"]=38000)
|
||||
limitUncompressed=(["inline"]=1600 ["main"]=525500 ["polyfills"]=38000)
|
||||
declare -A limitGzip7
|
||||
limitGzip7=(["inline"]=1000 ["main"]=127000 ["polyfills"]=12500)
|
||||
declare -A limitGzip9
|
||||
|
@ -51,6 +51,6 @@ readonly relevantChangedFilesCount=$(git diff --name-only $TRAVIS_COMMIT_RANGE |
|
||||
# Run PWA-score tests (unless the deployment is not public yet;
|
||||
# i.e. it could not be automatically verified).
|
||||
if [[ $httpCode -ne 202 ]] && [[ "$isHidden" != "true" ]]; then
|
||||
yarn test-pwa-score -- "$DEPLOYED_URL" "$MIN_PWA_SCORE"
|
||||
yarn test-pwa-score "$DEPLOYED_URL" "$MIN_PWA_SCORE"
|
||||
fi
|
||||
)
|
||||
|
@ -87,7 +87,7 @@ fi
|
||||
cd "`dirname $0`/.."
|
||||
|
||||
# Build the app
|
||||
yarn build -- --env=$deployEnv
|
||||
yarn build --env=$deployEnv
|
||||
|
||||
# Include any mode-specific files
|
||||
cp -rf src/extra-files/$deployEnv/. dist/
|
||||
@ -100,5 +100,5 @@ fi
|
||||
firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken"
|
||||
|
||||
# Run PWA-score tests
|
||||
yarn test-pwa-score -- "$deployedUrl" "$MIN_PWA_SCORE"
|
||||
yarn test-pwa-score "$deployedUrl" "$MIN_PWA_SCORE"
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ else
|
||||
# Nothing changed in aio/
|
||||
exit 0
|
||||
fi
|
||||
message=$(echo $TRAVIS_COMMIT_MESSAGE | sed 's/"/\\"/g' | sed 's/\\/\\\\/g')
|
||||
message=$(echo $TRAVIS_COMMIT_MESSAGE | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
|
||||
payloadData="$payloadData\"change\": \"$change\", \"message\": \"$message\""
|
||||
|
||||
payloadData="{${payloadData}}"
|
||||
|
@ -17,8 +17,16 @@ const printer = require('lighthouse/lighthouse-cli/printer');
|
||||
const config = require('lighthouse/lighthouse-core/config/default.js');
|
||||
|
||||
// Constants
|
||||
const CHROME_LAUNCH_OPTS = {};
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
|
||||
|
||||
// Specify the path and flags for Chrome on Travis
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
|
||||
CHROME_LAUNCH_OPTS.chromeFlags = ['--no-sandbox'];
|
||||
}
|
||||
|
||||
// Run
|
||||
_main(process.argv.slice(2));
|
||||
|
||||
@ -66,7 +74,7 @@ function ignoreHttpsAudits(config) {
|
||||
}
|
||||
|
||||
function launchChromeAndRunLighthouse(url, flags, config) {
|
||||
return chromeLauncher.launch().then(chrome => {
|
||||
return chromeLauncher.launch(CHROME_LAUNCH_OPTS).then(chrome => {
|
||||
flags.port = chrome.port;
|
||||
return lighthouse(url, flags, config).
|
||||
then(results => chrome.kill().then(() => results)).
|
||||
|
@ -13,7 +13,7 @@
|
||||
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||
</md-toolbar>
|
||||
<aio-search-results #searchResults *ngIf="showSearchResults" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||
|
||||
<md-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { async, inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { MdProgressBar, MdSidenav } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
@ -22,9 +21,9 @@ import { MockSearchService } from 'testing/search.service';
|
||||
import { NavigationNode } from 'app/navigation/navigation.service';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SelectComponent, Option } from 'app/shared/select/select.component';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocComponent } from 'app/embedded/toc/toc.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
|
||||
@ -1054,11 +1053,6 @@ class TestGaService {
|
||||
locationChanged = jasmine.createSpy('locationChanged');
|
||||
}
|
||||
|
||||
class TestSearchService {
|
||||
initWorker = jasmine.createSpy('initWorker');
|
||||
loadIndex = jasmine.createSpy('loadIndex');
|
||||
}
|
||||
|
||||
class TestHttpClient {
|
||||
|
||||
static versionInfo = {
|
||||
|
@ -2,18 +2,18 @@ import { Component, ElementRef, HostBinding, HostListener, OnInit,
|
||||
QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { MdSidenav } from '@angular/material';
|
||||
|
||||
import { CurrentNodes, NavigationService, NavigationViews, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { CurrentNodes, NavigationService, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { DocumentService, DocumentContents } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { combineLatest } from 'rxjs/observable/combineLatest';
|
||||
|
||||
@ -89,10 +89,9 @@ export class AppComponent implements OnInit {
|
||||
|
||||
// Search related properties
|
||||
showSearchResults = false;
|
||||
@ViewChildren('searchBox, searchResults', { read: ElementRef })
|
||||
searchResults: Observable<SearchResults>;
|
||||
@ViewChildren('searchBox, searchResultsView', { read: ElementRef })
|
||||
searchElements: QueryList<ElementRef>;
|
||||
@ViewChild(SearchResultsComponent)
|
||||
searchResults: SearchResultsComponent;
|
||||
@ViewChild(SearchBoxComponent)
|
||||
searchBox: SearchBoxComponent;
|
||||
|
||||
@ -332,7 +331,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
doSearch(query) {
|
||||
this.searchService.search(query);
|
||||
this.searchResults = this.searchService.search(query);
|
||||
this.showSearchResults = !!query;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,6 @@ import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
|
||||
import { NavItemComponent } from 'app/layout/nav-item/nav-item.component';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchResultsComponent } from './search/search-results/search-results.component';
|
||||
import { SearchBoxComponent } from './search/search-box/search-box.component';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
|
||||
@ -95,7 +94,6 @@ export const svgIconProviders = [
|
||||
ModeBannerComponent,
|
||||
NavMenuComponent,
|
||||
NavItemComponent,
|
||||
SearchResultsComponent,
|
||||
SearchBoxComponent,
|
||||
TopMenuComponent,
|
||||
],
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { Injector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import { ApiListComponent } from './api-list.component';
|
||||
|
@ -13,7 +13,7 @@ import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
import { combineLatest } from 'rxjs/observable/combineLatest';
|
||||
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { ApiItem, ApiSection, ApiService } from './api.service';
|
||||
import { ApiSection, ApiService } from './api.service';
|
||||
|
||||
import { Option } from 'app/shared/select/select.component';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { Injector } from '@angular/core';
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { Component, ElementRef, ViewChild, OnChanges, OnDestroy, Input } from '@angular/core';
|
||||
import { Component, ElementRef, ViewChild, OnChanges, Input } from '@angular/core';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { PrettyPrinter } from './pretty-printer.service';
|
||||
import { CopierService } from 'app/shared/copier.service';
|
||||
import { MdSnackBar } from '@angular/material';
|
||||
|
||||
const originalLabel = 'Copy Code';
|
||||
const copiedLabel = 'Copied!';
|
||||
const defaultLineNumsCount = 10; // by default, show linenums over this number
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ import { Injector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContributorService } from './contributor.service';
|
||||
import { Contributor, ContributorGroup } from './contributors.model';
|
||||
import { ContributorGroup } from './contributors.model';
|
||||
|
||||
describe('ContributorService', () => {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
import { CurrentLocationComponent } from './current-location.component';
|
||||
|
@ -20,6 +20,7 @@ import { CodeTabsComponent } from './code/code-tabs.component';
|
||||
import { ContributorListComponent } from './contributor/contributor-list.component';
|
||||
import { ContributorComponent } from './contributor/contributor.component';
|
||||
import { CurrentLocationComponent } from './current-location.component';
|
||||
import { FileNotFoundSearchComponent } from './search/file-not-found-search.component';
|
||||
import { LiveExampleComponent, EmbeddedPlunkerComponent } from './live-example/live-example.component';
|
||||
import { ResourceListComponent } from './resource/resource-list.component';
|
||||
import { ResourceService } from './resource/resource.service';
|
||||
@ -30,7 +31,8 @@ import { TocComponent } from './toc/toc.component';
|
||||
*/
|
||||
export const embeddedComponents: any[] = [
|
||||
ApiListComponent, CodeExampleComponent, CodeTabsComponent, ContributorListComponent,
|
||||
CurrentLocationComponent, LiveExampleComponent, ResourceListComponent, TocComponent
|
||||
CurrentLocationComponent, FileNotFoundSearchComponent, LiveExampleComponent, ResourceListComponent,
|
||||
TocComponent
|
||||
];
|
||||
|
||||
/** Injectable class w/ property returning components that can be embedded in docs */
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Component, DebugElement, ElementRef } from '@angular/core';
|
||||
import { Component, DebugElement } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
import { LiveExampleComponent, EmbeddedPlunkerComponent } from './live-example.component';
|
||||
@ -71,7 +71,6 @@ describe('LiveExampleComponent', () => {
|
||||
|
||||
describe('when not embedded', () => {
|
||||
function getLiveExampleAnchor() { return getAnchors()[0]; }
|
||||
function getDownloadAnchor() { return getAnchors()[1]; }
|
||||
|
||||
it('should create LiveExampleComponent', () => {
|
||||
testComponent(() => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
import { PlatformLocation } from '@angular/common';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
|
||||
import { ResourceListComponent } from './resource-list.component';
|
||||
|
@ -3,7 +3,7 @@ import { Injector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ResourceService } from './resource.service';
|
||||
import { Category, SubCategory, Resource } from './resource.model';
|
||||
import { Category } from './resource.model';
|
||||
|
||||
describe('ResourceService', () => {
|
||||
|
||||
|
@ -0,0 +1,49 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { FileNotFoundSearchComponent } from './file-not-found-search.component';
|
||||
|
||||
|
||||
describe('FileNotFoundSearchComponent', () => {
|
||||
let element: HTMLElement;
|
||||
let fixture: ComponentFixture<FileNotFoundSearchComponent>;
|
||||
let searchService: SearchService;
|
||||
let searchResultSubject: Subject<SearchResults>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FileNotFoundSearchComponent, SearchResultsComponent ],
|
||||
providers: [
|
||||
{ provide: LocationService, useValue: new MockLocationService('base/initial-url?some-query') },
|
||||
SearchService
|
||||
]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(FileNotFoundSearchComponent);
|
||||
searchService = TestBed.get(SearchService);
|
||||
searchResultSubject = new Subject<SearchResults>();
|
||||
spyOn(searchService, 'search').and.callFake(() => searchResultSubject.asObservable());
|
||||
fixture.detectChanges();
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
|
||||
it('should run a search with a query built from the current url', () => {
|
||||
expect(searchService.search).toHaveBeenCalledWith('base initial url');
|
||||
});
|
||||
|
||||
it('should pass through any results to the `aio-search-results` component', () => {
|
||||
const searchResultsComponent = fixture.debugElement.query(By.directive(SearchResultsComponent)).componentInstance;
|
||||
expect(searchResultsComponent.searchResults).toBe(null);
|
||||
|
||||
const results = { query: 'base initial url', results: []};
|
||||
searchResultSubject.next(results);
|
||||
fixture.detectChanges();
|
||||
expect(searchResultsComponent.searchResults).toEqual(results);
|
||||
});
|
||||
});
|
@ -0,0 +1,23 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-file-not-found-search',
|
||||
template:
|
||||
`<p>Let's see if any of these search results help...</p>
|
||||
<aio-search-results class="embedded" [searchResults]="searchResults | async"></aio-search-results>`
|
||||
})
|
||||
export class FileNotFoundSearchComponent implements OnInit {
|
||||
searchResults: Observable<SearchResults>;
|
||||
constructor(private location: LocationService, private search: SearchService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchResults = this.location.currentPath.switchMap(path => {
|
||||
const query = path.split(/\W+/).join(' ');
|
||||
return this.search.search(query);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By, DOCUMENT } from '@angular/platform-browser';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { asap } from 'rxjs/scheduler/asap';
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
Component, ComponentFactoryResolver, DebugElement,
|
||||
ElementRef, Injector, NgModule, OnInit, ViewChild } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Component, DebugElement, ElementRef, NgModule, OnInit, ViewChild } from '@angular/core';
|
||||
import { DocViewerComponent } from './doc-viewer.component';
|
||||
import { DocumentContents } from 'app/documents/document.service';
|
||||
import { EmbeddedModule, embeddedComponents, EmbeddedComponents } from 'app/embedded/embedded.module';
|
||||
import { EmbeddedModule, EmbeddedComponents } from 'app/embedded/embedded.module';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
Component, ComponentFactory, ComponentFactoryResolver, ComponentRef,
|
||||
DoCheck, ElementRef, EventEmitter, Injector, Input, OnDestroy,
|
||||
Output, ViewEncapsulation
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
||||
import { EmbeddedComponents } from 'app/embedded/embedded.module';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { SimpleChange, SimpleChanges, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
import { NavItemComponent } from './nav-item.component';
|
||||
import { NavigationNode } from 'app/navigation/navigation.model';
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import { TopMenuComponent } from './top-menu.component';
|
||||
import { NavigationService, NavigationViews, NavigationNode } from 'app/navigation/navigation.service';
|
||||
import { NavigationService, NavigationViews } from 'app/navigation/navigation.service';
|
||||
|
||||
describe('TopMenuComponent', () => {
|
||||
let component: TopMenuComponent;
|
||||
|
@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AsyncSubject } from 'rxjs/AsyncSubject';
|
||||
import { combineLatest } from 'rxjs/observable/combineLatest';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/publishLast';
|
||||
|
19
aio/src/app/search/interfaces.ts
Normal file
19
aio/src/app/search/interfaces.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export interface SearchResults {
|
||||
query: string;
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
path: string;
|
||||
title: string;
|
||||
type: string;
|
||||
titleWords: string;
|
||||
keywords: string;
|
||||
}
|
||||
|
||||
export interface SearchArea {
|
||||
name: string;
|
||||
pages: SearchResult[];
|
||||
priorityPages: SearchResult[];
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { Component } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { SearchBoxComponent } from './search-box.component';
|
||||
import { MockSearchService } from 'testing/search.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ReflectiveInjector, NgZone } from '@angular/core';
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import { SearchService } from './search.service';
|
||||
import { WebWorkerClient } from 'app/shared/web-worker';
|
||||
|
||||
@ -36,27 +37,29 @@ describe('SearchService', () => {
|
||||
|
||||
describe('search', () => {
|
||||
beforeEach(() => {
|
||||
// We must initialize the service before calling search
|
||||
service.initWorker('some/url', 100);
|
||||
// We must initialize the service before calling connectSearches
|
||||
service.initWorker('some/url', 1000);
|
||||
// Simulate the index being ready so that searches get sent to the worker
|
||||
(service as any).ready = Observable.of(true);
|
||||
});
|
||||
|
||||
it('should trigger a `loadIndex` synchronously', () => {
|
||||
service.search('some query');
|
||||
it('should trigger a `loadIndex` synchronously (not waiting for the delay)', () => {
|
||||
expect(mockWorker.sendMessage).not.toHaveBeenCalled();
|
||||
service.search('some query').subscribe();
|
||||
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
|
||||
});
|
||||
|
||||
it('should send a "query-index" message to the worker', () => {
|
||||
service.search('some query');
|
||||
service.search('some query').subscribe();
|
||||
expect(mockWorker.sendMessage).toHaveBeenCalledWith('query-index', 'some query');
|
||||
});
|
||||
|
||||
it('should push the response to the `searchResults` observable', () => {
|
||||
it('should push the response to the returned observable', () => {
|
||||
const mockSearchResults = { results: ['a', 'b'] };
|
||||
let actualSearchResults;
|
||||
(mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults));
|
||||
let searchResults: any;
|
||||
service.searchResults.subscribe(results => searchResults = results);
|
||||
service.search('some query');
|
||||
expect(searchResults).toEqual(mockSearchResults);
|
||||
service.search('some query').subscribe(results => actualSearchResults = results);
|
||||
expect(actualSearchResults).toEqual(mockSearchResults);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,34 +4,21 @@ Use of this source code is governed by an MIT-style license that
|
||||
can be found in the LICENSE file at http://angular.io/license
|
||||
*/
|
||||
|
||||
import { NgZone, Injectable, Type } from '@angular/core';
|
||||
import { NgZone, Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ReplaySubject } from 'rxjs/ReplaySubject';
|
||||
import 'rxjs/add/observable/race';
|
||||
import 'rxjs/add/observable/timer';
|
||||
import 'rxjs/add/operator/concatMap';
|
||||
import 'rxjs/add/operator/publish';
|
||||
import 'rxjs/add/operator/publishReplay';
|
||||
import { WebWorkerClient } from 'app/shared/web-worker';
|
||||
|
||||
export interface SearchResults {
|
||||
query: string;
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
path: string;
|
||||
title: string;
|
||||
type: string;
|
||||
titleWords: string;
|
||||
keywords: string;
|
||||
}
|
||||
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
private ready: Observable<boolean>;
|
||||
private searchesSubject = new ReplaySubject<string>(1);
|
||||
searchResults: Observable<SearchResults>;
|
||||
|
||||
private worker: WebWorkerClient;
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
/**
|
||||
@ -43,36 +30,32 @@ export class SearchService {
|
||||
* @param initDelay the number of milliseconds to wait before we load the WebWorker and generate the search index
|
||||
*/
|
||||
initWorker(workerUrl: string, initDelay: number) {
|
||||
const searchResults = Observable
|
||||
const ready = this.ready = Observable
|
||||
// Wait for the initDelay or the first search
|
||||
.race(
|
||||
.race<any>(
|
||||
Observable.timer(initDelay),
|
||||
this.searchesSubject.first()
|
||||
(this.searchesSubject as Observable<string>).first()
|
||||
)
|
||||
.concatMap(() => {
|
||||
// Create the worker and load the index
|
||||
const worker = WebWorkerClient.create(workerUrl, this.zone);
|
||||
return worker.sendMessage('load-index').concatMap(() =>
|
||||
// Once the index has loaded, switch to listening to the searches coming in
|
||||
this.searchesSubject.switchMap((query) =>
|
||||
// Each search gets switched to a web worker message, whose results are returned via an observable
|
||||
worker.sendMessage<SearchResults>('query-index', query)
|
||||
)
|
||||
);
|
||||
}).publish();
|
||||
this.worker = WebWorkerClient.create(workerUrl, this.zone);
|
||||
return this.worker.sendMessage<boolean>('load-index');
|
||||
}).publishReplay(1);
|
||||
|
||||
// Connect to the observable to kick off the timer
|
||||
searchResults.connect();
|
||||
|
||||
// Expose the connected observable to the rest of the world
|
||||
this.searchResults = searchResults;
|
||||
ready.connect();
|
||||
return ready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a search query to the index.
|
||||
* The results will appear on the `searchResults` observable.
|
||||
* Search the index using the given query and emit results on the observable that is returned.
|
||||
* @param query The query to run against the index.
|
||||
* @returns an observable collection of search results
|
||||
*/
|
||||
search(query: string) {
|
||||
search(query: string): Observable<SearchResults> {
|
||||
// Trigger the searches subject to override the init delay timer
|
||||
this.searchesSubject.next(query);
|
||||
// Once the index has loaded, switch to listening to the searches coming in.
|
||||
return this.ready.concatMap(() => this.worker.sendMessage<SearchResults>('query-index', query));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MdIconRegistry } from '@angular/material';
|
||||
import { CustomMdIconRegistry, SVG_ICONS, SvgIconInfo } from './custom-md-icon-registry';
|
||||
import { CustomMdIconRegistry, SvgIconInfo } from './custom-md-icon-registry';
|
||||
|
||||
describe('CustomMdIconRegistry', () => {
|
||||
it('should get the SVG element for a preloaded icon from the cache', () => {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user