Compare commits

..

21 Commits

Author SHA1 Message Date
2ab8e88924 release: cut the v10.0.8 release 2020-08-04 15:51:57 -07:00
a91dd2e42a build: cleanup .bazelrc file to no longer set unused flags (#38124)
This option is no longer needed in Bazel and will be an error in the future

PR Close #38124
2020-08-03 12:53:11 -07:00
6eca80b4aa revert: docs(core): correct SomeService to SomeComponent (#38325)
This reverts commit b4449e35bf.

The example given from the previous change was for a component selector and not a provider selector.

This change fixes it.

Fixes #38323.

PR Close #38325
2020-08-03 12:52:20 -07:00
cea46786a2 fix(compiler): update unparsable character reference entity error messages (#38319)
Within an angular template, when a character entity is unable to be parsed, previously a generic
unexpected character error was thrown.  This does not properly express the issue that was discovered
as the issue is actually caused by the discovered character making the whole of the entity unparsable.
The compiler will now instead inform via the error message what string was attempted to be parsed
and what it was attempted to be parsed as.

Example, for this template:
```
<p>
  &#x123p
</p>
```
Before this change:
`Unexpected character "p"`

After this change:
`Unable to parse entity "&#x123p" - hexadecimal character reference entities must end with ";"`

Fixes #26067

PR Close #38319
2020-07-31 15:32:54 -07:00
9c5fc1693a refactor(docs-infra): update docs examples tslint.json to match CLI and fix failures (#38143)
This commit updates the `tslint.json` configuration file, that is used
to lint the docs examples, to match the one generated for new Angular
CLI apps. There are some minimal differences (marked with `TODO`
comments) for things, such as component selector prefix, that would
require extensive and/or difficult to validate changes in guides.

This commit also includes the final adjustments to make the docs
examples code compatible with the new tslint rules. (The bulk of the
work has been done in previous commits.)

PR Close #38143
2020-07-31 11:00:08 -07:00
42f5770b7b refactor(docs-infra): fix docs examples for Angular-specific tslint rules (#38143)
This commit updates the docs examples to be compatible with the
following Angular-specific tslint rules:
- `component-selector`
- `directive-selector`
- `no-conflicting-lifecycle`
- `no-host-metadata-property`
- `no-input-rename`
- `no-output-native`
- `no-output-rename`

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
01db37435f refactor(docs-infra): fix docs examples for tslint rule prefer-const (#38143)
This commit updates the docs examples to be compatible with the
`prefer-const` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
07a003352d refactor(docs-infra): fix docs examples for tslint rules related to underscores in variable names (#38143)
This commit updates the docs examples to be compatible with the
`variable-name` tslint rule without requiring the
`allow-leading-underscore` and `allow-trailing-underscore` options.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
ac009d293d refactor(docs-infra): fix docs examples for tslint rules related to variable names (#38143)
This commit updates the docs examples to be compatible with the
`no-shadowed-variable` and `variable-name` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
eb8f8c93c8 refactor(docs-infra): fix docs examples for tslint rule member-ordering (#38143)
This commit updates the docs examples to be compatible with the
`member-ordering` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
18a5117d1e refactor(docs-infra): fix docs examples for tslint rule no-angle-bracket-type-assertion (#38143)
This commit updates the docs examples to be compatible with the
`no-angle-bracket-type-assertion` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
ff72da60d3 refactor(docs-infra): fix docs examples for tslint rules related to object properties (#38143)
This commit updates the docs examples to be compatible with the
`no-string-literal`, `object-literal-key-quotes` and
`object-literal-shorthand` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
d4a723a464 refactor(docs-infra): fix docs examples for tslint rule only-arrow-functions (#38143)
This commit updates the docs examples to be compatible with the
`only-arrow-functions` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:08 -07:00
9c2f0b8ac4 style(docs-infra): fix docs examples for tslint rule jsdoc-format (#38143)
This commit updates the docs examples to be compatible with the
`jsdoc-format` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
dc081fb0bf style(docs-infra): fix docs examples for tslint rule semicolon (#38143)
This commit updates the docs examples to be compatible with the
`semicolon` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
f882099f9d style(docs-infra): fix docs examples for tslint rules related to whitespace (#38143)
This commit updates the docs examples to be compatible with the `align`,
`space-before-function-paren` and `typedef-whitespace` tslint rules.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
24498eb416 style(docs-infra): fix docs examples for tslint rule import-spacing (#38143)
This commit updates the docs examples to be compatible with the
`import-spacing` tslint rule.

This is in preparation of updating the docs examples `tslint.json` to
match the one generated for new Angular CLI apps in a future commit.

PR Close #38143
2020-07-31 11:00:07 -07:00
4c2cdc682b refactor(docs-infra): remove unnecessary use strict from docs examples TS files (#38143)
By default, TypeScript will emit `"use strict"` directives, so it is not
necessary to include `'use strict'` in `.ts` files:
https://www.typescriptlang.org/docs/handbook/compiler-options.html#:~:text=--noImplicitUseStrict

PR Close #38143
2020-07-31 11:00:07 -07:00
1cb66bb39f refactor(docs-infra): remove unused styleguide examples (#38143)
The `03-*` code style rule have been removed from the style guide in
be0bc799f3.

This commit removes the corresponding files and related unused code from
the`styleguide` example project.

PR Close #38143
2020-07-31 11:00:07 -07:00
7ff5ef27c7 fix(language-service): [ivy] do not retrieve ts.Program at startup (#38120)
This commit removes compiler instantiation at startup.
This is because the constructor is invoked during the plugin loading phase,
in which the project has not been completely loaded.

Retrieving `ts.Program` at startup will trigger an `updateGraph` operation,
which could only be called after the Project has loaded completely.
Without this change, the Ivy LS cannot be loaded as a tsserver plugin.

Note that the whole `Compiler` class is temporary, so changes made there are
only for development. Once we have proper integration with ngtsc the
`Compiler` class would be removed.

PR Close #38120
2020-07-30 16:54:20 -07:00
03d8e317c4 fix(compiler): add PURE annotation to getInheritedFactory calls (#38291)
Currently the `getInheritedFactory` function is implemented to allow
closure to remove the call if the base factory is unused.  However, this
method does not work with terser.  By adding the PURE annotation,
terser will also be able to remove the call when unused.

PR Close #38291
2020-07-30 16:53:52 -07:00
449 changed files with 2512 additions and 2977 deletions

View File

@ -136,15 +136,6 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times. # retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1 test:saucelabs --flaky_test_attempts=1
###############################
# NodeJS rules settings
# These settings are required for rules_nodejs
###############################
# Turn on managed directories feature in Bazel
# This allows us to avoid installing a second copy of node_modules
common --experimental_allow_incremental_repository_updates
#################################################### ####################################################
# User bazel configuration # User bazel configuration
# NOTE: This needs to be the *last* entry in the config. # NOTE: This needs to be the *last* entry in the config.

View File

@ -1,3 +1,14 @@
<a name="10.0.8"></a>
## 10.0.8 (2020-08-04)
### Bug Fixes
* **compiler:** add PURE annotation to getInheritedFactory calls ([#38291](https://github.com/angular/angular/issues/38291)) ([03d8e31](https://github.com/angular/angular/commit/03d8e31))
* **compiler:** update unparsable character reference entity error messages ([#38319](https://github.com/angular/angular/issues/38319)) ([cea4678](https://github.com/angular/angular/commit/cea4678)), closes [#26067](https://github.com/angular/angular/issues/26067)
<a name="10.0.7"></a> <a name="10.0.7"></a>
## 10.0.7 (2020-07-30) ## 10.0.7 (2020-07-30)

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Accessibility example e2e tests', () => { describe('Accessibility example e2e tests', () => {
@ -8,11 +6,11 @@ describe('Accessibility example e2e tests', () => {
browser.get(''); browser.get('');
}); });
it('should display Accessibility Example', function () { it('should display Accessibility Example', () => {
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example'); expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
}); });
it('should take a number and change progressbar width', function () { it('should take a number and change progressbar width', () => {
element(by.css('input')).sendKeys('16'); element(by.css('input')).sendKeys('16');
expect(element(by.css('input')).getAttribute('value')).toEqual('016'); expect(element(by.css('input')).getAttribute('value')).toEqual('016');
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px'); expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');

View File

@ -1,3 +1,4 @@
// tslint:disable: no-host-metadata-property
// #docregion progressbar-component // #docregion progressbar-component
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';

View File

@ -1,20 +1,18 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('AngularJS to Angular Quick Reference Tests', function () { describe('AngularJS to Angular Quick Reference Tests', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should display no poster images after bootstrap', function () { it('should display no poster images after bootstrap', () => {
testImagesAreDisplayed(false); testImagesAreDisplayed(false);
}); });
it('should display proper movie data', function () { it('should display proper movie data', () => {
// We check only a few samples // We check only a few samples
let expectedSamples: any[] = [ const expectedSamples: any[] = [
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true}, {row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
{row: 0, column: 2, value: 'Celeritas'}, {row: 0, column: 2, value: 'Celeritas'},
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format {row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
@ -25,18 +23,17 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
]; ];
// Go through the samples // Go through the samples
let movieRows = getMovieRows(); const movieRows = getMovieRows();
for (let i = 0; i < expectedSamples.length; i++) { for (const sample of expectedSamples) {
let sample = expectedSamples[i]; const tableCell = movieRows.get(sample.row)
let tableCell = movieRows.get(sample.row)
.all(by.tagName('td')).get(sample.column); .all(by.tagName('td')).get(sample.column);
// Check the cell or its nested element // Check the cell or its nested element
let elementToCheck = sample.element const elementToCheck = sample.element
? tableCell.element(by.tagName(sample.element)) ? tableCell.element(by.tagName(sample.element))
: tableCell; : tableCell;
// Check element attribute or text // Check element attribute or text
let valueToCheck = sample.attr const valueToCheck = sample.attr
? elementToCheck.getAttribute(sample.attr) ? elementToCheck.getAttribute(sample.attr)
: elementToCheck.getText(); : elementToCheck.getText();
@ -51,42 +48,42 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
} }
}); });
it('should display images after Show Poster', function () { it('should display images after Show Poster', () => {
testPosterButtonClick('Show Poster', true); testPosterButtonClick('Show Poster', true);
}); });
it('should hide images after Hide Poster', function () { it('should hide images after Hide Poster', () => {
testPosterButtonClick('Hide Poster', false); testPosterButtonClick('Hide Poster', false);
}); });
it('should display no movie when no favorite hero is specified', function () { it('should display no movie when no favorite hero is specified', () => {
testFavoriteHero(null, 'Please enter your favorite hero.'); testFavoriteHero(null, 'Please enter your favorite hero.');
}); });
it('should display no movie for Magneta', function () { it('should display no movie for Magneta', () => {
testFavoriteHero('Magneta', 'No movie, sorry!'); testFavoriteHero('Magneta', 'No movie, sorry!');
}); });
it('should display a movie for Dr Nice', function () { it('should display a movie for Dr Nice', () => {
testFavoriteHero('Dr Nice', 'Excellent choice!'); testFavoriteHero('Dr Nice', 'Excellent choice!');
}); });
function testImagesAreDisplayed(isDisplayed: boolean) { function testImagesAreDisplayed(isDisplayed: boolean) {
let expectedMovieCount = 3; const expectedMovieCount = 3;
let movieRows = getMovieRows(); const movieRows = getMovieRows();
expect(movieRows.count()).toBe(expectedMovieCount); expect(movieRows.count()).toBe(expectedMovieCount);
for (let i = 0; i < expectedMovieCount; i++) { for (let i = 0; i < expectedMovieCount; i++) {
let movieImage = movieRows.get(i).element(by.css('td > img')); const movieImage = movieRows.get(i).element(by.css('td > img'));
expect(movieImage.isDisplayed()).toBe(isDisplayed); expect(movieImage.isDisplayed()).toBe(isDisplayed);
} }
} }
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) { function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
let posterButton = element(by.css('app-movie-list tr > th > button')); const posterButton = element(by.css('app-movie-list tr > th > button'));
expect(posterButton.getText()).toBe(expectedButtonText); expect(posterButton.getText()).toBe(expectedButtonText);
posterButton.click().then(function () { posterButton.click().then(() => {
testImagesAreDisplayed(isDisplayed); testImagesAreDisplayed(isDisplayed);
}); });
} }
@ -96,12 +93,12 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
} }
function testFavoriteHero(heroName: string, expectedLabel: string) { function testFavoriteHero(heroName: string, expectedLabel: string) {
let movieListComp = element(by.tagName('app-movie-list')); const movieListComp = element(by.tagName('app-movie-list'));
let heroInput = movieListComp.element(by.tagName('input')); const heroInput = movieListComp.element(by.tagName('input'));
let favoriteHeroLabel = movieListComp.element(by.tagName('h3')); const favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
let resultLabel = movieListComp.element(by.css('span > p')); const resultLabel = movieListComp.element(by.css('span > p'));
heroInput.clear().then(function () { heroInput.clear().then(() => {
heroInput.sendKeys(heroName || ''); heroInput.sendKeys(heroName || '');
expect(resultLabel.getText()).toBe(expectedLabel); expect(resultLabel.getText()).toBe(expectedLabel);
if (heroName) { if (heroName) {

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, ExpectedConditions as EC } from 'protractor'; import { browser, ExpectedConditions as EC } from 'protractor';
import { logging } from 'selenium-webdriver'; import { logging } from 'selenium-webdriver';
import * as openClose from './open-close.po'; import * as openClose from './open-close.po';

View File

@ -34,7 +34,7 @@ export class AppComponent {
// #docregion prepare-router-outlet // #docregion prepare-router-outlet
prepareRoute(outlet: RouterOutlet) { prepareRoute(outlet: RouterOutlet) {
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
} }
// #enddocregion prepare-router-outlet // #enddocregion prepare-router-outlet

View File

@ -1,4 +1,6 @@
// tslint:disable: variable-name
// #docplaster // #docplaster
// #docregion
import { Component, HostBinding, OnInit } from '@angular/core'; import { Component, HostBinding, OnInit } from '@angular/core';
import { trigger, transition, animate, style, query, stagger } from '@angular/animations'; import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
@ -52,13 +54,11 @@ export class HeroListPageComponent implements OnInit {
@HostBinding('@pageAnimations') @HostBinding('@pageAnimations')
public animatePage = true; public animatePage = true;
_heroes = [];
// #docregion filter-animations // #docregion filter-animations
heroTotal = -1; heroTotal = -1;
// #enddocregion filter-animations // #enddocregion filter-animations
get heroes() { get heroes() { return this._heroes; }
return this._heroes; private _heroes = [];
}
ngOnInit() { ngOnInit() {
this._heroes = HEROES; this._heroes = HEROES;

View File

@ -8,8 +8,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
// #docregion trigger, trigger-wildcard1, trigger-transition // #docregion trigger, trigger-wildcard1, trigger-transition
animations: [ animations: [
trigger('openClose', [ trigger('openClose', [
// #enddocregion events1 // #docregion state1
// #docregion state1, events1
// ... // ...
// #enddocregion events1 // #enddocregion events1
state('open', style({ state('open', style({
@ -34,8 +33,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
transition('closed => open', [ transition('closed => open', [
animate('0.5s') animate('0.5s')
]), ]),
// #enddocregion trigger, component // #enddocregion transition2, trigger, component
// #enddocregion transition2
// #docregion trigger-wildcard1 // #docregion trigger-wildcard1
transition('* => closed', [ transition('* => closed', [
animate('1s') animate('1s')
@ -70,7 +68,9 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
}) })
// #docregion events // #docregion events
export class OpenCloseComponent { export class OpenCloseComponent {
// #enddocregion events1, events // #enddocregion events1, events, component
@Input() logging = false;
// #docregion component
isOpen = true; isOpen = true;
toggle() { toggle() {
@ -78,9 +78,8 @@ export class OpenCloseComponent {
} }
// #enddocregion component // #enddocregion component
@Input() logging = false;
// #docregion events1, events // #docregion events1, events
onAnimationEvent ( event: AnimationEvent ) { onAnimationEvent( event: AnimationEvent ) {
// #enddocregion events1, events // #enddocregion events1, events
if (!this.logging) { if (!this.logging) {
return; return;

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { protractor, browser, element, by, ElementFinder } from 'protractor'; import { protractor, browser, element, by, ElementFinder } from 'protractor';
const nameSuffix = 'X'; const nameSuffix = 'X';
@ -21,7 +19,7 @@ describe('Architecture', () => {
}); });
it(`has h2 '${expectedH2}'`, () => { it(`has h2 '${expectedH2}'`, () => {
let h2 = element.all(by.css('h2')).map((elt: any) => elt.getText()); const h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
expect(h2).toEqual(expectedH2); expect(h2).toEqual(expectedH2);
}); });
@ -34,42 +32,42 @@ function heroTests() {
const targetHero: Hero = { id: 2, name: 'Dr Nice' }; const targetHero: Hero = { id: 2, name: 'Dr Nice' };
it('has the right number of heroes', () => { it('has the right number of heroes', () => {
let page = getPageElts(); const page = getPageElts();
expect(page.heroes.count()).toEqual(3); expect(page.heroes.count()).toEqual(3);
}); });
it('has no hero details initially', function () { it('has no hero details initially', () => {
let page = getPageElts(); const page = getPageElts();
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail'); expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
}); });
it('shows selected hero details', async () => { it('shows selected hero details', async () => {
await element(by.cssContainingText('li', targetHero.name)).click(); await element(by.cssContainingText('li', targetHero.name)).click();
let page = getPageElts(); const page = getPageElts();
let hero = await heroFromDetail(page.heroDetail); const hero = await heroFromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id); expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(targetHero.name); expect(hero.name).toEqual(targetHero.name);
}); });
it(`shows updated hero name in details`, async () => { it(`shows updated hero name in details`, async () => {
let input = element.all(by.css('input')).first(); const input = element.all(by.css('input')).first();
input.sendKeys(nameSuffix); input.sendKeys(nameSuffix);
let page = getPageElts(); const page = getPageElts();
let hero = await heroFromDetail(page.heroDetail); const hero = await heroFromDetail(page.heroDetail);
let newName = targetHero.name + nameSuffix; const newName = targetHero.name + nameSuffix;
expect(hero.id).toEqual(targetHero.id); expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(newName); expect(hero.name).toEqual(newName);
}); });
} }
function salesTaxTests() { function salesTaxTests() {
it('has no sales tax initially', function () { it('has no sales tax initially', () => {
let page = getPageElts(); const page = getPageElts();
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info'); expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
}); });
it('shows sales tax', async function () { it('shows sales tax', async () => {
let page = getPageElts(); const page = getPageElts();
page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER); page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER);
expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00'); expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00');
}); });
@ -88,13 +86,11 @@ function getPageElts() {
async function heroFromDetail(detail: ElementFinder): Promise<Hero> { async function heroFromDetail(detail: ElementFinder): Promise<Hero> {
// Get hero id from the first <div> // Get hero id from the first <div>
// let _id = await detail.all(by.css('div')).first().getText(); const id = await detail.all(by.css('div')).first().getText();
let _id = await detail.all(by.css('div')).first().getText();
// Get name from the h2 // Get name from the h2
// let _name = await detail.element(by.css('h4')).getText(); const name = await detail.element(by.css('h4')).getText();
let _name = await detail.element(by.css('h4')).getText();
return { return {
id: +_id.substr(_id.indexOf(' ') + 1), id: +id.substr(id.indexOf(' ') + 1),
name: _name.substr(0, _name.lastIndexOf(' ')) name: name.substr(0, name.lastIndexOf(' ')),
}; };
} }

View File

@ -18,7 +18,7 @@ export class BackendService {
// TODO: get from the database // TODO: get from the database
return Promise.resolve<Hero[]>(HEROES); return Promise.resolve<Hero[]>(HEROES);
} }
let err = new Error('Cannot get object of this type'); const err = new Error('Cannot get object of this type');
this.logger.error(err); this.logger.error(err);
throw err; throw err;
} }

View File

@ -7,7 +7,7 @@ export class SalesTaxService {
constructor(private rateService: TaxRateService) { } constructor(private rateService: TaxRateService) { }
getVAT(value: string | number) { getVAT(value: string | number) {
let amount = (typeof value === 'string') ? const amount = (typeof value === 'string') ?
parseFloat(value) : value; parseFloat(value) : value;
return (amount || 0) * this.rateService.getRate('VAT'); return (amount || 0) * this.rateService.getRate('VAT');
} }

View File

@ -1,34 +1,32 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Attribute binding example', function () { describe('Attribute binding example', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
it('should display Property Binding with Angular', function () { it('should display Property Binding with Angular', () => {
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings'); expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
}); });
it('should display a table', function() { it('should display a table', () => {
expect(element.all(by.css('table')).isPresent()).toBe(true); expect(element.all(by.css('table')).isPresent()).toBe(true);
}); });
it('should display an Aria button', function () { it('should display an Aria button', () => {
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria'); expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
}); });
it('should display a blue background on div', function () { it('should display a blue background on div', () => {
expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)'); expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)');
}); });
it('should display a blue div with a red border', function () { it('should display a blue div with a red border', () => {
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)'); expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
}); });
it('should display a div with many classes', function () { it('should display a div with many classes', () => {
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special'); expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance'); expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
}); });

View File

@ -1,16 +1,16 @@
import { Component } from '@angular/core'; import { Component, HostBinding } from '@angular/core';
@Component({ @Component({
selector: 'comp-with-host-binding', selector: 'comp-with-host-binding',
template: 'I am a component!', template: 'I am a component!',
host: {
'[class.special]': 'isSpecial',
'[style.color]': 'color',
'[style.width]': 'width'
}
}) })
export class CompWithHostBindingComponent { export class CompWithHostBindingComponent {
@HostBinding('class.special')
isSpecial = false; isSpecial = false;
@HostBinding('style.color')
color = 'green'; color = 'green';
@HostBinding('style.width')
width = '200px'; width = '200px';
} }

View File

@ -1,17 +1,15 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Attribute directives', () => { describe('Attribute directives', () => {
let _title = 'My First Attribute Directive'; const title = 'My First Attribute Directive';
beforeAll(() => { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it(`should display correct title: ${_title}`, () => { it(`should display correct title: ${title}`, () => {
expect(element(by.css('h1')).getText()).toEqual(_title); expect(element(by.css('h1')).getText()).toEqual(title);
}); });
it('should be able to select green highlight', () => { it('should be able to select green highlight', () => {

View File

@ -3,57 +3,55 @@ import { logging } from 'selenium-webdriver';
describe('Binding syntax e2e tests', () => { describe('Binding syntax e2e tests', () => {
beforeEach(function () { beforeEach(() => browser.get(''));
browser.get('');
});
// helper function used to test what's logged to the console // helper function used to test what's logged to the console
async function logChecker(button, contents) { async function logChecker(button, contents) {
const logs = await browser.manage().logs().get(logging.Type.BROWSER); const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false); const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
expect(message.length).toBeGreaterThan(0); expect(messages.length).toBeGreaterThan(0);
} }
it('should display Binding syntax', function () { it('should display Binding syntax', () => {
expect(element(by.css('h1')).getText()).toEqual('Binding syntax'); expect(element(by.css('h1')).getText()).toEqual('Binding syntax');
}); });
it('should display Save button', function () { it('should display Save button', () => {
expect(element.all(by.css('button')).get(0).getText()).toBe('Save'); expect(element.all(by.css('button')).get(0).getText()).toBe('Save');
}); });
it('should display HTML attributes and DOM properties', function () { it('should display HTML attributes and DOM properties', () => {
expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties'); expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties');
}); });
it('should display 1. Use the inspector...', function () { it('should display 1. Use the inspector...', () => {
expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector'); expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector');
}); });
it('should display Disabled property vs. attribute', function () { it('should display Disabled property vs. attribute', () => {
expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute'); expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute');
}); });
it('should log a message including Sarah', async () => { it('should log a message including Sarah', async () => {
let attributeButton = element.all(by.css('button')).get(1); const attributeButton = element.all(by.css('button')).get(1);
await attributeButton.click(); await attributeButton.click();
const contents = 'Sarah'; const contents = 'Sarah';
logChecker(attributeButton, contents); logChecker(attributeButton, contents);
}); });
it('should log a message including Sarah for DOM property', async () => { it('should log a message including Sarah for DOM property', async () => {
let DOMPropertyButton = element.all(by.css('button')).get(2); const DOMPropertyButton = element.all(by.css('button')).get(2);
await DOMPropertyButton.click(); await DOMPropertyButton.click();
const contents = 'Sarah'; const contents = 'Sarah';
logChecker(DOMPropertyButton, contents); logChecker(DOMPropertyButton, contents);
}); });
it('should log a message including Sally for DOM property', async () => { it('should log a message including Sally for DOM property', async () => {
let DOMPropertyButton = element.all(by.css('button')).get(2); const DOMPropertyButton = element.all(by.css('button')).get(2);
let input = element(by.css('input')); const input = element(by.css('input'));
input.sendKeys('Sally'); input.sendKeys('Sally');
await DOMPropertyButton.click(); await DOMPropertyButton.click();
const contents = 'Sally'; const contents = 'Sally';
@ -61,14 +59,14 @@ describe('Binding syntax e2e tests', () => {
}); });
it('should log a message that Test Button works', async () => { it('should log a message that Test Button works', async () => {
let testButton = element.all(by.css('button')).get(3); const testButton = element.all(by.css('button')).get(3);
await testButton.click(); await testButton.click();
const contents = 'Test'; const contents = 'Test';
logChecker(testButton, contents); logChecker(testButton, contents);
}); });
it('should toggle Test Button disabled', async () => { it('should toggle Test Button disabled', async () => {
let toggleButton = element.all(by.css('button')).get(4); const toggleButton = element.all(by.css('button')).get(4);
await toggleButton.click(); await toggleButton.click();
const contents = 'true'; const contents = 'true';
logChecker(toggleButton, contents); logChecker(toggleButton, contents);

View File

@ -26,7 +26,7 @@ export class AppComponent {
toggleDisabled(): any { toggleDisabled(): any {
let testButton = <HTMLInputElement> document.getElementById('testButton'); const testButton = document.getElementById('testButton') as HTMLInputElement;
testButton.disabled = !testButton.disabled; testButton.disabled = !testButton.disabled;
console.warn(testButton.disabled); console.warn(testButton.disabled);
} }

View File

@ -1,21 +1,19 @@
'use strict';
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Built-in Directives', function () { describe('Built-in Directives', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should have title Built-in Directives', function () { it('should have title Built-in Directives', () => {
let title = element.all(by.css('h1')).get(0); const title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Directives'); expect(title.getText()).toEqual('Built-in Directives');
}); });
it('should change first Teapot header', async () => { it('should change first Teapot header', async () => {
let firstLabel = element.all(by.css('p')).get(0); const firstLabel = element.all(by.css('p')).get(0);
let firstInput = element.all(by.css('input')).get(0); const firstInput = element.all(by.css('input')).get(0);
expect(firstLabel.getText()).toEqual('Current item name: Teapot'); expect(firstLabel.getText()).toEqual('Current item name: Teapot');
firstInput.sendKeys('abc'); firstInput.sendKeys('abc');
@ -23,49 +21,49 @@ describe('Built-in Directives', function () {
}); });
it('should modify sentence when modified checkbox checked', function () { it('should modify sentence when modified checkbox checked', () => {
let modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1); const modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
let modifiedSentence = element.all(by.css('div')).get(1); const modifiedSentence = element.all(by.css('div')).get(1);
modifiedChkbxLabel.click(); modifiedChkbxLabel.click();
expect(modifiedSentence.getText()).toContain('modified'); expect(modifiedSentence.getText()).toContain('modified');
}); });
it('should modify sentence when normal checkbox checked', function () { it('should modify sentence when normal checkbox checked', () => {
let normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4); const normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
let normalSentence = element.all(by.css('div')).get(7); const normalSentence = element.all(by.css('div')).get(7);
normalChkbxLabel.click(); normalChkbxLabel.click();
expect(normalSentence.getText()).toContain('normal weight and, extra large'); expect(normalSentence.getText()).toContain('normal weight and, extra large');
}); });
it('should toggle app-item-detail', function () { it('should toggle app-item-detail', () => {
let toggleButton = element.all(by.css('button')).get(3); const toggleButton = element.all(by.css('button')).get(3);
let toggledDiv = element.all(by.css('app-item-detail')).get(0); const toggledDiv = element.all(by.css('app-item-detail')).get(0);
toggleButton.click(); toggleButton.click();
expect(toggledDiv.isDisplayed()).toBe(true); expect(toggledDiv.isDisplayed()).toBe(true);
}); });
it('should hide app-item-detail', function () { it('should hide app-item-detail', () => {
let hiddenMessage = element.all(by.css('p')).get(11); const hiddenMessage = element.all(by.css('p')).get(11);
let hiddenDiv = element.all(by.css('app-item-detail')).get(2); const hiddenDiv = element.all(by.css('app-item-detail')).get(2);
expect(hiddenMessage.getText()).toContain('in the DOM'); expect(hiddenMessage.getText()).toContain('in the DOM');
expect(hiddenDiv.isDisplayed()).toBe(true); expect(hiddenDiv.isDisplayed()).toBe(true);
}); });
it('should have 10 lists each containing the string Teapot', function () { it('should have 10 lists each containing the string Teapot', () => {
let listDiv = element.all(by.cssContainingText('.box', 'Teapot')); const listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
expect(listDiv.count()).toBe(10); expect(listDiv.count()).toBe(10);
}); });
it('should switch case', function () { it('should switch case', () => {
let tvRadioButton = element.all(by.css('input[type="radio"]')).get(3); const tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
let tvDiv = element(by.css('app-lost-item')); const tvDiv = element(by.css('app-lost-item'));
let fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4); const fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
let fishbowlDiv = element(by.css('app-unknown-item')); const fishbowlDiv = element(by.css('app-unknown-item'));
tvRadioButton.click(); tvRadioButton.click();
expect(tvDiv.getText()).toContain('Television'); expect(tvDiv.getText()).toContain('Television');

View File

@ -30,6 +30,14 @@ export class AppComponent implements OnInit {
itemsWithTrackByCountReset = 0; itemsWithTrackByCountReset = 0;
itemIdIncrement = 1; itemIdIncrement = 1;
// #docregion setClasses
currentClasses: {};
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
// #enddocregion setStyles
ngOnInit() { ngOnInit() {
this.resetItems(); this.resetItems();
this.setCurrentClasses(); this.setCurrentClasses();
@ -41,20 +49,18 @@ export class AppComponent implements OnInit {
this.currentItem.name = name.toUpperCase(); this.currentItem.name = name.toUpperCase();
} }
// #docregion setClasses // #docregion setClasses
currentClasses: {};
setCurrentClasses() { setCurrentClasses() {
// CSS classes: added/removed per current state of component properties // CSS classes: added/removed per current state of component properties
this.currentClasses = { this.currentClasses = {
'saveable': this.canSave, saveable: this.canSave,
'modified': !this.isUnchanged, modified: !this.isUnchanged,
'special': this.isSpecial special: this.isSpecial
}; };
} }
// #enddocregion setClasses // #enddocregion setClasses
// #docregion setStyles // #docregion setStyles
currentStyles: {};
setCurrentStyles() { setCurrentStyles() {
// CSS styles: set per current state of component properties // CSS styles: set per current state of component properties
this.currentStyles = { this.currentStyles = {
@ -70,11 +76,7 @@ export class AppComponent implements OnInit {
} }
giveNullCustomerValue() { giveNullCustomerValue() {
!(this.nullCustomer = null) ? (this.nullCustomer = 'Kelly') : (this.nullCustomer = null); this.nullCustomer = 'Kelly';
}
resetNullItem() {
this.nullCustomer = null;
} }
resetItems() { resetItems() {
@ -84,7 +86,7 @@ export class AppComponent implements OnInit {
} }
resetList() { resetList() {
this.resetItems() this.resetItems();
this.itemsWithTrackByCountReset = 0; this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount; this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
} }
@ -107,7 +109,7 @@ export class AppComponent implements OnInit {
trackByItems(index: number, item: Item): number { return item.id; } trackByItems(index: number, item: Item): number { return item.id; }
// #enddocregion trackByItems // #enddocregion trackByItems
trackById(index: number, item: any): number { return item['id']; } trackById(index: number, item: any): number { return item.id; }
} }

View File

@ -1,19 +1,17 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Built Template Functions Example', function () { describe('Built Template Functions Example', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should have title Built-in Template Functions', function () { it('should have title Built-in Template Functions', () => {
let title = element.all(by.css('h1')).get(0); const title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Template Functions'); expect(title.getText()).toEqual('Built-in Template Functions');
}); });
it('should display $any( ) in h2', function () { it('should display $any( ) in h2', () => {
let header = element(by.css('h2')); const header = element(by.css('h2'));
expect(header.getText()).toContain('$any( )'); expect(header.getText()).toContain('$any( )');
}); });

View File

@ -1,87 +1,85 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Component Communication Cookbook Tests', function () { describe('Component Communication Cookbook Tests', () => {
// Note: '?e2e' which app can read to know it is running in protractor // Note: '?e2e' which app can read to know it is running in protractor
// e.g. `if (!/e2e/.test(location.search)) { ...` // e.g. `if (!/e2e/.test(location.search)) { ...`
beforeAll(function () { beforeAll(() => {
browser.get('?e2e'); browser.get('?e2e');
}); });
describe('Parent-to-child communication', function() { describe('Parent-to-child communication', () => {
// #docregion parent-to-child // #docregion parent-to-child
// ... // ...
let _heroNames = ['Dr IQ', 'Magneta', 'Bombasto']; const heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
let _masterName = 'Master'; const masterName = 'Master';
it('should pass properties to children properly', function () { it('should pass properties to children properly', () => {
let parent = element.all(by.tagName('app-hero-parent')).get(0); const parent = element.all(by.tagName('app-hero-parent')).get(0);
let heroes = parent.all(by.tagName('app-hero-child')); const heroes = parent.all(by.tagName('app-hero-child'));
for (let i = 0; i < _heroNames.length; i++) { for (let i = 0; i < heroNames.length; i++) {
let childTitle = heroes.get(i).element(by.tagName('h3')).getText(); const childTitle = heroes.get(i).element(by.tagName('h3')).getText();
let childDetail = heroes.get(i).element(by.tagName('p')).getText(); const childDetail = heroes.get(i).element(by.tagName('p')).getText();
expect(childTitle).toEqual(_heroNames[i] + ' says:'); expect(childTitle).toEqual(heroNames[i] + ' says:');
expect(childDetail).toContain(_masterName); expect(childDetail).toContain(masterName);
} }
}); });
// ... // ...
// #enddocregion parent-to-child // #enddocregion parent-to-child
}); });
describe('Parent-to-child communication with setter', function() { describe('Parent-to-child communication with setter', () => {
// #docregion parent-to-child-setter // #docregion parent-to-child-setter
// ... // ...
it('should display trimmed, non-empty names', function () { it('should display trimmed, non-empty names', () => {
let _nonEmptyNameIndex = 0; const nonEmptyNameIndex = 0;
let _nonEmptyName = '"Dr IQ"'; const nonEmptyName = '"Dr IQ"';
let parent = element.all(by.tagName('app-name-parent')).get(0); const parent = element.all(by.tagName('app-name-parent')).get(0);
let hero = parent.all(by.tagName('app-name-child')).get(_nonEmptyNameIndex); const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
let displayName = hero.element(by.tagName('h3')).getText(); const displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_nonEmptyName); expect(displayName).toEqual(nonEmptyName);
}); });
it('should replace empty name with default name', function () { it('should replace empty name with default name', () => {
let _emptyNameIndex = 1; const emptyNameIndex = 1;
let _defaultName = '"<no name set>"'; const defaultName = '"<no name set>"';
let parent = element.all(by.tagName('app-name-parent')).get(0); const parent = element.all(by.tagName('app-name-parent')).get(0);
let hero = parent.all(by.tagName('app-name-child')).get(_emptyNameIndex); const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
let displayName = hero.element(by.tagName('h3')).getText(); const displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(_defaultName); expect(displayName).toEqual(defaultName);
}); });
// ... // ...
// #enddocregion parent-to-child-setter // #enddocregion parent-to-child-setter
}); });
describe('Parent-to-child communication with ngOnChanges', function() { describe('Parent-to-child communication with ngOnChanges', () => {
// #docregion parent-to-child-onchanges // #docregion parent-to-child-onchanges
// ... // ...
// Test must all execute in this exact order // Test must all execute in this exact order
it('should set expected initial values', function () { it('should set expected initial values', () => {
let actual = getActual(); const actual = getActual();
let initialLabel = 'Version 1.23'; const initialLabel = 'Version 1.23';
let initialLog = 'Initial value of major set to 1, Initial value of minor set to 23'; const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
expect(actual.label).toBe(initialLabel); expect(actual.label).toBe(initialLabel);
expect(actual.count).toBe(1); expect(actual.count).toBe(1);
expect(actual.logs.get(0).getText()).toBe(initialLog); expect(actual.logs.get(0).getText()).toBe(initialLog);
}); });
it('should set expected values after clicking \'Minor\' twice', function () { it('should set expected values after clicking \'Minor\' twice', () => {
let repoTag = element(by.tagName('app-version-parent')); const repoTag = element(by.tagName('app-version-parent'));
let newMinorButton = repoTag.all(by.tagName('button')).get(0); const newMinorButton = repoTag.all(by.tagName('button')).get(0);
newMinorButton.click().then(function() { newMinorButton.click().then(() => {
newMinorButton.click().then(function() { newMinorButton.click().then(() => {
let actual = getActual(); const actual = getActual();
let labelAfter2Minor = 'Version 1.25'; const labelAfter2Minor = 'Version 1.25';
let logAfter2Minor = 'minor changed from 24 to 25'; const logAfter2Minor = 'minor changed from 24 to 25';
expect(actual.label).toBe(labelAfter2Minor); expect(actual.label).toBe(labelAfter2Minor);
expect(actual.count).toBe(3); expect(actual.count).toBe(3);
@ -90,15 +88,15 @@ describe('Component Communication Cookbook Tests', function () {
}); });
}); });
it('should set expected values after clicking \'Major\' once', function () { it('should set expected values after clicking \'Major\' once', () => {
let repoTag = element(by.tagName('app-version-parent')); const repoTag = element(by.tagName('app-version-parent'));
let newMajorButton = repoTag.all(by.tagName('button')).get(1); const newMajorButton = repoTag.all(by.tagName('button')).get(1);
newMajorButton.click().then(function() { newMajorButton.click().then(() => {
let actual = getActual(); const actual = getActual();
let labelAfterMajor = 'Version 2.0'; const labelAfterMajor = 'Version 2.0';
let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0'; const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
expect(actual.label).toBe(labelAfterMajor); expect(actual.label).toBe(labelAfterMajor);
expect(actual.count).toBe(4); expect(actual.count).toBe(4);
@ -107,14 +105,14 @@ describe('Component Communication Cookbook Tests', function () {
}); });
function getActual() { function getActual() {
let versionTag = element(by.tagName('app-version-child')); const versionTag = element(by.tagName('app-version-child'));
let label = versionTag.element(by.tagName('h3')).getText(); const label = versionTag.element(by.tagName('h3')).getText();
let ul = versionTag.element((by.tagName('ul'))); const ul = versionTag.element((by.tagName('ul')));
let logs = ul.all(by.tagName('li')); const logs = ul.all(by.tagName('li'));
return { return {
label: label, label,
logs: logs, logs,
count: logs.count() count: logs.count()
}; };
} }
@ -123,30 +121,30 @@ describe('Component Communication Cookbook Tests', function () {
}); });
describe('Child-to-parent communication', function() { describe('Child-to-parent communication', () => {
// #docregion child-to-parent // #docregion child-to-parent
// ... // ...
it('should not emit the event initially', function () { it('should not emit the event initially', () => {
let voteLabel = element(by.tagName('app-vote-taker')) const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 0, Disagree: 0'); expect(voteLabel).toBe('Agree: 0, Disagree: 0');
}); });
it('should process Agree vote', function () { it('should process Agree vote', () => {
let agreeButton1 = element.all(by.tagName('app-voter')).get(0) const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
.all(by.tagName('button')).get(0); .all(by.tagName('button')).get(0);
agreeButton1.click().then(function() { agreeButton1.click().then(() => {
let voteLabel = element(by.tagName('app-vote-taker')) const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 0'); expect(voteLabel).toBe('Agree: 1, Disagree: 0');
}); });
}); });
it('should process Disagree vote', function () { it('should process Disagree vote', () => {
let agreeButton1 = element.all(by.tagName('app-voter')).get(1) const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
.all(by.tagName('button')).get(1); .all(by.tagName('button')).get(1);
agreeButton1.click().then(function() { agreeButton1.click().then(() => {
let voteLabel = element(by.tagName('app-vote-taker')) const voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 1'); expect(voteLabel).toBe('Agree: 1, Disagree: 1');
}); });
@ -157,31 +155,31 @@ describe('Component Communication Cookbook Tests', function () {
// Can't run timer tests in protractor because // Can't run timer tests in protractor because
// interaction w/ zones causes all tests to freeze & timeout. // interaction w/ zones causes all tests to freeze & timeout.
xdescribe('Parent calls child via local var', function() { xdescribe('Parent calls child via local var', () => {
countDownTimerTests('countdown-parent-lv'); countDownTimerTests('countdown-parent-lv');
}); });
xdescribe('Parent calls ViewChild', function() { xdescribe('Parent calls ViewChild', () => {
countDownTimerTests('countdown-parent-vc'); countDownTimerTests('countdown-parent-vc');
}); });
function countDownTimerTests(parentTag: string) { function countDownTimerTests(parentTag: string) {
// #docregion countdown-timer-tests // #docregion countdown-timer-tests
// ... // ...
it('timer and parent seconds should match', function () { it('timer and parent seconds should match', () => {
let parent = element(by.tagName(parentTag)); const parent = element(by.tagName(parentTag));
let message = parent.element(by.tagName('app-countdown-timer')).getText(); const message = parent.element(by.tagName('app-countdown-timer')).getText();
browser.sleep(10); // give `seconds` a chance to catchup with `message` browser.sleep(10); // give `seconds` a chance to catchup with `message`
let seconds = parent.element(by.className('seconds')).getText(); const seconds = parent.element(by.className('seconds')).getText();
expect(message).toContain(seconds); expect(message).toContain(seconds);
}); });
it('should stop the countdown', function () { it('should stop the countdown', () => {
let parent = element(by.tagName(parentTag)); const parent = element(by.tagName(parentTag));
let stopButton = parent.all(by.tagName('button')).get(1); const stopButton = parent.all(by.tagName('button')).get(1);
stopButton.click().then(function() { stopButton.click().then(() => {
let message = parent.element(by.tagName('app-countdown-timer')).getText(); const message = parent.element(by.tagName('app-countdown-timer')).getText();
expect(message).toContain('Holding'); expect(message).toContain('Holding');
}); });
}); });
@ -190,39 +188,39 @@ describe('Component Communication Cookbook Tests', function () {
} }
describe('Parent and children communicate via a service', function() { describe('Parent and children communicate via a service', () => {
// #docregion bidirectional-service // #docregion bidirectional-service
// ... // ...
it('should announce a mission', function () { it('should announce a mission', () => {
let missionControl = element(by.tagName('app-mission-control')); const missionControl = element(by.tagName('app-mission-control'));
let announceButton = missionControl.all(by.tagName('button')).get(0); const announceButton = missionControl.all(by.tagName('button')).get(0);
announceButton.click().then(function () { announceButton.click().then(() => {
let history = missionControl.all(by.tagName('li')); const history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(1); expect(history.count()).toBe(1);
expect(history.get(0).getText()).toMatch(/Mission.* announced/); expect(history.get(0).getText()).toMatch(/Mission.* announced/);
}); });
}); });
it('should confirm the mission by Lovell', function () { it('should confirm the mission by Lovell', () => {
testConfirmMission(1, 2, 'Lovell'); testConfirmMission(1, 2, 'Lovell');
}); });
it('should confirm the mission by Haise', function () { it('should confirm the mission by Haise', () => {
testConfirmMission(3, 3, 'Haise'); testConfirmMission(3, 3, 'Haise');
}); });
it('should confirm the mission by Swigert', function () { it('should confirm the mission by Swigert', () => {
testConfirmMission(2, 4, 'Swigert'); testConfirmMission(2, 4, 'Swigert');
}); });
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) { function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
let _confirmedLog = ' confirmed the mission'; const confirmedLog = ' confirmed the mission';
let missionControl = element(by.tagName('app-mission-control')); const missionControl = element(by.tagName('app-mission-control'));
let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
confirmButton.click().then(function () { confirmButton.click().then(() => {
let history = missionControl.all(by.tagName('li')); const history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(expectedLogCount); expect(history.count()).toBe(expectedLogCount);
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog); expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
}); });
} }
// ... // ...

View File

@ -15,7 +15,7 @@ import { VersionParentComponent } from './version-parent.component';
import { VoterComponent } from './voter.component'; import { VoterComponent } from './voter.component';
import { VoteTakerComponent } from './votetaker.component'; import { VoteTakerComponent } from './votetaker.component';
let directives: any[] = [ const directives: any[] = [
AppComponent, AppComponent,
AstronautComponent, AstronautComponent,
CountdownTimerComponent, CountdownTimerComponent,
@ -30,7 +30,7 @@ let directives: any[] = [
VoteTakerComponent VoteTakerComponent
]; ];
let schemas: any[] = []; const schemas: any[] = [];
// Include Countdown examples // Include Countdown examples
// unless in e2e tests which they break. // unless in e2e tests which they break.
@ -49,6 +49,6 @@ if (!/e2e/.test(location.search)) {
], ],
declarations: directives, declarations: directives,
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],
schemas: schemas schemas
}) })
export class AppModule { } export class AppModule { }

View File

@ -12,6 +12,6 @@ import { Hero } from './hero';
}) })
export class HeroChildComponent { export class HeroChildComponent {
@Input() hero: Hero; @Input() hero: Hero;
@Input('master') masterName: string; @Input('master') masterName: string; // tslint:disable-line: no-input-rename
} }
// #enddocregion // #enddocregion

View File

@ -34,7 +34,7 @@ export class MissionControlComponent {
} }
announce() { announce() {
let mission = this.missions[this.nextMission++]; const mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission); this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`); this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; } if (this.nextMission >= this.missions.length) { this.nextMission = 0; }

View File

@ -1,3 +1,4 @@
// tslint:disable: variable-name
// #docregion // #docregion
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
@ -6,13 +7,11 @@ import { Component, Input } from '@angular/core';
template: '<h3>"{{name}}"</h3>' template: '<h3>"{{name}}"</h3>'
}) })
export class NameChildComponent { export class NameChildComponent {
private _name = '';
@Input() @Input()
get name(): string { return this._name; }
set name(name: string) { set name(name: string) {
this._name = (name && name.trim()) || '<no name set>'; this._name = (name && name.trim()) || '<no name set>';
} }
private _name = '';
get name(): string { return this._name; }
} }
// #enddocregion // #enddocregion

View File

@ -18,14 +18,14 @@ export class VersionChildComponent implements OnChanges {
changeLog: string[] = []; changeLog: string[] = [];
ngOnChanges(changes: {[propKey: string]: SimpleChange}) { ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = []; const log: string[] = [];
for (let propName in changes) { for (const propName in changes) {
let changedProp = changes[propName]; const changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue); const to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) { if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`); log.push(`Initial value of ${propName} set to ${to}`);
} else { } else {
let from = JSON.stringify(changedProp.previousValue); const from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`); log.push(`${propName} changed from ${from} to ${to}`);
} }
} }

View File

@ -1,16 +1,14 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Component Style Tests', function () { describe('Component Style Tests', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('scopes component styles to component view', function() { it('scopes component styles to component view', () => {
let componentH1 = element(by.css('app-root > h1')); const componentH1 = element(by.css('app-root > h1'));
let externalH1 = element(by.css('body > h1')); const externalH1 = element(by.css('body > h1'));
// Note: sometimes webdriver returns the fontWeight as "normal", // Note: sometimes webdriver returns the fontWeight as "normal",
// other times as "400", both of which are equal in CSS terms. // other times as "400", both of which are equal in CSS terms.
@ -19,49 +17,49 @@ describe('Component Style Tests', function () {
}); });
it('allows styling :host element', function() { it('allows styling :host element', () => {
let host = element(by.css('app-hero-details')); const host = element(by.css('app-hero-details'));
expect(host.getCssValue('borderWidth')).toEqual('1px'); expect(host.getCssValue('borderWidth')).toEqual('1px');
}); });
it('supports :host() in function form', function() { it('supports :host() in function form', () => {
let host = element(by.css('app-hero-details')); const host = element(by.css('app-hero-details'));
host.element(by.buttonText('Activate')).click(); host.element(by.buttonText('Activate')).click();
expect(host.getCssValue('borderWidth')).toEqual('3px'); expect(host.getCssValue('borderWidth')).toEqual('3px');
}); });
it('allows conditional :host-context() styling', function() { it('allows conditional :host-context() styling', () => {
let h2 = element(by.css('app-hero-details h2')); const h2 = element(by.css('app-hero-details h2'));
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
}); });
it('styles both view and content children with /deep/', function() { it('styles both view and content children with /deep/', () => {
let viewH3 = element(by.css('app-hero-team h3')); const viewH3 = element(by.css('app-hero-team h3'));
let contentH3 = element(by.css('app-hero-controls h3')); const contentH3 = element(by.css('app-hero-controls h3'));
expect(viewH3.getCssValue('fontStyle')).toEqual('italic'); expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
expect(contentH3.getCssValue('fontStyle')).toEqual('italic'); expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
}); });
it('includes styles loaded with CSS @import', function() { it('includes styles loaded with CSS @import', () => {
let host = element(by.css('app-hero-details')); const host = element(by.css('app-hero-details'));
expect(host.getCssValue('padding')).toEqual('10px'); expect(host.getCssValue('padding')).toEqual('10px');
}); });
it('processes template inline styles', function() { it('processes template inline styles', () => {
let button = element(by.css('app-hero-controls button')); const button = element(by.css('app-hero-controls button'));
let externalButton = element(by.css('body > button')); const externalButton = element(by.css('body > button'));
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)'); expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
}); });
it('processes template <link>s', function() { it('processes template <link>s', () => {
let li = element(by.css('app-hero-team li:first-child')); const li = element(by.css('app-hero-team li:first-child'));
let externalLi = element(by.css('body > ul li')); const externalLi = element(by.css('body > ul li'));
expect(li.getCssValue('listStyleType')).toEqual('square'); expect(li.getCssValue('listStyleType')).toEqual('square');
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square'); expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');

View File

@ -1,76 +1,74 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Dependency Injection Cookbook', function () { describe('Dependency Injection Cookbook', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should render Logged in User example', function () { it('should render Logged in User example', () => {
let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0); const loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('"Bombasto" should be the logged in user', function () { it('"Bombasto" should be the logged in user', () => {
let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0); const loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('should render sorted heroes', function () { it('should render sorted heroes', () => {
let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0); const sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
expect(sortedHeroes).toBeDefined(); expect(sortedHeroes).toBeDefined();
}); });
it('Dr Nice should be in sorted heroes', function () { it('Dr Nice should be in sorted heroes', () => {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0); const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('RubberMan should be in sorted heroes', function () { it('RubberMan should be in sorted heroes', () => {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0); const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('Magma should be in sorted heroes', function () { it('Magma should be in sorted heroes', () => {
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0); const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('should render Hero of the Month', function () { it('should render Hero of the Month', () => {
let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0); const heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
expect(heroOfTheMonth).toBeDefined(); expect(heroOfTheMonth).toBeDefined();
}); });
it('should render Hero Bios', function () { it('should render Hero Bios', () => {
let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0); const heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
expect(heroBios).toBeDefined(); expect(heroBios).toBeDefined();
}); });
it('should render Magma\'s description in Hero Bios', function () { it('should render Magma\'s description in Hero Bios', () => {
let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0); const magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
expect(magmaText).toBeDefined(); expect(magmaText).toBeDefined();
}); });
it('should render Magma\'s phone in Hero Bios and Contacts', function () { it('should render Magma\'s phone in Hero Bios and Contacts', () => {
let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0); const magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
expect(magmaPhone).toBeDefined(); expect(magmaPhone).toBeDefined();
}); });
it('should render Hero-of-the-Month runner-ups', function () { it('should render Hero-of-the-Month runner-ups', () => {
let runnersUp = element(by.id('rups1')).getText(); const runnersUp = element(by.id('rups1')).getText();
expect(runnersUp).toContain('RubberMan, Dr Nice'); expect(runnersUp).toContain('RubberMan, Dr Nice');
}); });
it('should render DateLogger log entry in Hero-of-the-Month', function () { it('should render DateLogger log entry in Hero-of-the-Month', () => {
let logs = element.all(by.id('logs')).get(0).getText(); const logs = element.all(by.id('logs')).get(0).getText();
expect(logs).toContain('INFO: starting up at'); expect(logs).toContain('INFO: starting up at');
}); });
it('should highlight Hero Bios and Contacts container when mouseover', function () { it('should highlight Hero Bios and Contacts container when mouseover', () => {
let target = element(by.css('div[appHighlight="yellow"]')); const target = element(by.css('div[appHighlight="yellow"]'));
let yellow = 'rgba(255, 255, 0, 1)'; const yellow = 'rgba(255, 255, 0, 1)';
expect(target.getCssValue('background-color')).not.toEqual(yellow); expect(target.getCssValue('background-color')).not.toEqual(yellow);
@ -81,25 +79,25 @@ describe('Dependency Injection Cookbook', function () {
browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000); browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000);
}); });
describe('in Parent Finder', function () { describe('in Parent Finder', () => {
let cathy1 = element(by.css('alex cathy')); const cathy1 = element(by.css('alex cathy'));
let craig1 = element(by.css('alex craig')); const craig1 = element(by.css('alex craig'));
let carol1 = element(by.css('alex carol p')); const carol1 = element(by.css('alex carol p'));
let carol2 = element(by.css('barry carol p')); const carol2 = element(by.css('barry carol p'));
it('"Cathy" should find "Alex" via the component class', function () { it('"Cathy" should find "Alex" via the component class', () => {
expect(cathy1.getText()).toContain('Found Alex via the component'); expect(cathy1.getText()).toContain('Found Alex via the component');
}); });
it('"Craig" should not find "Alex" via the base class', function () { it('"Craig" should not find "Alex" via the base class', () => {
expect(craig1.getText()).toContain('Did not find Alex via the base'); expect(craig1.getText()).toContain('Did not find Alex via the base');
}); });
it('"Carol" within "Alex" should have "Alex" parent', function () { it('"Carol" within "Alex" should have "Alex" parent', () => {
expect(carol1.getText()).toContain('Alex'); expect(carol1.getText()).toContain('Alex');
}); });
it('"Carol" within "Barry" should have "Barry" parent', function () { it('"Carol" within "Barry" should have "Barry" parent', () => {
expect(carol2.getText()).toContain('Barry'); expect(carol2.getText()).toContain('Barry');
}); });
}); });

View File

@ -42,11 +42,11 @@ const declarations = [
ParentFinderComponent, ParentFinderComponent,
]; ];
const a_components = [AliceComponent, AlexComponent ]; const componentListA = [ AliceComponent, AlexComponent ];
const b_components = [ BarryComponent, BethComponent, BobComponent ]; const componentListB = [ BarryComponent, BethComponent, BobComponent ];
const c_components = [ const componentListC = [
CarolComponent, ChrisComponent, CraigComponent, CarolComponent, ChrisComponent, CraigComponent,
CathyComponent CathyComponent
]; ];
@ -61,9 +61,9 @@ const c_components = [
], ],
declarations: [ declarations: [
declarations, declarations,
a_components, componentListA,
b_components, componentListB,
c_components, componentListC,
StorageComponent, StorageComponent,
], ],
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],

View File

@ -3,7 +3,7 @@ import { Hero } from './hero';
export class HeroData { export class HeroData {
createDb() { createDb() {
let heroes = [ const heroes = [
new Hero(1, 'Windstorm'), new Hero(1, 'Windstorm'),
new Hero(2, 'Bombasto'), new Hero(2, 'Bombasto'),
new Hero(3, 'Magneta'), new Hero(3, 'Magneta'),

View File

@ -1,5 +1,4 @@
/* tslint:disable:no-unused-variable component-selector-name one-line check-open-brace */ // tslint:disable: component-selector space-before-function-paren
/* tslint:disable:*/
// #docplaster // #docplaster
// #docregion // #docregion
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core'; import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
@ -20,8 +19,7 @@ const DifferentParent = Parent;
// The `parentType` defaults to `Parent` when omitting the second parameter. // The `parentType` defaults to `Parent` when omitting the second parameter.
// #docregion provide-the-parent // #docregion provide-the-parent
export function provideParent export function provideParent
// #enddocregion provide-parent, provide-the-parent // #enddocregion provide-the-parent
// #docregion provide-parent
(component: any, parentType?: any) { (component: any, parentType?: any) {
return { provide: parentType || Parent, useExisting: forwardRef(() => component) }; return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
} }

View File

@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) {
.join(', '); .join(', ');
// #docregion factory-synopsis // #docregion factory-synopsis
}; };
}; }
// #enddocregion factory-synopsis // #enddocregion factory-synopsis

View File

@ -24,7 +24,7 @@ export class UserContextService {
// #enddocregion ctor, injectables // #enddocregion ctor, injectables
loadUser(userId: number) { loadUser(userId: number) {
let user = this.userService.getUserById(userId); const user = this.userService.getUserById(userId);
this.name = user.name; this.name = user.name;
this.role = user.role; this.role = user.role;

View File

@ -1,202 +1,196 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by, ElementFinder } from 'protractor'; import { browser, element, by, ElementFinder } from 'protractor';
describe('Dependency Injection Tests', function () { describe('Dependency Injection Tests', () => {
let expectedMsg: string; let expectedMsg: string;
let expectedMsgRx: RegExp; let expectedMsgRx: RegExp;
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
describe('Cars:', function() { describe('Cars:', () => {
it('DI car displays as expected', function () { it('DI car displays as expected', () => {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#di')).getText()).toEqual(expectedMsg); expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
}); });
it('No DI car displays as expected', function () { it('No DI car displays as expected', () => {
expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg); expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
}); });
it('Injector car displays as expected', function () { it('Injector car displays as expected', () => {
expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#injector')).getText()).toEqual(expectedMsg); expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
}); });
it('Factory car displays as expected', function () { it('Factory car displays as expected', () => {
expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#factory')).getText()).toEqual(expectedMsg); expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
}); });
it('Simple car displays as expected', function () { it('Simple car displays as expected', () => {
expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#simple')).getText()).toEqual(expectedMsg); expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
}); });
it('Super car displays as expected', function () { it('Super car displays as expected', () => {
expectedMsg = 'Super car with 12 cylinders and Flintstone tires.'; expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
expect(element(by.css('#super')).getText()).toEqual(expectedMsg); expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
}); });
it('Test car displays as expected', function () { it('Test car displays as expected', () => {
expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.'; expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
expect(element(by.css('#test')).getText()).toEqual(expectedMsg); expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('Other Injections:', function() { describe('Other Injections:', () => {
it('DI car displays as expected', function () { it('DI car displays as expected', () => {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#car')).getText()).toEqual(expectedMsg); expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
}); });
it('Hero displays as expected', function () { it('Hero displays as expected', () => {
expectedMsg = 'Dr Nice'; expectedMsg = 'Dr Nice';
expect(element(by.css('#hero')).getText()).toEqual(expectedMsg); expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
}); });
it('Optional injection displays as expected', function () { it('Optional injection displays as expected', () => {
expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!'; expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg); expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('Tests:', function() { describe('Tests:', () => {
it('Tests display as expected', function () { it('Tests display as expected', () => {
expectedMsgRx = /Tests passed/; expectedMsgRx = /Tests passed/;
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
}); });
}); });
describe('Provider variations:', function() { describe('Provider variations:', () => {
it('P1 (class) displays as expected', function () { it('P1 (class) displays as expected', () => {
expectedMsg = 'Hello from logger provided with Logger class'; expectedMsg = 'Hello from logger provided with Logger class';
expect(element(by.css('#p1')).getText()).toEqual(expectedMsg); expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
}); });
it('P3 (provide) displays as expected', function () { it('P3 (provide) displays as expected', () => {
expectedMsg = 'Hello from logger provided with useClass:Logger'; expectedMsg = 'Hello from logger provided with useClass:Logger';
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg); expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
}); });
it('P4 (useClass:BetterLogger) displays as expected', function () { it('P4 (useClass:BetterLogger) displays as expected', () => {
expectedMsg = 'Hello from logger provided with useClass:BetterLogger'; expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
expect(element(by.css('#p4')).getText()).toEqual(expectedMsg); expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
}); });
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () { it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', () => {
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger'; expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg); expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
}); });
it('P6a (no alias) displays as expected', function () { it('P6a (no alias) displays as expected', () => {
expectedMsg = 'Hello OldLogger (but we want NewLogger)'; expectedMsg = 'Hello OldLogger (but we want NewLogger)';
expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg); expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
}); });
it('P6b (alias) displays as expected', function () { it('P6b (alias) displays as expected', () => {
expectedMsg = 'Hello from NewLogger (via aliased OldLogger)'; expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg); expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
}); });
it('P7 (useValue) displays as expected', function () { it('P7 (useValue) displays as expected', () => {
expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"'; expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
expect(element(by.css('#p7')).getText()).toEqual(expectedMsg); expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
}); });
it('P8 (useFactory) displays as expected', function () { it('P8 (useFactory) displays as expected', () => {
expectedMsg = 'Hero service injected successfully via heroServiceProvider'; expectedMsg = 'Hero service injected successfully via heroServiceProvider';
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg); expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
}); });
it('P9 (InjectionToken) displays as expected', function () { it('P9 (InjectionToken) displays as expected', () => {
expectedMsg = 'APP_CONFIG Application title is Dependency Injection'; expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg); expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
}); });
it('P10 (optional dependency) displays as expected', function () { it('P10 (optional dependency) displays as expected', () => {
expectedMsg = 'Optional logger was not available'; expectedMsg = 'Optional logger was not available';
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg); expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('User/Heroes:', function() { describe('User/Heroes:', () => {
it('User is Bob - unauthorized', function () { it('User is Bob - unauthorized', () => {
expectedMsgRx = /Bob, is not authorized/; expectedMsgRx = /Bob, is not authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('should have button', function () { it('should have button', () => {
expect(element.all(by.cssContainingText('button', 'Next User')) expect(element.all(by.cssContainingText('button', 'Next User'))
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed'); .get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
}); });
it('unauthorized user should have multiple unauthorized heroes', function () { it('unauthorized user should have multiple unauthorized heroes', () => {
let heroes = element.all(by.css('#unauthorized app-hero-list div')); const heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('unauthorized user should have no secret heroes', function () { it('unauthorized user should have no secret heroes', () => {
let heroes = element.all(by.css('#unauthorized app-hero-list div')); const heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => { const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then((text: string) => { return elem.getText().then((text: string) => /secret/.test(text));
return /secret/.test(text);
});
}); });
expect(filteredHeroes.count()).toEqual(0); expect(filteredHeroes.count()).toEqual(0);
}); });
it('unauthorized user should have no authorized heroes listed', function () { it('unauthorized user should have no authorized heroes listed', () => {
expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0); expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0);
}); });
describe('after button click', function() { describe('after button click', () => {
beforeAll(function (done: any) { beforeAll((done: any) => {
let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0); const buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
buttonEle.click().then(done, done); buttonEle.click().then(done, done);
}); });
it('User is Alice - authorized', function () { it('User is Alice - authorized', () => {
expectedMsgRx = /Alice, is authorized/; expectedMsgRx = /Alice, is authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('authorized user should have multiple authorized heroes ', function () { it('authorized user should have multiple authorized heroes ', () => {
let heroes = element.all(by.css('#authorized app-hero-list div')); const heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', function () { it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', () => {
let heroes = element.all(by.css('#tspAuthorized app-hero-list div')); const heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have secret heroes', function () { it('authorized user should have secret heroes', () => {
let heroes = element.all(by.css('#authorized app-hero-list div')); const heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) { const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then(function(text: string) { return elem.getText().then((text: string) => /secret/.test(text));
return /secret/.test(text);
});
}); });
expect(filteredHeroes.count()).toBeGreaterThan(0); expect(filteredHeroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have no unauthorized heroes listed', function () { it('authorized user should have no unauthorized heroes listed', () => {
expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0); expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0);
}); });
}); });

View File

@ -7,7 +7,7 @@ import { Car, Engine, Tires } from './car';
export function simpleCar() { export function simpleCar() {
// #docregion car-ctor-instantiation // #docregion car-ctor-instantiation
// Simple car with 4 cylinders and Flintstone tires. // Simple car with 4 cylinders and Flintstone tires.
let car = new Car(new Engine(), new Tires()); const car = new Car(new Engine(), new Tires());
// #enddocregion car-ctor-instantiation // #enddocregion car-ctor-instantiation
car.description = 'Simple'; car.description = 'Simple';
return car; return car;
@ -16,30 +16,31 @@ export function simpleCar() {
///////// example 2 //////////// ///////// example 2 ////////////
// #docregion car-ctor-instantiation-with-param // #docregion car-ctor-instantiation-with-param
class Engine2 { class Engine2 {
constructor(public cylinders: number) { } constructor(public cylinders: number) { }
} }
// #enddocregion car-ctor-instantiation-with-param // #enddocregion car-ctor-instantiation-with-param
export function superCar() { export function superCar() {
// #docregion car-ctor-instantiation-with-param // #docregion car-ctor-instantiation-with-param
// Super car with 12 cylinders and Flintstone tires. // Super car with 12 cylinders and Flintstone tires.
let bigCylinders = 12; const bigCylinders = 12;
let car = new Car(new Engine2(bigCylinders), new Tires()); const car = new Car(new Engine2(bigCylinders), new Tires());
// #enddocregion car-ctor-instantiation-with-param // #enddocregion car-ctor-instantiation-with-param
car.description = 'Super'; car.description = 'Super';
return car; return car;
} }
/////////// example 3 ////////// /////////// example 3 //////////
// #docregion car-ctor-instantiation-with-mocks // #docregion car-ctor-instantiation-with-mocks
class MockEngine extends Engine { cylinders = 8; } class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; } class MockTires extends Tires { make = 'YokoGoodStone'; }
// #enddocregion car-ctor-instantiation-with-mocks // #enddocregion car-ctor-instantiation-with-mocks
export function testCar() { export function testCar() {
// #docregion car-ctor-instantiation-with-mocks // #docregion car-ctor-instantiation-with-mocks
// Test car with 8 cylinders and YokoGoodStone tires. // Test car with 8 cylinders and YokoGoodStone tires.
let car = new Car(new MockEngine(), new MockTires()); const car = new Car(new MockEngine(), new MockTires());
// #enddocregion car-ctor-instantiation-with-mocks // #enddocregion car-ctor-instantiation-with-mocks
car.description = 'Test'; car.description = 'Test';
return car; return car;

View File

@ -4,7 +4,7 @@ import { Engine, Tires, Car } from './car';
// BAD pattern! // BAD pattern!
export class CarFactory { export class CarFactory {
createCar() { createCar() {
let car = new Car(this.createEngine(), this.createTires()); const car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory'; car.description = 'Factory';
return car; return car;
} }

View File

@ -26,14 +26,14 @@ export function useInjector() {
] ]
}); });
// #docregion injector-call // #docregion injector-call
let car = injector.get(Car); const car = injector.get(Car);
// #enddocregion injector-call, injector-create-and-call // #enddocregion injector-call, injector-create-and-call
car.description = 'Injector'; car.description = 'Injector';
injector = Injector.create({ injector = Injector.create({
providers: [{ provide: Logger, deps: [] }] providers: [{ provide: Logger, deps: [] }]
}); });
let logger = injector.get(Logger); const logger = injector.get(Logger);
logger.log('Injector car.drive() said: ' + car.drive()); logger.log('Injector car.drive() said: ' + car.drive());
return car; return car;
} }

View File

@ -27,9 +27,9 @@ import { useInjector } from './car-injector';
providers: [Car, Engine, Tires] providers: [Car, Engine, Tires]
}) })
export class CarComponent { export class CarComponent {
factoryCar = (new CarFactory).createCar(); factoryCar = (new CarFactory()).createCar();
injectorCar = useInjector(); injectorCar = useInjector();
noDiCar = new CarNoDi; noDiCar = new CarNoDi();
simpleCar = simpleCar(); simpleCar = simpleCar();
superCar = superCar(); superCar = superCar();
testCar = testCar(); testCar = testCar();

View File

@ -5,7 +5,7 @@ import { Logger } from '../logger.service';
import { UserService } from '../user.service'; import { UserService } from '../user.service';
// #docregion factory // #docregion factory
let heroServiceFactory = (logger: Logger, userService: UserService) => { const heroServiceFactory = (logger: Logger, userService: UserService) => {
return new HeroService(logger, userService.user.isAuthorized); return new HeroService(logger, userService.user.isAuthorized);
}; };
// #enddocregion factory // #enddocregion factory

View File

@ -17,7 +17,7 @@ export class HeroService {
private isAuthorized: boolean) { } private isAuthorized: boolean) { }
getHeroes() { getHeroes() {
let auth = this.isAuthorized ? 'authorized ' : 'unauthorized'; const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`); this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret); return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
} }

View File

@ -36,7 +36,7 @@ export class InjectorComponent implements OnInit {
} }
get rodent() { get rodent() {
let rousDontExist = `R.O.U.S.'s? I don't think they exist!`; const rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
return this.injector.get(ROUS, rousDontExist); return this.injector.get(ROUS, rousDontExist);
} }
} }

View File

@ -18,7 +18,7 @@ const template = '{{log}}';
@Component({ @Component({
selector: 'provider-1', selector: 'provider-1',
template: template, template,
// #docregion providers-1, providers-logger // #docregion providers-1, providers-logger
providers: [Logger] providers: [Logger]
// #enddocregion providers-1, providers-logger // #enddocregion providers-1, providers-logger
@ -35,7 +35,7 @@ export class Provider1Component {
@Component({ @Component({
selector: 'provider-3', selector: 'provider-3',
template: template, template,
providers: providers:
// #docregion providers-3 // #docregion providers-3
[{ provide: Logger, useClass: Logger }] [{ provide: Logger, useClass: Logger }]
@ -54,7 +54,7 @@ export class BetterLogger extends Logger {}
@Component({ @Component({
selector: 'provider-4', selector: 'provider-4',
template: template, template,
providers: providers:
// #docregion providers-4 // #docregion providers-4
[{ provide: Logger, useClass: BetterLogger }] [{ provide: Logger, useClass: BetterLogger }]
@ -76,7 +76,7 @@ export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); } constructor(private userService: UserService) { super(); }
log(message: string) { log(message: string) {
let name = this.userService.user.name; const name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`); super.log(`Message to ${name}: ${message}`);
} }
} }
@ -84,7 +84,7 @@ export class EvenBetterLogger extends Logger {
@Component({ @Component({
selector: 'provider-5', selector: 'provider-5',
template: template, template,
providers: providers:
// #docregion providers-5 // #docregion providers-5
[ UserService, [ UserService,
@ -107,12 +107,12 @@ export class OldLogger {
logs: string[] = []; logs: string[] = [];
log(message: string) { log(message: string) {
throw new Error('Should not call the old logger!'); throw new Error('Should not call the old logger!');
}; }
} }
@Component({ @Component({
selector: 'provider-6a', selector: 'provider-6a',
template: template, template,
providers: providers:
// #docregion providers-6a // #docregion providers-6a
[ NewLogger, [ NewLogger,
@ -135,7 +135,7 @@ export class Provider6aComponent {
@Component({ @Component({
selector: 'provider-6b', selector: 'provider-6b',
template: template, template,
providers: providers:
// #docregion providers-6b // #docregion providers-6b
[ NewLogger, [ NewLogger,
@ -168,7 +168,7 @@ export const SilentLogger = {
@Component({ @Component({
selector: 'provider-7', selector: 'provider-7',
template: template, template,
providers: providers:
// #docregion providers-7 // #docregion providers-7
[{ provide: Logger, useValue: SilentLogger }] [{ provide: Logger, useValue: SilentLogger }]
@ -186,7 +186,7 @@ export class Provider7Component {
@Component({ @Component({
selector: 'provider-8', selector: 'provider-8',
template: template, template,
providers: [heroServiceProvider, Logger, UserService] providers: [heroServiceProvider, Logger, UserService]
}) })
export class Provider8Component { export class Provider8Component {
@ -202,7 +202,7 @@ export class Provider8Component {
@Component({ @Component({
selector: 'provider-9', selector: 'provider-9',
template: template, template,
/* /*
// #docregion providers-9-interface // #docregion providers-9-interface
// FAIL! Can't use interface as provider token // FAIL! Can't use interface as provider token
@ -237,11 +237,11 @@ export class Provider9Component implements OnInit {
import { Optional } from '@angular/core'; import { Optional } from '@angular/core';
// #enddocregion import-optional // #enddocregion import-optional
let some_message = 'Hello from the injected logger'; const someMessage = 'Hello from the injected logger';
@Component({ @Component({
selector: 'provider-10', selector: 'provider-10',
template: template, template,
providers: [{ provide: Logger, useValue: null }] providers: [{ provide: Logger, useValue: null }]
}) })
export class Provider10Component implements OnInit { export class Provider10Component implements OnInit {
@ -249,7 +249,7 @@ export class Provider10Component implements OnInit {
// #docregion provider-10-ctor // #docregion provider-10-ctor
constructor(@Optional() private logger?: Logger) { constructor(@Optional() private logger?: Logger) {
if (this.logger) { if (this.logger) {
this.logger.log(some_message); this.logger.log(someMessage);
} }
} }
// #enddocregion provider-10-ctor // #enddocregion provider-10-ctor

View File

@ -43,7 +43,7 @@ var testResults: {pass: string; message: string};
function expect(actual: any) { function expect(actual: any) {
return { return {
toEqual: function(expected: any){ toEqual: (expected: any) => {
testResults = actual === expected ? testResults = actual === expected ?
{pass: 'passed', message: testName} : {pass: 'passed', message: testName} :
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`}; {pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};

View File

@ -8,8 +8,8 @@ export class User {
} }
// TODO: get the user; don't 'new' it. // TODO: get the user; don't 'new' it.
let alice = new User('Alice', true); const alice = new User('Alice', true);
let bob = new User('Bob', false); const bob = new User('Bob', false);
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -1,29 +1,27 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Displaying Data Tests', function () { describe('Displaying Data Tests', () => {
let _title = 'Tour of Heroes'; const title = 'Tour of Heroes';
let _defaultHero = 'Windstorm'; const defaultHero = 'Windstorm';
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should display correct title: ' + _title, function () { it('should display correct title: ' + title, () => {
expect(element(by.css('h1')).getText()).toEqual(_title); expect(element(by.css('h1')).getText()).toEqual(title);
}); });
it('should have correct default hero: ' + _defaultHero, function () { it('should have correct default hero: ' + defaultHero, () => {
expect(element(by.css('h2')).getText()).toContain(_defaultHero); expect(element(by.css('h2')).getText()).toContain(defaultHero);
}); });
it('should have heroes', function () { it('should have heroes', () => {
let heroEls = element.all(by.css('li')); const heroEls = element.all(by.css('li'));
expect(heroEls.count()).not.toBe(0, 'should have heroes'); expect(heroEls.count()).not.toBe(0, 'should have heroes');
}); });
it('should display "there are many heroes!"', function () { it('should display "there are many heroes!"', () => {
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!'); expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
}); });
}); });

View File

@ -1,15 +1,13 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Docs Style Guide', function () { describe('Docs Style Guide', () => {
let _title = 'Authors Style Guide Sample'; const title = 'Authors Style Guide Sample';
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should display correct title: ' + _title, function () { it('should display correct title: ' + title, () => {
expect(element(by.css('h1')).getText()).toEqual(_title); expect(element(by.css('h1')).getText()).toEqual(title);
}); });
}); });

View File

@ -1,18 +1,16 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
/* tslint:disable:quotemark */ /* tslint:disable:quotemark */
describe('Dynamic Component Loader', function () { describe('Dynamic Component Loader', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
it('should load ad banner', function () { it('should load ad banner', () => {
let headline = element(by.xpath("//h4[text()='Featured Hero Profile']")); const headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
let name = element(by.xpath("//h3[text()='Bombasto']")); const name = element(by.xpath("//h3[text()='Bombasto']"));
let bio = element(by.xpath("//p[text()='Brave as they come']")); const bio = element(by.xpath("//p[text()='Brave as they come']"));
expect(name).toBeDefined(); expect(name).toBeDefined();
expect(headline).toBeDefined(); expect(headline).toBeDefined();

View File

@ -11,7 +11,7 @@ import { AdComponent } from './ad.component';
template: ` template: `
<div class="ad-banner-example"> <div class="ad-banner-example">
<h3>Advertisements</h3> <h3>Advertisements</h3>
<ng-template ad-host></ng-template> <ng-template adHost></ng-template>
</div> </div>
` `
// #enddocregion ad-host // #enddocregion ad-host
@ -43,8 +43,8 @@ export class AdBannerComponent implements OnInit, OnDestroy {
const viewContainerRef = this.adHost.viewContainerRef; const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
(<AdComponent>componentRef.instance).data = adItem.data; componentRef.instance.data = adItem.data;
} }
getAds() { getAds() {

View File

@ -1,8 +1,9 @@
// tslint:disable: directive-selector
// #docregion // #docregion
import { Directive, ViewContainerRef } from '@angular/core'; import { Directive, ViewContainerRef } from '@angular/core';
@Directive({ @Directive({
selector: '[ad-host]', selector: '[adHost]',
}) })
export class AdDirective { export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { } constructor(public viewContainerRef: ViewContainerRef) { }

View File

@ -1,27 +1,25 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
/* tslint:disable:quotemark */ /* tslint:disable:quotemark */
describe('Dynamic Form', function () { describe('Dynamic Form', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it('should submit form', function () { it('should submit form', () => {
let firstNameElement = element.all(by.css('input[id=firstName]')).get(0); const firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
expect(firstNameElement.getAttribute('value')).toEqual('Bombasto'); expect(firstNameElement.getAttribute('value')).toEqual('Bombasto');
let emailElement = element.all(by.css('input[id=emailAddress]')).get(0); const emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
let email = 'test@test.com'; const email = 'test@test.com';
emailElement.sendKeys(email); emailElement.sendKeys(email);
expect(emailElement.getAttribute('value')).toEqual(email); expect(emailElement.getAttribute('value')).toEqual(email);
element(by.css('select option[value="solid"]')).click(); element(by.css('select option[value="solid"]')).click();
let saveButton = element.all(by.css('button')).get(0); const saveButton = element.all(by.css('button')).get(0);
saveButton.click().then(function() { saveButton.click().then(() => {
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true); expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
}); });
}); });

View File

@ -10,13 +10,14 @@ export class QuestionBase<T> {
options: {key: string, value: string}[]; options: {key: string, value: string}[];
constructor(options: { constructor(options: {
value?: T, value?: T;
key?: string, key?: string;
label?: string, label?: string;
required?: boolean, required?: boolean;
order?: number, order?: number;
controlType?: string, controlType?: string;
type?: string type?: string;
options?: {key: string, value: string}[];
} = {}) { } = {}) {
this.value = options.value; this.value = options.value;
this.key = options.key || ''; this.key = options.key || '';
@ -25,5 +26,6 @@ export class QuestionBase<T> {
this.order = options.order === undefined ? 1 : options.order; this.order = options.order === undefined ? 1 : options.order;
this.controlType = options.controlType || ''; this.controlType = options.controlType || '';
this.type = options.type || ''; this.type = options.type || '';
this.options = options.options || [];
} }
} }

View File

@ -9,7 +9,7 @@ export class QuestionControlService {
constructor() { } constructor() { }
toFormGroup(questions: QuestionBase<string>[] ) { toFormGroup(questions: QuestionBase<string>[] ) {
let group: any = {}; const group: any = {};
questions.forEach(question => { questions.forEach(question => {
group[question.key] = question.required ? new FormControl(question.value || '', Validators.required) group[question.key] = question.required ? new FormControl(question.value || '', Validators.required)

View File

@ -3,10 +3,4 @@ import { QuestionBase } from './question-base';
export class DropdownQuestion extends QuestionBase<string> { export class DropdownQuestion extends QuestionBase<string> {
controlType = 'dropdown'; controlType = 'dropdown';
options: {key: string, value: string}[] = [];
constructor(options: {} = {}) {
super(options);
this.options = options['options'] || [];
}
} }

View File

@ -3,10 +3,4 @@ import { QuestionBase } from './question-base';
export class TextboxQuestion extends QuestionBase<string> { export class TextboxQuestion extends QuestionBase<string> {
controlType = 'textbox'; controlType = 'textbox';
type: string;
constructor(options: {} = {}) {
super(options);
this.type = options['type'] || '';
}
} }

View File

@ -12,7 +12,7 @@ export class QuestionService {
// TODO: get from a remote source of question metadata // TODO: get from a remote source of question metadata
getQuestions() { getQuestions() {
let questions: QuestionBase<string>[] = [ const questions: QuestionBase<string>[] = [
new DropdownQuestion({ new DropdownQuestion({
key: 'brave', key: 'brave',

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, by, element, ElementFinder, ExpectedConditions as EC } from 'protractor'; import { browser, by, element, ElementFinder, ExpectedConditions as EC } from 'protractor';
/* tslint:disable:quotemark */ /* tslint:disable:quotemark */
@ -16,7 +14,7 @@ describe('Elements', () => {
const waitForText = (elem: ElementFinder) => { const waitForText = (elem: ElementFinder) => {
// Waiting for the element to have some text, makes the tests less flaky. // Waiting for the element to have some text, makes the tests less flaky.
browser.wait(async () => /\S/.test(await elem.getText()), 5000); browser.wait(async () => /\S/.test(await elem.getText()), 5000);
} };
beforeEach(() => browser.get('')); beforeEach(() => browser.get(''));

View File

@ -1,4 +1,6 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; // tslint:disable: variable-name
// #docregion
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations'; import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({ @Component({
@ -7,9 +9,6 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
<span>Popup: {{message}}</span> <span>Popup: {{message}}</span>
<button (click)="closed.next()">&#x2716;</button> <button (click)="closed.next()">&#x2716;</button>
`, `,
host: {
'[@state]': 'state',
},
animations: [ animations: [
trigger('state', [ trigger('state', [
state('opened', style({transform: 'translateY(0%)'})), state('opened', style({transform: 'translateY(0%)'})),
@ -39,15 +38,16 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
`] `]
}) })
export class PopupComponent { export class PopupComponent {
@HostBinding('@state')
state: 'opened' | 'closed' = 'closed'; state: 'opened' | 'closed' = 'closed';
@Input() @Input()
get message(): string { return this._message; }
set message(message: string) { set message(message: string) {
this._message = message; this._message = message;
this.state = 'opened'; this.state = 'opened';
} }
get message(): string { return this._message; } private _message: string;
_message: string;
@Output() @Output()
closed = new EventEmitter(); closed = new EventEmitter();

View File

@ -1,26 +1,24 @@
'use strict'; // necessary for es6 output in node import { browser, element, by } from 'protractor';
import { browser, element, by, protractor } from 'protractor'; describe('Event binding example', () => {
describe('Event binding example', function () { beforeEach(() => {
beforeEach(function () {
browser.get(''); browser.get('');
}); });
let saveButton = element.all(by.css('button')).get(0); const saveButton = element.all(by.css('button')).get(0);
let onSaveButton = element.all(by.css('button')).get(1); const onSaveButton = element.all(by.css('button')).get(1);
let myClick = element.all(by.css('button')).get(2); const myClick = element.all(by.css('button')).get(2);
let deleteButton = element.all(by.css('button')).get(3); const deleteButton = element.all(by.css('button')).get(3);
let saveNoProp = element.all(by.css('button')).get(4); const saveNoProp = element.all(by.css('button')).get(4);
let saveProp = element.all(by.css('button')).get(5); const saveProp = element.all(by.css('button')).get(5);
it('should display Event Binding with Angular', function () { it('should display Event Binding with Angular', () => {
expect(element(by.css('h1')).getText()).toEqual('Event Binding'); expect(element(by.css('h1')).getText()).toEqual('Event Binding');
}); });
it('should display 6 buttons', function() { it('should display 6 buttons', () => {
expect(saveButton.getText()).toBe('Save'); expect(saveButton.getText()).toBe('Save');
expect(onSaveButton.getText()).toBe('on-click Save'); expect(onSaveButton.getText()).toBe('on-click Save');
expect(myClick.getText()).toBe('click with myClick'); expect(myClick.getText()).toBe('click with myClick');
@ -29,24 +27,23 @@ describe('Event binding example', function () {
expect(saveProp.getText()).toBe('Save with propagation'); expect(saveProp.getText()).toBe('Save with propagation');
}); });
it('should support user input', function () { it('should support user input', () => {
let input = element(by.css('input')); const input = element(by.css('input'));
let bindingResult = element.all(by.css('h4')).get(1); const bindingResult = element.all(by.css('h4')).get(1);
expect(bindingResult.getText()).toEqual('Result: teapot'); expect(bindingResult.getText()).toEqual('Result: teapot');
input.sendKeys('abc'); input.sendKeys('abc');
expect(bindingResult.getText()).toEqual('Result: teapotabc'); expect(bindingResult.getText()).toEqual('Result: teapotabc');
}); });
it('should hide the item img', async () => { it('should hide the item img', async () => {
let deleteButton = element.all(by.css('button')).get(3);
await deleteButton.click(); await deleteButton.click();
browser.switchTo().alert().accept(); browser.switchTo().alert().accept();
expect(element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none'); expect(element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none');
}); });
it('should show two alerts', async () => { it('should show two alerts', async () => {
let parentDiv = element.all(by.css('.parent-div')); const parentDiv = element.all(by.css('.parent-div'));
let childDiv = element.all(by.css('div > div')).get(1); const childDiv = element.all(by.css('div > div')).get(1);
await parentDiv.click(); await parentDiv.click();
browser.switchTo().alert().accept(); browser.switchTo().alert().accept();
expect(childDiv.getText()).toEqual('Click me too! (child)'); expect(childDiv.getText()).toEqual('Click me too! (child)');

View File

@ -12,7 +12,7 @@ export class AppComponent {
clickMessage = ''; clickMessage = '';
onSave(event?: KeyboardEvent) { onSave(event?: KeyboardEvent) {
const evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : ''; const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
alert('Saved.' + evtMsg); alert('Saved.' + evtMsg);
if (event) { event.stopPropagation(); } if (event) { event.stopPropagation(); }
} }
@ -22,7 +22,7 @@ export class AppComponent {
} }
onClickMe(event?: KeyboardEvent) { onClickMe(event?: KeyboardEvent) {
const evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : ''; const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
alert('Click me.' + evtMsg); alert('Click me.' + evtMsg);
} }

View File

@ -1,4 +1,4 @@
/* tslint:disable use-output-property-decorator directive-class-suffix */ // tslint:disable: directive-selector
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core'; import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
@Directive({selector: '[myClick]'}) @Directive({selector: '[myClick]'})

View File

@ -1,11 +1,9 @@
'use strict'; // necessary for node!
import { browser, element, by, protractor, ElementFinder, ElementArrayFinder } from 'protractor'; import { browser, element, by, protractor, ElementFinder, ElementArrayFinder } from 'protractor';
// THESE TESTS ARE INCOMPLETE // THESE TESTS ARE INCOMPLETE
describe('Form Validation Tests', function () { describe('Form Validation Tests', () => {
beforeAll(function () { beforeAll(() => {
browser.get(''); browser.get('');
}); });
@ -52,11 +50,11 @@ let page: {
}; };
function getPage(sectionTag: string) { function getPage(sectionTag: string) {
let section = element(by.css(sectionTag)); const section = element(by.css(sectionTag));
let buttons = section.all(by.css('button')); const buttons = section.all(by.css('button'));
page = { page = {
section: section, section,
form: section.element(by.css('form')), form: section.element(by.css('form')),
title: section.element(by.css('h1')), title: section.element(by.css('h1')),
nameInput: section.element(by.css('#name')), nameInput: section.element(by.css('#name')),
@ -73,31 +71,31 @@ function getPage(sectionTag: string) {
function tests(title: string) { function tests(title: string) {
it('should display correct title', function () { it('should display correct title', () => {
expect(page.title.getText()).toContain(title); expect(page.title.getText()).toContain(title);
}); });
it('should not display submitted message before submit', function () { it('should not display submitted message before submit', () => {
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false); expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false);
}); });
it('should have form buttons', function () { it('should have form buttons', () => {
expect(page.heroFormButtons.count()).toEqual(2); expect(page.heroFormButtons.count()).toEqual(2);
}); });
it('should have error at start', function () { it('should have error at start', () => {
expectFormIsInvalid(); expectFormIsInvalid();
}); });
// it('showForm', function () { // it('showForm', () => {
// page.form.getInnerHtml().then(html => console.log(html)); // page.form.getInnerHtml().then(html => console.log(html));
// }); // });
it('should have disabled submit button', function () { it('should have disabled submit button', () => {
expect(page.heroFormButtons.get(0).isEnabled()).toBe(false); expect(page.heroFormButtons.get(0).isEnabled()).toBe(false);
}); });
it('resetting name to valid name should clear errors', function () { it('resetting name to valid name should clear errors', () => {
const ele = page.nameInput; const ele = page.nameInput;
expect(ele.isPresent()).toBe(true, 'nameInput should exist'); expect(ele.isPresent()).toBe(true, 'nameInput should exist');
ele.clear(); ele.clear();
@ -105,7 +103,7 @@ function tests(title: string) {
expectFormIsValid(); expectFormIsValid();
}); });
it('should produce "required" error after clearing name', function () { it('should produce "required" error after clearing name', () => {
page.nameInput.clear(); page.nameInput.clear();
// page.alterEgoInput.click(); // to blur ... didn't work // page.alterEgoInput.click(); // to blur ... didn't work
page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh! page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh!
@ -113,37 +111,37 @@ function tests(title: string) {
expect(page.errorMessages.get(0).getText()).toContain('required'); expect(page.errorMessages.get(0).getText()).toContain('required');
}); });
it('should produce "at least 4 characters" error when name="x"', function () { it('should produce "at least 4 characters" error when name="x"', () => {
page.nameInput.clear(); page.nameInput.clear();
page.nameInput.sendKeys('x'); // too short page.nameInput.sendKeys('x'); // too short
expectFormIsInvalid(); expectFormIsInvalid();
expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters'); expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters');
}); });
it('resetting name to valid name again should clear errors', function () { it('resetting name to valid name again should clear errors', () => {
page.nameInput.sendKeys(testName); page.nameInput.sendKeys(testName);
expectFormIsValid(); expectFormIsValid();
}); });
it('should have enabled submit button', function () { it('should have enabled submit button', () => {
const submitBtn = page.heroFormButtons.get(0); const submitBtn = page.heroFormButtons.get(0);
expect(submitBtn.isEnabled()).toBe(true); expect(submitBtn.isEnabled()).toBe(true);
}); });
it('should hide form after submit', function () { it('should hide form after submit', () => {
page.heroFormButtons.get(0).click(); page.heroFormButtons.get(0).click();
expect(page.heroFormButtons.get(0).isDisplayed()).toBe(false); expect(page.heroFormButtons.get(0).isDisplayed()).toBe(false);
}); });
it('submitted form should be displayed', function () { it('submitted form should be displayed', () => {
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true); expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true);
}); });
it('submitted form should have new hero name', function () { it('submitted form should have new hero name', () => {
expect(page.heroSubmitted.getText()).toContain(testName); expect(page.heroSubmitted.getText()).toContain(testName);
}); });
it('clicking edit button should reveal form again', function () { it('clicking edit button should reveal form again', () => {
const newFormBtn = page.heroSubmitted.element(by.css('button')); const newFormBtn = page.heroSubmitted.element(by.css('button'));
newFormBtn.click(); newFormBtn.click();
expect(page.heroSubmitted.isElementPresent(by.css('p'))) expect(page.heroSubmitted.isElementPresent(by.css('p')))
@ -162,7 +160,7 @@ function expectFormIsInvalid() {
function triggerAlterEgoValidation() { function triggerAlterEgoValidation() {
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event // alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
element(by.css('app-root')).click() element(by.css('app-root')).click();
} }
function waitForAlterEgoValidation() { function waitForAlterEgoValidation() {
@ -173,7 +171,7 @@ function waitForAlterEgoValidation() {
function bobTests() { function bobTests() {
const emsg = 'Name cannot be Bob.'; const emsg = 'Name cannot be Bob.';
it('should produce "no bob" error after setting name to "Bobby"', function () { it('should produce "no bob" error after setting name to "Bobby"', () => {
// Re-populate select element // Re-populate select element
page.powerSelect.click(); page.powerSelect.click();
page.powerOption.click(); page.powerOption.click();
@ -184,7 +182,7 @@ function bobTests() {
expect(page.errorMessages.get(0).getText()).toBe(emsg); expect(page.errorMessages.get(0).getText()).toBe(emsg);
}); });
it('should be ok again with valid name', function () { it('should be ok again with valid name', () => {
page.nameInput.clear(); page.nameInput.clear();
page.nameInput.sendKeys(testName); page.nameInput.sendKeys(testName);
expectFormIsValid(); expectFormIsValid();
@ -194,7 +192,7 @@ function bobTests() {
function asyncValidationTests() { function asyncValidationTests() {
const emsg = 'Alter ego is already taken.'; const emsg = 'Alter ego is already taken.';
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () { it(`should produce "${emsg}" error after setting alterEgo to Eric`, () => {
page.alterEgoInput.clear(); page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Eric'); page.alterEgoInput.sendKeys('Eric');
@ -205,7 +203,7 @@ function asyncValidationTests() {
expect(page.alterEgoErrors.getText()).toBe(emsg); expect(page.alterEgoErrors.getText()).toBe(emsg);
}); });
it('should be ok again with different values', function () { it('should be ok again with different values', () => {
page.alterEgoInput.clear(); page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('John'); page.alterEgoInput.sendKeys('John');
@ -220,7 +218,7 @@ function asyncValidationTests() {
function crossValidationTests() { function crossValidationTests() {
const emsg = 'Name cannot match alter ego.'; const emsg = 'Name cannot match alter ego.';
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () { it(`should produce "${emsg}" error after setting name and alter ego to the same value`, () => {
page.nameInput.clear(); page.nameInput.clear();
page.nameInput.sendKeys('Batman'); page.nameInput.sendKeys('Batman');
@ -234,7 +232,7 @@ function crossValidationTests() {
expect(page.crossValidationErrorMessage.getText()).toBe(emsg); expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
}); });
it('should be ok again with different values', function () { it('should be ok again with different values', () => {
page.nameInput.clear(); page.nameInput.clear();
page.nameInput.sendKeys('Batman'); page.nameInput.sendKeys('Batman');

View File

@ -22,13 +22,13 @@ export class HeroFormReactiveComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
// #docregion custom-validator // #docregion custom-validator
this.heroForm = new FormGroup({ this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [ name: new FormControl(this.hero.name, [
Validators.required, Validators.required,
Validators.minLength(4), Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator. forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]), ]),
'alterEgo': new FormControl(this.hero.alterEgo), alterEgo: new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required) power: new FormControl(this.hero.power, Validators.required)
}); });
// #enddocregion custom-validator // #enddocregion custom-validator

View File

@ -22,16 +22,16 @@ export class HeroFormReactiveComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
// #docregion async-validation // #docregion async-validation
this.heroForm = new FormGroup({ this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [ name: new FormControl(this.hero.name, [
Validators.required, Validators.required,
Validators.minLength(4), Validators.minLength(4),
forbiddenNameValidator(/bob/i) forbiddenNameValidator(/bob/i)
]), ]),
'alterEgo': new FormControl(this.hero.alterEgo, { alterEgo: new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)], asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur' updateOn: 'blur'
}), }),
'power': new FormControl(this.hero.power, Validators.required) power: new FormControl(this.hero.power, Validators.required)
}); });
// #enddocregion async-validation // #enddocregion async-validation
} }

View File

@ -21,16 +21,16 @@ export class HeroFormReactiveComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.heroForm = new FormGroup({ this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [ name: new FormControl(this.hero.name, [
Validators.required, Validators.required,
Validators.minLength(4), Validators.minLength(4),
forbiddenNameValidator(/bob/i) forbiddenNameValidator(/bob/i)
]), ]),
'alterEgo': new FormControl(this.hero.alterEgo, { alterEgo: new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)], asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur' updateOn: 'blur'
}), }),
'power': new FormControl(this.hero.power, Validators.required) power: new FormControl(this.hero.power, Validators.required)
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level }, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
} }

View File

@ -7,7 +7,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } fr
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => { return (control: AbstractControl): {[key: string]: any} | null => {
const forbidden = nameRe.test(control.value); const forbidden = nameRe.test(control.value);
return forbidden ? {'forbiddenName': {value: control.value}} : null; return forbidden ? {forbiddenName: {value: control.value}} : null;
}; };
} }
// #enddocregion custom-validator // #enddocregion custom-validator

View File

@ -8,7 +8,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali
const name = control.get('name'); const name = control.get('name');
const alterEgo = control.get('alterEgo'); const alterEgo = control.get('alterEgo');
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null; return name && alterEgo && name.value === alterEgo.value ? { identityRevealed: true } : null;
}; };
// #enddocregion cross-validation-validator // #enddocregion cross-validation-validator
@ -19,7 +19,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali
}) })
export class IdentityRevealedValidatorDirective implements Validator { export class IdentityRevealedValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors { validate(control: AbstractControl): ValidationErrors {
return identityRevealedValidator(control) return identityRevealedValidator(control);
} }
} }
// #enddocregion cross-validation-directive // #enddocregion cross-validation-directive

View File

@ -1,8 +1,8 @@
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Forms Overview Tests', function () { describe('Forms Overview Tests', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });

View File

@ -1,5 +1,5 @@
export function createNewEvent(eventName: string, bubbles = false, cancelable = false) { export function createNewEvent(eventName: string, bubbles = false, cancelable = false) {
let evt = document.createEvent('CustomEvent'); const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(eventName, bubbles, cancelable, null); evt.initCustomEvent(eventName, bubbles, cancelable, null);
return evt; return evt;
} }

View File

@ -1,60 +1,60 @@
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Forms Tests', function () { describe('Forms Tests', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
it('should display correct title', function () { it('should display correct title', () => {
expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form'); expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
}); });
it('should not display message before submit', function () { it('should not display message before submit', () => {
let ele = element(by.css('h2')); const ele = element(by.css('h2'));
expect(ele.isDisplayed()).toBe(false); expect(ele.isDisplayed()).toBe(false);
}); });
it('should hide form after submit', function () { it('should hide form after submit', () => {
let ele = element.all(by.css('h1')).get(0); const ele = element.all(by.css('h1')).get(0);
expect(ele.isDisplayed()).toBe(true); expect(ele.isDisplayed()).toBe(true);
let b = element.all(by.css('button[type=submit]')).get(0); const b = element.all(by.css('button[type=submit]')).get(0);
b.click().then(function() { b.click().then(() => {
expect(ele.isDisplayed()).toBe(false); expect(ele.isDisplayed()).toBe(false);
}); });
}); });
it('should display message after submit', function () { it('should display message after submit', () => {
let b = element.all(by.css('button[type=submit]')).get(0); const b = element.all(by.css('button[type=submit]')).get(0);
b.click().then(function() { b.click().then(() => {
expect(element(by.css('h2')).getText()).toContain('You submitted the following'); expect(element(by.css('h2')).getText()).toContain('You submitted the following');
}); });
}); });
it('should hide form after submit', function () { it('should hide form after submit', () => {
let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
expect(alterEgoEle.isDisplayed()).toBe(true); expect(alterEgoEle.isDisplayed()).toBe(true);
let submitButtonEle = element.all(by.css('button[type=submit]')).get(0); const submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
submitButtonEle.click().then(function() { submitButtonEle.click().then(() => {
expect(alterEgoEle.isDisplayed()).toBe(false); expect(alterEgoEle.isDisplayed()).toBe(false);
}); });
}); });
it('should reflect submitted data after submit', function () { it('should reflect submitted data after submit', () => {
let test = 'testing 1 2 3'; const test = 'testing 1 2 3';
let newValue: string; let newValue: string;
let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
alterEgoEle.getAttribute('value').then(function(value: string) { alterEgoEle.getAttribute('value').then((value: string) => {
alterEgoEle.sendKeys(test); alterEgoEle.sendKeys(test);
newValue = value + test; newValue = value + test;
expect(alterEgoEle.getAttribute('value')).toEqual(newValue); expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
let b = element.all(by.css('button[type=submit]')).get(0); const b = element.all(by.css('button[type=submit]')).get(0);
return b.click(); return b.click();
}).then(function() { }).then(() => {
let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego')); const alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label'); expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
let divEle = element(by.cssContainingText('div', newValue)); const divEle = element(by.cssContainingText('div', newValue));
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue); expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
}); });
}); });

View File

@ -35,7 +35,7 @@ export class HeroFormComponent {
skyDog(): Hero { skyDog(): Hero {
// #docregion SkyDog // #docregion SkyDog
let myHero = new Hero(42, 'SkyDog', const myHero = new Hero(42, 'SkyDog',
'Fetch any object at any distance', 'Fetch any object at any distance',
'Leslie Rollover'); 'Leslie Rollover');
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog" console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
@ -48,9 +48,9 @@ export class HeroFormComponent {
// Reveal in html: // Reveal in html:
// Name via form.controls = {{showFormControls(heroForm)}} // Name via form.controls = {{showFormControls(heroForm)}}
showFormControls(form: any) { showFormControls(form: any) {
return form && form.controls['name'] && return form && form.controls.name &&
// #docregion form-controls // #docregion form-controls
form.controls['name'].value; // Dr. IQ form.controls.name.value; // Dr. IQ
// #enddocregion form-controls // #enddocregion form-controls
} }

View File

@ -1,19 +1,15 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Getting Started V0', () => { describe('Getting Started V0', () => {
beforeEach(() => { beforeEach(() => browser.get('/'));
return browser.get('/');
});
it('should display "My Store" in the top bar', async() => { it('should display "My Store" in the top bar', async () => {
const title = await element(by.css('app-root app-top-bar h1')).getText(); const title = await element(by.css('app-root app-top-bar h1')).getText();
expect(title).toEqual('My Store'); expect(title).toEqual('My Store');
}); });
it('should display "Products" on the homepage', async() => { it('should display "Products" on the homepage', async () => {
const title = await element(by.css('app-root app-product-list h2')).getText(); const title = await element(by.css('app-root app-product-list h2')).getText();
expect(title).toEqual('Products'); expect(title).toEqual('Products');

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor'; import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor';
describe('Getting Started', () => { describe('Getting Started', () => {
@ -15,17 +13,17 @@ describe('Getting Started', () => {
}; };
describe('General', () => { describe('General', () => {
beforeAll(async() => { beforeAll(async () => {
await browser.get('/'); await browser.get('/');
}); });
it('should display "My Store"', async() => { it('should display "My Store"', async () => {
const title = await pageElements.topBarHeader.getText(); const title = await pageElements.topBarHeader.getText();
expect(title).toEqual('My Store'); expect(title).toEqual('My Store');
}); });
it('should display "Products" on the homepage', async() => { it('should display "Products" on the homepage', async () => {
const title = await pageElements.productListHeader.getText(); const title = await pageElements.productListHeader.getText();
expect(title).toEqual('Products'); expect(title).toEqual('Products');
@ -33,11 +31,11 @@ describe('Getting Started', () => {
}); });
describe('Product List', () => { describe('Product List', () => {
beforeAll(async() => { beforeAll(async () => {
await browser.get('/'); await browser.get('/');
}); });
it('should display 3 items', async() => { it('should display 3 items', async () => {
const products = await pageElements.productListItems; const products = await pageElements.productListItems;
expect(products.length).toEqual(3); expect(products.length).toEqual(3);
@ -45,11 +43,11 @@ describe('Getting Started', () => {
}); });
describe('Product Details', () => { describe('Product Details', () => {
beforeEach(async() => { beforeEach(async () => {
await browser.get('/'); await browser.get('/');
}); });
it('should display information for a product', async() => { it('should display information for a product', async () => {
await pageElements.productListLinks.get(0).click(); await pageElements.productListLinks.get(0).click();
const product = pageElements.productDetailsPage; const product = pageElements.productDetailsPage;
@ -63,7 +61,7 @@ describe('Getting Started', () => {
expect(productDescription).toBe('A large phone with one of the best screens'); expect(productDescription).toBe('A large phone with one of the best screens');
}); });
it('should add the product to the cart', async() => { it('should add the product to the cart', async () => {
await pageElements.productListLinks.get(0).click(); await pageElements.productListLinks.get(0).click();
const product = pageElements.productDetailsPage; const product = pageElements.productDetailsPage;
@ -82,11 +80,11 @@ describe('Getting Started', () => {
describe('Cart', () => { describe('Cart', () => {
beforeEach(async() => { beforeEach(async () => {
await browser.get('/'); await browser.get('/');
}); });
it('should go through the checkout process', async() => { it('should go through the checkout process', async () => {
await pageElements.productListLinks.get(0).click(); await pageElements.productListLinks.get(0).click();
const checkoutLink = pageElements.topBarCheckoutLink; const checkoutLink = pageElements.topBarCheckoutLink;

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, by, element } from 'protractor'; import { browser, by, element } from 'protractor';
describe('Hierarchical dependency injection', () => { describe('Hierarchical dependency injection', () => {
@ -9,7 +7,7 @@ describe('Hierarchical dependency injection', () => {
}); });
describe('Heroes Scenario', () => { describe('Heroes Scenario', () => {
let page = { const page = {
heroName: '', heroName: '',
income: '', income: '',

View File

@ -1,3 +1,4 @@
// tslint:disable: no-output-native
// #docregion // #docregion
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { HeroTaxReturn } from './hero'; import { HeroTaxReturn } from './hero';
@ -21,7 +22,7 @@ export class HeroTaxReturnComponent {
} }
@Input() @Input()
set taxReturn (htr: HeroTaxReturn) { set taxReturn(htr: HeroTaxReturn) {
this.heroTaxReturnService.taxReturn = htr; this.heroTaxReturnService.taxReturn = htr;
} }
@ -30,9 +31,9 @@ export class HeroTaxReturnComponent {
onCanceled() { onCanceled() {
this.flashMessage('Canceled'); this.flashMessage('Canceled');
this.heroTaxReturnService.restoreTaxReturn(); this.heroTaxReturnService.restoreTaxReturn();
}; }
onClose() { this.close.emit(); }; onClose() { this.close.emit(); }
onSaved() { onSaved() {
this.flashMessage('Saved'); this.flashMessage('Saved');

View File

@ -10,12 +10,12 @@ export class HeroTaxReturnService {
constructor(private heroService: HeroesService) { } constructor(private heroService: HeroesService) { }
set taxReturn (htr: HeroTaxReturn) { set taxReturn(htr: HeroTaxReturn) {
this.originalTaxReturn = htr; this.originalTaxReturn = htr;
this.currentTaxReturn = htr.clone(); this.currentTaxReturn = htr.clone();
} }
get taxReturn (): HeroTaxReturn { get taxReturn(): HeroTaxReturn {
return this.currentTaxReturn; return this.currentTaxReturn;
} }

View File

@ -24,11 +24,11 @@ const page = {
uploadMessage: element(by.css('app-uploader p')) uploadMessage: element(by.css('app-uploader p'))
}; };
let checkLogForMessage = (message: string) => { const checkLogForMessage = (message: string) => {
expect(page.logList.getText()).toContain(message); expect(page.logList.getText()).toContain(message);
}; };
describe('Http Tests', function() { describe('Http Tests', () => {
beforeEach(() => { beforeEach(() => {
browser.get(''); browser.get('');
}); });

View File

@ -40,8 +40,8 @@ export class ConfigComponent {
this.configService.getConfig_1() this.configService.getConfig_1()
// #docregion v1, v1_callback // #docregion v1, v1_callback
.subscribe((data: Config) => this.config = { .subscribe((data: Config) => this.config = {
heroesUrl: data['heroesUrl'], heroesUrl: data.heroesUrl,
textfile: data['textfile'] textfile: data.textfile
}); });
// #enddocregion v1_callback // #enddocregion v1_callback
} }

View File

@ -16,7 +16,7 @@ import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
const httpOptions = { const httpOptions = {
headers: new HttpHeaders({ headers: new HttpHeaders({
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'my-auth-token' Authorization: 'my-auth-token'
}) })
}; };
// #enddocregion http-options // #enddocregion http-options
@ -33,7 +33,7 @@ export class HeroesService {
} }
/** GET heroes from the server */ /** GET heroes from the server */
getHeroes (): Observable<Hero[]> { getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl) return this.http.get<Hero[]>(this.heroesUrl)
.pipe( .pipe(
catchError(this.handleError('getHeroes', [])) catchError(this.handleError('getHeroes', []))
@ -60,7 +60,7 @@ export class HeroesService {
// #docregion addHero // #docregion addHero
/** POST: add a new hero to the database */ /** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> { addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions) return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe( .pipe(
catchError(this.handleError('addHero', hero)) catchError(this.handleError('addHero', hero))
@ -70,7 +70,7 @@ export class HeroesService {
// #docregion deleteHero // #docregion deleteHero
/** DELETE: delete the hero from the server */ /** DELETE: delete the hero from the server */
deleteHero (id: number): Observable<{}> { deleteHero(id: number): Observable<{}> {
const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42 const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42
return this.http.delete(url, httpOptions) return this.http.delete(url, httpOptions)
.pipe( .pipe(
@ -81,7 +81,7 @@ export class HeroesService {
// #docregion updateHero // #docregion updateHero
/** PUT: update the hero on the server. Returns the updated hero upon success. */ /** PUT: update the hero on the server. Returns the updated hero upon success. */
updateHero (hero: Hero): Observable<Hero> { updateHero(hero: Hero): Observable<Hero> {
// #enddocregion updateHero // #enddocregion updateHero
// #docregion update-headers // #docregion update-headers
httpOptions.headers = httpOptions.headers =

View File

@ -15,8 +15,10 @@ export class HttpErrorHandler {
constructor(private messageService: MessageService) { } constructor(private messageService: MessageService) { }
/** Create curried handleError function that already knows the service name */ /** Create curried handleError function that already knows the service name */
createHandleError = (serviceName = '') => <T> createHandleError = (serviceName = '') => {
(operation = 'operation', result = {} as T) => this.handleError(serviceName, operation, result); return <T>(operation = 'operation', result = {} as T) =>
this.handleError(serviceName, operation, result);
}
/** /**
* Returns a function that handles Http operation failures. * Returns a function that handles Http operation failures.
@ -25,7 +27,7 @@ export class HttpErrorHandler {
* @param operation - name of the operation that failed * @param operation - name of the operation that failed
* @param result - optional value to return as the observable result * @param result - optional value to return as the observable result
*/ */
handleError<T> (serviceName = '', operation = 'operation', result = {} as T) { handleError<T>(serviceName = '', operation = 'operation', result = {} as T) {
return (error: HttpErrorResponse): Observable<T> => { return (error: HttpErrorResponse): Observable<T> => {
// TODO: send the error to remote logging infrastructure // TODO: send the error to remote logging infrastructure

View File

@ -39,7 +39,7 @@ export class PackageSearchService {
this.handleError = httpErrorHandler.createHandleError('HeroesService'); this.handleError = httpErrorHandler.createHandleError('HeroesService');
} }
search (packageName: string, refresh = false): Observable<NpmPackageInfo[]> { search(packageName: string, refresh = false): Observable<NpmPackageInfo[]> {
// clear if no pkg name // clear if no pkg name
if (!packageName.trim()) { return of([]); } if (!packageName.trim()) { return of([]); }

View File

@ -12,7 +12,7 @@ export interface RequestCacheEntry {
// #docregion request-cache // #docregion request-cache
export abstract class RequestCache { export abstract class RequestCache {
abstract get(req: HttpRequest<any>): HttpResponse<any> | undefined; abstract get(req: HttpRequest<any>): HttpResponse<any> | undefined;
abstract put(req: HttpRequest<any>, response: HttpResponse<any>): void abstract put(req: HttpRequest<any>, response: HttpResponse<any>): void;
} }
// #enddocregion request-cache // #enddocregion request-cache
@ -44,8 +44,8 @@ export class RequestCacheWithMap implements RequestCache {
const url = req.urlWithParams; const url = req.urlWithParams;
this.messenger.add(`Caching response from "${url}".`); this.messenger.add(`Caching response from "${url}".`);
const entry = { url, response, lastRead: Date.now() }; const newEntry = { url, response, lastRead: Date.now() };
this.cache.set(url, entry); this.cache.set(url, newEntry);
// remove expired cache entries // remove expired cache entries
const expired = Date.now() - maxAge; const expired = Date.now() - maxAge;

View File

@ -22,13 +22,13 @@ import './testing/http-client.spec.ts';
bootstrap(); bootstrap();
// //
function bootstrap () { function bootstrap() {
if (window['jasmineRef']) { if ((window as any).jasmineRef) {
location.reload(); location.reload();
return; return;
} else { } else {
window.onload(undefined); window.onload(undefined);
window['jasmineRef'] = jasmine.getEnv(); (window as any).jasmineRef = jasmine.getEnv();
} }
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.

View File

@ -1,3 +1,3 @@
import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js'; import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js';
window['jasmineRequire'] = jasmineRequire; (window as any).jasmineRequire = jasmineRequire;

View File

@ -73,7 +73,7 @@ describe('HttpClient testing', () => {
// Make an HTTP GET request with specific header // Make an HTTP GET request with specific header
httpClient.get<Data>(testUrl, { httpClient.get<Data>(testUrl, {
headers: new HttpHeaders({'Authorization': 'my-auth-token'}) headers: new HttpHeaders({Authorization: 'my-auth-token'})
}) })
.subscribe(data => .subscribe(data =>
expect(data).toEqual(testData) expect(data).toEqual(testData)
@ -83,14 +83,14 @@ describe('HttpClient testing', () => {
// #docregion predicate // #docregion predicate
// Expect one request with an authorization header // Expect one request with an authorization header
const req = httpTestingController.expectOne( const req = httpTestingController.expectOne(
req => req.headers.has('Authorization') request => request.headers.has('Authorization')
); );
// #enddocregion predicate // #enddocregion predicate
req.flush(testData); req.flush(testData);
}); });
it('can test multiple requests', () => { it('can test multiple requests', () => {
let testData: Data[] = [ const testData: Data[] = [
{ name: 'bob' }, { name: 'carol' }, { name: 'bob' }, { name: 'carol' },
{ name: 'ted' }, { name: 'alice' } { name: 'ted' }, { name: 'alice' }
]; ];

View File

@ -2,35 +2,35 @@ import { browser, element, by } from 'protractor';
describe('i18n E2E Tests', () => { describe('i18n E2E Tests', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
it('should display i18n translated welcome: Bonjour !', function () { it('should display i18n translated welcome: Bonjour !', () => {
expect(element(by.css('h1')).getText()).toEqual('Bonjour i18n !'); expect(element(by.css('h1')).getText()).toEqual('Bonjour i18n !');
}); });
it('should display the node texts without elements', function () { it('should display the node texts without elements', () => {
expect(element(by.css('app-root')).getText()).toContain(`Je n'affiche aucun élément`); expect(element(by.css('app-root')).getText()).toContain(`Je n'affiche aucun élément`);
}); });
it('should display the translated title attribute', function () { it('should display the translated title attribute', () => {
const title = element(by.css('img')).getAttribute('title'); const title = element(by.css('img')).getAttribute('title');
expect(title).toBe(`Logo d'Angular`); expect(title).toBe(`Logo d'Angular`);
}); });
it('should display the ICU plural expression', function () { it('should display the ICU plural expression', () => {
expect(element.all(by.css('span')).get(0).getText()).toBe(`Mis à jour à l'instant`); expect(element.all(by.css('span')).get(0).getText()).toBe(`Mis à jour à l'instant`);
}); });
it('should display the ICU select expression', function () { it('should display the ICU select expression', () => {
const selectIcuExp = element.all(by.css('span')).get(1); const selectIcuExp = element.all(by.css('span')).get(1);
expect(selectIcuExp.getText()).toBe(`L'auteur est une femme`); expect(selectIcuExp.getText()).toBe(`L'auteur est une femme`);
element.all(by.css('button')).get(2).click(); element.all(by.css('button')).get(2).click();
expect(selectIcuExp.getText()).toBe(`L'auteur est un homme`); expect(selectIcuExp.getText()).toBe(`L'auteur est un homme`);
}); });
it('should display the nested expression', function() { it('should display the nested expression', () => {
const nestedExp = element.all(by.css('span')).get(2); const nestedExp = element.all(by.css('span')).get(2);
const incBtn = element.all(by.css('button')).get(0); const incBtn = element.all(by.css('button')).get(0);
expect(nestedExp.getText()).toBe(`Mis à jour: à l'instant`); expect(nestedExp.getText()).toBe(`Mis à jour: à l'instant`);

View File

@ -1,14 +1,10 @@
'use strict';
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
import { logging } from 'selenium-webdriver'; import { logging } from 'selenium-webdriver';
describe('Inputs and Outputs', function () { describe('Inputs and Outputs', () => {
beforeEach(() => { beforeEach(() => browser.get(''));
browser.get('');
});
// helper function used to test what's logged to the console // helper function used to test what's logged to the console
@ -17,51 +13,48 @@ describe('Inputs and Outputs', function () {
.manage() .manage()
.logs() .logs()
.get(logging.Type.BROWSER); .get(logging.Type.BROWSER);
const message = logs.filter(({ message }) => const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1);
message.indexOf(contents) !== -1 ? true : false expect(messages.length).toBeGreaterThan(0);
);
console.log(message);
expect(message.length).toBeGreaterThan(0);
} }
it('should have title Inputs and Outputs', function () { it('should have title Inputs and Outputs', () => {
let title = element.all(by.css('h1')).get(0); const title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Inputs and Outputs'); expect(title.getText()).toEqual('Inputs and Outputs');
}); });
it('should add 123 to the parent list', async () => { it('should add 123 to the parent list', async () => {
let addToParentButton = element.all(by.css('button')).get(0); const addToParentButton = element.all(by.css('button')).get(0);
let addToListInput = element.all(by.css('input')).get(0); const addToListInput = element.all(by.css('input')).get(0);
let addedItem = element.all(by.css('li')).get(4); const addedItem = element.all(by.css('li')).get(4);
await addToListInput.sendKeys('123'); await addToListInput.sendKeys('123');
await addToParentButton.click(); await addToParentButton.click();
expect(addedItem.getText()).toEqual('123'); expect(addedItem.getText()).toEqual('123');
}); });
it('should delete item', async () => { it('should delete item', async () => {
let deleteButton = element.all(by.css('button')).get(1); const deleteButton = element.all(by.css('button')).get(1);
const contents = 'Child'; const contents = 'Child';
await deleteButton.click(); await deleteButton.click();
await logChecker(deleteButton, contents); await logChecker(deleteButton, contents);
}); });
it('should log buy the item', async () => { it('should log buy the item', async () => {
let buyButton = element.all(by.css('button')).get(2); const buyButton = element.all(by.css('button')).get(2);
const contents = 'Child'; const contents = 'Child';
await buyButton.click(); await buyButton.click();
await logChecker(buyButton, contents); await logChecker(buyButton, contents);
}); });
it('should save item for later', async () => { it('should save item for later', async () => {
let saveButton = element.all(by.css('button')).get(3); const saveButton = element.all(by.css('button')).get(3);
const contents = 'Child'; const contents = 'Child';
await saveButton.click(); await saveButton.click();
await logChecker(saveButton, contents); await logChecker(saveButton, contents);
}); });
it('should add item to wishlist', async () => { it('should add item to wishlist', async () => {
let addToParentButton = element.all(by.css('button')).get(4); const addToParentButton = element.all(by.css('button')).get(4);
let addedItem = element.all(by.css('li')).get(6); const addedItem = element.all(by.css('li')).get(6);
await addToParentButton.click(); await addToParentButton.click();
expect(addedItem.getText()).toEqual('Television'); expect(addedItem.getText()).toEqual('Television');
}); });

View File

@ -1,9 +1,4 @@
/* tslint:disable:use-input-property-decorator */ // tslint:disable: no-input-rename no-output-rename use-input-property-decorator use-output-property-decorator
/* tslint:disable:use-output-property-decorator */
/* tslint:disable:no-input-rename */
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({ @Component({

View File

@ -2,33 +2,33 @@ import { browser, element, by } from 'protractor';
describe('Interpolation e2e tests', () => { describe('Interpolation e2e tests', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
it('should display Interpolation and Template Expressions', function () { it('should display Interpolation and Template Expressions', () => {
expect(element(by.css('h1')).getText()).toEqual('Interpolation and Template Expressions'); expect(element(by.css('h1')).getText()).toEqual('Interpolation and Template Expressions');
}); });
it('should display Current customer: Maria', function () { it('should display Current customer: Maria', () => {
expect(element.all(by.css('h3')).get(0).getText()).toBe(`Current customer: Maria`); expect(element.all(by.css('h3')).get(0).getText()).toBe(`Current customer: Maria`);
}); });
it('should display The sum of 1 + 1 is not 4.', function () { it('should display The sum of 1 + 1 is not 4.', () => {
expect(element.all(by.css('p:last-child')).get(0).getText()).toBe(`The sum of 1 + 1 is not 4.`); expect(element.all(by.css('p:last-child')).get(0).getText()).toBe(`The sum of 1 + 1 is not 4.`);
}); });
it('should display Expression Context', function () { it('should display Expression Context', () => {
expect(element.all(by.css('h2')).get(1).getText()).toBe(`Expression Context`); expect(element.all(by.css('h2')).get(1).getText()).toBe(`Expression Context`);
}); });
it('should display a list of customers', function () { it('should display a list of customers', () => {
expect(element.all(by.css('li')).get(0).getText()).toBe(`Maria`); expect(element.all(by.css('li')).get(0).getText()).toBe(`Maria`);
}); });
it('should display two pictures', function() { it('should display two pictures', () => {
let pottedPlant = element.all(by.css('img')).get(0); const pottedPlant = element.all(by.css('img')).get(0);
let lamp = element.all(by.css('img')).get(1); const lamp = element.all(by.css('img')).get(1);
expect(pottedPlant.getAttribute('src')).toContain('potted-plant'); expect(pottedPlant.getAttribute('src')).toContain('potted-plant');
expect(pottedPlant.isDisplayed()).toBe(true); expect(pottedPlant.isDisplayed()).toBe(true);
@ -37,9 +37,9 @@ describe('Interpolation e2e tests', () => {
expect(lamp.isDisplayed()).toBe(true); expect(lamp.isDisplayed()).toBe(true);
}); });
it('should support user input', function () { it('should support user input', () => {
let input = element(by.css('input')); const input = element(by.css('input'));
let label = element(by.css('label')); const label = element(by.css('label'));
expect(label.getText()).toEqual('Type something:'); expect(label.getText()).toEqual('Type something:');
input.sendKeys('abc'); input.sendKeys('abc');
expect(label.getText()).toEqual('Type something: abc'); expect(label.getText()).toEqual('Type something: abc');

View File

@ -18,25 +18,25 @@ describe('providers App', () => {
expect(page.getTitleText()).toEqual('Lazy loading feature modules'); expect(page.getTitleText()).toEqual('Lazy loading feature modules');
}); });
describe('Customers', function() { describe('Customers', () => {
beforeEach(function() { beforeEach(() => {
customersButton.click(); customersButton.click();
}); });
it('should show customers when the button is clicked', function() { it('should show customers when the button is clicked', () => {
let customersMessage = element(by.css('app-customers > p')); const customersMessage = element(by.css('app-customers > p'));
expect(customersMessage.getText()).toBe('customers works!'); expect(customersMessage.getText()).toBe('customers works!');
}); });
}); });
describe('Orders', function() { describe('Orders', () => {
beforeEach(function() { beforeEach(() => {
ordersButton.click(); ordersButton.click();
}); });
it('should show orders when the button is clicked', function() { it('should show orders when the button is clicked', () => {
let ordersMessage = element(by.css('app-orders > p')); const ordersMessage = element(by.css('app-orders > p'));
expect(ordersMessage.getText()).toBe('orders works!'); expect(ordersMessage.getText()).toBe('orders works!');
}); });

View File

@ -1,5 +1,3 @@
'use strict'; // necessary for es6 output in node
import { browser, element, ElementFinder, by } from 'protractor'; import { browser, element, ElementFinder, by } from 'protractor';
describe('Lifecycle hooks', () => { describe('Lifecycle hooks', () => {
@ -15,11 +13,11 @@ describe('Lifecycle hooks', () => {
}); });
it('should support peek-a-boo', async () => { it('should support peek-a-boo', async () => {
let pabComp = element(by.css('peek-a-boo-parent peek-a-boo')); const pabComp = element(by.css('peek-a-boo-parent peek-a-boo'));
expect(pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component'); expect(pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component');
let pabButton = element.all(by.css('peek-a-boo-parent button')).get(0); const pabButton = element.all(by.css('peek-a-boo-parent button')).get(0);
let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1); const updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1);
expect(pabButton.getText()).toContain('Create Peek'); expect(pabButton.getText()).toContain('Create Peek');
await pabButton.click(); await pabButton.click();
@ -37,12 +35,12 @@ describe('Lifecycle hooks', () => {
}); });
it('should support OnChanges hook', () => { it('should support OnChanges hook', () => {
let onChangesViewEle = element.all(by.css('on-changes div')).get(0); const onChangesViewEle = element.all(by.css('on-changes div')).get(0);
let inputEles = element.all(by.css('on-changes-parent input')); const inputEles = element.all(by.css('on-changes-parent input'));
let heroNameInputEle = inputEles.get(1); const heroNameInputEle = inputEles.get(1);
let powerInputEle = inputEles.get(0); const powerInputEle = inputEles.get(0);
let titleEle = onChangesViewEle.element(by.css('p')); const titleEle = onChangesViewEle.element(by.css('p'));
let changeLogEles = onChangesViewEle.all(by.css('div')); const changeLogEles = onChangesViewEle.all(by.css('div'));
expect(titleEle.getText()).toContain('Windstorm can sing'); expect(titleEle.getText()).toContain('Windstorm can sing');
expect(changeLogEles.count()).toEqual(2, 'should start with 2 messages'); expect(changeLogEles.count()).toEqual(2, 'should start with 2 messages');
@ -56,12 +54,12 @@ describe('Lifecycle hooks', () => {
}); });
it('should support DoCheck hook', async () => { it('should support DoCheck hook', async () => {
let doCheckViewEle = element.all(by.css('do-check div')).get(0); const doCheckViewEle = element.all(by.css('do-check div')).get(0);
let inputEles = element.all(by.css('do-check-parent input')); const inputEles = element.all(by.css('do-check-parent input'));
let heroNameInputEle = inputEles.get(1); const heroNameInputEle = inputEles.get(1);
let powerInputEle = inputEles.get(0); const powerInputEle = inputEles.get(0);
let titleEle = doCheckViewEle.element(by.css('p')); const titleEle = doCheckViewEle.element(by.css('p'));
let changeLogEles = doCheckViewEle.all(by.css('div')); const changeLogEles = doCheckViewEle.all(by.css('div'));
let logCount: number; let logCount: number;
expect(titleEle.getText()).toContain('Windstorm can sing'); expect(titleEle.getText()).toContain('Windstorm can sing');
@ -84,11 +82,11 @@ describe('Lifecycle hooks', () => {
}); });
it('should support AfterView hooks', async () => { it('should support AfterView hooks', async () => {
let parentEle = element(by.tagName('after-view-parent')); const parentEle = element(by.tagName('after-view-parent'));
let buttonEle = parentEle.element(by.tagName('button')); // Reset const buttonEle = parentEle.element(by.tagName('button')); // Reset
let commentEle = parentEle.element(by.className('comment')); const commentEle = parentEle.element(by.className('comment'));
let logEles = parentEle.all(by.css('h4 ~ div')); const logEles = parentEle.all(by.css('h4 ~ div'));
let childViewInputEle = parentEle.element(by.css('app-child-view input')); const childViewInputEle = parentEle.element(by.css('app-child-view input'));
let logCount: number; let logCount: number;
expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
@ -101,7 +99,7 @@ describe('Lifecycle hooks', () => {
expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars');
expect(commentEle.getText()).toContain('long name'); expect(commentEle.getText()).toContain('long name');
let count = await logEles.count(); const count = await logEles.count();
expect(logCount + 7).toBeGreaterThan(count - 3, '7 additional log messages should have been added'); expect(logCount + 7).toBeGreaterThan(count - 3, '7 additional log messages should have been added');
expect(logCount + 7).toBeLessThan(count + 3, '7 additional log messages should have been added'); expect(logCount + 7).toBeLessThan(count + 3, '7 additional log messages should have been added');
@ -111,18 +109,18 @@ describe('Lifecycle hooks', () => {
}); });
it('should support AfterContent hooks', async () => { it('should support AfterContent hooks', async () => {
let parentEle = element(by.tagName('after-content-parent')); const parentEle = element(by.tagName('after-content-parent'));
let buttonEle = parentEle.element(by.tagName('button')); // Reset const buttonEle = parentEle.element(by.tagName('button')); // Reset
let commentEle = parentEle.element(by.className('comment')); const commentEle = parentEle.element(by.className('comment'));
let logEles = parentEle.all(by.css('h4 ~ div')); const logEles = parentEle.all(by.css('h4 ~ div'));
let childViewInputEle = parentEle.element(by.css('app-child input')); const childViewInputEle = parentEle.element(by.css('app-child input'));
let logCount = await logEles.count(); let logCount = await logEles.count();
expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM');
await sendKeys(childViewInputEle, '-test-'); await sendKeys(childViewInputEle, '-test-');
let count = await logEles.count(); const count = await logEles.count();
expect(childViewInputEle.getAttribute('value')).toContain('-test-'); expect(childViewInputEle.getAttribute('value')).toContain('-test-');
expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars');
expect(commentEle.getText()).toContain('long name'); expect(commentEle.getText()).toContain('long name');
@ -134,11 +132,11 @@ describe('Lifecycle hooks', () => {
}); });
it('should support spy\'s OnInit & OnDestroy hooks', async () => { it('should support spy\'s OnInit & OnDestroy hooks', async () => {
let inputEle = element(by.css('spy-parent input')); const inputEle = element(by.css('spy-parent input'));
let addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero')); const addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero'));
let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes')); const resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes'));
let heroEles = element.all(by.css('spy-parent div[mySpy')); const heroEles = element.all(by.css('spy-parent div[appSpy'));
let logEles = element.all(by.css('spy-parent h4 ~ div')); const logEles = element.all(by.css('spy-parent h4 ~ div'));
expect(heroEles.count()).toBe(2, 'should have two heroes displayed'); expect(heroEles.count()).toBe(2, 'should have two heroes displayed');
expect(logEles.count()).toBe(2, 'should have two log entries'); expect(logEles.count()).toBe(2, 'should have two log entries');
@ -155,10 +153,10 @@ describe('Lifecycle hooks', () => {
}); });
it('should support "spy counter"', async () => { it('should support "spy counter"', async () => {
let updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update')); const updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update'));
let resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset')); const resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset'));
let textEle = element(by.css('counter-parent app-counter > div')); const textEle = element(by.css('counter-parent app-counter > div'));
let logEles = element.all(by.css('counter-parent h4 ~ div')); const logEles = element.all(by.css('counter-parent h4 ~ div'));
expect(textEle.getText()).toContain('Counter = 0'); expect(textEle.getText()).toContain('Counter = 0');
expect(logEles.count()).toBe(2, 'should start with two log entries'); expect(logEles.count()).toBe(2, 'should start with two log entries');

View File

@ -67,8 +67,8 @@ export class AfterContentComponent implements AfterContentChecked, AfterContentI
} }
private logIt(method: string) { private logIt(method: string) {
let child = this.contentChild; const child = this.contentChild;
let message = `${method}: ${child ? child.hero : 'no'} child content`; const message = `${method}: ${child ? child.hero : 'no'} child content`;
this.logger.log(message); this.logger.log(message);
} }
// #docregion hooks // #docregion hooks

View File

@ -32,17 +32,20 @@ export class ChildViewComponent {
}) })
// #docregion hooks // #docregion hooks
export class AfterViewComponent implements AfterViewChecked, AfterViewInit { export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
// #enddocregion hooks
comment = '';
// #docregion hooks
private prevHero = ''; private prevHero = '';
// Query for a VIEW child of type `ChildViewComponent` // Query for a VIEW child of type `ChildViewComponent`
@ViewChild(ChildViewComponent) viewChild: ChildViewComponent; @ViewChild(ChildViewComponent) viewChild: ChildViewComponent;
// #enddocregion hooks // #enddocregion hooks
constructor(private logger: LoggerService) { constructor(private logger: LoggerService) {
this.logIt('AfterView constructor'); this.logIt('AfterView constructor');
} }
// #docregion hooks // #docregion hooks
ngAfterViewInit() { ngAfterViewInit() {
// viewChild is set after the view has been initialized // viewChild is set after the view has been initialized
this.logIt('AfterViewInit'); this.logIt('AfterViewInit');
@ -59,27 +62,25 @@ export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
this.doSomething(); this.doSomething();
} }
} }
// #enddocregion hooks // #enddocregion hooks
comment = ''; // #docregion do-something
// #docregion do-something
// This surrogate for real business logic sets the `comment` // This surrogate for real business logic sets the `comment`
private doSomething() { private doSomething() {
let c = this.viewChild.hero.length > 10 ? `That's a long name` : ''; const c = this.viewChild.hero.length > 10 ? `That's a long name` : '';
if (c !== this.comment) { if (c !== this.comment) {
// Wait a tick because the component's view has already been checked // Wait a tick because the component's view has already been checked
this.logger.tick_then(() => this.comment = c); this.logger.tick_then(() => this.comment = c);
} }
} }
// #enddocregion do-something // #enddocregion do-something
private logIt(method: string) { private logIt(method: string) {
let child = this.viewChild; const child = this.viewChild;
let message = `${method}: ${child ? child.hero : 'no'} child view`; const message = `${method}: ${child ? child.hero : 'no'} child view`;
this.logger.log(message); this.logger.log(message);
} }
// #docregion hooks // #docregion hooks
// ... // ...
} }
// #enddocregion hooks // #enddocregion hooks

View File

@ -13,7 +13,7 @@ import { LoggerService } from './logger.service';
Counter = {{counter}} Counter = {{counter}}
<h5>-- Counter Change Log --</h5> <h5>-- Counter Change Log --</h5>
<div *ngFor="let chg of changeLog" mySpy>{{chg}}</div> <div *ngFor="let chg of changeLog" appSpy>{{chg}}</div>
</div> </div>
`, `,
styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'] styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}']
@ -31,9 +31,9 @@ export class MyCounterComponent implements OnChanges {
} }
// A change to `counter` is the only change we care about // A change to `counter` is the only change we care about
let chng = changes['counter']; const chng = changes.counter;
let cur = chng.currentValue; const cur = chng.currentValue;
let prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer const prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer
this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`); this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`);
} }
} }

View File

@ -51,8 +51,8 @@ export class DoCheckComponent implements DoCheck {
this.noChangeCount = 0; this.noChangeCount = 0;
} else { } else {
// log that hook was called when there was no relevant change. // log that hook was called when there was no relevant change.
let count = this.noChangeCount += 1; const count = this.noChangeCount += 1;
let noChangeMsg = `DoCheck called ${count}x when no change to hero or power`; const noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
if (count === 1) { if (count === 1) {
// add new "no change" message // add new "no change" message
this.changeLog.push(noChangeMsg); this.changeLog.push(noChangeMsg);

View File

@ -34,10 +34,10 @@ export class OnChangesComponent implements OnChanges {
// #docregion ng-on-changes // #docregion ng-on-changes
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) { for (const propName in changes) {
let chng = changes[propName]; const chng = changes[propName];
let cur = JSON.stringify(chng.currentValue); const cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue); const prev = JSON.stringify(chng.previousValue);
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`); this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
} }
} }

View File

@ -1,3 +1,5 @@
// tslint:disable: no-conflicting-lifecycle
// #docregion
import { import {
AfterContentChecked, AfterContentChecked,
AfterContentInit, AfterContentInit,
@ -48,16 +50,16 @@ export class PeekABooComponent extends PeekABooDirective implements
constructor(logger: LoggerService) { constructor(logger: LoggerService) {
super(logger); super(logger);
let is = this.name ? 'is' : 'is not'; const is = this.name ? 'is' : 'is not';
this.logIt(`name ${is} known at construction`); this.logIt(`name ${is} known at construction`);
} }
// only called for/if there is an @input variable set by parent. // only called for/if there is an @input variable set by parent.
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
let changesMsgs: string[] = []; const changesMsgs: string[] = [];
for (let propName in changes) { for (const propName in changes) {
if (propName === 'name') { if (propName === 'name') {
let name = changes['name'].currentValue; const name = changes.name.currentValue;
changesMsgs.push(`name ${this.verb} to "${name}"`); changesMsgs.push(`name ${this.verb} to "${name}"`);
} else { } else {
changesMsgs.push(propName + ' ' + this.verb); changesMsgs.push(propName + ' ' + this.verb);

View File

@ -7,7 +7,7 @@
<p></p> <p></p>
<!-- #docregion template --> <!-- #docregion template -->
<div *ngFor="let hero of heroes" mySpy class="heroes"> <div *ngFor="let hero of heroes" appSpy class="heroes">
{{hero}} {{hero}}
</div> </div>
<!-- #enddocregion template --> <!-- #enddocregion template -->

View File

@ -7,8 +7,8 @@ let nextId = 1;
// #docregion spy-directive // #docregion spy-directive
// Spy on any element to which it is applied. // Spy on any element to which it is applied.
// Usage: <div mySpy>...</div> // Usage: <div appSpy>...</div>
@Directive({selector: '[mySpy]'}) @Directive({selector: '[appSpy]'})
export class SpyDirective implements OnInit, OnDestroy { export class SpyDirective implements OnInit, OnDestroy {
constructor(private logger: LoggerService) { } constructor(private logger: LoggerService) { }

View File

@ -1,8 +1,6 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('NgModule-example', function () { describe('NgModule-example', () => {
// helpers // helpers
const lightgray = 'rgba(239, 238, 237, 1)'; const lightgray = 'rgba(239, 238, 237, 1)';
@ -57,13 +55,13 @@ describe('NgModule-example', function () {
// tests // tests
function appTitleTests(color: string, name?: string) { function appTitleTests(color: string, name?: string) {
return function() { return () => {
it('should have a gray header', function() { it('should have a gray header', () => {
const commons = getCommonsSectionStruct(); const commons = getCommonsSectionStruct();
expect(commons.title.getCssValue('backgroundColor')).toBe(color); expect(commons.title.getCssValue('backgroundColor')).toBe(color);
}); });
it('should welcome us', function () { it('should welcome us', () => {
const commons = getCommonsSectionStruct(); const commons = getCommonsSectionStruct();
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple')); expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple'));
}); });
@ -71,39 +69,39 @@ describe('NgModule-example', function () {
} }
function contactTests(color: string, name?: string) { function contactTests(color: string, name?: string) {
return function() { return () => {
it('shows the contact\'s owner', function() { it('shows the contact\'s owner', () => {
const contacts = getContactSectionStruct(); const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts'); expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts');
}); });
it('can cycle between contacts', function () { it('can cycle between contacts', () => {
const contacts = getContactSectionStruct(); const contacts = getContactSectionStruct();
const nextButton = contacts.nextContactButton; const nextButton = contacts.nextContactButton;
expect(contacts.contactNameHeader.getText()).toBe('Awesome Yasha'); expect(contacts.contactNameHeader.getText()).toBe('Awesome Yasha');
expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color); expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color);
nextButton.click().then(function () { nextButton.click().then(() => {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Iulia'); expect(contacts.contactNameHeader.getText()).toBe('Awesome Iulia');
return nextButton.click(); return nextButton.click();
}).then(function () { }).then(() => {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Karina'); expect(contacts.contactNameHeader.getText()).toBe('Awesome Karina');
}); });
}); });
it('can create a new contact', function () { it('can create a new contact', () => {
const contacts = getContactSectionStruct(); const contacts = getContactSectionStruct();
const newContactButton = contacts.newContactButton; const newContactButton = contacts.newContactButton;
const nextButton = contacts.nextContactButton; const nextButton = contacts.nextContactButton;
const input = contacts.input; const input = contacts.input;
const saveButton = contacts.saveButton; const saveButton = contacts.saveButton;
newContactButton.click().then(function () { newContactButton.click().then(() => {
input.click(); input.click();
nextButton.click() nextButton.click();
expect(contacts.validationError.getText()).toBe('Name is required.'); expect(contacts.validationError.getText()).toBe('Name is required.');
input.click(); input.click();
contacts.input.sendKeys('Watson'); contacts.input.sendKeys('Watson');
saveButton.click() saveButton.click();
expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson'); expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson');
}); });
@ -111,8 +109,8 @@ describe('NgModule-example', function () {
}; };
} }
describe('index.html', function () { describe('index.html', () => {
beforeEach(function () { beforeEach(() => {
browser.get(''); browser.get('');
}); });
@ -120,36 +118,36 @@ describe('NgModule-example', function () {
describe('contact', contactTests(lightgray, 'Miss Marple')); describe('contact', contactTests(lightgray, 'Miss Marple'));
describe('item center', function () { describe('item center', () => {
beforeEach(function () { beforeEach(() => {
getCommonsSectionStruct().itemButton.click(); getCommonsSectionStruct().itemButton.click();
}); });
it('shows a list of items', function () { it('shows a list of items', () => {
const item = getItemSectionStruct(); const item = getItemSectionStruct();
expect(item.title.getText()).toBe('Items List'); expect(item.title.getText()).toBe('Items List');
expect(item.items.count()).toBe(4); expect(item.items.count()).toBe(4);
expect(item.items.get(0).getText()).toBe('1 - Sticky notes'); expect(item.items.get(0).getText()).toBe('1 - Sticky notes');
}); });
it('can navigate to one item details', function () { it('can navigate to one item details', () => {
const item = getItemSectionStruct(); const item = getItemSectionStruct();
item.items.get(0).click().then(function() { item.items.get(0).click().then(() => {
expect(item.itemId.getText()).toBe('Item id: 1'); expect(item.itemId.getText()).toBe('Item id: 1');
return item.listLink.click(); return item.listLink.click();
}).then(function () { }).then(() => {
// We are back to the list // We are back to the list
expect(item.items.count()).toBe(4); expect(item.items.count()).toBe(4);
}); });
}); });
}); });
describe('customers', function () { describe('customers', () => {
beforeEach(function () { beforeEach(() => {
getCommonsSectionStruct().customersButton.click(); getCommonsSectionStruct().customersButton.click();
}); });
it('shows a list of customers', function() { it('shows a list of customers', () => {
const customers = getCustomersSectionStruct(); const customers = getCustomersSectionStruct();
expect(customers.header.getText()).toBe('Customers of Miss Marple times 2'); expect(customers.header.getText()).toBe('Customers of Miss Marple times 2');
expect(customers.title.getText()).toBe('Customer List'); expect(customers.title.getText()).toBe('Customer List');
@ -157,13 +155,13 @@ describe('NgModule-example', function () {
expect(customers.items.get(0).getText()).toBe('11 - Julian'); expect(customers.items.get(0).getText()).toBe('11 - Julian');
}); });
it('can navigate and edit one customer details', function () { it('can navigate and edit one customer details', () => {
const customers = getCustomersSectionStruct(); const customers = getCustomersSectionStruct();
customers.items.get(0).click().then(function () { customers.items.get(0).click().then(() => {
expect(customers.itemId.getText()).toBe('Id: 11'); expect(customers.itemId.getText()).toBe('Id: 11');
customers.itemInput.sendKeys(' try'); customers.itemInput.sendKeys(' try');
return customers.listLink.click(); return customers.listLink.click();
}).then(function () { }).then(() => {
// We are back to the list // We are back to the list
expect(customers.items.count()).toBe(6); expect(customers.items.count()).toBe(6);
expect(customers.items.get(0).getText()).toBe('11 - Julian try'); expect(customers.items.get(0).getText()).toBe('11 - Julian try');

View File

@ -46,7 +46,7 @@ export class ContactComponent implements OnInit {
} }
onSubmit() { onSubmit() {
let newName = this.contactForm.get('name').value; const newName = this.contactForm.get('name').value;
this.displayMessage('Saved ' + newName); this.displayMessage('Saved ' + newName);
this.contact.name = newName; this.contact.name = newName;
} }

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