1- import { Component , DebugElement , ElementRef , OnInit , Type , NgZone } from '@angular/core' ;
1+ import { Component , ElementRef , OnInit , Type , NgZone } from '@angular/core' ;
22import { ComponentFixture , TestBed } from '@angular/core/testing' ;
33import { By } from '@angular/platform-browser' ;
44import { BrowserAnimationsModule , NoopAnimationsModule } from '@angular/platform-browser/animations' ;
55import { Router } from '@angular/router' ;
66import { RouterTestingModule } from '@angular/router/testing' ;
77import { fireEvent , FireFunction , FireObject , getQueriesForElement , prettyDOM } from '@testing-library/dom' ;
8- import { RenderOptions , RenderResult } from './models' ;
8+ import { RenderComponentOptions , RenderDirectiveOptions , RenderResult } from './models' ;
99import { createSelectOptions , createType } from './user-events' ;
1010
1111@Component ( { selector : 'wrapper-component' , template : '' } )
1212class WrapperComponent implements OnInit {
13- constructor ( private elemtRef : ElementRef ) { }
13+ constructor ( private elementRef : ElementRef ) { }
1414
1515 ngOnInit ( ) {
16- this . elemtRef . nativeElement . removeAttribute ( 'ng-version' ) ;
16+ this . elementRef . nativeElement . removeAttribute ( 'ng-version' ) ;
1717 }
1818}
1919
20- export async function render < T > ( template : string , renderOptions : RenderOptions < T > ) : Promise < RenderResult > ;
21- export async function render < T > ( component : Type < T > , renderOptions ?: RenderOptions < T > ) : Promise < RenderResult > ;
22- export async function render < T > (
23- templateOrComponent : string | Type < T > ,
24- renderOptions : RenderOptions < T > = { } ,
25- ) : Promise < RenderResult > {
20+ export async function render < ComponentType > (
21+ component : Type < ComponentType > ,
22+ renderOptions ?: RenderComponentOptions < ComponentType > ,
23+ ) : Promise < RenderResult < ComponentType , ComponentType > > ;
24+ export async function render < DirectiveType , WrapperType = WrapperComponent > (
25+ component : Type < DirectiveType > ,
26+ renderOptions ?: RenderDirectiveOptions < DirectiveType , WrapperType > ,
27+ ) : Promise < RenderResult < DirectiveType , WrapperType > > ;
28+
29+ export async function render < SutType , WrapperType = SutType > (
30+ sut : Type < SutType > ,
31+ renderOptions : RenderComponentOptions < SutType > | RenderDirectiveOptions < SutType , WrapperType > = { } ,
32+ ) : Promise < RenderResult < SutType > > {
2633 const {
2734 detectChanges = true ,
2835 declarations = [ ] ,
2936 imports = [ ] ,
3037 providers = [ ] ,
3138 schemas = [ ] ,
3239 queries,
40+ template,
3341 wrapper = WrapperComponent ,
3442 componentProperties = { } ,
3543 componentProviders = [ ] ,
3644 excludeComponentDeclaration = false ,
37- routes,
38- } = renderOptions ;
39-
40- const isTemplate = typeof templateOrComponent === 'string' ;
41- const componentDeclarations = declareComponents ( {
42- templateOrComponent,
43- wrapper,
44- isTemplate,
45- excludeComponentDeclaration,
46- } ) ;
45+ routes
46+ } = renderOptions as RenderDirectiveOptions < SutType , WrapperType > ;
4747
4848 TestBed . configureTestingModule ( {
49- declarations : [ ... declarations , ... componentDeclarations ] ,
50- imports : addAutoImports ( { imports, routes } ) ,
49+ declarations : addAutoDeclarations ( sut , { declarations, excludeComponentDeclaration , template , wrapper } ) ,
50+ imports : addAutoImports ( { imports, routes} ) ,
5151 providers : [ ...providers ] ,
5252 schemas : [ ...schemas ] ,
5353 } ) ;
@@ -61,9 +61,8 @@ export async function render<T>(
6161 } ) ;
6262 }
6363
64- const fixture = isTemplate
65- ? createWrapperComponentFixture ( templateOrComponent as string , { wrapper, componentProperties } )
66- : createComponentFixture ( templateOrComponent as Type < T > , { componentProperties } ) ;
64+ const fixture = createComponentFixture ( sut , { template, wrapper } ) ;
65+ setComponentProperties ( fixture , { componentProperties } ) ;
6766
6867 await TestBed . compileComponents ( ) ;
6968
@@ -93,12 +92,16 @@ export async function render<T>(
9392
9493 const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath . getAttribute ( 'href' ) ;
9594
96- await zone . run ( ( ) => router . navigate ( [ basePath + href ] ) ) ;
95+ let result ;
96+ await zone . run ( ( ) => result = router . navigate ( [ basePath + href ] ) ) ;
9797 fixture . detectChanges ( ) ;
98+ return result ;
9899 }
100+ const debugElement = fixture . debugElement . query ( By . directive ( sut ) ) ;
99101
100102 return {
101103 fixture,
104+ debugElement,
102105 container : fixture . nativeElement ,
103106 debug : ( element = fixture . nativeElement ) => console . log ( prettyDOM ( element ) ) ,
104107 detectChanges : ( ) => fixture . detectChanges ( ) ,
@@ -107,86 +110,54 @@ export async function render<T>(
107110 type : createType ( eventsWithDetectChanges ) ,
108111 selectOptions : createSelectOptions ( eventsWithDetectChanges ) ,
109112 navigate,
110- } as any ;
113+ } ;
111114}
112115
113- /**
114- * Creates the wrapper component and sets its the template to the to-be-tested component
115- */
116- function createWrapperComponentFixture < T > (
117- template : string ,
118- {
119- wrapper,
120- componentProperties,
121- } : {
122- wrapper : RenderOptions < T > [ 'wrapper' ] ;
123- componentProperties : RenderOptions < T > [ 'componentProperties' ] ;
124- } ,
125- ) : ComponentFixture < any > {
126- TestBed . overrideComponent ( wrapper , {
127- set : {
128- template : template ,
129- } ,
130- } ) ;
131-
132- const fixture = TestBed . createComponent ( wrapper ) ;
133- // get the component selector, e.g. <foo color="green"> and <foo> results in foo
134- const componentSelector = template . match ( / \< ( .* ?) \ / ) || template . match ( / \< ( .* ?) \> / ) ;
135- if ( ! componentSelector ) {
136- throw Error ( `Template ${ template } is not valid.` ) ;
116+ function createComponentFixture < SutType > (
117+ component : Type < SutType > ,
118+ { template, wrapper } : Pick < RenderDirectiveOptions < SutType , any > , 'template' | 'wrapper' > ,
119+ ) : ComponentFixture < SutType > {
120+ if ( template ) {
121+ TestBed . overrideTemplate ( wrapper , template ) ;
122+ return TestBed . createComponent ( wrapper ) ;
137123 }
138-
139- const sut = fixture . debugElement . query ( By . css ( componentSelector [ 1 ] ) ) ;
140- setComponentProperties ( sut , { componentProperties } ) ;
141- return fixture ;
124+ return TestBed . createComponent ( component ) ;
142125}
143126
144- /**
145- * Creates the components and sets its properties
146- */
147- function createComponentFixture < T > (
148- component : Type < T > ,
149- {
150- componentProperties = { } ,
151- } : {
152- componentProperties : RenderOptions < T > [ 'componentProperties' ] ;
153- } ,
154- ) : ComponentFixture < T > {
155- const fixture = TestBed . createComponent ( component ) ;
156- setComponentProperties ( fixture , { componentProperties } ) ;
157- return fixture ;
158- }
159-
160- /**
161- * Set the component properties
162- */
163- function setComponentProperties < T > (
164- fixture : ComponentFixture < T > | DebugElement ,
165- {
166- componentProperties = { } ,
167- } : {
168- componentProperties : RenderOptions < T > [ 'componentProperties' ] ;
169- } ,
127+ function setComponentProperties < SutType > (
128+ fixture : ComponentFixture < SutType > ,
129+ { componentProperties = { } } : Pick < RenderDirectiveOptions < SutType , any > , 'componentProperties' > ,
170130) {
171131 for ( const key of Object . keys ( componentProperties ) ) {
172132 fixture . componentInstance [ key ] = componentProperties [ key ] ;
173133 }
174134 return fixture ;
175135}
176136
177- function declareComponents ( { isTemplate, wrapper, excludeComponentDeclaration, templateOrComponent } ) {
178- if ( isTemplate ) {
179- return [ wrapper ] ;
180- }
137+ function addAutoDeclarations < SutType > (
138+ component : Type < SutType > ,
139+ {
140+ declarations,
141+ excludeComponentDeclaration,
142+ template,
143+ wrapper,
144+ } : Pick <
145+ RenderDirectiveOptions < SutType , any > ,
146+ 'declarations' | 'excludeComponentDeclaration' | 'template' | 'wrapper'
147+ > ,
148+ ) {
149+ const wrappers = ( ) => {
150+ return template ? [ wrapper ] : [ ] ;
151+ } ;
181152
182- if ( excludeComponentDeclaration ) {
183- return [ ] ;
184- }
153+ const components = ( ) => {
154+ return excludeComponentDeclaration ? [ ] : [ component ] ;
155+ } ;
185156
186- return [ templateOrComponent ] ;
157+ return [ ... declarations , ... wrappers ( ) , ... components ( ) ] ;
187158}
188159
189- function addAutoImports ( { imports, routes } : Pick < RenderOptions < any > , 'imports' | 'routes' > ) {
160+ function addAutoImports ( { imports, routes } : Pick < RenderComponentOptions < any > , 'imports' | 'routes' > ) {
190161 const animations = ( ) => {
191162 const animationIsDefined =
192163 imports . indexOf ( NoopAnimationsModule ) > - 1 || imports . indexOf ( BrowserAnimationsModule ) > - 1 ;
0 commit comments