From 7edc32f35031ebada243b1b8049433d863c14084 Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Fri, 3 Jun 2016 18:15:14 -0700 Subject: [PATCH 1/9] docs(testing): more samples --- .../_examples/testing/ts/app/app.component.ts | 17 +- .../testing/ts/app/bad-tests.spec.ts | 4 +- .../docs/_examples/testing/ts/app/bag.spec.ts | 307 ++++++++++++------ public/docs/_examples/testing/ts/app/bag.ts | 39 ++- .../_examples/testing/ts/app/expect-proper.ts | 9 + public/docs/_examples/testing/ts/app/main.ts | 3 +- public/docs/_examples/testing/ts/index.html | 2 - 7 files changed, 265 insertions(+), 116 deletions(-) create mode 100644 public/docs/_examples/testing/ts/app/expect-proper.ts diff --git a/public/docs/_examples/testing/ts/app/app.component.ts b/public/docs/_examples/testing/ts/app/app.component.ts index bf10177937..f2da2da067 100644 --- a/public/docs/_examples/testing/ts/app/app.component.ts +++ b/public/docs/_examples/testing/ts/app/app.component.ts @@ -13,6 +13,8 @@ import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; import { HeroService } from './hero.service'; +import { BAG_DIRECTIVES, BAG_PROVIDERS } from './bag'; + @Component({ selector: 'my-app', template: ` @@ -22,12 +24,23 @@ import { HeroService } from './hero.service'; Heroes +
+

Bag-a-specs

+ +

External Template Comp

+ +

Comp With External Template Comp

+ `, + /* + + */ styleUrls: ['app/app.component.css'], - directives: [RouterLink, RouterOutlet], + directives: [RouterLink, RouterOutlet, BAG_DIRECTIVES], providers: [ ROUTER_PROVIDERS, - HeroService + HeroService, + BAG_PROVIDERS ] }) @RouteConfig([ diff --git a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts index eaeab08128..6521bb2e38 100644 --- a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts +++ b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts @@ -4,7 +4,7 @@ * Tests that show what goes wrong when the tests are incorrectly written or have a problem */ import { - BadTemplateUrl, ButtonComp, + BadTemplateUrlComp, ButtonComp, ChildChildComp, ChildComp, ChildWithChildComp, ExternalTemplateComp, FancyService, MockFancyService, @@ -134,7 +134,7 @@ xdescribe('async & inject testing errors', () => { it('should fail with an error from a promise', async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - tcb.createAsync(BadTemplateUrl); + tcb.createAsync(BadTemplateUrlComp); }))); itPromise.then( diff --git a/public/docs/_examples/testing/ts/app/bag.spec.ts b/public/docs/_examples/testing/ts/app/bag.spec.ts index c6eae1bc77..2ad3e13341 100644 --- a/public/docs/_examples/testing/ts/app/bag.spec.ts +++ b/public/docs/_examples/testing/ts/app/bag.spec.ts @@ -1,9 +1,9 @@ // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts /* tslint:disable:no-unused-variable */ import { - BadTemplateUrl, ButtonComp, + BadTemplateUrlComp, ButtonComp, ChildChildComp, ChildComp, ChildWithChildComp, - ExternalTemplateComp, + CompWithCompWithExternalTemplate, ExternalTemplateComp, FancyService, MockFancyService, InputComp, MyIfComp, MyIfChildComp, MyIfParentComp, @@ -18,14 +18,16 @@ import { By } from '@angular/platform-browser'; import { beforeEach, beforeEachProviders, describe, ddescribe, xdescribe, - expect, it, iit, xit, + it, iit, xit, async, inject, fakeAsync, tick, withProviders } from '@angular/core/testing'; +// https://github.com/angular/angular/issues/9017 +import {expect} from './expect-proper'; + import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; -import { provide } from '@angular/core'; import { ViewMetadata } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -116,7 +118,7 @@ describe('using the test injector with the inject helper', () => { describe('setting up Providers with FancyService', () => { beforeEachProviders(() => [ - provide(FancyService, {useValue: new FancyService()}) + {provide: FancyService, useValue: new FancyService()} ]); it('should use FancyService', @@ -183,16 +185,42 @@ describe('using the test injector with the inject helper', () => { describe('using `withProviders` for per-test provision', () => { it('should inject test-local FancyService for this test', // `withProviders`: set up providers at individual test level - withProviders(() => [provide(FancyService, {useValue: {value: 'fake value'}})]) + withProviders(() => [{provide: FancyService, useValue: {value: 'fake value'}}]) // now inject and test .inject([FancyService], (service: FancyService) => { expect(service.value).toEqual('fake value'); })); }); + + + describe('can spy on FancyService', () => { + + let spy: jasmine.Spy; + let value: string; + + beforeEachProviders(() => [ + {provide: FancyService, useValue: new FancyService()} + ]); + + beforeEach(inject([FancyService], (service: FancyService) => { + spy = spyOn(service, 'getValue').and.callFake(() => 'fake value'); + value = service.getValue(); + })); + + it('FancyService.getValue spy should return fake', () => { + expect(value).toBe('fake value'); + }); + + it('FancyService.getValue spy should have been called', () => { + expect(spy.calls.count()).toBe(1, 'should be called once'); + }); + }); }); describe('test component builder', function() { + beforeEachProviders(() => [ FancyService ]); + it('should instantiate a component with valid DOM', async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { @@ -314,7 +342,7 @@ describe('test component builder', function() { tcb.overrideProviders( TestProvidersComp, - [provide(FancyService, {useClass: MockFancyService})] + [{provide: FancyService, useClass: MockFancyService}] ) .createAsync(TestProvidersComp) .then(fixture => { @@ -329,7 +357,7 @@ describe('test component builder', function() { tcb.overrideViewProviders( TestViewProvidersComp, - [provide(FancyService, {useClass: MockFancyService})] + [{provide: FancyService, useClass: MockFancyService}] ) .createAsync(TestViewProvidersComp) .then(fixture => { @@ -339,7 +367,7 @@ describe('test component builder', function() { }); }))); - it('should allow an external templateUrl', + it('should allow an external template', async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { tcb.createAsync(ExternalTemplateComp) @@ -348,130 +376,201 @@ describe('test component builder', function() { expect(fixture.nativeElement) .toHaveText('from external template\n'); }); - })), 10000); // Long timeout because this test makes an actual XHR. + })), 10000); // Long timeout because of actual XHR to fetch template. - describe('(lifecycle hooks w/ MyIfParentComp)', () => { - let fixture: ComponentFixture; - let parent: MyIfParentComp; - let child: MyIfChildComp; + it('should create a component with a component that has an external template', + async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + tcb.createAsync(CompWithCompWithExternalTemplate) + .then(fixture => { + fixture.detectChanges(); + let h3 = fixture.debugElement.query(By.css('h3')); + expect(h3).not.toBeNull('should create CompWithExtComp component'); + }) + .catch((err) => { + // console.error(err); + throw (err); + }); + })), 10000); // Long timeout because of actual XHR to fetch template. - /** - * Get the MyIfChildComp from parent; fail w/ good message if cannot. - */ - function getChild() { - let childDe: DebugElement; // DebugElement that should hold the MyIfChildComp + it('should spy on injected component service', + async(inject([TestComponentBuilder, FancyService], + (tcb: TestComponentBuilder, service: FancyService) => { - // The Hard Way: requires detailed knowledge of the parent template - try { - childDe = fixture.debugElement.children[4].children[0]; - } catch (err) { /* we'll report the error */ } + let spy = spyOn(service, 'getValue').and.callThrough(); - // DebugElement.queryAll: if we wanted all of many instances: - childDe = fixture.debugElement - .queryAll(function (de) { return de.componentInstance instanceof MyIfChildComp; })[0]; + tcb + .createAsync(ExternalTemplateComp) + .then(fixture => { - // WE'LL USE THIS APPROACH ! - // DebugElement.query: find first instance (if any) - childDe = fixture.debugElement - .query(function (de) { return de.componentInstance instanceof MyIfChildComp; }); + // let spy = spyOn(service, 'getValue').and.callThrough(); - if (childDe && childDe.componentInstance) { - child = childDe.componentInstance; - } else { - fail('Unable to find MyIfChildComp within MyIfParentComp'); - } + fixture.detectChanges(); + expect(spy.calls.count()).toBe(1, 'should be called once'); + }); + })), 10000); // Long timeout because of actual XHR to fetch template. - return child; + describe('(lifecycle hooks w/ MyIfParentComp)', () => { + let fixture: ComponentFixture; + let parent: MyIfParentComp; + let child: MyIfChildComp; + + /** + * Get the MyIfChildComp from parent; fail w/ good message if cannot. + */ + function getChild() { + + let childDe: DebugElement; // DebugElement that should hold the MyIfChildComp + + // The Hard Way: requires detailed knowledge of the parent template + try { + childDe = fixture.debugElement.children[4].children[0]; + } catch (err) { /* we'll report the error */ } + + // DebugElement.queryAll: if we wanted all of many instances: + childDe = fixture.debugElement + .queryAll(function (de) { return de.componentInstance instanceof MyIfChildComp; })[0]; + + // WE'LL USE THIS APPROACH ! + // DebugElement.query: find first instance (if any) + childDe = fixture.debugElement + .query(function (de) { return de.componentInstance instanceof MyIfChildComp; }); + + if (childDe && childDe.componentInstance) { + child = childDe.componentInstance; + } else { + fail('Unable to find MyIfChildComp within MyIfParentComp'); } - // Create MyIfParentComp TCB and component instance before each test (async beforeEach) - beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - tcb.createAsync(MyIfParentComp) - .then(fix => { - fixture = fix; - parent = fixture.debugElement.componentInstance; - }); - }))); + return child; + } - it('should instantiate parent component', () => { - expect(parent).not.toBeNull('parent component should exist'); - }); + // Create MyIfParentComp TCB and component instance before each test (async beforeEach) + beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + tcb.createAsync(MyIfParentComp) + .then(fix => { + fixture = fix; + parent = fixture.debugElement.componentInstance; + }); + }))); - it('parent component OnInit should NOT be called before first detectChanges()', () => { - expect(parent.ngOnInitCalled).toEqual(false); - }); + it('should instantiate parent component', () => { + expect(parent).not.toBeNull('parent component should exist'); + }); - it('parent component OnInit should be called after first detectChanges()', () => { - fixture.detectChanges(); - expect(parent.ngOnInitCalled).toEqual(true); - }); + it('parent component OnInit should NOT be called before first detectChanges()', () => { + expect(parent.ngOnInitCalled).toEqual(false); + }); - it('child component should exist after OnInit', () => { - fixture.detectChanges(); - getChild(); - expect(child instanceof MyIfChildComp).toEqual(true, 'should create child'); - }); + it('parent component OnInit should be called after first detectChanges()', () => { + fixture.detectChanges(); + expect(parent.ngOnInitCalled).toEqual(true); + }); - it('should have called child component\'s OnInit ', () => { - fixture.detectChanges(); - getChild(); - expect(child.ngOnInitCalled).toEqual(true); - }); + it('child component should exist after OnInit', () => { + fixture.detectChanges(); + getChild(); + expect(child instanceof MyIfChildComp).toEqual(true, 'should create child'); + }); - it('child component called OnChanges once', () => { - fixture.detectChanges(); - getChild(); - expect(child.ngOnChangesCounter).toEqual(1); - }); + it('should have called child component\'s OnInit ', () => { + fixture.detectChanges(); + getChild(); + expect(child.ngOnInitCalled).toEqual(true); + }); - it('changed parent value flows to child', () => { - fixture.detectChanges(); - getChild(); + it('child component called OnChanges once', () => { + fixture.detectChanges(); + getChild(); + expect(child.ngOnChangesCounter).toEqual(1); + }); - parent.parentValue = 'foo'; + it('changed parent value flows to child', () => { + fixture.detectChanges(); + getChild(); + + parent.parentValue = 'foo'; + fixture.detectChanges(); + + expect(child.ngOnChangesCounter).toEqual(2, + 'expected 2 changes: initial value and changed value'); + expect(child.childValue).toEqual('foo', + 'childValue should eq changed parent value'); + }); + + it('changed child value flows to parent', async(() => { + fixture.detectChanges(); + getChild(); + + child.childValue = 'bar'; + + return new Promise(resolve => { + // Wait one JS engine turn! + setTimeout(() => resolve(), 0); + }).then(() => { fixture.detectChanges(); expect(child.ngOnChangesCounter).toEqual(2, 'expected 2 changes: initial value and changed value'); - expect(child.childValue).toEqual('foo', - 'childValue should eq changed parent value'); + expect(parent.parentValue).toEqual('bar', + 'parentValue should eq changed parent value'); }); - it('changed child value flows to parent', async(() => { - fixture.detectChanges(); - getChild(); + })); - child.childValue = 'bar'; + it('clicking "Close Child" triggers child OnDestroy', () => { + fixture.detectChanges(); + getChild(); - return new Promise(resolve => { - // Wait one JS engine turn! - setTimeout(() => resolve(), 0); - }).then(() => { - fixture.detectChanges(); - - expect(child.ngOnChangesCounter).toEqual(2, - 'expected 2 changes: initial value and changed value'); - expect(parent.parentValue).toEqual('bar', - 'parentValue should eq changed parent value'); - }); - - })); - - it('clicking "Close Child" triggers child OnDestroy', () => { - fixture.detectChanges(); - getChild(); - - let btn = fixture.debugElement.query(By.css('button')); - btn.triggerEventHandler('click', null); - - fixture.detectChanges(); - expect(child.ngOnDestroyCalled).toEqual(true); - }); + let btn = fixture.debugElement.query(By.css('button')); + btn.triggerEventHandler('click', null); + fixture.detectChanges(); + expect(child.ngOnDestroyCalled).toEqual(true); }); + + }); }); +describe('test component builder in beforeEach (comp w/ external template)', function() { + let fixture: ComponentFixture; + + beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + tcb.createAsync(ExternalTemplateComp).then(fix => fixture = fix); + })) + ); + // May need Long timeout because this test makes an actual XHR to get template + // , 10000); WHY CAN'T I ADD TIMEOUT OVERRIDE + + it('should allow an external template', () => { + fixture.detectChanges(); + expect(fixture.nativeElement) + .toHaveText('from external template\n'); + }); + +}); + +describe('test component builder in beforeEach (comp w/ internal comp w/ external template)', function() { + let fixture: ComponentFixture; + + beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + tcb.createAsync(CompWithCompWithExternalTemplate).then(fix => fixture = fix) + .catch((err) => { + // console.error(err); + throw (err); + }); + })) + ); + // May need Long timeout because this test makes an actual XHR to get template + // , 10000); WHY CAN'T I ADD TIMEOUT OVERRIDE + + it('should allow an external template', () => { + fixture.detectChanges(); + let h3 = fixture.debugElement.query(By.css('h3')); + expect(h3).not.toBeNull('should create CompWithExtComp component'); + }); + +}); //////// Testing Framework Bugs? ///// import { HeroService } from './hero.service'; @@ -495,7 +594,7 @@ describe('tcb.overrideProviders', () => { tcb.overrideProviders( AnotherProvidersComp, - [provide(HeroService, {useValue: {}})] + [{provide: HeroService, useValue: {}}] ) .createAsync(AnotherProvidersComp); }))); diff --git a/public/docs/_examples/testing/ts/app/bag.ts b/public/docs/_examples/testing/ts/app/bag.ts index 0da4d7fb20..94be448e7e 100644 --- a/public/docs/_examples/testing/ts/app/bag.ts +++ b/public/docs/_examples/testing/ts/app/bag.ts @@ -1,6 +1,6 @@ // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts /* tslint:disable:forin */ -import { Component, EventEmitter, Injectable, Input, Output, +import { Component, EventEmitter, Injectable, Input, Output, Optional, OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -13,6 +13,8 @@ import { Observable } from 'rxjs/Rx'; export class FancyService { value: string = 'real value'; + getValue() { return this.value; } + getAsyncValue() { return Promise.resolve('async value'); } getObservableValue() { return Observable.of('observable value'); } @@ -123,20 +125,36 @@ export class TestViewProvidersComp { constructor(private fancyService: FancyService) {} } - @Component({ moduleId: module.id, selector: 'external-template-comp', templateUrl: 'bag-external-template.html' }) -export class ExternalTemplateComp { } +export class ExternalTemplateComp { + serviceValue: string; + constructor(@Optional() private service: FancyService) { } + + ngOnInit() { + if (this.service) { this.serviceValue = this.service.getValue(); } + } +} + +@Component({ + selector: 'comp-w-ext-comp', + template: ` +

comp-w-ext-comp

+ + `, + directives: [ExternalTemplateComp] +}) +export class CompWithCompWithExternalTemplate { } @Component({ selector: 'bad-template-comp', templateUrl: 'non-existant.html' }) -export class BadTemplateUrl { } +export class BadTemplateUrlComp { } ///////// MyIfChildComp //////// @@ -222,3 +240,16 @@ export class MyIfParentComp implements OnInit { this.toggleLabel = this.showChild ? 'Close' : 'Show'; } } + +export const BAG_PROVIDERS = [FancyService]; + +export const BAG_DIRECTIVES = [ + ButtonComp, + ChildChildComp, ChildComp, ChildWithChildComp, + ExternalTemplateComp, CompWithCompWithExternalTemplate, + InputComp, + MyIfComp, MyIfChildComp, MyIfParentComp, + MockChildComp, MockChildChildComp, + ParentComp, + TestProvidersComp, TestViewProvidersComp +]; diff --git a/public/docs/_examples/testing/ts/app/expect-proper.ts b/public/docs/_examples/testing/ts/app/expect-proper.ts new file mode 100644 index 0000000000..6712b879d1 --- /dev/null +++ b/public/docs/_examples/testing/ts/app/expect-proper.ts @@ -0,0 +1,9 @@ +// See https://github.com/angular/angular/issues/9017 +import { expect as expectCore} from '@angular/core/testing'; +import { NgMatchers } from '@angular/platform-browser/testing'; + +export function expect(spy: Function): NgMatchers; +export function expect(actual: any): NgMatchers; +export function expect(actual: any): NgMatchers { + return expectCore(actual) as NgMatchers; +} diff --git a/public/docs/_examples/testing/ts/app/main.ts b/public/docs/_examples/testing/ts/app/main.ts index 33b9c2b641..5f185788a3 100644 --- a/public/docs/_examples/testing/ts/app/main.ts +++ b/public/docs/_examples/testing/ts/app/main.ts @@ -1,6 +1,5 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; -import { MyIfParentComp } from './bag'; bootstrap(AppComponent); -bootstrap(MyIfParentComp); + diff --git a/public/docs/_examples/testing/ts/index.html b/public/docs/_examples/testing/ts/index.html index 02b11078c5..bfde80afe3 100644 --- a/public/docs/_examples/testing/ts/index.html +++ b/public/docs/_examples/testing/ts/index.html @@ -22,7 +22,5 @@ Loading... -
- Loading MyIfParentComp ... From 8fe68f414a478d39afa4a70c9862a76df6113506 Mon Sep 17 00:00:00 2001 From: Foxandxss Date: Thu, 2 Jun 2016 00:13:57 +0200 Subject: [PATCH 2/9] docs(style-guide): fix 03-03 css class closes #1582 --- public/docs/ts/latest/guide/style-guide.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/docs/ts/latest/guide/style-guide.jade b/public/docs/ts/latest/guide/style-guide.jade index 092100f5f0..f2a8490012 100644 --- a/public/docs/ts/latest/guide/style-guide.jade +++ b/public/docs/ts/latest/guide/style-guide.jade @@ -694,7 +694,7 @@ a(href="#toc") Back to top :marked **Do** name an interface using upper camel case. -.s-rule.do +.s-rule.consider :marked **Consider** naming an interface without an `I` prefix. From 1d294a831cccd84a986a8133217dd0187e449918 Mon Sep 17 00:00:00 2001 From: Foxandxss Date: Thu, 2 Jun 2016 01:54:54 +0200 Subject: [PATCH 3/9] docs(upgrade): escape script tags closes #1584 --- public/docs/ts/latest/guide/upgrade.jade | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/docs/ts/latest/guide/upgrade.jade b/public/docs/ts/latest/guide/upgrade.jade index 8ce47a8a07..6e5d0c31cc 100644 --- a/public/docs/ts/latest/guide/upgrade.jade +++ b/public/docs/ts/latest/guide/upgrade.jade @@ -96,7 +96,7 @@ include ../_util-fns up with a project structure with a large number of relatively small files. This is a much neater way to organize things than a small number of large files, but it doesn't work that well if you have to load all those files to the HTML page with - `