@@ -7,37 +7,131 @@ import { By } from '@angular/platform-browser';
7
7
import { DebugElement } from '@angular/core' ;
8
8
9
9
import {
10
- addMatchers , newEvent ,
11
- ActivatedRoute , ActivatedRouteStub , Router , RouterStub
10
+ ActivatedRoute , ActivatedRouteStub , newEvent , Router , RouterStub
12
11
} from '../../testing' ;
13
12
14
- import { HEROES , FakeHeroService } from '../model/testing' ;
15
-
16
- import { HeroModule } from './hero.module' ;
13
+ import { Hero } from '../model' ;
17
14
import { HeroDetailComponent } from './hero-detail.component' ;
18
15
import { HeroDetailService } from './hero-detail.service' ;
19
- import { Hero , HeroService } from '../model ' ;
16
+ import { HeroModule } from './hero.module ' ;
20
17
21
18
////// Testing Vars //////
22
19
let activatedRoute : ActivatedRouteStub ;
23
20
let comp : HeroDetailComponent ;
24
21
let fixture : ComponentFixture < HeroDetailComponent > ;
25
22
let page : Page ;
26
23
27
- ////////// Tests ////////////////////
28
-
24
+ ////// Tests //////
29
25
describe ( 'HeroDetailComponent' , ( ) => {
30
-
31
- beforeEach ( async ( ( ) => {
32
- addMatchers ( ) ;
26
+ beforeEach ( ( ) => {
33
27
activatedRoute = new ActivatedRouteStub ( ) ;
28
+ } ) ;
29
+ describe ( 'with HeroModule setup' , heroModuleSetup ) ;
30
+ describe ( 'when override its provided HeroDetailService' , overrideSetup ) ;
31
+ describe ( 'with FormsModule setup' , formsModuleSetup ) ;
32
+ describe ( 'with SharedModule setup' , sharedModuleSetup ) ;
33
+ } ) ;
34
34
35
+ ////////////////////
36
+ function overrideSetup ( ) {
37
+ // #docregion stub-hds
38
+ class StubHeroDetailService {
39
+ testHero = new Hero ( 42 , 'Test Hero' ) ;
40
+
41
+ getHero ( id : number | string ) : Promise < Hero > {
42
+ return Promise . resolve ( true ) . then ( ( ) => Object . assign ( { } , this . testHero ) ) ;
43
+ }
44
+
45
+ saveHero ( hero : Hero ) : Promise < Hero > {
46
+ return Promise . resolve ( true ) . then ( ( ) => Object . assign ( this . testHero , hero ) ) ;
47
+ }
48
+ }
49
+ // #enddocregion stub-hds
50
+
51
+ // the `id` value is irrelevant because ignored by service stub
52
+ beforeEach ( ( ) => activatedRoute . testParams = { id : 99999 } ) ;
53
+
54
+ // #docregion setup-override
55
+ beforeEach ( async ( ( ) => {
35
56
TestBed . configureTestingModule ( {
36
- imports : [ HeroModule ] ,
57
+ imports : [ HeroModule ] ,
58
+ providers : [
59
+ { provide : ActivatedRoute , useValue : activatedRoute } ,
60
+ { provide : Router , useClass : RouterStub } ,
61
+ // #enddocregion setup-override
62
+ // HeroDetailService at this level is IRRELEVANT!
63
+ { provide : HeroDetailService , useValue : { } }
64
+ // #docregion setup-override
65
+ ]
66
+ } )
67
+
68
+ // Override component's own provider
69
+ // #docregion override-component-method
70
+ . overrideComponent ( HeroDetailComponent , {
71
+ set : {
72
+ providers : [
73
+ { provide : HeroDetailService , useClass : StubHeroDetailService }
74
+ ]
75
+ }
76
+ } )
77
+ // #enddocregion override-component-method
78
+
79
+ . compileComponents ( ) ;
80
+ } ) ) ;
81
+ // #enddocregion setup-override
82
+
83
+ // #docregion override-tests
84
+ let hds : StubHeroDetailService ;
85
+
86
+ beforeEach ( async ( ( ) => {
87
+ createComponent ( ) ;
88
+ // get the component's injected StubHeroDetailService
89
+ hds = fixture . debugElement . injector . get ( HeroDetailService ) ;
90
+ } ) ) ;
91
+
92
+ it ( 'should display stub hero\'s name' , ( ) => {
93
+ expect ( page . nameDisplay . textContent ) . toBe ( hds . testHero . name ) ;
94
+ } ) ;
95
+
96
+ it ( 'should save stub hero change' , fakeAsync ( ( ) => {
97
+ const origName = hds . testHero . name ;
98
+ const newName = 'New Name' ;
99
+
100
+ page . nameInput . value = newName ;
101
+ page . nameInput . dispatchEvent ( newEvent ( 'input' ) ) ; // tell Angular
102
+
103
+ expect ( comp . hero . name ) . toBe ( newName , 'component hero has new name' ) ;
104
+ expect ( hds . testHero . name ) . toBe ( origName , 'service hero unchanged before save' ) ;
105
+
106
+ page . saveBtn . triggerEventHandler ( 'click' , null ) ;
107
+ tick ( ) ; // wait for async save to complete
108
+ expect ( hds . testHero . name ) . toBe ( newName , 'service hero has new name after save' ) ;
109
+ expect ( page . navSpy . calls . any ( ) ) . toBe ( true , 'router.navigate called' ) ;
110
+ } ) ) ;
111
+ // #enddocregion override-tests
112
+
113
+ it ( 'fixture injected service is not the component injected service' ,
114
+ inject ( [ HeroDetailService ] , ( service : HeroDetailService ) => {
115
+
116
+ expect ( service ) . toEqual ( { } , 'service injected from fixture' ) ;
117
+ expect ( hds ) . toBeTruthy ( 'service injected into component' ) ;
118
+ } ) ) ;
119
+ }
37
120
38
- // DON'T RE-DECLARE because already declared in HeroModule
39
- // declarations: [HeroDetailComponent, TitleCasePipe], // No!
121
+ ////////////////////
122
+ import { HEROES , FakeHeroService } from '../model/testing' ;
123
+ import { HeroService } from '../model' ;
124
+
125
+ const firstHero = HEROES [ 0 ] ;
40
126
127
+ function heroModuleSetup ( ) {
128
+ // #docregion setup-hero-module
129
+ beforeEach ( async ( ( ) => {
130
+ TestBed . configureTestingModule ( {
131
+ imports : [ HeroModule ] ,
132
+ // #enddocregion setup-hero-module
133
+ // declarations: [ HeroDetailComponent ], // NO! DOUBLE DECLARATION
134
+ // #docregion setup-hero-module
41
135
providers : [
42
136
{ provide : ActivatedRoute , useValue : activatedRoute } ,
43
137
{ provide : HeroService , useClass : FakeHeroService } ,
@@ -46,13 +140,14 @@ describe('HeroDetailComponent', () => {
46
140
} )
47
141
. compileComponents ( ) ;
48
142
} ) ) ;
143
+ // #enddocregion setup-hero-module
49
144
50
145
// #docregion route-good-id
51
- describe ( 'when navigate to hero id=' + HEROES [ 0 ] . id , ( ) => {
146
+ describe ( 'when navigate to existing hero' , ( ) => {
52
147
let expectedHero : Hero ;
53
148
54
149
beforeEach ( async ( ( ) => {
55
- expectedHero = HEROES [ 0 ] ;
150
+ expectedHero = firstHero ;
56
151
activatedRoute . testParams = { id : expectedHero . id } ;
57
152
createComponent ( ) ;
58
153
} ) ) ;
@@ -76,7 +171,7 @@ describe('HeroDetailComponent', () => {
76
171
77
172
it ( 'should navigate when click save and save resolves' , fakeAsync ( ( ) => {
78
173
page . saveBtn . triggerEventHandler ( 'click' , null ) ;
79
- tick ( ) ; // wait for async save to " complete" before navigating
174
+ tick ( ) ; // wait for async save to complete
80
175
expect ( page . navSpy . calls . any ( ) ) . toBe ( true , 'router.navigate called' ) ;
81
176
} ) ) ;
82
177
@@ -91,8 +186,7 @@ describe('HeroDetailComponent', () => {
91
186
// dispatch a DOM event so that Angular learns of input value change.
92
187
page . nameInput . dispatchEvent ( newEvent ( 'input' ) ) ;
93
188
94
- // detectChanges() makes [(ngModel)] push input value to component property
95
- // and Angular updates the output span through the title pipe
189
+ // Tell Angular to update the output span through the title pipe
96
190
fixture . detectChanges ( ) ;
97
191
98
192
expect ( page . nameDisplay . textContent ) . toBe ( titleCaseName ) ;
@@ -131,10 +225,8 @@ describe('HeroDetailComponent', () => {
131
225
} ) ;
132
226
// #enddocregion route-bad-id
133
227
134
- ///////////////////////////
135
-
136
228
// Why we must use `fixture.debugElement.injector` in `Page()`
137
- it ( 'cannot use `inject` to get component\'s provided service ' , ( ) => {
229
+ it ( 'cannot use `inject` to get component\'s provided HeroDetailService ' , ( ) => {
138
230
let service : HeroDetailService ;
139
231
fixture = TestBed . createComponent ( HeroDetailComponent ) ;
140
232
expect (
@@ -148,7 +240,64 @@ describe('HeroDetailComponent', () => {
148
240
service = fixture . debugElement . injector . get ( HeroDetailService ) ;
149
241
expect ( service ) . toBeDefined ( 'debugElement.injector' ) ;
150
242
} ) ;
151
- } ) ;
243
+ }
244
+
245
+ /////////////////////
246
+ import { FormsModule } from '@angular/forms' ;
247
+ import { TitleCasePipe } from '../shared/title-case.pipe' ;
248
+
249
+ function formsModuleSetup ( ) {
250
+ // #docregion setup-forms-module
251
+ beforeEach ( async ( ( ) => {
252
+ TestBed . configureTestingModule ( {
253
+ imports : [ FormsModule ] ,
254
+ declarations : [ HeroDetailComponent , TitleCasePipe ] ,
255
+ providers : [
256
+ { provide : ActivatedRoute , useValue : activatedRoute } ,
257
+ { provide : HeroService , useClass : FakeHeroService } ,
258
+ { provide : Router , useClass : RouterStub } ,
259
+ ]
260
+ } )
261
+ . compileComponents ( ) ;
262
+ } ) ) ;
263
+ // #enddocregion setup-forms-module
264
+
265
+ it ( 'should display 1st hero\'s name' , fakeAsync ( ( ) => {
266
+ const expectedHero = firstHero ;
267
+ activatedRoute . testParams = { id : expectedHero . id } ;
268
+ createComponent ( ) . then ( ( ) => {
269
+ expect ( page . nameDisplay . textContent ) . toBe ( expectedHero . name ) ;
270
+ } ) ;
271
+ } ) ) ;
272
+ }
273
+
274
+ ///////////////////////
275
+ import { SharedModule } from '../shared/shared.module' ;
276
+
277
+ function sharedModuleSetup ( ) {
278
+ // #docregion setup-shared-module
279
+ beforeEach ( async ( ( ) => {
280
+ TestBed . configureTestingModule ( {
281
+ imports : [ SharedModule ] ,
282
+ declarations : [ HeroDetailComponent ] ,
283
+ providers : [
284
+ { provide : ActivatedRoute , useValue : activatedRoute } ,
285
+ { provide : HeroService , useClass : FakeHeroService } ,
286
+ { provide : Router , useClass : RouterStub } ,
287
+ ]
288
+ } )
289
+ . compileComponents ( ) ;
290
+ } ) ) ;
291
+ // #enddocregion setup-shared-module
292
+
293
+ it ( 'should display 1st hero\'s name' , fakeAsync ( ( ) => {
294
+ const expectedHero = firstHero ;
295
+ activatedRoute . testParams = { id : expectedHero . id } ;
296
+ createComponent ( ) . then ( ( ) => {
297
+ expect ( page . nameDisplay . textContent ) . toBe ( expectedHero . name ) ;
298
+ } ) ;
299
+ } ) ) ;
300
+ }
152
301
153
302
/////////// Helpers /////
154
303
@@ -185,9 +334,10 @@ class Page {
185
334
const compInjector = fixture . debugElement . injector ;
186
335
const hds = compInjector . get ( HeroDetailService ) ;
187
336
const router = compInjector . get ( Router ) ;
337
+
188
338
this . gotoSpy = spyOn ( comp , 'gotoList' ) . and . callThrough ( ) ;
189
- this . saveSpy = spyOn ( hds , 'saveHero' ) . and . callThrough ( ) ;
190
339
this . navSpy = spyOn ( router , 'navigate' ) ;
340
+ this . saveSpy = spyOn ( hds , 'saveHero' ) . and . callThrough ( ) ;
191
341
}
192
342
193
343
/** Add page elements after hero arrives */
0 commit comments