66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { NgIf } from '@angular/common' ;
9+ import { CommonModule , DOCUMENT } from '@angular/common' ;
10+ import { TestBed } from '@angular/core/testing' ;
1011import { withBody } from '@angular/private/testing' ;
1112
12- import { ChangeDetectionStrategy , DoCheck , OnInit } from '../../src/core' ;
13+ import { Component , DoCheck , OnInit , Renderer2 , RendererFactory2 } from '../../src/core' ;
1314import { whenRendered } from '../../src/render3/component' ;
14- import { AttributeMarker , getRenderedText , LifecycleHooksFeature , ɵɵadvance , ɵɵdefineComponent , ɵɵgetCurrentView , ɵɵproperty , ɵɵtextInterpolate1 , ɵɵtextInterpolate2 } from '../../src/render3/index' ;
15- import { detectChanges , markDirty , tick , ɵɵelement , ɵɵelementEnd , ɵɵelementStart , ɵɵlistener , ɵɵtemplate , ɵɵtext , ɵɵtextInterpolate } from '../../src/render3/instructions/all' ;
16- import { RenderFlags } from '../../src/render3/interfaces/definition' ;
17- import { Renderer3 , RendererFactory3 } from '../../src/render3/interfaces/renderer' ;
18- import { FLAGS , LViewFlags } from '../../src/render3/interfaces/view' ;
19-
20- import { containerEl , createComponent , renderComponent , requestAnimationFrame } from './render_util' ;
15+ import { getRenderedText } from '../../src/render3/index' ;
16+ import { detectChanges , markDirty } from '../../src/render3/instructions/all' ;
2117
2218describe ( 'change detection' , ( ) => {
2319 describe ( 'markDirty, detectChanges, whenRendered, getRenderedText' , ( ) => {
2420 let mycompOninit : MyComponentWithOnInit ;
21+
22+ @Component ( {
23+ selector : 'my-comp' ,
24+ standalone : true ,
25+ template : '<span>{{ value }}</span>' ,
26+ } )
2527 class MyComponent implements DoCheck {
2628 value : string = 'works' ;
2729 doCheckCount = 0 ;
2830 ngDoCheck ( ) : void {
2931 this . doCheckCount ++ ;
3032 }
31-
32- static ɵfac = ( ) => new MyComponent ( ) ;
33- static ɵcmp = ɵɵdefineComponent ( {
34- type : MyComponent ,
35- selectors : [ [ 'my-comp' ] ] ,
36- decls : 2 ,
37- vars : 1 ,
38- template :
39- ( rf : RenderFlags , ctx : MyComponent ) => {
40- if ( rf & RenderFlags . Create ) {
41- ɵɵelementStart ( 0 , 'span' ) ;
42- ɵɵtext ( 1 ) ;
43- ɵɵelementEnd ( ) ;
44- }
45- if ( rf & RenderFlags . Update ) {
46- ɵɵadvance ( 1 ) ;
47- ɵɵtextInterpolate ( ctx . value ) ;
48- }
49- }
50- } ) ;
5133 }
5234
35+ @Component ( {
36+ selector : 'my-comp-oninit' ,
37+ standalone : true ,
38+ template : '<span>{{ value }}</span>' ,
39+ } )
5340 class MyComponentWithOnInit implements OnInit , DoCheck {
5441 value : string = 'works' ;
5542 doCheckCount = 0 ;
5643
44+ constructor ( ) {
45+ mycompOninit = this ;
46+ }
47+
5748 ngOnInit ( ) {
5849 markDirty ( this ) ;
5950 }
@@ -66,28 +57,19 @@ describe('change detection', () => {
6657 this . value = 'click works' ;
6758 markDirty ( this ) ;
6859 }
69-
70- static ɵfac = ( ) => mycompOninit = new MyComponentWithOnInit ( ) ;
71- static ɵcmp = ɵɵdefineComponent ( {
72- type : MyComponentWithOnInit ,
73- selectors : [ [ 'my-comp-oninit' ] ] ,
74- decls : 2 ,
75- vars : 1 ,
76- template :
77- ( rf : RenderFlags , ctx : MyComponentWithOnInit ) => {
78- if ( rf & RenderFlags . Create ) {
79- ɵɵelementStart ( 0 , 'span' ) ;
80- ɵɵtext ( 1 ) ;
81- ɵɵelementEnd ( ) ;
82- }
83- if ( rf & RenderFlags . Update ) {
84- ɵɵadvance ( 1 ) ;
85- ɵɵtextInterpolate ( ctx . value ) ;
86- }
87- }
88- } ) ;
8960 }
9061
62+ @Component ( {
63+ selector : 'my-parent-comp' ,
64+ standalone : true ,
65+ imports : [ CommonModule , MyComponentWithOnInit ] ,
66+ template : `
67+ -->
68+ <div *ngIf="show">
69+ <my-comp-oninit></my-comp-oninit>
70+ </div>
71+ ` ,
72+ } )
9173 class MyParentComponent implements OnInit {
9274 show = false ;
9375 value = 'parent' ;
@@ -99,47 +81,28 @@ describe('change detection', () => {
9981 this . show = true ;
10082 markDirty ( this ) ;
10183 }
102-
103- static ɵfac = ( ) => new MyParentComponent ( ) ;
104- static ɵcmp = ɵɵdefineComponent ( {
105- type : MyParentComponent ,
106- selectors : [ [ 'my-parent-comp' ] ] ,
107- decls : 2 ,
108- vars : 1 ,
109- dependencies : [ NgIf , MyComponentWithOnInit ] ,
110- consts : [ [ AttributeMarker . Template , 'ngIf' ] ] ,
111- template :
112- ( rf : RenderFlags , ctx : MyParentComponent ) => {
113- if ( rf & RenderFlags . Create ) {
114- ɵɵtext ( 0 , ' -->\n' ) ;
115- ɵɵtemplate ( 1 , ( rf , ctx ) => {
116- if ( rf & RenderFlags . Create ) {
117- ɵɵelementStart ( 0 , 'div' ) ;
118- ɵɵelement ( 1 , 'my-comp-oninit' ) ;
119- ɵɵelementEnd ( ) ;
120- }
121- } , 2 , 0 , 'div' , 0 ) ;
122- }
123- if ( rf & RenderFlags . Update ) {
124- ɵɵadvance ( 1 ) ;
125- ɵɵproperty ( 'ngIf' , ctx . show ) ;
126- }
127- }
128- } ) ;
12984 }
13085
13186 it ( 'should mark a component dirty and schedule change detection' , withBody ( 'my-comp' , ( ) => {
132- const myComp = renderComponent ( MyComponent , { hostFeatures : [ LifecycleHooksFeature ] } ) ;
87+ const fixture = TestBed . createComponent ( MyComponent ) ;
88+ fixture . detectChanges ( ) ;
89+
90+ const myComp = fixture . componentInstance ;
13391 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
13492 myComp . value = 'updated' ;
13593 markDirty ( myComp ) ;
13694 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
137- requestAnimationFrame . flush ( ) ;
95+
96+ fixture . detectChanges ( ) ;
97+
13898 expect ( getRenderedText ( myComp ) ) . toEqual ( 'updated' ) ;
13999 } ) ) ;
140100
141101 it ( 'should detectChanges on a component' , withBody ( 'my-comp' , ( ) => {
142- const myComp = renderComponent ( MyComponent , { hostFeatures : [ LifecycleHooksFeature ] } ) ;
102+ const fixture = TestBed . createComponent ( MyComponent ) ;
103+ fixture . detectChanges ( ) ;
104+
105+ const myComp = fixture . componentInstance ;
143106 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
144107 myComp . value = 'updated' ;
145108 detectChanges ( myComp ) ;
@@ -148,85 +111,110 @@ describe('change detection', () => {
148111
149112 it ( 'should detectChanges after markDirty is called multiple times within ngOnInit' ,
150113 withBody ( 'my-comp-oninit' , ( ) => {
151- const myParentComp =
152- renderComponent ( MyParentComponent , { hostFeatures : [ LifecycleHooksFeature ] } ) ;
114+ const fixture = TestBed . createComponent ( MyParentComponent ) ;
115+ fixture . detectChanges ( ) ;
116+
117+ const myParentComp = fixture . componentInstance ;
153118 expect ( myParentComp . show ) . toBe ( false ) ;
154119 myParentComp . click ( ) ;
155- requestAnimationFrame . flush ( ) ;
120+ fixture . detectChanges ( ) ;
121+
156122 expect ( myParentComp . show ) . toBe ( true ) ;
157123 const myComp = mycompOninit ;
158124 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
159125 expect ( myComp . doCheckCount ) . toBe ( 1 ) ;
160126 myComp . click ( ) ;
161127 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
162- requestAnimationFrame . flush ( ) ;
128+
129+ fixture . detectChanges ( ) ;
130+
163131 expect ( getRenderedText ( myComp ) ) . toEqual ( 'click works' ) ;
164132 expect ( myComp . doCheckCount ) . toBe ( 2 ) ;
165133 } ) ) ;
166134
167135 it ( 'should detectChanges only once if markDirty is called multiple times' ,
168136 withBody ( 'my-comp' , ( ) => {
169- const myComp = renderComponent ( MyComponent , { hostFeatures : [ LifecycleHooksFeature ] } ) ;
137+ const fixture = TestBed . createComponent ( MyComponent ) ;
138+ fixture . detectChanges ( ) ;
139+
140+ const myComp = fixture . componentInstance ;
141+
170142 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
171143 expect ( myComp . doCheckCount ) . toBe ( 1 ) ;
172144 myComp . value = 'ignore' ;
173145 markDirty ( myComp ) ;
174146 myComp . value = 'updated' ;
175147 markDirty ( myComp ) ;
176148 expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
177- requestAnimationFrame . flush ( ) ;
149+
150+ fixture . detectChanges ( ) ;
151+
178152 expect ( getRenderedText ( myComp ) ) . toEqual ( 'updated' ) ;
179153 expect ( myComp . doCheckCount ) . toBe ( 2 ) ;
180154 } ) ) ;
181155
182156 it ( 'should notify whenRendered' , withBody ( 'my-comp' , async ( ) => {
183- const myComp = renderComponent ( MyComponent , { hostFeatures : [ LifecycleHooksFeature ] } ) ;
157+ const fixture = TestBed . createComponent ( MyComponent ) ;
158+ fixture . detectChanges ( ) ;
159+
160+ const myComp = fixture . componentInstance ;
184161 await whenRendered ( myComp ) ;
185162 myComp . value = 'updated' ;
186163 markDirty ( myComp ) ;
187- setTimeout ( requestAnimationFrame . flush , 0 ) ;
164+ setTimeout ( ( ) => fixture . detectChanges ( ) , 0 ) ;
188165 await whenRendered ( myComp ) ;
189166 expect ( getRenderedText ( myComp ) ) . toEqual ( 'updated' ) ;
190167 } ) ) ;
191168 } ) ;
192169
193- it ( 'should call begin and end when the renderer factory implements them' , ( ) => {
194- const log : string [ ] = [ ] ;
195-
196- const testRendererFactory : RendererFactory3 = {
197- createRenderer : ( ) : Renderer3 => {
198- return document ;
199- } ,
200- begin : ( ) => log . push ( 'begin' ) ,
201- end : ( ) => log . push ( 'end' ) ,
202- } ;
203-
204- class MyComponent {
205- get value ( ) : string {
206- log . push ( 'detect changes' ) ;
207- return 'works' ;
208- }
209-
210- static ɵfac = ( ) => new MyComponent ( ) ;
211- static ɵcmp = ɵɵdefineComponent ( {
212- type : MyComponent ,
213- selectors : [ [ 'my-comp' ] ] ,
214- decls : 1 ,
215- vars : 1 ,
216- template :
217- ( rf : RenderFlags , ctx : MyComponent ) => {
218- if ( rf & RenderFlags . Create ) {
219- ɵɵtext ( 0 ) ;
220- }
221- if ( rf & RenderFlags . Update ) {
222- ɵɵtextInterpolate ( ctx . value ) ;
223- }
224- }
225- } ) ;
226- }
227-
228- const myComp = renderComponent ( MyComponent , { rendererFactory : testRendererFactory } ) ;
229- expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
230- expect ( log ) . toEqual ( [ 'begin' , 'detect changes' , 'end' ] ) ;
231- } ) ;
170+ it ( 'should call begin and end when the renderer factory implements them' ,
171+ withBody ( '<my-comp></my-comp>' , ( ) => {
172+ const log : string [ ] = [ ] ;
173+
174+ const testRendererFactory : RendererFactory2 = {
175+ createRenderer : ( ) : Renderer2 => {
176+ return document as unknown as Renderer2 ;
177+ } ,
178+ begin : ( ) => log . push ( 'begin' ) ,
179+ end : ( ) => log . push ( 'end' ) ,
180+ } ;
181+
182+ @Component ( {
183+ selector : 'my-comp' ,
184+ standalone : true ,
185+ template : '{{ value }}' ,
186+ } )
187+ class MyComponent {
188+ get value ( ) : string {
189+ log . push ( 'detect changes' ) ;
190+ return 'works' ;
191+ }
192+ }
193+
194+ TestBed . configureTestingModule ( {
195+ providers : [
196+ {
197+ provide : DOCUMENT ,
198+ useFactory : ( ) => document ,
199+ } ,
200+ {
201+ provide : RendererFactory2 ,
202+ useValue : testRendererFactory ,
203+ }
204+ ]
205+ } ) ;
206+
207+ const fixture = TestBed . createComponent ( MyComponent ) ;
208+ fixture . detectChanges ( ) ;
209+
210+ const myComp = fixture . componentInstance ;
211+ expect ( getRenderedText ( myComp ) ) . toEqual ( 'works' ) ;
212+
213+ expect ( log ) . toEqual ( [
214+ 'begin' ,
215+ 'detect changes' , // regular change detection cycle
216+ 'end' ,
217+ 'detect changes' // check no changes cycle
218+ ] ) ;
219+ } ) ) ;
232220} ) ;
0 commit comments