@@ -11,16 +11,8 @@ import {
1111 hide ,
1212 limitShift ,
1313} from '../src' ;
14- import { renderHook } from '@testing-library/react-hooks' ;
15- import { render , waitFor , fireEvent } from '@testing-library/react' ;
16- import { useRef , useState } from 'react' ;
17-
18- test ( '`x` and `y` are initially `null`' , async ( ) => {
19- const { result} = renderHook ( ( ) => useFloating ( ) ) ;
20-
21- expect ( result . current . x ) . toBe ( null ) ;
22- expect ( result . current . y ) . toBe ( null ) ;
23- } ) ;
14+ import { render , fireEvent , screen , cleanup , act } from '@testing-library/react' ;
15+ import { useRef , useState , useEffect } from 'react' ;
2416
2517test ( 'middleware is always fresh and does not cause an infinite loop' , async ( ) => {
2618 function InlineMiddleware ( ) {
@@ -129,16 +121,142 @@ test('middleware is always fresh and does not cause an infinite loop', async ()
129121 ) ;
130122 }
131123
132- await waitFor ( ( ) => render ( < InlineMiddleware /> ) ) ;
124+ render ( < InlineMiddleware /> ) ;
125+
126+ const { getByTestId} = render ( < StateMiddleware /> ) ;
127+ fireEvent . click ( getByTestId ( 'step1' ) ) ;
128+
129+ await act ( async ( ) => { } ) ;
133130
134- const { getByTestId} = await waitFor ( ( ) => render ( < StateMiddleware /> ) ) ;
135- await waitFor ( ( ) => fireEvent . click ( getByTestId ( 'step1' ) ) ) ;
136- await waitFor ( ( ) => expect ( getByTestId ( 'x' ) . textContent ) . toBe ( '10' ) ) ;
131+ expect ( getByTestId ( 'x' ) . textContent ) . toBe ( '10' ) ;
137132
138- await waitFor ( ( ) => fireEvent . click ( getByTestId ( 'step2' ) ) ) ;
139- await waitFor ( ( ) => expect ( getByTestId ( 'x' ) . textContent ) . toBe ( '5' ) ) ;
133+ fireEvent . click ( getByTestId ( 'step2' ) ) ;
134+
135+ await act ( async ( ) => { } ) ;
136+
137+ expect ( getByTestId ( 'x' ) . textContent ) . toBe ( '5' ) ;
140138
141139 // No `expect` as this test will fail if a render loop occurs
142- await waitFor ( ( ) => fireEvent . click ( getByTestId ( 'step3' ) ) ) ;
143- await waitFor ( ( ) => fireEvent . click ( getByTestId ( 'step4' ) ) ) ;
140+ fireEvent . click ( getByTestId ( 'step3' ) ) ;
141+ fireEvent . click ( getByTestId ( 'step4' ) ) ;
142+
143+ await act ( async ( ) => { } ) ;
144+ } ) ;
145+
146+ describe ( 'whileElementsMounted' , ( ) => {
147+ test ( 'is called a single time when both elements mount' , ( ) => {
148+ const spy = jest . fn ( ) ;
149+
150+ function App ( ) {
151+ const { reference, floating} = useFloating ( { whileElementsMounted : spy } ) ;
152+ return (
153+ < >
154+ < button ref = { reference } />
155+ < div ref = { floating } />
156+ </ >
157+ ) ;
158+ }
159+
160+ render ( < App /> ) ;
161+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
162+ cleanup ( ) ;
163+ } ) ;
164+
165+ test ( 'is called a single time after floating mounts conditionally' , ( ) => {
166+ const spy = jest . fn ( ) ;
167+
168+ function App ( ) {
169+ const [ open , setOpen ] = useState ( false ) ;
170+ const { reference, floating} = useFloating ( { whileElementsMounted : spy } ) ;
171+ return (
172+ < >
173+ < button ref = { reference } onClick = { ( ) => setOpen ( true ) } />
174+ { open && < div ref = { floating } /> }
175+ </ >
176+ ) ;
177+ }
178+
179+ render ( < App /> ) ;
180+ expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ;
181+ fireEvent . click ( screen . getByRole ( 'button' ) ) ;
182+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
183+
184+ cleanup ( ) ;
185+ } ) ;
186+
187+ test ( 'is called a single time after reference mounts conditionally' , ( ) => {
188+ const spy = jest . fn ( ) ;
189+
190+ function App ( ) {
191+ const [ open , setOpen ] = useState ( false ) ;
192+ const { reference, floating} = useFloating ( { whileElementsMounted : spy } ) ;
193+ return (
194+ < >
195+ { open && < button ref = { reference } /> }
196+ < div role = "tooltip" ref = { floating } onClick = { ( ) => setOpen ( true ) } />
197+ </ >
198+ ) ;
199+ }
200+
201+ render ( < App /> ) ;
202+ expect ( spy ) . toHaveBeenCalledTimes ( 0 ) ;
203+ fireEvent . click ( screen . getByRole ( 'tooltip' ) ) ;
204+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
205+
206+ cleanup ( ) ;
207+ } ) ;
208+
209+ test ( 'is called a single time both elements mount conditionally' , ( ) => {
210+ const spy = jest . fn ( ) ;
211+
212+ function App ( ) {
213+ const [ open , setOpen ] = useState ( false ) ;
214+ const { reference, floating} = useFloating ( { whileElementsMounted : spy } ) ;
215+
216+ useEffect ( ( ) => {
217+ setOpen ( true ) ;
218+ } , [ ] ) ;
219+
220+ return (
221+ < >
222+ { open && < button ref = { reference } /> }
223+ { open && < div role = "tooltip" ref = { floating } /> }
224+ </ >
225+ ) ;
226+ }
227+
228+ render ( < App /> ) ;
229+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
230+
231+ cleanup ( ) ;
232+ } ) ;
233+
234+ test ( 'calls the cleanup function' , ( ) => {
235+ const cleanupSpy = jest . fn ( ) ;
236+ const spy = jest . fn ( ( ) => cleanupSpy ) ;
237+
238+ function App ( ) {
239+ const [ open , setOpen ] = useState ( true ) ;
240+ const { reference, floating} = useFloating ( { whileElementsMounted : spy } ) ;
241+
242+ useEffect ( ( ) => {
243+ setOpen ( false ) ;
244+ } , [ ] ) ;
245+
246+ return (
247+ < >
248+ { open && < button ref = { reference } /> }
249+ { open && < div role = "tooltip" ref = { floating } /> }
250+ </ >
251+ ) ;
252+ }
253+
254+ render ( < App /> ) ;
255+ expect ( cleanupSpy ) . toHaveBeenCalledTimes ( 1 ) ;
256+
257+ // Does not get called again post-cleanup
258+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
259+
260+ cleanup ( ) ;
261+ } ) ;
144262} ) ;
0 commit comments