1
- import { inject , ComponentFixture , TestBed , async } from '@angular/core/testing' ;
1
+ import { inject , ComponentFixture , TestBed } from '@angular/core/testing' ;
2
2
import {
3
3
NgModule ,
4
4
Component ,
@@ -20,13 +20,11 @@ import {DomPortalOutlet} from './dom-portal-outlet';
20
20
21
21
describe ( 'Portals' , ( ) => {
22
22
23
- beforeEach ( async ( ( ) => {
24
- TestBed . configureTestingModule ( {
25
- imports : [ PortalModule , PortalTestModule ] ,
26
- } ) ;
27
-
28
- TestBed . compileComponents ( ) ;
29
- } ) ) ;
23
+ beforeEach ( ( ) => {
24
+ TestBed
25
+ . configureTestingModule ( { imports : [ PortalModule , PortalTestModule ] } )
26
+ . compileComponents ( ) ;
27
+ } ) ;
30
28
31
29
describe ( 'CdkPortalOutlet' , ( ) => {
32
30
let fixture : ComponentFixture < PortalTestApp > ;
@@ -37,28 +35,33 @@ describe('Portals', () => {
37
35
38
36
it ( 'should load a component into the portal' , ( ) => {
39
37
// Set the selectedHost to be a ComponentPortal.
40
- let testAppComponent = fixture . debugElement . componentInstance ;
41
- testAppComponent . selectedPortal = new ComponentPortal ( PizzaMsg ) ;
38
+ let testAppComponent = fixture . componentInstance ;
39
+ let componentPortal = new ComponentPortal ( PizzaMsg ) ;
40
+ let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
41
+
42
+ testAppComponent . selectedPortal = componentPortal ;
42
43
fixture . detectChanges ( ) ;
43
44
44
45
// Expect that the content of the attached portal is present.
45
- let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
46
46
expect ( hostContainer . textContent ) . toContain ( 'Pizza' ) ;
47
+ expect ( testAppComponent . portalOutlet . portal ) . toBe ( componentPortal ) ;
47
48
} ) ;
48
49
49
50
it ( 'should load a template into the portal' , ( ) => {
50
- let testAppComponent = fixture . debugElement . componentInstance ;
51
+ let testAppComponent = fixture . componentInstance ;
51
52
let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
52
-
53
53
let templatePortal = new TemplatePortal ( testAppComponent . templateRef , null ! ) ;
54
+
54
55
testAppComponent . selectedPortal = templatePortal ;
55
56
fixture . detectChanges ( ) ;
57
+
56
58
// Expect that the content of the attached portal is present and no context is projected
57
59
expect ( hostContainer . textContent ) . toContain ( 'Banana' ) ;
60
+ expect ( testAppComponent . portalOutlet . portal ) . toBe ( templatePortal ) ;
58
61
} ) ;
59
62
60
63
it ( 'should project template context bindings in the portal' , ( ) => {
61
- let testAppComponent = fixture . debugElement . componentInstance ;
64
+ let testAppComponent = fixture . componentInstance ;
62
65
let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
63
66
64
67
// TemplatePortal without context:
@@ -99,7 +102,7 @@ describe('Portals', () => {
99
102
100
103
it ( 'should dispose the host when destroyed' , ( ) => {
101
104
// Set the selectedHost to be a ComponentPortal.
102
- let testAppComponent = fixture . debugElement . componentInstance ;
105
+ let testAppComponent = fixture . componentInstance ;
103
106
testAppComponent . selectedPortal = new ComponentPortal ( PizzaMsg ) ;
104
107
105
108
fixture . detectChanges ( ) ;
@@ -114,7 +117,7 @@ describe('Portals', () => {
114
117
let chocolateInjector = new ChocolateInjector ( fixture . componentInstance . injector ) ;
115
118
116
119
// Set the selectedHost to be a ComponentPortal.
117
- let testAppComponent = fixture . debugElement . componentInstance ;
120
+ let testAppComponent = fixture . componentInstance ;
118
121
testAppComponent . selectedPortal = new ComponentPortal ( PizzaMsg , undefined , chocolateInjector ) ;
119
122
fixture . detectChanges ( ) ;
120
123
@@ -125,7 +128,7 @@ describe('Portals', () => {
125
128
} ) ;
126
129
127
130
it ( 'should load a <ng-template> portal' , ( ) => {
128
- let testAppComponent = fixture . debugElement . componentInstance ;
131
+ let testAppComponent = fixture . componentInstance ;
129
132
130
133
// Detect changes initially so that the component's ViewChildren are resolved.
131
134
fixture . detectChanges ( ) ;
@@ -140,7 +143,7 @@ describe('Portals', () => {
140
143
} ) ;
141
144
142
145
it ( 'should load a <ng-template> portal with the `*` sugar' , ( ) => {
143
- let testAppComponent = fixture . debugElement . componentInstance ;
146
+ let testAppComponent = fixture . componentInstance ;
144
147
145
148
// Detect changes initially so that the component's ViewChildren are resolved.
146
149
fixture . detectChanges ( ) ;
@@ -155,7 +158,7 @@ describe('Portals', () => {
155
158
} ) ;
156
159
157
160
it ( 'should load a <ng-template> portal with a binding' , ( ) => {
158
- let testAppComponent = fixture . debugElement . componentInstance ;
161
+ let testAppComponent = fixture . componentInstance ;
159
162
160
163
// Detect changes initially so that the component's ViewChildren are resolved.
161
164
fixture . detectChanges ( ) ;
@@ -177,7 +180,7 @@ describe('Portals', () => {
177
180
} ) ;
178
181
179
182
it ( 'should load a <ng-template> portal with an inner template' , ( ) => {
180
- let testAppComponent = fixture . debugElement . componentInstance ;
183
+ let testAppComponent = fixture . componentInstance ;
181
184
182
185
// Detect changes initially so that the component's ViewChildren are resolved.
183
186
fixture . detectChanges ( ) ;
@@ -199,7 +202,7 @@ describe('Portals', () => {
199
202
} ) ;
200
203
201
204
it ( 'should change the attached portal' , ( ) => {
202
- let testAppComponent = fixture . debugElement . componentInstance ;
205
+ let testAppComponent = fixture . componentInstance ;
203
206
204
207
// Detect changes initially so that the component's ViewChildren are resolved.
205
208
fixture . detectChanges ( ) ;
@@ -219,22 +222,22 @@ describe('Portals', () => {
219
222
} ) ;
220
223
221
224
it ( 'should detach the portal when it is set to null' , ( ) => {
222
- let testAppComponent = fixture . debugElement . componentInstance ;
225
+ let testAppComponent = fixture . componentInstance ;
223
226
testAppComponent . selectedPortal = new ComponentPortal ( PizzaMsg ) ;
224
227
225
228
fixture . detectChanges ( ) ;
226
229
expect ( testAppComponent . portalOutlet . hasAttached ( ) ) . toBe ( true ) ;
227
230
expect ( testAppComponent . portalOutlet . portal ) . toBe ( testAppComponent . selectedPortal ) ;
228
231
229
- testAppComponent . selectedPortal = null ;
232
+ testAppComponent . selectedPortal = null ! ;
230
233
fixture . detectChanges ( ) ;
231
234
232
235
expect ( testAppComponent . portalOutlet . hasAttached ( ) ) . toBe ( false ) ;
233
236
expect ( testAppComponent . portalOutlet . portal ) . toBeNull ( ) ;
234
237
} ) ;
235
238
236
239
it ( 'should set the `portal` when attaching a component portal programmatically' , ( ) => {
237
- let testAppComponent = fixture . debugElement . componentInstance ;
240
+ let testAppComponent = fixture . componentInstance ;
238
241
let portal = new ComponentPortal ( PizzaMsg ) ;
239
242
240
243
testAppComponent . portalOutlet . attachComponentPortal ( portal ) ;
@@ -243,7 +246,7 @@ describe('Portals', () => {
243
246
} ) ;
244
247
245
248
it ( 'should set the `portal` when attaching a template portal programmatically' , ( ) => {
246
- let testAppComponent = fixture . debugElement . componentInstance ;
249
+ let testAppComponent = fixture . componentInstance ;
247
250
fixture . detectChanges ( ) ;
248
251
249
252
testAppComponent . portalOutlet . attachTemplatePortal ( testAppComponent . cakePortal ) ;
@@ -252,7 +255,7 @@ describe('Portals', () => {
252
255
} ) ;
253
256
254
257
it ( 'should clear the portal reference on destroy' , ( ) => {
255
- let testAppComponent = fixture . debugElement . componentInstance ;
258
+ let testAppComponent = fixture . componentInstance ;
256
259
257
260
testAppComponent . selectedPortal = new ComponentPortal ( PizzaMsg ) ;
258
261
fixture . detectChanges ( ) ;
@@ -263,6 +266,40 @@ describe('Portals', () => {
263
266
264
267
expect ( testAppComponent . portalOutlet . portal ) . toBeNull ( ) ;
265
268
} ) ;
269
+
270
+ it ( 'should not clear programmatically-attached portals on init' , ( ) => {
271
+ fixture . destroy ( ) ;
272
+
273
+ const unboundFixture = TestBed . createComponent ( UnboundPortalTestApp ) ;
274
+
275
+ // Note: calling `detectChanges` here will cause a false positive.
276
+ // What we're testing is attaching before the first CD cycle.
277
+ unboundFixture . componentInstance . portalOutlet . attach ( new ComponentPortal ( PizzaMsg ) ) ;
278
+ unboundFixture . detectChanges ( ) ;
279
+
280
+ expect ( unboundFixture . nativeElement . querySelector ( '.portal-container' ) . textContent )
281
+ . toContain ( 'Pizza' ) ;
282
+ } ) ;
283
+
284
+ it ( 'should be considered attached when attaching using `attach`' , ( ) => {
285
+ expect ( fixture . componentInstance . portalOutlet . hasAttached ( ) ) . toBe ( false ) ;
286
+ fixture . componentInstance . portalOutlet . attach ( new ComponentPortal ( PizzaMsg ) ) ;
287
+ expect ( fixture . componentInstance . portalOutlet . hasAttached ( ) ) . toBe ( true ) ;
288
+ } ) ;
289
+
290
+ it ( 'should be considered attached when attaching using `attachComponentPortal`' , ( ) => {
291
+ expect ( fixture . componentInstance . portalOutlet . hasAttached ( ) ) . toBe ( false ) ;
292
+ fixture . componentInstance . portalOutlet . attachComponentPortal ( new ComponentPortal ( PizzaMsg ) ) ;
293
+ expect ( fixture . componentInstance . portalOutlet . hasAttached ( ) ) . toBe ( true ) ;
294
+ } ) ;
295
+
296
+ it ( 'should be considered attached when attaching using `attachTemplatePortal`' , ( ) => {
297
+ const instance = fixture . componentInstance ;
298
+ expect ( instance . portalOutlet . hasAttached ( ) ) . toBe ( false ) ;
299
+ instance . portalOutlet . attachTemplatePortal ( new TemplatePortal ( instance . templateRef , null ! ) ) ;
300
+ expect ( instance . portalOutlet . hasAttached ( ) ) . toBe ( true ) ;
301
+ } ) ;
302
+
266
303
} ) ;
267
304
268
305
describe ( 'DomPortalOutlet' , ( ) => {
@@ -345,7 +382,7 @@ describe('Portals', () => {
345
382
it ( 'should attach and detach a template portal with a binding' , ( ) => {
346
383
let fixture = TestBed . createComponent ( PortalTestApp ) ;
347
384
348
- let testAppComponent = fixture . debugElement . componentInstance ;
385
+ let testAppComponent = fixture . componentInstance ;
349
386
350
387
// Detect changes initially so that the component's ViewChildren are resolved.
351
388
fixture . detectChanges ( ) ;
@@ -484,7 +521,7 @@ class PortalTestApp {
484
521
@ViewChild ( CdkPortalOutlet ) portalOutlet : CdkPortalOutlet ;
485
522
@ViewChild ( 'templateRef' , { read : TemplateRef } ) templateRef : TemplateRef < any > ;
486
523
487
- selectedPortal : Portal < any > ;
524
+ selectedPortal : Portal < any > | undefined ;
488
525
fruit : string = 'Banana' ;
489
526
fruits = [ 'Apple' , 'Pineapple' , 'Durian' ] ;
490
527
@@ -508,9 +545,27 @@ class PortalTestApp {
508
545
509
546
}
510
547
548
+ /** Test-bed component that contains a portal outlet and a couple of template portals. */
549
+ @Component ( {
550
+ template : `
551
+ <div class="portal-container">
552
+ <ng-template cdkPortalOutlet></ng-template>
553
+ </div>
554
+ ` ,
555
+ } )
556
+ class UnboundPortalTestApp {
557
+ @ViewChild ( CdkPortalOutlet ) portalOutlet : CdkPortalOutlet ;
558
+ }
559
+
511
560
// Create a real (non-test) NgModule as a workaround for
512
561
// https://github.com/angular/angular/issues/10760
513
- const TEST_COMPONENTS = [ PortalTestApp , ArbitraryViewContainerRefComponent , PizzaMsg ] ;
562
+ const TEST_COMPONENTS = [
563
+ PortalTestApp ,
564
+ UnboundPortalTestApp ,
565
+ ArbitraryViewContainerRefComponent ,
566
+ PizzaMsg
567
+ ] ;
568
+
514
569
@NgModule ( {
515
570
imports : [ CommonModule , PortalModule ] ,
516
571
exports : TEST_COMPONENTS ,
0 commit comments