From b9733fdbb6f0351d78d25f7f3992b12b605d71e1 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Sat, 17 Sep 2016 08:00:14 -0700 Subject: [PATCH 01/42] chore(harp): use official pre-release 0.21 of harp (#2376) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 96040cee6b..71125dcd07 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "gulp-tslint": "^5.0.0", "gulp-util": "^3.0.6", "gulp-watch": "^4.3.4", - "harp": "git://github.com/filipesilva/harp.git#8da8d3497ddbfcbcbadd8be63e0fd731d7310cc4", + "harp": "0.21.0-pre.1", "html2jade": "^0.8.4", "indent-string": "^2.1.0", "jasmine-core": "^2.3.4", @@ -77,4 +77,4 @@ "jstransformer-marked": "^1.0.1" }, "homepage": "http://angular.io/" -} \ No newline at end of file +} From 84b5297cc2fb5c89ebc57c42faad8f5e8b8fc560 Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Sat, 17 Sep 2016 12:44:34 -0700 Subject: [PATCH 02/42] docs(testing): feedback from Joe Eames/add more scenarios (#2391) --- .../docs/_examples/testing/ts/app-specs.html | 2 +- .../testing/ts/app/app.component.spec.ts | 18 +- .../app/dashboard/dashboard.component.spec.ts | 8 +- .../ts/app/hero/hero-detail.component.html | 5 +- .../hero-detail.component.no-testbed.spec.ts | 6 +- .../ts/app/hero/hero-detail.component.spec.ts | 75 ++-- .../ts/app/hero/hero-detail.component.ts | 36 +- .../ts/app/hero/hero-list.component.spec.ts | 4 +- .../testing/ts/app/welcome.component.spec.ts | 19 +- .../testing/ts/testing/fake-router.ts | 49 --- .../_examples/testing/ts/testing/index.ts | 2 +- .../testing/ts/testing/router-stubs.ts | 65 ++++ public/docs/ts/latest/guide/testing.jade | 332 ++++++++++++------ .../testing/hero-detail.component.png | Bin 0 -> 2746 bytes 14 files changed, 394 insertions(+), 227 deletions(-) delete mode 100644 public/docs/_examples/testing/ts/testing/fake-router.ts create mode 100644 public/docs/_examples/testing/ts/testing/router-stubs.ts create mode 100644 public/resources/images/devguide/testing/hero-detail.component.png diff --git a/public/docs/_examples/testing/ts/app-specs.html b/public/docs/_examples/testing/ts/app-specs.html index c51f6cdedc..276659e26b 100644 --- a/public/docs/_examples/testing/ts/app-specs.html +++ b/public/docs/_examples/testing/ts/app-specs.html @@ -42,7 +42,7 @@ 'app/model/hero.spec', 'app/model/http-hero.service.spec', 'app/shared/title-case.pipe.spec', - 'app/twain.component.spec', + 'app/shared/twain.component.spec', 'app/welcome.component.spec' ]; diff --git a/public/docs/_examples/testing/ts/app/app.component.spec.ts b/public/docs/_examples/testing/ts/app/app.component.spec.ts index c9fd54535f..388e29a73e 100644 --- a/public/docs/_examples/testing/ts/app/app.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/app.component.spec.ts @@ -8,7 +8,7 @@ import { AppComponent } from './app.component'; import { BannerComponent } from './banner.component'; import { SharedModule } from './shared/shared.module'; -import { Router, FakeRouter, FakeRouterLinkDirective, FakeRouterOutletComponent +import { Router, RouterStub, RouterLinkDirectiveStub, RouterOutletStubComponent } from '../testing'; @@ -20,9 +20,9 @@ describe('AppComponent & TestModule', () => { TestBed.configureTestingModule({ declarations: [ AppComponent, BannerComponent, - FakeRouterLinkDirective, FakeRouterOutletComponent + RouterLinkDirectiveStub, RouterOutletStubComponent ], - providers: [{ provide: Router, useClass: FakeRouter }], + providers: [{ provide: Router, useClass: RouterStub }], schemas: [NO_ERRORS_SCHEMA] }) @@ -49,9 +49,9 @@ function tests() { const links = fixture.debugElement // find all elements with an attached FakeRouterLink directive - .queryAll(By.directive(FakeRouterLinkDirective)) + .queryAll(By.directive(RouterLinkDirectiveStub)) // use injector to get the RouterLink directive instance attached to each element - .map(de => de.injector.get(FakeRouterLinkDirective) as FakeRouterLinkDirective); + .map(de => de.injector.get(RouterLinkDirectiveStub) as RouterLinkDirectiveStub); expect(links.length).toBe(3, 'should have 3 links'); expect(links[0].linkParams).toBe('/dashboard', '1st link should go to Dashboard'); @@ -63,11 +63,11 @@ function tests() { // Heroes RouterLink DebugElement const heroesLinkDe = fixture.debugElement - .queryAll(By.directive(FakeRouterLinkDirective))[1]; + .queryAll(By.directive(RouterLinkDirectiveStub))[1]; expect(heroesLinkDe).toBeDefined('should have a 2nd RouterLink'); - const link = heroesLinkDe.injector.get(FakeRouterLinkDirective) as FakeRouterLinkDirective; + const link = heroesLinkDe.injector.get(RouterLinkDirectiveStub) as RouterLinkDirectiveStub; expect(link.navigatedTo).toBeNull('link should not have navigate yet'); @@ -101,8 +101,8 @@ describe('AppComponent & AppModule', () => { // Separate override because cannot both `set` and `add/remove` in same override .overrideModule(AppModule, { add: { - declarations: [ FakeRouterLinkDirective, FakeRouterOutletComponent ], - providers: [{ provide: Router, useClass: FakeRouter }] + declarations: [ RouterLinkDirectiveStub, RouterOutletStubComponent ], + providers: [{ provide: Router, useClass: RouterStub }] } }) diff --git a/public/docs/_examples/testing/ts/app/dashboard/dashboard.component.spec.ts b/public/docs/_examples/testing/ts/app/dashboard/dashboard.component.spec.ts index 981e51db0f..7159bb820b 100644 --- a/public/docs/_examples/testing/ts/app/dashboard/dashboard.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/dashboard/dashboard.component.spec.ts @@ -12,11 +12,11 @@ import { Router } from '@angular/router'; import { DashboardComponent } from './dashboard.component'; import { DashboardModule } from './dashboard.module'; -// #docregion fake-router -class FakeRouter { +// #docregion router-stub +class RouterStub { navigateByUrl(url: string) { return url; } } -// #enddocregion fake-router +// #enddocregion router-stub beforeEach ( addMatchers ); @@ -73,7 +73,7 @@ function compileAndCreate() { TestBed.configureTestingModule({ providers: [ { provide: HeroService, useClass: FakeHeroService }, - { provide: Router, useClass: FakeRouter } + { provide: Router, useClass: RouterStub } ] }) .compileComponents().then(() => { diff --git a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.html b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.html index 6927fc83ad..7e86a668f6 100644 --- a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.html +++ b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.html @@ -1,10 +1,11 @@ +

{{hero.name | titlecase}} Details

{{hero.id}}
- - + +
diff --git a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.no-testbed.spec.ts b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.no-testbed.spec.ts index 73c22f29e7..a6c1af98d7 100644 --- a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.no-testbed.spec.ts +++ b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.no-testbed.spec.ts @@ -1,12 +1,12 @@ import { HeroDetailComponent } from './hero-detail.component'; import { Hero } from '../model'; -import { FakeActivatedRoute } from '../../testing'; +import { ActivatedRouteStub } from '../../testing'; ////////// Tests //////////////////// describe('HeroDetailComponent - no TestBed', () => { - let activatedRoute: FakeActivatedRoute; + let activatedRoute: ActivatedRouteStub; let comp: HeroDetailComponent; let expectedHero: Hero; let hds: any; @@ -14,7 +14,7 @@ describe('HeroDetailComponent - no TestBed', () => { beforeEach( done => { expectedHero = new Hero(42, 'Bubba'); - activatedRoute = new FakeActivatedRoute(); + activatedRoute = new ActivatedRouteStub(); activatedRoute.testParams = { id: expectedHero.id }; router = jasmine.createSpyObj('router', ['navigate']); diff --git a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.spec.ts b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.spec.ts index 0dd52ed54e..6c914a5011 100644 --- a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.spec.ts @@ -1,3 +1,4 @@ +// #docplaster import { async, ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; @@ -7,7 +8,7 @@ import { DebugElement } from '@angular/core'; import { addMatchers, newEvent, - ActivatedRoute, FakeActivatedRoute, Router, FakeRouter + ActivatedRoute, ActivatedRouteStub, Router, RouterStub } from '../../testing'; import { HEROES, FakeHeroService } from '../model/testing'; @@ -18,7 +19,7 @@ import { HeroDetailService } from './hero-detail.service'; import { Hero, HeroService } from '../model'; ////// Testing Vars ////// -let activatedRoute: FakeActivatedRoute; +let activatedRoute: ActivatedRouteStub; let comp: HeroDetailComponent; let fixture: ComponentFixture; let page: Page; @@ -29,7 +30,7 @@ describe('HeroDetailComponent', () => { beforeEach( async(() => { addMatchers(); - activatedRoute = new FakeActivatedRoute(); + activatedRoute = new ActivatedRouteStub(); TestBed.configureTestingModule({ imports: [ HeroModule ], @@ -40,12 +41,13 @@ describe('HeroDetailComponent', () => { providers: [ { provide: ActivatedRoute, useValue: activatedRoute }, { provide: HeroService, useClass: FakeHeroService }, - { provide: Router, useClass: FakeRouter}, + { provide: Router, useClass: RouterStub}, ] }) .compileComponents(); })); + // #docregion route-good-id describe('when navigate to hero id=' + HEROES[0].id, () => { let expectedHero: Hero; @@ -55,51 +57,53 @@ describe('HeroDetailComponent', () => { createComponent(); })); + // #docregion selected-tests it('should display that hero\'s name', () => { expect(page.nameDisplay.textContent).toBe(expectedHero.name); }); + // #enddocregion route-good-id it('should navigate when click cancel', () => { page.cancelBtn.triggerEventHandler('click', null); expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called'); }); - it('should save when click save', () => { + it('should save when click save but not navigate immediately', () => { page.saveBtn.triggerEventHandler('click', null); expect(page.saveSpy.calls.any()).toBe(true, 'HeroDetailService.save called'); + expect(page.navSpy.calls.any()).toBe(false, 'router.navigate not called'); }); - it('should navigate when click click save resolves', fakeAsync(() => { + it('should navigate when click save and save resolves', fakeAsync(() => { page.saveBtn.triggerEventHandler('click', null); - tick(); // waits for async save to "complete" before navigating + tick(); // wait for async save to "complete" before navigating expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called'); })); - // #docregion title-case-pipe - it('should convert original hero name to Title Case', () => { - expect(page.nameDisplay.textContent).toBe(comp.hero.name); - }); - // #enddocregion title-case-pipe - it('should convert hero name to Title Case', fakeAsync(() => { const inputName = 'quick BROWN fox'; - const expectedName = 'Quick Brown Fox'; + const titleCaseName = 'Quick Brown Fox'; - // simulate user entering new name in input + // simulate user entering new name into the input box page.nameInput.value = inputName; // dispatch a DOM event so that Angular learns of input value change. - // detectChanges() makes ngModel push input value to component property - // and Angular updates the output span page.nameInput.dispatchEvent(newEvent('input')); + + // detectChanges() makes [(ngModel)] push input value to component property + // and Angular updates the output span through the title pipe fixture.detectChanges(); - expect(page.nameDisplay.textContent).toBe(expectedName, 'hero name display'); - expect(comp.hero.name).toBe(inputName, 'comp.hero.name'); + + expect(page.nameDisplay.textContent).toBe(titleCaseName); })); - + // #enddocregion title-case-pipe + // #enddocregion selected-tests + // #docregion route-good-id }); + // #enddocregion route-good-id + // #docregion route-no-id describe('when navigate with no hero id', () => { beforeEach( async( createComponent )); @@ -111,7 +115,9 @@ describe('HeroDetailComponent', () => { expect(page.nameDisplay.textContent).toBe(''); }); }); + // #enddocregion route-no-id + // #docregion route-bad-id describe('when navigate to non-existant hero id', () => { beforeEach( async(() => { activatedRoute.testParams = { id: 99999 }; @@ -123,6 +129,7 @@ describe('HeroDetailComponent', () => { expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called'); }); }); + // #enddocregion route-bad-id /////////////////////////// @@ -145,22 +152,24 @@ describe('HeroDetailComponent', () => { /////////// Helpers ///// +// #docregion create-component /** Create the HeroDetailComponent, initialize it, set test variables */ function createComponent() { fixture = TestBed.createComponent(HeroDetailComponent); comp = fixture.componentInstance; page = new Page(); - // change detection triggers ngOnInit which gets a hero + // 1st change detection triggers ngOnInit which gets a hero fixture.detectChanges(); return fixture.whenStable().then(() => { - // got the hero and updated component - // change detection updates the view + // 2nd change detection displays the async-fetched hero fixture.detectChanges(); page.addPageElements(); }); } +// #enddocregion create-component +// #docregion page class Page { gotoSpy: jasmine.Spy; navSpy: jasmine.Spy; @@ -173,19 +182,19 @@ class Page { constructor() { // Use component's injector to see the services it injected. - let compInjector = fixture.debugElement.injector; - let hds = compInjector.get(HeroDetailService); - let router = compInjector.get(Router); - this.gotoSpy = spyOn(comp, 'gotoList').and.callThrough(); - this.saveSpy = spyOn(hds, 'saveHero').and.callThrough(); - this.navSpy = spyOn(router, 'navigate').and.callThrough(); + const compInjector = fixture.debugElement.injector; + const hds = compInjector.get(HeroDetailService); + const router = compInjector.get(Router); + this.gotoSpy = spyOn(comp, 'gotoList').and.callThrough(); + this.saveSpy = spyOn(hds, 'saveHero').and.callThrough(); + this.navSpy = spyOn(router, 'navigate').and.callThrough(); } - /** Add page elements after page initializes */ + /** Add page elements after hero arrives */ addPageElements() { if (comp.hero) { - // have a hero so these DOM elements can be reached - let buttons = fixture.debugElement.queryAll(By.css('button')); + // have a hero so these elements are now in the DOM + const buttons = fixture.debugElement.queryAll(By.css('button')); this.saveBtn = buttons[0]; this.cancelBtn = buttons[1]; this.nameDisplay = fixture.debugElement.query(By.css('span')).nativeElement; @@ -193,4 +202,4 @@ class Page { } } } - +// #enddocregion page diff --git a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.ts b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.ts index 9350c369af..3b5555c481 100644 --- a/public/docs/_examples/testing/ts/app/hero/hero-detail.component.ts +++ b/public/docs/_examples/testing/ts/app/hero/hero-detail.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; +import 'rxjs/add/operator/pluck'; import { Hero } from '../model'; import { HeroDetailService } from './hero-detail.service'; @@ -16,31 +17,34 @@ import { HeroDetailService } from './hero-detail.service'; export class HeroDetailComponent implements OnInit { @Input() hero: Hero; + // #docregion ctor constructor( private heroDetailService: HeroDetailService, private route: ActivatedRoute, private router: Router) { } + // #enddocregion ctor - ngOnInit() { - let id = this.route.snapshot.params['id']; + // #docregion ng-on-init + ngOnInit(): void { + // get hero when `id` param changes + this.route.params.pluck('id') + .forEach(id => this.getHero(id)) + .catch(() => this.hero = new Hero()); // no id; should edit new hero + } + // #enddocregion ng-on-init - // tslint:disable-next-line:triple-equals - if (id == undefined) { - // no id; act as if is new - this.hero = new Hero(); - } else { - this.heroDetailService.getHero(id).then(hero => { - if (hero) { - this.hero = hero; - } else { - this.gotoList(); // id not found; navigate to list - } - }); - } + private getHero(id: string): void { + this.heroDetailService.getHero(id).then(hero => { + if (hero) { + this.hero = hero; + } else { + this.gotoList(); // id not found; navigate to list + } + }); } - save() { + save(): void { this.heroDetailService.saveHero(this.hero).then(() => this.gotoList()); } diff --git a/public/docs/_examples/testing/ts/app/hero/hero-list.component.spec.ts b/public/docs/_examples/testing/ts/app/hero/hero-list.component.spec.ts index f997cf787e..ea08cd499c 100644 --- a/public/docs/_examples/testing/ts/app/hero/hero-list.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/hero/hero-list.component.spec.ts @@ -4,7 +4,7 @@ import { async, ComponentFixture, fakeAsync, TestBed, tick import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; -import { addMatchers, newEvent, Router, FakeRouter +import { addMatchers, newEvent, Router, RouterStub } from '../../testing'; import { HEROES, FakeHeroService } from '../model/testing'; @@ -28,7 +28,7 @@ describe('HeroListComponent', () => { imports: [HeroModule], providers: [ { provide: HeroService, useClass: FakeHeroService }, - { provide: Router, useClass: FakeRouter} + { provide: Router, useClass: RouterStub} ] }) .compileComponents() diff --git a/public/docs/_examples/testing/ts/app/welcome.component.spec.ts b/public/docs/_examples/testing/ts/app/welcome.component.spec.ts index ec59ef5bc2..f99687148f 100644 --- a/public/docs/_examples/testing/ts/app/welcome.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/welcome.component.spec.ts @@ -13,15 +13,20 @@ describe('WelcomeComponent', () => { let userService: UserService; // the actually injected service let welcomeEl: DebugElement; // the element with the welcome message + let userServiceStub: { + isLoggedIn: boolean; + user: { name: string} + }; + // #docregion setup beforeEach(() => { - // fake UserService for test purposes - // #docregion fake-userservice - const fakeUserService = { + // stub UserService for test purposes + // #docregion user-service-stub + userServiceStub = { isLoggedIn: true, user: { name: 'Test User'} }; - // #enddocregion fake-userservice + // #enddocregion user-service-stub // #docregion config-test-module TestBed.configureTestingModule({ @@ -29,7 +34,7 @@ describe('WelcomeComponent', () => { // #enddocregion setup // providers: [ UserService ] // a real service would be a problem! // #docregion setup - providers: [ {provide: UserService, useValue: fakeUserService } ] + providers: [ {provide: UserService, useValue: userServiceStub } ] }); // #enddocregion config-test-module @@ -80,4 +85,8 @@ describe('WelcomeComponent', () => { expect(content).toMatch(/log in/i, '"log in"'); }); // #enddocregion tests + + it('orig stub and injected UserService are not the same object', () => { + expect(userServiceStub === userService).toBe(false); + }); }); diff --git a/public/docs/_examples/testing/ts/testing/fake-router.ts b/public/docs/_examples/testing/ts/testing/fake-router.ts deleted file mode 100644 index d42a3f8ad9..0000000000 --- a/public/docs/_examples/testing/ts/testing/fake-router.ts +++ /dev/null @@ -1,49 +0,0 @@ - // export for convenience. -export { ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router'; - -import { Component, Directive, Injectable, Input } from '@angular/core'; -import { NavigationExtras } from '@angular/router'; - -@Directive({ - selector: '[routerLink]', - host: { - '(click)': 'onClick()', - '[attr.href]': 'visibleHref', - '[class.router-link-active]': 'isRouteActive' - } -}) -export class FakeRouterLinkDirective { - - isRouteActive = false; - visibleHref: string; // the url displayed on the anchor element. - - @Input('routerLink') linkParams: any; - navigatedTo: any = null; - - onClick() { - this.navigatedTo = this.linkParams; - } -} - -@Component({selector: 'router-outlet', template: ''}) -export class FakeRouterOutletComponent { } - -@Injectable() -export class FakeRouter { - lastCommand: any[]; - navigate(commands: any[], extras?: NavigationExtras) { - this.lastCommand = commands; - return commands; - } -} - -@Injectable() -export class FakeActivatedRoute { - testParams: {} = {}; - - get snapshot() { - return { - params: this.testParams - }; - } -} diff --git a/public/docs/_examples/testing/ts/testing/index.ts b/public/docs/_examples/testing/ts/testing/index.ts index f648a212e9..907b968c0c 100644 --- a/public/docs/_examples/testing/ts/testing/index.ts +++ b/public/docs/_examples/testing/ts/testing/index.ts @@ -1,7 +1,7 @@ import { tick, ComponentFixture } from '@angular/core/testing'; export * from './jasmine-matchers'; -export * from './fake-router'; +export * from './router-stubs'; // Short utilities /** diff --git a/public/docs/_examples/testing/ts/testing/router-stubs.ts b/public/docs/_examples/testing/ts/testing/router-stubs.ts new file mode 100644 index 0000000000..83a84fcb3a --- /dev/null +++ b/public/docs/_examples/testing/ts/testing/router-stubs.ts @@ -0,0 +1,65 @@ + // export for convenience. +export { ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router'; + +import { Component, Directive, Injectable, Input } from '@angular/core'; +import { NavigationExtras } from '@angular/router'; + +@Directive({ + selector: '[routerLink]', + host: { + '(click)': 'onClick()', + '[attr.href]': 'href', + '[class.router-link-active]': 'isRouteActive' + } +}) +export class RouterLinkDirectiveStub { + + isRouteActive = false; + href: string; // the url displayed on the anchor element. + + @Input('routerLink') linkParams: any; + navigatedTo: any = null; + + onClick() { + this.navigatedTo = this.linkParams; + } +} + +@Component({selector: 'router-outlet', template: ''}) +export class RouterOutletStubComponent { } + +@Injectable() +export class RouterStub { + lastCommand: any[]; + navigate(commands: any[], extras?: NavigationExtras) { + this.lastCommand = commands; + return commands; + } +} + + +// Only implements params and part of snapshot.params +// #docregion activated-route-stub +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class ActivatedRouteStub { + + // ActivatedRoute.params is Observable + private subject = new BehaviorSubject(this.testParams); + params = this.subject.asObservable(); + + // Test parameters + private _testParams: {}; + get testParams() { return this._testParams; } + set testParams(params: {}) { + this._testParams = params; + this.subject.next(params); + } + + // ActivatedRoute.snapshot.params + get snapshot() { + return { params: this.testParams }; + } +} +// #enddocregion activated-route-stub diff --git a/public/docs/ts/latest/guide/testing.jade b/public/docs/ts/latest/guide/testing.jade index d52179b9bd..51ba85876f 100644 --- a/public/docs/ts/latest/guide/testing.jade +++ b/public/docs/ts/latest/guide/testing.jade @@ -26,7 +26,9 @@ a#top * [Test a component with an external template](#component-with-external-template) * [Test a component with inputs and outputs](#component-with-inputs-output) * [Test a component inside a test host component](#component-inside-test-host) - * [Test a routed component](#routed-component) + * [Test a routed component](#routed-component) + * [Test a routed component with parameters](#routed-component-w-param) + * [Use a _page_ object to simplify setup](#page-object) * [Isolated tests](#testing-without-atp "Testing without the Angular Testing Platform") * [_TestBed_ API](#atp-api) * [FAQ](#faq "Frequently asked questions") @@ -177,9 +179,9 @@ table(width="100%") td :marked [SystemJS](https://github.com/systemjs/systemjs/blob/master/README.md) - loads the application and test modules. - This script tells SystemJS where to find the module files and how to load them. - It's the same version of the file used by QuickStart-based applications. + loads the application and test files. + This script tells SystemJS where to find those files and how to load them. + It's the same version of `systemjs.config.js` used by QuickStart-based applications. tr td(style="vertical-align: top") systemjs.config.extras.js td @@ -344,7 +346,7 @@ a#atp-intro for components, directives, pipes, and services. Isolated unit tests examine an instance of a class all by itself without any dependence on Angular or any injected values. - The tester creates a test instance of the class with new, supplying fake constructor parameters as needed, and + The tester creates a test instance of the class with new, supplying test doubles for the constructor parameters as needed, and then probes the test instance API surface. Isolated tests don't reveal how the class interacts with Angular. @@ -355,12 +357,8 @@ a#atp-intro ### Testing with the _ Angular Testing Platform_ The _Angular Testing Platform_ consists of the `TestBed` class and some helper functions from `@angular/core/testing`. -.alert.is-important - :marked - The _TestBed_ is officially _experimental_ and thus subject to change. - Consult the [API reference](../api/core/testing/index/TestBed-class.html) for the latest status. -:marked - The `TestBed` creates an Angular test module — an `@NgModule` class — + + The `TestBed` creates an Angular testing module — an `@NgModule` class — that you configure to produce the module environment for the class you want to test. You tell the `TestBed` to create an instance of the test component and probe that instance with tests. @@ -372,19 +370,20 @@ a#atp-intro :marked You can access that hidden instance anytime by calling `getTestBed()`; :marked - This `TestBed` instance comes pre-configured with a baseline of default providers and declarables (components, directives, and pipes) + Thanks to initialization in the [testing shims](#setup), + the default `TestBed` instance is pre-configured with a baseline of default providers and declarables (components, directives, and pipes) that almost everyone needs. - This chapter tests a browser application so the default includes the `CommonModule` declarables from `@angular/common` + + The shims in this chapter are designed for testing a browser application so the default configuration includes the `CommonModule` declarables from `@angular/common` and the `BrowserModule` providers (some of them mocked) from `@angular/platform-browser`. - You refine the default test module configuration with application and test specifics - so that it can produce an instance of the test component in the Angular environment suitable for your tests. - - Start by calling `TestBed.configureTestingModule` with an object that looks like `@NgModule` metadata. - This object defines additional imports, declarations, providers and schemas. + This default testing module configuration is a _foundation_ for testing _any_ browser app. + You call `TestBed.configureTestingModule` with an object that defines additional imports, declarations, providers and schemas + to reshape the testing module to fit your application tests. + Optional `override...` methods can fine-tune aspects of the configuration. After configuring the `TestBed`, tell it to create an instance of the test component and the test fixture - you'll need to inspect and control the component's immediate environment. + that you'll need to inspect and control the component's immediate environment. +makeExample('testing/ts/app/banner.component.spec.ts', 'simple-example-before-each', 'app/banner.component.spec.ts (simplified)')(format='.') :marked @@ -393,7 +392,7 @@ a#atp-intro and see the effects of these actions both in the test component and in the test DOM. +makeExample('testing/ts/app/banner.component.spec.ts', 'simple-example-it', 'app/banner.component.spec.ts (simplified)')(format='.') :marked - A comprehensive review of the _TestBed_ API appears [later in the chapter](#atp-api). + A comprehensive review of the _Angular Testing Platform_ APIs appears [later in the chapter](#atp-api). Let's dive right into Angular testing, starting with with the components of a sample application. a(href="#top").to-top Back to top @@ -415,7 +414,7 @@ a#sample-app It includes the tests discussed in this chapter and additional tests for you to explore. This live example contains both application and test code. - It is large and can take several minutes to start. Please be patient. + It is large and can take up to a minute to start. Please be patient. a(href="#top").to-top Back to top @@ -444,7 +443,7 @@ a#simple-component-test `TestBed.configureTestingModule` takes an `@NgModule`-like metadata object. This one simply declares the component to test, `BannerComponent`. - It lacks `imports` because (a) it extends the default test module configuration which + It lacks `imports` because (a) it extends the default testing module configuration which already has what `BannerComponent` needs and (b) `BannerComponent` doesn't interact with any other components. @@ -539,18 +538,18 @@ a#component-with-dependency :marked The `WelcomeComponent` has decision logic that interacts with the service; such logic makes this component worth testing. - Here's the test module configuration for the spec file, `app/welcome.component.spec.ts`: + Here's the testing module configuration for the spec file, `app/welcome.component.spec.ts`: +makeExample('testing/ts/app/welcome.component.spec.ts', 'config-test-module', 'app/welcome.component.spec.ts')(format='.') :marked This time, in addition to declaring the component under test, the configurations sets the `providers` list with the dependent `UserService`. - This example configures the test module with a _fake_ `UserService`. + This example configures the testing module with a stub `UserService`. - ## Provide service fakes + ## Provide service test doubles A component under test doesn't have to be injected with real services. - In fact, it is usually better if they are fakes. + In fact, it is usually better if they are test doubles (stubs, fakes, spies, or mocks). The purpose of the spec is to test the component, not the service, and real services can be trouble. @@ -558,22 +557,21 @@ a#component-with-dependency The real service might try to ask the user for login credentials and try to reach an authentication server. These behaviors could be hard to intercept. - It is far easier to create and register a fake `UserService`. + It is far easier to create and register a test double in place of the real `UserService`. - There are many ways to fake a service. - This test suit supplies a minimal `UserService` that satisfies the needs of the `WelcomeComponent` + This particular test suite supplies a minimal `UserService` stub that satisfies the needs of the `WelcomeComponent` and its tests: -+makeExample('testing/ts/app/welcome.component.spec.ts', 'fake-userservice')(format='.') ++makeExample('testing/ts/app/welcome.component.spec.ts', 'user-service-stub')(format='.') a#injected-service-reference :marked ## Referencing injected services - The tests need access to the injected (fake) `UserService`. + The tests need access to the injected (stubbed) `UserService`. - You cannot reference the `fakeUserService` object provided to the test module. + You cannot reference the `userServiceStub` object provided to the testing module. **It does not work!** - Surprisingly, the instance actually injected into the component is _not the same_ - as the provided `fakeUserService` object. + Surprisingly, the instance actually injected into the component is _not the same_ object + as the provided `userServiceStub`. .alert.is-important :marked @@ -609,7 +607,7 @@ a#welcome-spec-setup And here are some tests: +makeExample('testing/ts/app/welcome.component.spec.ts', 'tests', 'app/welcome.component.spec.ts')(format='.') :marked - The first is a sanity test; it confirms that the fake `UserService` is working. + The first is a sanity test; it confirms that the stubbed `UserService` is called and working. The remaining tests confirm the logic of the component when the service returns different values. The second test validates the effect of changing the user name. The third test checks that the component displays the proper message when there is no logged-in user. @@ -634,7 +632,7 @@ a#component-with-async-service It is sufficient to see within `ngOnInit` that `twainService.getQuote` returns a promise which means it is asynchronous. In general, tests should not make calls to remote servers. - They should fake such calls. The setup in this `app/shared/twain.component.spec.ts` shows one way to do that: + They should emulate such calls. The setup in this `app/shared/twain.component.spec.ts` shows one way to do that: +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'setup', 'app/shared/twain.component.spec.ts (setup)')(format='.') a#service-spy @@ -642,7 +640,7 @@ a#service-spy ### Spying on the real service This setup is similar to the [`welcome.component.spec` setup](#welcome-spec-setup). - But instead of creating a fake service object, it injects the _real_ service (see the test module `providers`) and + But instead of creating a stubbed service object, it injects the _real_ service (see the testing module `providers`) and replaces the critical `getQuote` method with a Jasmine spy. +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'spy')(format='.') :marked @@ -669,24 +667,27 @@ a#service-spy The test must become an "async test" ... like the third test -a#async-fn-in-it +a#async :marked ## The _async_ function in _it_ Notice the `async` in the third test. +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'async-test', 'app/shared/twain.component.spec.ts (async test)')(format='.') :marked - The `async` function is part of the _Angular TestBed_ feature set. - It _takes_ a parameterless function and _returns_ a parameterless function + The `async` function is an independent feature of the _Angular Testing Platform_. + + It simplifyies coding of asynchronous tests by arranging for the tester's code to run in a special _async test zone_. + + The `async` function _takes_ a parameterless function and _returns_ a parameterless function which becomes the argument to the Jasmine `it` call. The body of the `async` argument looks much like the body of a normal `it` argument. - There is nothing obviously asynchronous about it. For example, it doesn't return a promise. + There is nothing obviously asynchronous about it. + For example, it doesn't return a promise and + there is no `done` function to call as there is in standard Jasmine asynchronous tests. - The `async` function arranges for the tester's code to run in a special _async test zone_ - that almost hides the mechanics of asynchronous execution. - - Almost but not completely. + Some functions called within a test (such as `fixture.whenStable`) continue to reveal their asynchronous behavior. + Consider also the [_fakeAsync_](#fake-async) alternative which affords a more linear coding experience. a#when-stable :marked @@ -720,20 +721,18 @@ a#fake-async +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'fake-async-test', 'app/shared/twain.component.spec.ts (fakeAsync test)')(format='.') :marked Notice that `fakeAsync` replaces `async` as the `it` argument. - The `fakeAsync` function is also part of the _Angular TestBed_ feature set. - Like `async`, it too _takes_ a parameterless function and _returns_ a parameterless function + The `fakeAsync` function is another, independent feature of the _Angular Testing Platform_. + + Like [async](#), it _takes_ a parameterless function and _returns_ a parameterless function which becomes the argument to the Jasmine `it` call. - The `async` function arranges for the tester's code to run in a special _fakeAsync test zone_. + The `fakeAsync` function enables a linear coding style by running the test body in a special _fakeAsync test zone_. - The key advantage of `fakeAsync` is that the test body looks entirely synchronous. + The principle advantage of `fakeAsync` over `async` is that the test appears to be synchronous. There are no promises at all. No `then(...)` chains to disrupt the visible flow of control. -.l-sub-section - :marked - There are limitations. For example, you cannot make an XHR call from within a `fakeAsync`. -:marked + There are limitations. For example, you cannot make an XHR call from within a `fakeAsync`. a#tick a#tick-first-look @@ -741,7 +740,7 @@ a#tick-first-look ## The _tick_ function Compare the third and fourth tests. Notice that `fixture.whenStable` is gone, replaced by `tick()`. - The `tick` function is a part of the _Angular TestBed_ feature set and a companion to `fakeAsync`. + The `tick` function is a part of the _Angular Testing Platform_ and a companion to `fakeAsync`. It can only be called within a `fakeAsync` body. Calling `tick()` simulates the passage of time until all pending asynchronous activities complete, @@ -796,7 +795,7 @@ a#component-with-external-template The compiler must read these files from a file system before it can create a component instance. The `TestBed.compileComponents` method asynchronously compiles all the components configured in its - current test module. After it completes, external templates and css files, have been "inlined" + current testing module. After it completes, external templates and css files, have been "inlined" and `TestBed.createComponent` can do its job synchronously. .l-sub-section :marked @@ -810,23 +809,15 @@ a#async-fn-in-before-each :marked ## The _async_ function in _beforeEach_ - Notice the `async` call in the `beforeEach`. - - The `async` function is part of the _Angular TestBed_ feature set. - It _takes_ a parameterless function and _returns_ a parameterless function - which becomes the argument to the Jasmine `beforeEach` call. - - The body of the `async` argument looks much like the body of a normal `beforEach` argument. - There is nothing obviously asynchronous about it. For example, it doesn't return a promise. - + Notice the `async` call in the `beforeEach`. The `async` function arranges for the tester's code to run in a special _async test zone_ - that hides the mechanics of asynchronous execution. + that hides the mechanics of asynchronous execution, just as it does when passed to an [_it_ test)(#async). a#compile-components :marked ## _compileComponents_ In this example, `Testbed.compileComponents` compiles one component, the `DashboardComponent`. - It's the only declared component in this test module. + It's the only declared component in this testing module. Tests later in this chapter have more declared components and some of them import application modules that declare yet more components. @@ -884,8 +875,12 @@ a#component-with-inputs-outputs +makeExample('testing/ts/app/dashboard/dashboard.component.ts', 'ctor', 'app/dashboard/dashboard.component.ts (constructor)')(format='.') :marked The `DashboardComponent` depends upon the Angular router and the `HeroService`. - You'd probably have to fake them both and that's a lot of work. The router is particularly challenging (see below). - + You'd probably have to replace them both with test doubles and that looks like a lot of work. + The router seems particularly challenging. +.l-sub-section + :marked + The [discussion below](#routed-component) covers testing components that requre the router. +:marked The immediate goal is to test the `DashboardHeroComponent`, not the `DashboardComponent`, and there's no need to work hard unnecessarily. Let's try the second and third options. @@ -957,7 +952,7 @@ a#component-inside-test-host The setup for the test-host tests is similar to the setup for the stand-alone tests: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'test-host-setup', 'app/dashboard/dashboard-hero.component.spec.ts (test host setup)')(format='.') :marked - This test module configuration shows two important differences: + This testing module configuration shows two important differences: 1. It _declares_ both the `DashboardHeroComponent` and the `TestHostComponent`. 1. It _creates_ the `TestHostComponent` instead of the `DashboardHeroComponent`. @@ -994,10 +989,10 @@ a#routed-component This is often the case. As a rule you test the component, not the router, and care only if the component navigates with the right address under the given conditions. - Faking the router is an easy option. This should do the trick: -+makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'fake-router', 'app/dashboard/dashboard.component.spec.ts (fakeRouter)')(format='.') + Stubbing the router with a test implementation is an easy option. This should do the trick: ++makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'router-stub', 'app/dashboard/dashboard.component.spec.ts (Router Stub)')(format='.') :marked - Now we setup the test module with the `fakeRouter` and a fake `HeroService` and + Now we setup the testing module with test stubs for the `Router` and `HeroService` and create a test instance of the `DashbaordComponent` for subsequent testing. +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'compile-and-create-body', 'app/dashboard/dashboard.component.spec.ts (compile and create)')(format='.') :marked @@ -1011,7 +1006,7 @@ a#inject Notice the `inject` function in the second `it` argument. +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'inject')(format='.') :marked - The `inject` function is part of the _Angular TestBed_ feature set. + The `inject` function is an independent feature of the _Angular Testing Platform_. It injects services into the test function where you can alter, spy on, and manipulate them. The `inject` function has two parameters @@ -1046,6 +1041,138 @@ a(href="#top").to-top Back to top .l-hr +a#routed-component-w-param +:marked + # Test a routed component with parameters + + Clicking a _Dashboard_ hero triggers navigation to `heroes/:id` where `:id` + is a route parameter whose value is the `id` of the hero to edit. + That URL matches a route to the `HeroDetailComponent`. + + The router pushes the `:id` token value into the `ActivatedRoute.params` _Observable_ property, + Angular injects the `ActivatedRoute` into the `HeroDetailComponent`, + and the component extracts the `id` so it can fetch the corresponding hero via the `HeroDetailService`. + Here's the `HeroDetailComponent` constructor: ++makeExample('testing/ts/app/hero/hero-detail.component.ts', 'ctor', 'app/hero/hero-detail.component.ts (constructor)')(format='.') +:marked + `HeroDetailComponent` listens for changes to the `ActivatedRoute.params` in its `ngOnInit` method. ++makeExample('testing/ts/app/hero/hero-detail.component.ts', 'ng-on-init', 'app/hero/hero-detail.component.ts (ngOnInit)')(format='.') +.l-sub-section + :marked + The expression after `route.params` chains an _Observable_ operator that _plucks_ the `id` from the `params` + and then chains a `forEach` operator to subscribes to `id`-changing events. + The `id` changes every time the user navigates to a different hero. + + The `forEach` passes the new `id` value to the component's `getHero` method (not shown) + which fetches a hero and sets the component's `hero` property. + If the`id` parameter is missing, the `pluck` operator fails and the `catch` treats failure as a request to edit a new hero. + + The [Router](router.html#route-parameters) chapter covers `ActivatedRoute.params` in more detail. +:marked + A test can explore how the `HeroDetailComponent` responds to different `id` parameter values + by manipulating the `ActivatedRoute` injected into the component's constructor. + + By now you know how to stub the `Router` and a data service. + Stubbing the `ActivatedRoute` would follow the same pattern except for a complication: + the `ActivatedRoute.params` is an _Observable_. +a#stub-observable +:marked + ### _Observable_ test double + + The `hero-detail.component.spec.ts` relies on an `ActivatedRouteStub` to set `ActivatedRoute.params` values for each test. + This is a cross-application, re-usable _test helper class_. + We recommend locating such helpers in a `testing` folder sibling to the `app` folder. + This sample keeps `ActivatedRouteStub` in `testing/router-stubs.ts`: + ++makeExample('testing/ts/testing/router-stubs.ts', 'activated-route-stub', 'testing/router-stubs.ts (ActivatedRouteStub)')(format='.') +:marked + Notable features of this stub: + + * The stub implements only two of the `ActivatedRoute` capabilities: `params` and `snapshot.params`. + + * _BehaviorSubject_ + drives the stub's `params` _Observable_ and returns the same value to every `params` subscriber until it's given a new value. + + * The `HeroDetailComponent` chain its expressions to this stub `params` _Observable_ which is now under the tester's control. + + * Setting the `testParams` property causes the `subject` to push the assigned value into `params`. + That triggers the `HeroDetailComponent` _params_ subscription, described above, in the same way that navigation does. + + * Setting the `testParams` property also updates the stub's internal value for the `snapshot` property to return. +.l-sub-section(style="margin-left:30px") + :marked + The [_snapshot_](router.html#snapshot "Router Chapter: snapshot") is another popular way for components to consume route parameters. +.callout.is-helpful + :marked + The router stubs in this chapter are meant to inspire you. Create your own stubs to fit your testing needs. + +a#observable-tests +:marked + ### _Observable_ tests + Here's a test demonstrating the component's behavior when the observed `id` refers to an existing hero: ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-good-id', 'app/hero/hero-detail.component.spec.ts (existing id)')(format='.') +.l-sub-section + :marked + The `createComponent` method and `page` object are discussed [in the next section](#page-object). + Rely on your intuition for now. +:marked + When the `id` cannot be found, the component should re-route to the `HeroListComponent`. + The test suite setup provided the same `RouterStub` [described above](#routed-component) which spies on the router without actually navigating. + This test supplies a "bad" id and expects the component to try to navigate. ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-bad-id', 'app/hero/hero-detail.component.spec.ts (bad id)')(format='.') +:marked +:marked + While this app doesn't have a route to the `HeroDetailComponent` that omits the `id` parameter, it might add such a route someday. + The component should do something reasonable when there is no `id`. + + In this implementation, the component should create and display a new hero. + New heroes have `id=0` and a blank `name`. This test confirms that the component behaves as expected: ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-no-id', 'app/hero/hero-detail.component.spec.ts (no id)')(format='.') +:marked +.callout.is-helpful + :marked + Inspect and download _all_ of the chapter's application test code with this live example. + +.l-hr + +a#page-object +:marked + # Use a _page_ object to simplify setup + + The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons. +figure.image-display + img(src='/resources/images/devguide/testing/hero-detail.component.png' alt="HeroDetailComponent in action") +:marked + But there's already plenty of template complexity. ++makeExample('testing/ts/app/hero/hero-detail.component.html', '', 'app/hero/hero-detail.component.html')(format='.') +:marked + To fully exercise the component, the test needs ... + * to wait until a `hero` arrives before `*ngIf` allows any element in DOM + * element references for the title name span and name input-box to inspect their values + * two button references to click + * spies on services and component methods + + Even a small form such as this one can produce a mess of tortured conditional setup and CSS element selection. + + Tame the madness with a `Page` class that simplifies access to component properties and encapsulates the logic that sets them. + Here's the `Page` class for the `hero-detail.component.spec.ts` ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'page', 'app/hero/hero-detail.component.spec.ts (Page)')(format='.') +:marked + Now the important hooks for component manipulation and inspection are neatly organized and accessible from an instance of `Page`. + + A `createComponent` method creates a `page` and fills in the blanks once the `hero` arrives. ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'create-component', 'app/hero/hero-detail.component.spec.ts (createComponent)')(format='.') +:marked + The [observable tests](#observable-tests) in the previous section demonstrate how `createComponent` and `page` + keep the tests short and _on message_. + There are no distractions: no waiting for promises to resolve and no searching the DOM for element values to compare. + Here are a few more `HeroDetailComponent` tests to drive the point home. ++makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'selected-tests', 'app/hero/hero-detail.component.spec.ts (selected tests)')(format='.') +:marked + +a(href="#top").to-top Back to top +.l-hr + a#isolated-tests a#testing-without-atp :marked @@ -1067,7 +1194,7 @@ a#testing-without-atp They do * exhibit standard, Angular-agnostic testing techniques * create instances directly with `new` - * use stubs, spys, and mocks to fake dependencies. + * substitute test doubles (stubs, spys, and mocks) for the real dependencies. .callout.is-important header Write both kinds of tests @@ -1119,7 +1246,7 @@ a#testing-without-atp The first test creates a `FancyService` with `new` and passes it to the `DependentService` constructor. It's rarely that simple. The injected service can be difficult to create or control. - You can mock the dependency, or use a fake value, or stub the pertinent service method + You can mock the dependency, or use a dummy value, or stub the pertinent service method with a substitute method that is easy to control. These _isolated_ unit testing techniques are great for exploring the inner logic of a service or its @@ -1143,7 +1270,7 @@ a#testing-without-atp Use simple Jasmine to explore the expected cases and the edge cases. +makeExample('testing/ts/app/shared/title-case.pipe.spec.ts', 'excerpt', 'app/shared/title-case.pipe.spec.ts') :marked - ## Write ATP tests too + ### Write ATP tests too These are tests of the pipe _in isolation_. They can't tell if the `TitleCasePipe` is working properly as applied in the application components. @@ -1208,24 +1335,30 @@ table td :marked Runs the body of a test (`it`) or setup (`beforeEach`) function within a special _async test zone_. - See [here](#async-fn-in-it) and [here](#async-fn-in-before-each). + See [discussion above](#async). tr td(style="vertical-align: top") fakeAsync td :marked Runs the body of a test (`it`) within a special _fakeAsync test zone_, enabling - a linear control flow coding style. See [above](#fake-async). + a linear control flow coding style. See [discussion above](#fake-async). tr td(style="vertical-align: top") tick td :marked Simulates the passage of time and the completion of pending asynchronous activities - by flushing timer and micro-task queues in the _fakeAsync test zone_. - + by flushing both _timer_ and _micro-task_ queues within the _fakeAsync test zone_. + + .l-sub-section + :marked + The curious, dedicated reader might enjoy this lengthy blog post, + "_Tasks, microtasks, queues and schedules_". + :marked Accepts an optional argument that moves the virtual clock forward the specified number of milliseconds, clearing asynchronous activities scheduled within that timeframe. - See [above](#tick). + See [discussion bove](#tick). tr td(style="vertical-align: top") inject @@ -1238,22 +1371,22 @@ table td(style="vertical-align: top") discardPeriodicTasks td :marked - When a `fakeAsync` test ends with pending timer event tasks (queued `setTimeOut` and `setInterval` callbacks), + When a `fakeAsync` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks), the test fails with a clear error message. In general, a test should end with no queued tasks. - When pending timer tasks are expected, call `discardPeriodicTasks` to flush the queues + When pending timer tasks are expected, call `discardPeriodicTasks` to flush the _task_ queue and avoid the error. tr td(style="vertical-align: top") flushMicrotasks td :marked - When a `fakeAsync` test ends with pending "microtasks" such as unresolved promises, + When a `fakeAsync` test ends with pending _micro-tasks_ such as unresolved promises, the test fails with a clear error message. - In general, a test should wait for microtasks to finish. - When pending microtasks are expected, call `discardPeriodicTasks` to flush the queues + In general, a test should wait for micro-tasks to finish. + When pending microtasks are expected, call `flushMicrotasks` to flush the _micro-task_ queue and avoid the error. tr @@ -1277,16 +1410,11 @@ table a#testbed-class-summary :marked # _TestBed_ Class Summary - The `TestBed` class API is quite large and can be overwhelming until you've explored it first + The `TestBed` class is a principle feature of the _Angular Testing Platform_. + Its API is quite large and can be overwhelming until you've explored it first a little at a time. Read the early part of this chapter first to get the basics before trying to absorb the full API. -.alert.is-important - :marked - The _TestBed_ is officially _experimental_ and thus subject to change. - Consult the [API reference](../api/core/testing/index/TestBed-class.html) for the latest status. - -:marked The module definition passed to `configureTestingModule`, is a subset of the `@NgModule` metadata properties. code-example(format="." language="javascript"). @@ -1327,18 +1455,18 @@ table td :marked The testing shims (`karma-test-shim`, `browser-test-shim`) - establish the [initial test environment](#a#testbed-initTestEnvironment) and a default test module. - The default test module is configured with basic declaratives and some Angular service substitutes (e.g. `DebugDomRender`) + establish the [initial test environment](#a#testbed-initTestEnvironment) and a default testing module. + The default testing module is configured with basic declaratives and some Angular service substitutes (e.g. `DebugDomRender`) that every tester needs. - Call `configureTestingModule` to refine the test module configuration for a particular set of tests + Call `configureTestingModule` to refine the testing module configuration for a particular set of tests by adding and removing imports, declarations (of components, directives, and pipes), and providers. tr td(style="vertical-align: top") compileComponents td :marked - Compile the test module asynchronously after you've finished configuring it. - You **must** call this method if _any_ of the test module components have a `templateUrl` + Compile the testing module asynchronously after you've finished configuring it. + You **must** call this method if _any_ of the testing module components have a `templateUrl` or `styleUrls` because fetching component template and style files is necessarily asynchronous. See [above](#compile-components). @@ -1355,7 +1483,7 @@ table td :marked Replace metadata for the given `NgModule`. Recall that modules can import other modules. - The `overrideModule` method can reach deeply into the current test module to + The `overrideModule` method can reach deeply into the current testing module to modify one of these inner modules. tr td(style="vertical-align: top") overrideComponent @@ -1408,14 +1536,14 @@ table This method may be called _exactly once_. Call `resetTestEnvironment` first if you absolutely need to change this default in the middle of your test run. - Specify the Angular compiler factory, a `PlatformRef`, and a default Angular test module. - Test modules and platforms for individual platforms are available from + Specify the Angular compiler factory, a `PlatformRef`, and a default Angular testing module. + Alternatives for non-browser platforms are available from `angular2/platform/testing/`. tr td(style="vertical-align: top") resetTestEnvironment td :marked - Reset the initial test environment including the default test module. + Reset the initial test environment including the default testing module. :marked A few of the `TestBed` instance methods are not covered by static `TestBed` _class_ methods. diff --git a/public/resources/images/devguide/testing/hero-detail.component.png b/public/resources/images/devguide/testing/hero-detail.component.png new file mode 100644 index 0000000000000000000000000000000000000000..3510be66dba05fb3f6e1c625d545e429dc8a29cb GIT binary patch literal 2746 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 AR{#J2 literal 0 HcmV?d00001 From 8b7c3b6e70df005a9386bf6f8338795d37b8dc19 Mon Sep 17 00:00:00 2001 From: Alex Wolfe Date: Sat, 17 Sep 2016 14:52:46 -0700 Subject: [PATCH 03/42] update title on sidenav (#2386) --- public/docs/_includes/_side-nav.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/_includes/_side-nav.jade b/public/docs/_includes/_side-nav.jade index 0f3fedb90f..f34ad29074 100644 --- a/public/docs/_includes/_side-nav.jade +++ b/public/docs/_includes/_side-nav.jade @@ -104,7 +104,7 @@ nav(class="sidenav l-pinned-left l-layer-4 l-offset-nav" ng-class="appCtrl.show li(class="nav-list-item #{item.class}"): a(href="#{item.href}" title="#{item.tooltip}") #{index + 1}. #{item.navTitle} li.sidenav-section - a(class="nav-title is-parent #{anyItemSelected(guide)}" href="#{guide[0].href}" title="#{guide[0].tooltip}") Advanced Documentation + a(class="nav-title is-parent #{anyItemSelected(guide)}" href="#{guide[0].href}" title="#{guide[0].tooltip}") Advanced img(class="inline-arrow-down-svg" src="/resources/images/icons/ic_keyboard_arrow_down_black_24px.svg") .nav-unordered-lists(class="#{isCollapsed(anyItemSelected(guide))}") From 5bd269484a6c9ad8674558df5fe9f12327ce19d6 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Sat, 17 Sep 2016 23:56:36 +0200 Subject: [PATCH 04/42] fix(ngmodule): fix broken anchor (#2373) --- public/docs/ts/latest/guide/ngmodule.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/ts/latest/guide/ngmodule.jade b/public/docs/ts/latest/guide/ngmodule.jade index f8ff77c7a6..bb0cc6ca7a 100644 --- a/public/docs/ts/latest/guide/ngmodule.jade +++ b/public/docs/ts/latest/guide/ngmodule.jade @@ -94,7 +94,7 @@ a#angular-modularity We'll see how later in the page. Let's start with the _root module_. -a#root_module +a#root-module .l-main-section :marked ## _AppModule_ - the application root module From c6cf4dc310a3bbd1c53833254bd3cd3fd867c108 Mon Sep 17 00:00:00 2001 From: Sarun Intaralawan Date: Sun, 18 Sep 2016 04:56:55 +0700 Subject: [PATCH 05/42] docs(router): add missing backticks (#2366) --- public/docs/ts/latest/guide/router.jade | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index 0af9b5d1d3..a35b29bde1 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -1365,13 +1365,13 @@ a#child-routing-component They *extend* the path of the parent route. With each step down the route tree, we add a slash followed by the route path (unless the route path is _empty_). - For example, the parent path to the `CrisisCenterComponent` is `/crisis-center - The router appends these child paths to the parent path to the `CrisisCenterComponent` (`/crisis-center). + For example, the parent path to the `CrisisCenterComponent` is `/crisis-center` + The router appends these child paths to the parent path to the `CrisisCenterComponent` (`/crisis-center`). - * to navigate to the `CrisisCenterHomeComponent, the full URL is `/crisis-center` (/crisis-center` + `''` + `''`). + * to navigate to the `CrisisCenterHomeComponent`, the full URL is `/crisis-center` (`/crisis-center` + `''` + `''`). * to navigate to the `CrisisDetailComponent` for a crisis with `id=2`, the full URL is - `/crisis-center/2` (/crisis-center` + `''` + `'/2'`). + `/crisis-center/2` (`/crisis-center` + `''` + `'/2'`). The absolute URL for the latter example, including the origin, is code-example. From 0605019c06920908b2ee617698164df547d1013a Mon Sep 17 00:00:00 2001 From: Aristeidis Bampakos Date: Sun, 18 Sep 2016 00:57:21 +0300 Subject: [PATCH 06/42] docs(news): Fix RC7 announcement link (#2363) Fix RC7 announcement link --- public/news.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/news.jade b/public/news.jade index 2cda4af6b4..0541debdf0 100644 --- a/public/news.jade +++ b/public/news.jade @@ -23,7 +23,7 @@ .title a( target="_blank" - href="http://angularjs.blogspot.com/2016/08/angular-2-rc5-ngmodules-lazy-loading.html" + href="http://angularjs.blogspot.com/2016/09/rc7-now-available.html" ) RC7 Now Available p Today we’re happy to announce that we are shipping Angular 2.0.0-rc.7. This small release is focused on bugfixes. What's fixed? Lazy loading, RxJS, IDE Docs Integration... .author From 665c346d8f6a99772066dbec54dd25a3a4cfc8b0 Mon Sep 17 00:00:00 2001 From: Mike Reid Date: Sat, 17 Sep 2016 15:58:54 -0600 Subject: [PATCH 07/42] docs(devguide): fix invalid JavaScript syntax in hero-form.component.js example (#2343) Fix Unmatched '{' JavaScript syntax error in hero-form.component.js example --- public/docs/_examples/forms/js/app/hero-form.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/_examples/forms/js/app/hero-form.component.js b/public/docs/_examples/forms/js/app/hero-form.component.js index 428ff82d35..505993a1fd 100644 --- a/public/docs/_examples/forms/js/app/hero-form.component.js +++ b/public/docs/_examples/forms/js/app/hero-form.component.js @@ -48,5 +48,5 @@ // #docregion first, final }); - // #enddocregion first, final })(window.app || (window.app = {})); +// #enddocregion first, final From 159b196baf8261a70d9c453d6f232a24d1ea7ce9 Mon Sep 17 00:00:00 2001 From: John Papa Date: Sun, 18 Sep 2016 12:34:32 -0400 Subject: [PATCH 08/42] docs(toh): fix spelling of ActivateRoute to ActivatedRoute (#2400) * fixed spelling of ActivateRoute to ActivatedRoute * dont do cache --- public/docs/ts/latest/tutorial/toh-pt5.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/ts/latest/tutorial/toh-pt5.jade b/public/docs/ts/latest/tutorial/toh-pt5.jade index 2bab094724..1ea7702e1d 100644 --- a/public/docs/ts/latest/tutorial/toh-pt5.jade +++ b/public/docs/ts/latest/tutorial/toh-pt5.jade @@ -536,7 +536,7 @@ block route-params block ngOnInit :marked Inside the `ngOnInit` lifecycle hook, we use the `params` observable to - extract the `id` parameter value from the `ActivateRoute` service + extract the `id` parameter value from the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`. +makeExcerpt('app/hero-detail.component.ts', 'ngOnInit') From 40f320d7210508092dc94e9f27cbf0e1fe86c266 Mon Sep 17 00:00:00 2001 From: Maxime Date: Sun, 18 Sep 2016 18:54:40 +0200 Subject: [PATCH 09/42] docs(testing): fix small typo (#2401) --- public/docs/ts/latest/guide/testing.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/ts/latest/guide/testing.jade b/public/docs/ts/latest/guide/testing.jade index 51ba85876f..3e61e0023a 100644 --- a/public/docs/ts/latest/guide/testing.jade +++ b/public/docs/ts/latest/guide/testing.jade @@ -142,7 +142,7 @@ a#setup .alert.is-helpful :marked - If youur application was based on the QuickStart repository, + If your application was based on the QuickStart repository, you can skip the rest of this section and get on with your first test. The QuickStart repo provides all necessary setup. From a384065576c7c9e936777d60f4757fc2ae8c0ff7 Mon Sep 17 00:00:00 2001 From: Martin-Wegner Date: Mon, 19 Sep 2016 13:59:00 +0200 Subject: [PATCH 10/42] docs(webpack): remove unused ts-loader (#2361) https://github.com/angular/angular.io/commit/69ae63c4b1d525936c1e445320258b79bd64efca changed `public/docs/_examples/webpack/ts/config/webpack.common.js` and `public/docs/_examples/webpack/ts/config/webpack.test.js` and replaced `ts` with `awesome-typescript-loader`. So we can remove `"ts-loader": "^0.8.1",` from the `public/docs/_examples/webpack/ts/package.webpack.json`. --- public/docs/_examples/webpack/ts/package.webpack.json | 1 - 1 file changed, 1 deletion(-) diff --git a/public/docs/_examples/webpack/ts/package.webpack.json b/public/docs/_examples/webpack/ts/package.webpack.json index 5e3630dc2a..ff8477675c 100644 --- a/public/docs/_examples/webpack/ts/package.webpack.json +++ b/public/docs/_examples/webpack/ts/package.webpack.json @@ -41,7 +41,6 @@ "raw-loader": "^0.5.1", "rimraf": "^2.5.2", "style-loader": "^0.13.1", - "ts-loader": "^0.8.1", "typescript": "^2.0.2", "typings": "^1.3.2", "webpack": "^1.13.0", From c17f8596e506ee538b1ef2abc1241dfce3acb09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Rodr=C3=ADguez?= Date: Mon, 19 Sep 2016 18:38:54 +0200 Subject: [PATCH 11/42] docs(webpack): move to core-js in testing (#2404) --- public/docs/_examples/webpack/ts/config/karma-test-shim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/_examples/webpack/ts/config/karma-test-shim.js b/public/docs/_examples/webpack/ts/config/karma-test-shim.js index 973543a02a..2ea37fbd72 100644 --- a/public/docs/_examples/webpack/ts/config/karma-test-shim.js +++ b/public/docs/_examples/webpack/ts/config/karma-test-shim.js @@ -2,7 +2,7 @@ Error.stackTraceLimit = Infinity; require('core-js/es6'); -require('reflect-metadata'); +require('core-js/es7/reflect'); require('zone.js/dist/zone'); require('zone.js/dist/long-stack-trace-zone'); From 45fec1c13501dcdce8c8ba20d417c72e183827fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Rodr=C3=ADguez?= Date: Mon, 19 Sep 2016 18:39:53 +0200 Subject: [PATCH 12/42] docs(forms): remove deprecated forms notice for js (#2395) --- public/docs/js/latest/guide/forms.jade | 6 ------ 1 file changed, 6 deletions(-) diff --git a/public/docs/js/latest/guide/forms.jade b/public/docs/js/latest/guide/forms.jade index 6b08f641cc..f8fb0606db 100644 --- a/public/docs/js/latest/guide/forms.jade +++ b/public/docs/js/latest/guide/forms.jade @@ -1,11 +1,5 @@ include ../_util-fns -.alert.is-important - :marked - This guide is using the new forms API. - - The old forms API is deprecated, but we still maintain a separate version of the guide using the deprecated forms API here. - :marked We’ve all used a form to login, submit a help request, place an order, book a flight, schedule a meeting and perform countless other data entry tasks. From d07d42873ad7ec907085c86067af8f7a75f2816f Mon Sep 17 00:00:00 2001 From: Mike Reid Date: Mon, 19 Sep 2016 10:41:31 -0600 Subject: [PATCH 13/42] docs(devguide): add missing app.module.js source in Basics > Forms (#2342) Add missing app.module.js source example in Basics > Forms dev guide - File is missing for JavaScript, but present for TypeScript --- public/docs/js/latest/guide/forms.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/docs/js/latest/guide/forms.jade b/public/docs/js/latest/guide/forms.jade index f8fb0606db..5efeb01199 100644 --- a/public/docs/js/latest/guide/forms.jade +++ b/public/docs/js/latest/guide/forms.jade @@ -636,6 +636,7 @@ figure.image-display `forms/js/app/hero-form.component.js, forms/js/app/hero-form.component.html, forms/js/app/hero.js, + forms/js/app/app.module.js, forms/js/app/app.component.js, forms/js/app/main.js, forms/js/index.html, @@ -644,6 +645,7 @@ figure.image-display `hero-form.component.js, hero-form.component.html, hero.js, + app.module.js, app.component.js, main.js, index.html, From d26007aa67f055f6b3ab4b4fb8aedd3987c1e17e Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Mon, 19 Sep 2016 15:52:41 -0700 Subject: [PATCH 14/42] docs: fix {ts,dart,js}/latest/_data.json (#2387) * docs: fix {ts,dart,js}/latest/_data.json Fixes #2384. Fixes #2385. * post-review updates Addressing #2407. --- public/docs/dart/latest/_data.json | 24 ++++++++++++++++-------- public/docs/js/latest/_data.json | 25 ++++++++++++++++--------- public/docs/ts/latest/_data.json | 20 +++++++++----------- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/public/docs/dart/latest/_data.json b/public/docs/dart/latest/_data.json index 420f3b50f8..c23053213e 100644 --- a/public/docs/dart/latest/_data.json +++ b/public/docs/dart/latest/_data.json @@ -4,66 +4,74 @@ "title": "Angular Docs", "subtitle": "Dart", "menuTitle": "Docs Home", - "banner": "Current release is beta.21. View the change Log to see enhancements, fixes, and breaking changes." + "banner": "Angular release is beta.21. View the change log to see enhancements, fixes, and breaking changes." }, "quickstart": { "icon": "query-builder", - "title": "5 Min Quickstart", - "description": "Get up and running with Angular 2" + "title": "Quickstart", + "subtitle": "Dart", + "description": "Get up and running with Angular", + "banner": "This QuickStart guide demonstrates how to build and run a simple Angular application." }, "tutorial": { "icon": "list", "title": "Tutorial", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "Dart" }, "guide": { "icon": "list", "title": "Developer Guides", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "Dart" }, "cookbook": { "icon": "list", "title": "Cookbook", + "subtitle": "Dart", "banner": "How to solve common implementation challenges." }, "api/": { "icon": "book", - "title": "API Preview", + "title": "API Reference", + "subtitle": "Dart", "reference": true }, "cheatsheet": { "title": "Angular Cheat Sheet", + "subtitle": "Dart", "intro": "A quick guide to Angular syntax. (Content is provisional and may change.)", "reference": false }, "glossary": { "title": "Glossary", - "intro": "Brief definitions of the most important words in the Angular 2 vocabulary", + "subtitle": "Dart", + "intro": "Brief definitions of the most important words in the Angular vocabulary", "reference": false }, "resources": { "icon": "play-circle-fill", "title": "Angular Resources", - "banner": "Angular 2 is currently in Release Candidate.", + "subtitle": "Dart", "resources": true }, "help": { "icon": "chat", "title": "Help & Support", + "subtitle": "From our team & community", "resources": true }, "styleguide": { "title": "Docs Style Guide", + "subtitle": "Dart", "intro": "Design & Layout Patterns For Documentation" } } diff --git a/public/docs/js/latest/_data.json b/public/docs/js/latest/_data.json index 22e6851bd4..67e96e2f7f 100644 --- a/public/docs/js/latest/_data.json +++ b/public/docs/js/latest/_data.json @@ -4,66 +4,73 @@ "title": "Angular Docs", "subtitle": "JavaScript", "menuTitle": "Docs Home", - "banner": "Angular 2 release is 2.0.0. View the change Log to see enhancements, fixes, and breaking changes." + "banner": "Angular release is 2.0.0. View the change log to see enhancements, fixes, and breaking changes." }, "quickstart": { "icon": "query-builder", - "title": "5 Min Quickstart", - "description": "Get up and running with Angular 2" + "title": "Quickstart", + "subtitle": "JavaScript", + "description": "Get up and running with Angular", + "banner": "This QuickStart guide demonstrates how to build and run a simple Angular application." }, "tutorial": { "icon": "list", "title": "Tutorial", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "JavaScript" }, "guide": { "icon": "list", "title": "Developer Guides", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "JavaScript" }, "cookbook": { "icon": "list", "title": "Cookbook", - "banner": "How to solve common implementation challenges." + "subtitle": "JavaScript" }, "api/": { "icon": "book", - "title": "API Preview", + "title": "API Reference", + "subtitle": "JavaScript", "reference": true }, "cheatsheet": { "title": "Angular Cheat Sheet", + "subtitle": "JavaScript", "intro": "A quick guide to Angular syntax. (Content is provisional and may change.)", "reference": false }, "glossary": { "title": "Glossary", - "intro": "Brief definitions of the most important words in the Angular 2 vocabulary", + "subtitle": "JavaScript", + "intro": "Brief definitions of the most important words in the Angular vocabulary", "reference": false }, "resources": { "icon": "play-circle-fill", "title": "Angular Resources", - "banner": "Angular 2 is currently in Release Candidate.", + "subtitle": "JavaScript", "resources": true }, "help": { "icon": "chat", "title": "Help & Support", + "subtitle": "From our team & community", "resources": true }, "styleguide": { "title": "Docs Style Guide", + "subtitle": "JavaScript", "intro": "Design & Layout Patterns For Documentation" } } diff --git a/public/docs/ts/latest/_data.json b/public/docs/ts/latest/_data.json index c893f78305..93bfe769c4 100644 --- a/public/docs/ts/latest/_data.json +++ b/public/docs/ts/latest/_data.json @@ -4,36 +4,35 @@ "title": "Angular Docs", "subtitle": "TypeScript", "menuTitle": "Docs Home", - "banner": "Angular 2 release is 2.0.0. View the change Log to see enhancements, fixes, and breaking changes." + "banner": "Angular release is 2.0.0. View the change log to see enhancements, fixes, and breaking changes." }, "cli-quickstart": { "icon": "query-builder", "title": "CLI Quickstart", "subtitle": "TypeScript", - "description": "Use the CLI tool to build apps quickly in Angular 2", + "description": "Use the CLI tool to quickly build Angular applications", "hide": true }, "quickstart": { "icon": "query-builder", + "title": "Quickstart", "subtitle": "TypeScript", - "description": "Get up and running with Angular 2", - "banner": "This QuickStart guide demonstrates how to build and run a simple Angular 2 application in TypeScript." + "description": "Get up and running with Angular", + "banner": "This QuickStart guide demonstrates how to build and run a simple Angular application." }, "tutorial": { "icon": "list", "title": "Tutorial", - "subtitle": "TypeScript", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "TypeScript" }, "guide": { "icon": "list", "title": "Developer Guides", - "subtitle": "TypeScript", - "banner": "Angular 2 is currently in Release Candidate." + "subtitle": "TypeScript" }, "cookbook": { @@ -45,7 +44,7 @@ "api/": { "icon": "book", - "title": "API Preview", + "title": "API Reference", "subtitle": "TypeScript", "reference": true }, @@ -60,7 +59,7 @@ "glossary": { "title": "Glossary", "subtitle": "TypeScript", - "intro": "Brief definitions of the most important words in the Angular 2 vocabulary", + "intro": "Brief definitions of the most important words in the Angular vocabulary", "reference": false }, @@ -68,7 +67,6 @@ "icon": "play-circle-fill", "title": "Angular Resources", "subtitle": "TypeScript", - "banner": "Angular 2 is currently in Release Candidate.", "resources": true }, From e016f3f31eeff0e5139dd6488c4a50ff4390139c Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Mon, 19 Sep 2016 16:56:17 -0700 Subject: [PATCH 15/42] docs(tutorial): don't use shredder for intro page (#2388) An initial multilang cleanup for ng2.io --- public/docs/dart/latest/tutorial/index.jade | 6 +- public/docs/ts/_cache/tutorial/index.jade | 85 +++++++++++++++++++++ public/docs/ts/latest/tutorial/index.jade | 8 +- 3 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 public/docs/ts/_cache/tutorial/index.jade diff --git a/public/docs/dart/latest/tutorial/index.jade b/public/docs/dart/latest/tutorial/index.jade index f18795e25e..d586842a35 100644 --- a/public/docs/dart/latest/tutorial/index.jade +++ b/public/docs/dart/latest/tutorial/index.jade @@ -1,5 +1,5 @@ -include ../_util-fns +extends ../../../ts/_cache/tutorial/index.jade -+includeShared('{ts}', 'intro') -+includeShared('{ts}', 'main') +block includes + include ../_util-fns diff --git a/public/docs/ts/_cache/tutorial/index.jade b/public/docs/ts/_cache/tutorial/index.jade new file mode 100644 index 0000000000..77770baa20 --- /dev/null +++ b/public/docs/ts/_cache/tutorial/index.jade @@ -0,0 +1,85 @@ +block includes + include ../_util-fns + +:marked + # Tour of Heroes: the vision + + Our grand plan is to build an app to help a staffing agency manage its stable of heroes. + Even heroes need to find work. + + Of course we'll only make a little progress in this tutorial. What we do build will + have many of the features we expect to find in a full-blown, data-driven application: acquiring and displaying + a list of heroes, editing a selected hero's detail, and navigating among different + views of heroic data. + + The Tour of Heroes covers the core fundamentals of Angular. + We’ll use built-in directives to show/hide elements and display lists of hero data. + We’ll create a component to display hero details and another to show an array of heroes. + We'll use one-way data binding for read-only data. We'll add editable fields to update a model + with two-way data binding. We'll bind component methods to user events like key strokes and clicks. + We’ll learn to select a hero from a master list and edit that hero in the details view. We'll + format data with pipes. We'll create a shared service to assemble our heroes. And we'll use routing to navigate among different views and their components. + + We’ll learn enough core Angular to get started and gain confidence that + Angular can do whatever we need it to do. + We'll be covering a lot of ground at an introductory level but we’ll find plenty of links + to chapters with greater depth. + + Run the . + +.l-main-section +:marked + ## The End Game + + Here's a visual idea of where we're going in this tour, beginning with the "Dashboard" + view and our most heroic heroes: + +figure.image-display + img(src='/resources/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard") + +:marked + Above the dashboard are two links ("Dashboard" and "Heroes"). + We could click them to navigate between this Dashboard and a Heroes view. + + Instead we click the dashboard hero named "Magneta" and the router takes us to a "Hero Details" view + of that hero where we can change the hero's name. + +figure.image-display + img(src='/resources/images/devguide/toh/hero-details-1.png' alt="Details of hero in app") + +:marked + Clicking the "Back" button would return us to the "Dashboard". + Links at the top can take us to either of the main views. + We'll click "Heroes". The app takes to the "Heroes" master list view. + +figure.image-display + img(src='/resources/images/devguide/toh/heroes-list-2.png' alt="Output of heroes list app") + +:marked + We click a different hero and the readonly mini-detail beneath the list reflects our new choice. + + We click the "View Details" button to drill into the + editable details of our selected hero. + + The following diagram captures all of our navigation options. + +figure.image-display + img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations") + +:marked + Here's our app in action + +figure.image-display + img(src='/resources/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action") + +.l-main-section +:marked + ## Up Next + + We’ll build this Tour of Heroes together, step by step. + We'll motivate each step with a requirement that we've + met in countless applications. Everything has a reason. + + And we’ll meet many of the core fundamentals of Angular along the way. + + [Let's get started!](./toh-pt1.html) diff --git a/public/docs/ts/latest/tutorial/index.jade b/public/docs/ts/latest/tutorial/index.jade index 21f67233f7..77770baa20 100644 --- a/public/docs/ts/latest/tutorial/index.jade +++ b/public/docs/ts/latest/tutorial/index.jade @@ -1,6 +1,6 @@ -include ../_util-fns +block includes + include ../_util-fns -// #docregion intro :marked # Tour of Heroes: the vision @@ -26,8 +26,7 @@ include ../_util-fns to chapters with greater depth. Run the . -// #enddocregion intro -// #docregion main + .l-main-section :marked ## The End Game @@ -84,4 +83,3 @@ figure.image-display And we’ll meet many of the core fundamentals of Angular along the way. [Let's get started!](./toh-pt1.html) -// #enddocregion main From 1027e3e9be70e7b327323d6c9f7ac891fa6615d5 Mon Sep 17 00:00:00 2001 From: Naomi Black Date: Mon, 19 Sep 2016 17:40:50 -0700 Subject: [PATCH 16/42] about(shannon): add shannon's bio --- harp.json | 6 ++++++ public/resources/images/bios/shannon.jpg | Bin 0 -> 18898 bytes 2 files changed, 6 insertions(+) create mode 100644 public/resources/images/bios/shannon.jpg diff --git a/harp.json b/harp.json index 0b50cc5308..38277fcd4a 100644 --- a/harp.json +++ b/harp.json @@ -316,6 +316,12 @@ "bio": "Max Sills is Angular's Open Source lawyer.", "type": "Google" }, + "shannon": { + "name": "Shannon Ayres", + "picture": "/resources/images/bios/shannon.jpg", + "bio": "Shannon is a technical editor in Developer Relations at Google. She loves movies, especially Sunset Boulevard, and her favorite TV show is The Walking Dead. Her mission: Righting wrong writing!", + "type": "Google" + }, "pawel": { "name": "Pawel Kozlowski", diff --git a/public/resources/images/bios/shannon.jpg b/public/resources/images/bios/shannon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..23d98dc23d0dcb089c39b646fda08b6930b71839 GIT binary patch literal 18898 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 Date: Mon, 19 Sep 2016 19:57:59 -0700 Subject: [PATCH 17/42] docs(testing): more scenarios (#2396) --- .../docs/_examples/testing/ts/app-specs.html | 1 + .../testing/ts/app/about.component.spec.ts | 26 ++ .../testing/ts/app/about.component.ts | 7 +- .../testing/ts/app/app-routing.module.ts | 16 + .../testing/ts/app/app.component.html | 1 + .../testing/ts/app/app.component.spec.ts | 157 +++++---- .../_examples/testing/ts/app/app.component.ts | 3 +- .../_examples/testing/ts/app/app.module.ts | 10 +- .../app/dashboard/dashboard.component.spec.ts | 2 +- .../ts/app/dashboard/dashboard.component.ts | 5 +- .../ts/app/hero/hero-detail.component.spec.ts | 2 +- .../ts/app/hero/hero-detail.component.ts | 5 +- .../ts/app/hero/hero-list.component.spec.ts | 2 +- .../ts/app/hero/hero-list.component.ts | 5 +- .../ts/app/shared/highlight.directive.spec.ts | 94 ++++-- .../ts/app/shared/highlight.directive.ts | 12 +- .../testing/ts/app/shared/styles.css | 1 - .../testing/ts/testing/router-stubs.ts | 18 +- public/docs/ts/latest/guide/testing.jade | 298 +++++++++++++++--- .../testing/highlight-directive-spec.png | Bin 0 -> 7965 bytes 20 files changed, 487 insertions(+), 178 deletions(-) create mode 100644 public/docs/_examples/testing/ts/app/about.component.spec.ts create mode 100644 public/docs/_examples/testing/ts/app/app-routing.module.ts delete mode 100644 public/docs/_examples/testing/ts/app/shared/styles.css create mode 100644 public/resources/images/devguide/testing/highlight-directive-spec.png diff --git a/public/docs/_examples/testing/ts/app-specs.html b/public/docs/_examples/testing/ts/app-specs.html index 276659e26b..c41fbf88ea 100644 --- a/public/docs/_examples/testing/ts/app-specs.html +++ b/public/docs/_examples/testing/ts/app-specs.html @@ -30,6 +30,7 @@