88
99import { Component , NgModule , ViewEncapsulation } from '@angular/core' ;
1010import { TestBed } from '@angular/core/testing' ;
11- import { BrowserModule } from '../../index' ;
11+ import { BrowserModule , createApplication } from '../../index' ;
1212import { expect } from '@angular/private/testing/matchers' ;
1313import { isNode } from '@angular/private/testing' ;
1414
@@ -23,6 +23,10 @@ describe('ShadowDOM Support', () => {
2323 TestBed . configureTestingModule ( { imports : [ TestModule ] } ) ;
2424 } ) ;
2525
26+ beforeEach ( ( ) => {
27+ for ( const node of Array . from ( document . body . childNodes ) ) node . remove ( ) ;
28+ } ) ;
29+
2630 it ( 'should attach and use a shadowRoot when ViewEncapsulation.ShadowDom is set' , ( ) => {
2731 const compEl = TestBed . createComponent ( ShadowComponent ) . nativeElement ;
2832 expect ( compEl . shadowRoot ! . textContent ) . toEqual ( 'Hello World' ) ;
@@ -81,6 +85,153 @@ describe('ShadowDOM Support', () => {
8185 expect ( articleContent . assignedSlot ) . toBe ( articleSlot ) ;
8286 expect ( articleSubcontent . assignedSlot ) . toBe ( articleSlot ) ;
8387 } ) ;
88+
89+ it ( 'should support bootstrapping under a shadow root' , async ( ) => {
90+ @Component ( {
91+ selector : 'app-root' ,
92+ template : '<div>Hello, World!</div>' ,
93+ styles : `
94+ div {
95+ color: red;
96+ }
97+ ` ,
98+ } )
99+ class Root { }
100+
101+ const container = document . createElement ( 'div' ) ;
102+ container . attachShadow ( { mode : 'open' } ) ;
103+ const root = document . createElement ( 'app-root' ) ;
104+ container . shadowRoot ! . append ( root ) ;
105+ document . body . append ( container ) ;
106+
107+ const appRef = await createApplication ( ) ;
108+ appRef . bootstrap ( Root , root ) ;
109+
110+ expect ( getComputedStyle ( root . querySelector ( 'div' ) ! ) . color ) . toBe ( 'rgb(255, 0, 0)' ) ;
111+
112+ expect ( document . head . innerHTML ) . not . toContain ( '<style>' ) ;
113+
114+ appRef . destroy ( ) ;
115+
116+ expect ( container . shadowRoot ! . innerHTML ) . not . toContain ( '<style>' ) ;
117+ } ) ;
118+
119+ it ( 'should support bootstrapping multiple root components under different shadow roots' , async ( ) => {
120+ const appRef = await createApplication ( ) ;
121+
122+ {
123+ @Component ( {
124+ selector : 'app-root' ,
125+ template : '<div>Hello, World!</div>' ,
126+ styles : `
127+ div {
128+ color: red;
129+ }
130+ ` ,
131+ } )
132+ class Root { }
133+
134+ const container = document . createElement ( 'div' ) ;
135+ container . attachShadow ( { mode : 'open' } ) ;
136+ const root = document . createElement ( 'app-root' ) ;
137+ container . shadowRoot ! . append ( root ) ;
138+ document . body . append ( container ) ;
139+
140+ appRef . bootstrap ( Root , root ) ;
141+ expect ( getComputedStyle ( root . querySelector ( 'div' ) ! ) . color ) . toBe ( 'rgb(255, 0, 0)' ) ;
142+ }
143+
144+ {
145+ @Component ( {
146+ selector : 'app-root-2' ,
147+ template : '<div>Hello, World!</div>' ,
148+ styles : `
149+ div {
150+ color: lime;
151+ }
152+ ` ,
153+ } )
154+ class Root { }
155+
156+ const container = document . createElement ( 'div' ) ;
157+ container . attachShadow ( { mode : 'open' } ) ;
158+ const root = document . createElement ( 'app-root-2' ) ;
159+ container . shadowRoot ! . append ( root ) ;
160+ document . body . append ( container ) ;
161+
162+ appRef . bootstrap ( Root , root ) ;
163+ expect ( getComputedStyle ( root . querySelector ( 'div' ) ! ) . color ) . toBe ( 'rgb(0, 255, 0)' ) ;
164+ }
165+
166+ expect ( document . head . innerHTML ) . not . toContain ( '<style>' ) ;
167+
168+ appRef . destroy ( ) ;
169+
170+ const containers = Array . from ( document . querySelectorAll ( 'div' ) ) ;
171+ const [ shadowRoot1 , shadowRoot2 ] = containers . map ( ( container ) => container . shadowRoot ! ) ;
172+ expect ( shadowRoot1 . innerHTML ) . not . toContain ( '<style>' ) ;
173+ expect ( shadowRoot2 . innerHTML ) . not . toContain ( '<style>' ) ;
174+ } ) ;
175+
176+ it ( 'should not leak styles into previously used shadow roots' , async ( ) => {
177+ const container1 = document . createElement ( 'div' ) ;
178+ container1 . attachShadow ( { mode : 'open' } ) ;
179+ document . body . append ( container1 ) ;
180+
181+ const container2 = document . createElement ( 'div' ) ;
182+ container2 . attachShadow ( { mode : 'open' } ) ;
183+ document . body . append ( container2 ) ;
184+
185+ const appRef = await createApplication ( ) ;
186+
187+ {
188+ @Component ( {
189+ selector : 'app-root' ,
190+ template : '<div>Hello, World!</div>' ,
191+ styles : `
192+ div {
193+ color: red;
194+ }
195+ ` ,
196+ } )
197+ class Root { }
198+
199+ const rootEl = document . createElement ( 'app-root' ) ;
200+ container1 . shadowRoot ! . append ( rootEl ) ;
201+
202+ const root = appRef . bootstrap ( Root , rootEl ) ;
203+ expect ( getComputedStyle ( rootEl . querySelector ( 'div' ) ! ) . color ) . toBe ( 'rgb(255, 0, 0)' ) ;
204+ root . destroy ( ) ;
205+
206+ expect ( container1 . shadowRoot ! . innerHTML ) . not . toContain ( '<style>' ) ;
207+ }
208+
209+ {
210+ @Component ( {
211+ selector : 'app-root-2' ,
212+ template : '<div>Hello, World!</div>' ,
213+ styles : `
214+ div {
215+ color: lime;
216+ }
217+ ` ,
218+ } )
219+ class Root2 { }
220+
221+ const root2El = document . createElement ( 'app-root-2' ) ;
222+ container2 . shadowRoot ! . append ( root2El ) ;
223+
224+ const root = appRef . bootstrap ( Root2 , root2El ) ;
225+ expect ( getComputedStyle ( root2El . querySelector ( 'div' ) ! ) . color ) . toBe ( 'rgb(0, 255, 0)' ) ;
226+
227+ // Should not leak into `container1`.
228+ expect ( container1 . shadowRoot ! . innerHTML ) . not . toContain ( '<style>' ) ;
229+
230+ root . destroy ( ) ;
231+ }
232+
233+ appRef . destroy ( ) ;
234+ } ) ;
84235} ) ;
85236
86237@Component ( {
0 commit comments