docs(testing): more samples

This commit is contained in:
Ward Bell 2016-06-03 18:15:14 -07:00
parent 05864c2584
commit 7edc32f350
7 changed files with 265 additions and 116 deletions

View File

@ -13,6 +13,8 @@ import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component'; import { HeroDetailComponent } from './hero-detail.component';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
import { BAG_DIRECTIVES, BAG_PROVIDERS } from './bag';
@Component({ @Component({
selector: 'my-app', selector: 'my-app',
template: ` template: `
@ -22,12 +24,23 @@ import { HeroService } from './hero.service';
<a [routerLink]="['Heroes']">Heroes</a> <a [routerLink]="['Heroes']">Heroes</a>
</nav> </nav>
<router-outlet></router-outlet> <router-outlet></router-outlet>
<hr>
<h1>Bag-a-specs</h1>
<my-if-parent-comp></my-if-parent-comp>
<h3>External Template Comp</h3>
<external-template-comp></external-template-comp>
<h3>Comp With External Template Comp</h3>
<comp-w-ext-comp></comp-w-ext-comp>
`, `,
/*
*/
styleUrls: ['app/app.component.css'], styleUrls: ['app/app.component.css'],
directives: [RouterLink, RouterOutlet], directives: [RouterLink, RouterOutlet, BAG_DIRECTIVES],
providers: [ providers: [
ROUTER_PROVIDERS, ROUTER_PROVIDERS,
HeroService HeroService,
BAG_PROVIDERS
] ]
}) })
@RouteConfig([ @RouteConfig([

View File

@ -4,7 +4,7 @@
* Tests that show what goes wrong when the tests are incorrectly written or have a problem * Tests that show what goes wrong when the tests are incorrectly written or have a problem
*/ */
import { import {
BadTemplateUrl, ButtonComp, BadTemplateUrlComp, ButtonComp,
ChildChildComp, ChildComp, ChildWithChildComp, ChildChildComp, ChildComp, ChildWithChildComp,
ExternalTemplateComp, ExternalTemplateComp,
FancyService, MockFancyService, FancyService, MockFancyService,
@ -134,7 +134,7 @@ xdescribe('async & inject testing errors', () => {
it('should fail with an error from a promise', it('should fail with an error from a promise',
async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
tcb.createAsync(BadTemplateUrl); tcb.createAsync(BadTemplateUrlComp);
}))); })));
itPromise.then( itPromise.then(

View File

@ -1,9 +1,9 @@
// Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts
/* tslint:disable:no-unused-variable */ /* tslint:disable:no-unused-variable */
import { import {
BadTemplateUrl, ButtonComp, BadTemplateUrlComp, ButtonComp,
ChildChildComp, ChildComp, ChildWithChildComp, ChildChildComp, ChildComp, ChildWithChildComp,
ExternalTemplateComp, CompWithCompWithExternalTemplate, ExternalTemplateComp,
FancyService, MockFancyService, FancyService, MockFancyService,
InputComp, InputComp,
MyIfComp, MyIfChildComp, MyIfParentComp, MyIfComp, MyIfChildComp, MyIfParentComp,
@ -18,14 +18,16 @@ import { By } from '@angular/platform-browser';
import { import {
beforeEach, beforeEachProviders, beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe, describe, ddescribe, xdescribe,
expect, it, iit, xit, it, iit, xit,
async, inject, async, inject,
fakeAsync, tick, withProviders fakeAsync, tick, withProviders
} from '@angular/core/testing'; } from '@angular/core/testing';
// https://github.com/angular/angular/issues/9017
import {expect} from './expect-proper';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { provide } from '@angular/core';
import { ViewMetadata } from '@angular/core'; import { ViewMetadata } from '@angular/core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
@ -116,7 +118,7 @@ describe('using the test injector with the inject helper', () => {
describe('setting up Providers with FancyService', () => { describe('setting up Providers with FancyService', () => {
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(FancyService, {useValue: new FancyService()}) {provide: FancyService, useValue: new FancyService()}
]); ]);
it('should use 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', () => { describe('using `withProviders` for per-test provision', () => {
it('should inject test-local FancyService for this test', it('should inject test-local FancyService for this test',
// `withProviders`: set up providers at individual test level // `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 // now inject and test
.inject([FancyService], (service: FancyService) => { .inject([FancyService], (service: FancyService) => {
expect(service.value).toEqual('fake value'); 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() { describe('test component builder', function() {
beforeEachProviders(() => [ FancyService ]);
it('should instantiate a component with valid DOM', it('should instantiate a component with valid DOM',
async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
@ -314,7 +342,7 @@ describe('test component builder', function() {
tcb.overrideProviders( tcb.overrideProviders(
TestProvidersComp, TestProvidersComp,
[provide(FancyService, {useClass: MockFancyService})] [{provide: FancyService, useClass: MockFancyService}]
) )
.createAsync(TestProvidersComp) .createAsync(TestProvidersComp)
.then(fixture => { .then(fixture => {
@ -329,7 +357,7 @@ describe('test component builder', function() {
tcb.overrideViewProviders( tcb.overrideViewProviders(
TestViewProvidersComp, TestViewProvidersComp,
[provide(FancyService, {useClass: MockFancyService})] [{provide: FancyService, useClass: MockFancyService}]
) )
.createAsync(TestViewProvidersComp) .createAsync(TestViewProvidersComp)
.then(fixture => { .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) => { async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
tcb.createAsync(ExternalTemplateComp) tcb.createAsync(ExternalTemplateComp)
@ -348,7 +376,39 @@ describe('test component builder', function() {
expect(fixture.nativeElement) expect(fixture.nativeElement)
.toHaveText('from external template\n'); .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.
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.
it('should spy on injected component service',
async(inject([TestComponentBuilder, FancyService],
(tcb: TestComponentBuilder, service: FancyService) => {
let spy = spyOn(service, 'getValue').and.callThrough();
tcb
.createAsync(ExternalTemplateComp)
.then(fixture => {
// let spy = spyOn(service, 'getValue').and.callThrough();
fixture.detectChanges();
expect(spy.calls.count()).toBe(1, 'should be called once');
});
})), 10000); // Long timeout because of actual XHR to fetch template.
describe('(lifecycle hooks w/ MyIfParentComp)', () => { describe('(lifecycle hooks w/ MyIfParentComp)', () => {
let fixture: ComponentFixture<MyIfParentComp>; let fixture: ComponentFixture<MyIfParentComp>;
@ -472,6 +532,45 @@ describe('test component builder', function() {
}); });
}); });
describe('test component builder in beforeEach (comp w/ external template)', function() {
let fixture: ComponentFixture<ExternalTemplateComp>;
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<CompWithCompWithExternalTemplate>;
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? ///// //////// Testing Framework Bugs? /////
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
@ -495,7 +594,7 @@ describe('tcb.overrideProviders', () => {
tcb.overrideProviders( tcb.overrideProviders(
AnotherProvidersComp, AnotherProvidersComp,
[provide(HeroService, {useValue: {}})] [{provide: HeroService, useValue: {}}]
) )
.createAsync(AnotherProvidersComp); .createAsync(AnotherProvidersComp);
}))); })));

View File

@ -1,6 +1,6 @@
// Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts
/* tslint:disable:forin */ /* tslint:disable:forin */
import { Component, EventEmitter, Injectable, Input, Output, import { Component, EventEmitter, Injectable, Input, Output, Optional,
OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core'; OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
@ -13,6 +13,8 @@ import { Observable } from 'rxjs/Rx';
export class FancyService { export class FancyService {
value: string = 'real value'; value: string = 'real value';
getValue() { return this.value; }
getAsyncValue() { return Promise.resolve('async value'); } getAsyncValue() { return Promise.resolve('async value'); }
getObservableValue() { return Observable.of('observable value'); } getObservableValue() { return Observable.of('observable value'); }
@ -123,20 +125,36 @@ export class TestViewProvidersComp {
constructor(private fancyService: FancyService) {} constructor(private fancyService: FancyService) {}
} }
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
selector: 'external-template-comp', selector: 'external-template-comp',
templateUrl: 'bag-external-template.html' 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: `
<h3>comp-w-ext-comp</h3>
<external-template-comp></external-template-comp>
`,
directives: [ExternalTemplateComp]
})
export class CompWithCompWithExternalTemplate { }
@Component({ @Component({
selector: 'bad-template-comp', selector: 'bad-template-comp',
templateUrl: 'non-existant.html' templateUrl: 'non-existant.html'
}) })
export class BadTemplateUrl { } export class BadTemplateUrlComp { }
///////// MyIfChildComp //////// ///////// MyIfChildComp ////////
@ -222,3 +240,16 @@ export class MyIfParentComp implements OnInit {
this.toggleLabel = this.showChild ? 'Close' : 'Show'; 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
];

View File

@ -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;
}

View File

@ -1,6 +1,5 @@
import { bootstrap } from '@angular/platform-browser-dynamic'; import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { MyIfParentComp } from './bag';
bootstrap(AppComponent); bootstrap(AppComponent);
bootstrap(MyIfParentComp);

View File

@ -22,7 +22,5 @@
<body> <body>
<my-app>Loading...</my-app> <my-app>Loading...</my-app>
<hr>
<my-if-parent-comp>Loading MyIfParentComp ...</my-if-parent-comp>
</body> </body>
</html> </html>