@@ -31,31 +31,36 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
3131 document . body . removeChild ( container ) ;
3232 } ) ;
3333
34+ function dispatchAndSetCurrentEvent ( el , event ) {
35+ try {
36+ window . event = event ;
37+ el . dispatchEvent ( event ) ;
38+ } finally {
39+ window . event = undefined ;
40+ }
41+ }
42+
3443 // @gate experimental
35- it ( 'ignores discrete events on a pending removed element' , ( ) => {
44+ // @gate enableDiscreteEventMicroTasks && enableNativeEventPriorityInference
45+ it ( 'ignores discrete events on a pending removed element' , async ( ) => {
3646 const disableButtonRef = React . createRef ( ) ;
3747 const submitButtonRef = React . createRef ( ) ;
3848
39- let formSubmitted = false ;
40-
4149 function Form ( ) {
4250 const [ active , setActive ] = React . useState ( true ) ;
51+
52+ React . useLayoutEffect ( ( ) => {
53+ disableButtonRef . current . onclick = disableForm ;
54+ } ) ;
55+
4356 function disableForm ( ) {
4457 setActive ( false ) ;
4558 }
46- function submitForm ( ) {
47- formSubmitted = true ; // This should not get invoked
48- }
59+
4960 return (
5061 < div >
51- < button onClick = { disableForm } ref = { disableButtonRef } >
52- Disable
53- </ button >
54- { active ? (
55- < button onClick = { submitForm } ref = { submitButtonRef } >
56- Submit
57- </ button >
58- ) : null }
62+ < button ref = { disableButtonRef } > Disable</ button >
63+ { active ? < button ref = { submitButtonRef } > Submit</ button > : null }
5964 </ div >
6065 ) ;
6166 }
@@ -71,54 +76,56 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
7176 // Dispatch a click event on the Disable-button.
7277 const firstEvent = document . createEvent ( 'Event' ) ;
7378 firstEvent . initEvent ( 'click' , true , true ) ;
74- disableButton . dispatchEvent ( firstEvent ) ;
79+ expect ( ( ) =>
80+ dispatchAndSetCurrentEvent ( disableButton , firstEvent ) ,
81+ ) . toErrorDev ( [ 'An update to Form inside a test was not wrapped in act' ] ) ;
7582
7683 // There should now be a pending update to disable the form.
77-
7884 // This should not have flushed yet since it's in concurrent mode.
7985 const submitButton = submitButtonRef . current ;
8086 expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
8187
82- // In the meantime, we can dispatch a new client event on the submit button.
83- const secondEvent = document . createEvent ( 'Event' ) ;
84- secondEvent . initEvent ( 'click' , true , true ) ;
85- // This should force the pending update to flush which disables the submit button before the event is invoked.
86- submitButton . dispatchEvent ( secondEvent ) ;
87-
88- // Therefore the form should never have been submitted.
89- expect ( formSubmitted ) . toBe ( false ) ;
90-
88+ // Discrete events should be flushed in a microtask.
89+ // Verify that the second button was removed.
90+ await null ;
9191 expect ( submitButtonRef . current ) . toBe ( null ) ;
92+ // We'll assume that the browser won't let the user click it.
9293 } ) ;
9394
9495 // @gate experimental
95- it ( 'ignores discrete events on a pending removed event listener' , ( ) => {
96+ // @gate enableDiscreteEventMicroTasks && enableNativeEventPriorityInference
97+ it ( 'ignores discrete events on a pending removed event listener' , async ( ) => {
9698 const disableButtonRef = React . createRef ( ) ;
9799 const submitButtonRef = React . createRef ( ) ;
98100
99101 let formSubmitted = false ;
100102
101103 function Form ( ) {
102104 const [ active , setActive ] = React . useState ( true ) ;
105+
106+ React . useLayoutEffect ( ( ) => {
107+ disableButtonRef . current . onclick = disableForm ;
108+ submitButtonRef . current . onclick = active
109+ ? submitForm
110+ : disabledSubmitForm ;
111+ } ) ;
112+
103113 function disableForm ( ) {
104114 setActive ( false ) ;
105115 }
116+
106117 function submitForm ( ) {
107118 formSubmitted = true ; // This should not get invoked
108119 }
120+
109121 function disabledSubmitForm ( ) {
110122 // The form is disabled.
111123 }
124+
112125 return (
113126 < div >
114- < button onClick = { disableForm } ref = { disableButtonRef } >
115- Disable
116- </ button >
117- < button
118- onClick = { active ? submitForm : disabledSubmitForm }
119- ref = { submitButtonRef } >
120- Submit
121- </ button >
127+ < button ref = { disableButtonRef } > Disable</ button >
128+ < button ref = { submitButtonRef } > Submit</ button >
122129 </ div >
123130 ) ;
124131 }
@@ -134,47 +141,55 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
134141 // Dispatch a click event on the Disable-button.
135142 const firstEvent = document . createEvent ( 'Event' ) ;
136143 firstEvent . initEvent ( 'click' , true , true ) ;
137- disableButton . dispatchEvent ( firstEvent ) ;
144+ expect ( ( ) => {
145+ dispatchAndSetCurrentEvent ( disableButton , firstEvent ) ;
146+ } ) . toErrorDev ( [ 'An update to Form inside a test was not wrapped in act' ] ) ;
138147
139148 // There should now be a pending update to disable the form.
140-
141149 // This should not have flushed yet since it's in concurrent mode.
142150 const submitButton = submitButtonRef . current ;
143151 expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
144152
145- // In the meantime, we can dispatch a new client event on the submit button.
153+ // Discrete events should be flushed in a microtask.
154+ await null ;
155+
156+ // Now let's dispatch an event on the submit button.
146157 const secondEvent = document . createEvent ( 'Event' ) ;
147158 secondEvent . initEvent ( 'click' , true , true ) ;
148- // This should force the pending update to flush which disables the submit button before the event is invoked.
149- submitButton . dispatchEvent ( secondEvent ) ;
159+ dispatchAndSetCurrentEvent ( submitButton , secondEvent ) ;
150160
151161 // Therefore the form should never have been submitted.
152162 expect ( formSubmitted ) . toBe ( false ) ;
153163 } ) ;
154164
155165 // @gate experimental
156- it ( 'uses the newest discrete events on a pending changed event listener' , ( ) => {
166+ // @gate enableDiscreteEventMicroTasks && enableNativeEventPriorityInference
167+ it ( 'uses the newest discrete events on a pending changed event listener' , async ( ) => {
157168 const enableButtonRef = React . createRef ( ) ;
158169 const submitButtonRef = React . createRef ( ) ;
159170
160171 let formSubmitted = false ;
161172
162173 function Form ( ) {
163174 const [ active , setActive ] = React . useState ( false ) ;
175+
176+ React . useLayoutEffect ( ( ) => {
177+ enableButtonRef . current . onclick = enableForm ;
178+ submitButtonRef . current . onclick = active ? submitForm : null ;
179+ } ) ;
180+
164181 function enableForm ( ) {
165182 setActive ( true ) ;
166183 }
184+
167185 function submitForm ( ) {
168186 formSubmitted = true ; // This should not get invoked
169187 }
188+
170189 return (
171190 < div >
172- < button onClick = { enableForm } ref = { enableButtonRef } >
173- Enable
174- </ button >
175- < button onClick = { active ? submitForm : null } ref = { submitButtonRef } >
176- Submit
177- </ button >
191+ < button ref = { enableButtonRef } > Enable</ button >
192+ < button ref = { submitButtonRef } > Submit</ button >
178193 </ div >
179194 ) ;
180195 }
@@ -190,19 +205,22 @@ describe('ReactDOMNativeEventHeuristic-test', () => {
190205 // Dispatch a click event on the Enable-button.
191206 const firstEvent = document . createEvent ( 'Event' ) ;
192207 firstEvent . initEvent ( 'click' , true , true ) ;
193- enableButton . dispatchEvent ( firstEvent ) ;
208+ expect ( ( ) => {
209+ dispatchAndSetCurrentEvent ( enableButton , firstEvent ) ;
210+ } ) . toErrorDev ( [ 'An update to Form inside a test was not wrapped in act' ] ) ;
194211
195212 // There should now be a pending update to enable the form.
196-
197213 // This should not have flushed yet since it's in concurrent mode.
198214 const submitButton = submitButtonRef . current ;
199215 expect ( submitButton . tagName ) . toBe ( 'BUTTON' ) ;
200216
201- // In the meantime, we can dispatch a new client event on the submit button.
217+ // Discrete events should be flushed in a microtask.
218+ await null ;
219+
220+ // Now let's dispatch an event on the submit button.
202221 const secondEvent = document . createEvent ( 'Event' ) ;
203222 secondEvent . initEvent ( 'click' , true , true ) ;
204- // This should force the pending update to flush which enables the submit button before the event is invoked.
205- submitButton . dispatchEvent ( secondEvent ) ;
223+ dispatchAndSetCurrentEvent ( submitButton , secondEvent ) ;
206224
207225 // Therefore the form should have been submitted.
208226 expect ( formSubmitted ) . toBe ( true ) ;
0 commit comments