@@ -168,6 +168,254 @@ describe('TestBed', () => {
168168 } ) ;
169169} ) ;
170170
171+ describe ( 'TestBed with Standalone types' , ( ) => {
172+ beforeEach ( ( ) => {
173+ getTestBed ( ) . resetTestingModule ( ) ;
174+ } ) ;
175+
176+ it ( 'should override providers on standalone component itself' , ( ) => {
177+ const A = new InjectionToken ( 'A' ) ;
178+
179+ @Component ( {
180+ standalone : true ,
181+ template : '{{ a }}' ,
182+ providers : [ { provide : A , useValue : 'A' } ] ,
183+ } )
184+ class MyStandaloneComp {
185+ constructor ( @Inject ( A ) public a : string ) { }
186+ }
187+
188+ // NOTE: the `TestBed.configureTestingModule` is load-bearing here: it instructs
189+ // TestBed to examine and override providers in dependencies.
190+ TestBed . configureTestingModule ( { imports : [ MyStandaloneComp ] } ) ;
191+ TestBed . overrideProvider ( A , { useValue : 'Overridden A' } ) ;
192+
193+ const fixture = TestBed . createComponent ( MyStandaloneComp ) ;
194+ fixture . detectChanges ( ) ;
195+
196+ expect ( fixture . nativeElement . innerHTML ) . toBe ( 'Overridden A' ) ;
197+ } ) ;
198+
199+ it ( 'should override providers in standalone component dependencies via overrideProvider' , ( ) => {
200+ const A = new InjectionToken ( 'A' ) ;
201+ @NgModule ( {
202+ providers : [ { provide : A , useValue : 'A' } ] ,
203+ } )
204+ class ComponentDependenciesModule {
205+ }
206+
207+ @Component ( {
208+ standalone : true ,
209+ template : '{{ a }}' ,
210+ imports : [ ComponentDependenciesModule ] ,
211+ } )
212+ class MyStandaloneComp {
213+ constructor ( @Inject ( A ) public a : string ) { }
214+ }
215+
216+ // NOTE: the `TestBed.configureTestingModule` is load-bearing here: it instructs
217+ // TestBed to examine and override providers in dependencies.
218+ TestBed . configureTestingModule ( { imports : [ MyStandaloneComp ] } ) ;
219+ TestBed . overrideProvider ( A , { useValue : 'Overridden A' } ) ;
220+
221+ const fixture = TestBed . createComponent ( MyStandaloneComp ) ;
222+ fixture . detectChanges ( ) ;
223+
224+ expect ( fixture . nativeElement . innerHTML ) . toBe ( 'Overridden A' ) ;
225+ } ) ;
226+
227+ it ( 'should override providers in standalone component dependencies via overrideModule' , ( ) => {
228+ const A = new InjectionToken ( 'A' ) ;
229+ @NgModule ( {
230+ providers : [ { provide : A , useValue : 'A' } ] ,
231+ } )
232+ class ComponentDependenciesModule {
233+ }
234+
235+ @Component ( {
236+ standalone : true ,
237+ template : '{{ a }}' ,
238+ imports : [ ComponentDependenciesModule ] ,
239+ } )
240+ class MyStandaloneComp {
241+ constructor ( @Inject ( A ) public a : string ) { }
242+ }
243+
244+ // NOTE: the `TestBed.configureTestingModule` is *not* needed here, since the TestBed
245+ // knows which NgModule was overridden and needs re-compilation.
246+ TestBed . overrideModule (
247+ ComponentDependenciesModule , { set : { providers : [ { provide : A , useValue : 'Overridden A' } ] } } ) ;
248+
249+ const fixture = TestBed . createComponent ( MyStandaloneComp ) ;
250+ fixture . detectChanges ( ) ;
251+
252+ expect ( fixture . nativeElement . innerHTML ) . toBe ( 'Overridden A' ) ;
253+ } ) ;
254+
255+ it ( 'should allow overriding a template of a standalone component' , ( ) => {
256+ @Component ( {
257+ standalone : true ,
258+ template : 'Original' ,
259+ } )
260+ class MyStandaloneComp {
261+ }
262+
263+ // NOTE: the `TestBed.configureTestingModule` call is *not* required here, since TestBed already
264+ // knows that the `MyStandaloneComp` should be overridden/recompiled.
265+ TestBed . overrideComponent ( MyStandaloneComp , { set : { template : 'Overridden' } } ) ;
266+
267+ const fixture = TestBed . createComponent ( MyStandaloneComp ) ;
268+ fixture . detectChanges ( ) ;
269+
270+ expect ( fixture . nativeElement . innerHTML ) . toBe ( 'Overridden' ) ;
271+ } ) ;
272+
273+ it ( 'should allow overriding the set of directives and pipes used in a standalone component' ,
274+ ( ) => {
275+ @Directive ( {
276+ selector : '[dir]' ,
277+ standalone : true ,
278+ host : { '[id]' : 'id' } ,
279+ } )
280+ class MyStandaloneDirectiveA {
281+ id = 'A' ;
282+ }
283+
284+ @Directive ( {
285+ selector : '[dir]' ,
286+ standalone : true ,
287+ host : { '[id]' : 'id' } ,
288+ } )
289+ class MyStandaloneDirectiveB {
290+ id = 'B' ;
291+ }
292+
293+ @Pipe ( { name : 'pipe' , standalone : true } )
294+ class MyStandalonePipeA {
295+ transform ( value : string ) : string {
296+ return `transformed ${ value } (A)` ;
297+ }
298+ }
299+ @Pipe ( { name : 'pipe' , standalone : true } )
300+ class MyStandalonePipeB {
301+ transform ( value : string ) : string {
302+ return `transformed ${ value } (B)` ;
303+ }
304+ }
305+
306+ @Component ( {
307+ standalone : true ,
308+ template : '<div dir>{{ name | pipe }}</div>' ,
309+ imports : [ MyStandalonePipeA , MyStandaloneDirectiveA ] ,
310+ } )
311+ class MyStandaloneComp {
312+ name = 'MyStandaloneComp' ;
313+ }
314+
315+ // NOTE: the `TestBed.configureTestingModule` call is *not* required here, since TestBed
316+ // already knows that the `MyStandaloneComp` should be overridden/recompiled.
317+ TestBed . overrideComponent (
318+ MyStandaloneComp , { set : { imports : [ MyStandalonePipeB , MyStandaloneDirectiveB ] } } ) ;
319+
320+ const fixture = TestBed . createComponent ( MyStandaloneComp ) ;
321+ fixture . detectChanges ( ) ;
322+
323+ const rootElement = fixture . nativeElement . firstChild ;
324+ expect ( rootElement . id ) . toBe ( 'B' ) ;
325+ expect ( rootElement . innerHTML ) . toBe ( 'transformed MyStandaloneComp (B)' ) ;
326+ } ) ;
327+
328+ it ( 'should reflect overrides on imported standalone directive' , ( ) => {
329+ @Directive ( {
330+ selector : '[dir]' ,
331+ standalone : true ,
332+ host : { '[id]' : 'id' } ,
333+ } )
334+ class DepStandaloneDirective {
335+ id = 'A' ;
336+ }
337+
338+ @Component ( {
339+ selector : 'standalone-cmp' ,
340+ standalone : true ,
341+ template : 'Original MyStandaloneComponent' ,
342+ } )
343+ class DepStandaloneComponent {
344+ id = 'A' ;
345+ }
346+
347+ @Component ( {
348+ standalone : true ,
349+ template : '<standalone-cmp dir>Hello world!</standalone-cmp>' ,
350+ imports : [ DepStandaloneDirective , DepStandaloneComponent ] ,
351+ } )
352+ class RootStandaloneComp {
353+ }
354+
355+ // NOTE: the `TestBed.configureTestingModule` call is *not* required here, since TestBed
356+ // already knows which Components/Directives are overridden and should be recompiled.
357+ TestBed . overrideComponent (
358+ DepStandaloneComponent , { set : { template : 'Overridden MyStandaloneComponent' } } ) ;
359+ TestBed . overrideDirective ( DepStandaloneDirective , { set : { host : { '[id]' : '\'Overridden\'' } } } ) ;
360+
361+ const fixture = TestBed . createComponent ( RootStandaloneComp ) ;
362+ fixture . detectChanges ( ) ;
363+
364+ const rootElement = fixture . nativeElement . firstChild ;
365+
366+ expect ( rootElement . id ) . toBe ( 'Overridden' ) ;
367+ expect ( rootElement . innerHTML ) . toBe ( 'Overridden MyStandaloneComponent' ) ;
368+ } ) ;
369+
370+ describe ( 'NgModules as dependencies' , ( ) => {
371+ @Component ( {
372+ selector : 'test-cmp' ,
373+ template : '...' ,
374+ } )
375+ class TestComponent {
376+ testField = 'default' ;
377+ }
378+
379+ @Component ( {
380+ selector : 'test-cmp' ,
381+ template : '...' ,
382+ } )
383+ class MockTestComponent {
384+ testField = 'overridden' ;
385+ }
386+
387+ @NgModule ( {
388+ declarations : [ TestComponent ] ,
389+ exports : [ TestComponent ] ,
390+ } )
391+ class TestModule {
392+ }
393+
394+ @Component ( {
395+ standalone : true ,
396+ selector : 'app-root' ,
397+ template : `<test-cmp #testCmpCtrl></test-cmp>` ,
398+ imports : [ TestModule ] ,
399+ } )
400+ class AppComponent {
401+ @ViewChild ( 'testCmpCtrl' , { static : true } ) testCmpCtrl ! : TestComponent ;
402+ }
403+
404+ it ( 'should allow declarations and exports overrides on an imported NgModule' , ( ) => {
405+ // replace TestComponent with MockTestComponent
406+ TestBed . overrideModule ( TestModule , {
407+ remove : { declarations : [ TestComponent ] , exports : [ TestComponent ] } ,
408+ add : { declarations : [ MockTestComponent ] , exports : [ MockTestComponent ] }
409+ } ) ;
410+ const fixture = TestBed . createComponent ( AppComponent ) ;
411+ fixture . detectChanges ( ) ;
412+
413+ const app = fixture . componentInstance ;
414+ expect ( app . testCmpCtrl . testField ) . toBe ( 'overridden' ) ;
415+ } ) ;
416+ } ) ;
417+ } ) ;
418+
171419describe ( 'TestBed' , ( ) => {
172420 beforeEach ( ( ) => {
173421 getTestBed ( ) . resetTestingModule ( ) ;
0 commit comments