11import * as React from 'react' ;
2- import * as ReactDOM from 'react-dom' ;
3- import * as ReactTestUtils from 'react-dom/test-utils' ;
4- import { render } from '@testing-library/react' ;
5- import { safeCreate } from '@fluentui/test-utilities' ;
2+ import { render , act } from '@testing-library/react' ;
63import { isConformant } from '../../common/isConformant' ;
74import { DirectionalHint } from '../../common/DirectionalHint' ;
85import { resetIds } from '../../Utilities' ;
@@ -13,14 +10,10 @@ import { expectNoHiddenParents } from '../../common/testUtilities';
1310
1411describe ( 'Callout' , ( ) => {
1512 beforeEach ( ( ) => {
16- realDom = document . createElement ( 'div' ) ;
17- document . body . appendChild ( realDom ) ;
1813 resetIds ( ) ;
1914 } ) ;
2015
2116 afterEach ( ( ) => {
22- ReactDOM . unmountComponentAtNode ( realDom ) ;
23- document . body . removeChild ( realDom ) ;
2417 jest . useRealTimers ( ) ;
2518 jest . resetAllMocks ( ) ;
2619 } ) ;
@@ -29,8 +22,6 @@ describe('Callout', () => {
2922 resetIds ( ) ;
3023 } ) ;
3124
32- let realDom : HTMLDivElement ;
33-
3425 isConformant ( {
3526 Component : Callout ,
3627 displayName : 'Callout' ,
@@ -40,167 +31,116 @@ describe('Callout', () => {
4031 } ) ;
4132
4233 it ( 'renders Callout correctly' , ( ) => {
43- safeCreate ( < CalloutContent > Content</ CalloutContent > , component => {
44- const tree = component . toJSON ( ) ;
45- expect ( tree ) . toMatchSnapshot ( ) ;
46- } ) ;
34+ const { container } = render ( < CalloutContent > Content</ CalloutContent > ) ;
35+ expect ( container ) . toMatchSnapshot ( ) ;
4736 } ) ;
4837
49- it ( 'target id strings does not throw exception' , ( ) => {
50- let threwException = false ;
51- try {
52- ReactTestUtils . renderIntoDocument < HTMLDivElement > (
38+ it ( 'does not throw with target id string' , ( ) => {
39+ expect ( ( ) => {
40+ render (
5341 < div >
5442 < button id = "target" style = { { top : '10px' , left : '10px' , height : '0' , width : '0px' } } >
55- { ' ' }
56- target{ ' ' }
43+ target
5744 </ button >
5845 < Callout target = "#target" directionalHint = { DirectionalHint . topLeftEdge } >
5946 < div > Content</ div >
6047 </ Callout >
6148 </ div > ,
6249 ) ;
63- } catch ( e ) {
64- threwException = true ;
65- }
66-
67- expect ( threwException ) . toEqual ( false ) ;
50+ } ) . not . toThrow ( ) ;
6851 } ) ;
6952
70- it ( 'target MouseEvents does not throw exception' , ( ) => {
71- const mouseEvent = document . createEvent ( 'MouseEvent' ) ;
53+ it ( 'does not throw with target MouseEvent' , ( ) => {
7254 const eventTarget = document . createElement ( 'div' ) ;
73- mouseEvent . initMouseEvent ( 'click' , false , false , window , 0 , 0 , 0 , 0 , 0 , false , false , false , false , 1 , eventTarget ) ;
74- let threwException = false ;
75- try {
76- ReactTestUtils . renderIntoDocument < HTMLDivElement > (
55+ const mouseEvent = new MouseEvent ( 'click' , { relatedTarget : eventTarget } ) ;
56+
57+ expect ( ( ) => {
58+ render (
7759 < div >
78- < Callout target = { eventTarget } directionalHint = { DirectionalHint . topLeftEdge } >
60+ < Callout target = { mouseEvent } directionalHint = { DirectionalHint . topLeftEdge } >
7961 < div > Content</ div >
8062 </ Callout >
8163 </ div > ,
8264 ) ;
83- } catch ( e ) {
84- threwException = true ;
85- }
86-
87- expect ( threwException ) . toEqual ( false ) ;
65+ } ) . not . toThrow ( ) ;
8866 } ) ;
8967
90- it ( 'target Elements does not throw exception ' , ( ) => {
68+ it ( 'does not throw with target Element ' , ( ) => {
9169 const targetElement = document . createElement ( 'div' ) ;
9270 document . body . appendChild ( targetElement ) ;
93- let threwException = false ;
94- try {
95- ReactTestUtils . renderIntoDocument < HTMLDivElement > (
71+
72+ expect ( ( ) => {
73+ render (
9674 < div >
9775 < Callout target = { targetElement } directionalHint = { DirectionalHint . topLeftEdge } >
9876 < div > Content</ div >
9977 </ Callout >
10078 </ div > ,
10179 ) ;
102- } catch ( e ) {
103- threwException = true ;
104- }
105-
106- expect ( threwException ) . toEqual ( false ) ;
80+ } ) . not . toThrow ( ) ;
10781 } ) ;
10882
109- it ( 'without target does not throw exception' , ( ) => {
110- let threwException = false ;
111- try {
112- ReactTestUtils . renderIntoDocument < HTMLDivElement > (
83+ it ( 'does not throw without target' , ( ) => {
84+ expect ( ( ) => {
85+ render (
11386 < div >
11487 < Callout directionalHint = { DirectionalHint . topLeftEdge } >
11588 < div > Content</ div >
11689 </ Callout >
11790 </ div > ,
11891 ) ;
119- } catch ( e ) {
120- threwException = true ;
121- }
122- expect ( threwException ) . toEqual ( false ) ;
92+ } ) . not . toThrow ( ) ;
12393 } ) ;
12494
12595 it ( 'passes event to onDismiss prop' , ( ) => {
12696 jest . useFakeTimers ( ) ;
127- let threwException = false ;
128- let gotEvent = false ;
129- const onDismiss = ( ev ?: unknown ) => {
130- if ( ev ) {
131- gotEvent = true ;
132- }
133- } ;
134-
135- // In order to have eventlisteners that have been added to the window to be called the JSX needs
136- // to be rendered into the real dom rather than the testutil simulated dom.
137-
138- try {
139- ReactTestUtils . act ( ( ) => {
140- ReactDOM . render < HTMLDivElement > (
141- < div >
142- < button id = "focustarget" > button </ button >
143- < button id = "target" style = { { top : '10px' , left : '10px' , height : '0' , width : '0px' } } >
144- { ' ' }
145- target{ ' ' }
146- </ button >
147- < Callout target = "#target" directionalHint = { DirectionalHint . topLeftEdge } onDismiss = { onDismiss } >
148- < div > Content</ div >
149- </ Callout >
150- </ div > ,
151- realDom ,
152- ) ;
153- } ) ;
154- } catch ( e ) {
155- threwException = true ;
156- }
157- expect ( threwException ) . toEqual ( false ) ;
97+ const onDismiss = jest . fn ( ) ;
15898
159- ReactTestUtils . act ( ( ) => {
160- const focusTarget = document . querySelector ( '#focustarget' ) as HTMLButtonElement ;
99+ const { getByText } = render (
100+ < div >
101+ < button > button</ button >
102+ < button id = "target" style = { { top : '10px' , left : '10px' , height : '0' , width : '0px' } } >
103+ target
104+ </ button >
105+ < Callout target = "#target" directionalHint = { DirectionalHint . topLeftEdge } onDismiss = { onDismiss } >
106+ < div > Content</ div >
107+ </ Callout >
108+ </ div > ,
109+ ) ;
161110
111+ act ( ( ) => {
162112 // Move focus
163113 jest . runAllTimers ( ) ;
114+ } ) ;
164115
165- focusTarget . focus ( ) ;
116+ getByText ( 'button' ) . focus ( ) ;
166117
167- expect ( gotEvent ) . toEqual ( true ) ;
168- } ) ;
118+ // ensure event is passed to callback
119+ expect ( onDismiss ) . toHaveBeenCalledWith ( expect . objectContaining ( { type : 'focus' } ) ) ;
169120 } ) ;
170121
171122 it ( 'prevents dismiss when preventDismissOnEvent is passed' , ( ) => {
172123 jest . useFakeTimers ( ) ;
173- let threwException = false ;
174124 const onDismiss = jest . fn ( ) ;
175125 const preventAllDismiss = ( ) => true ;
176126
177- try {
178- ReactTestUtils . act ( ( ) => {
179- ReactDOM . render < HTMLDivElement > (
180- < div >
181- < button id = "focustarget" > button </ button >
182- < Callout target = "#target" preventDismissOnEvent = { preventAllDismiss } onDismiss = { onDismiss } >
183- < div > Content</ div >
184- </ Callout >
185- </ div > ,
186- realDom ,
187- ) ;
188- } ) ;
189- } catch ( e ) {
190- threwException = true ;
191- }
192- expect ( threwException ) . toEqual ( false ) ;
193-
194- ReactTestUtils . act ( ( ) => {
195- const focusTarget = document . querySelector ( '#focustarget' ) as HTMLButtonElement ;
127+ const { getByText, queryByText } = render (
128+ < div >
129+ < button > button</ button >
130+ < Callout target = "#target" preventDismissOnEvent = { preventAllDismiss } onDismiss = { onDismiss } >
131+ < div > Content</ div >
132+ </ Callout >
133+ </ div > ,
134+ ) ;
196135
136+ act ( ( ) => {
197137 // Move focus
198138 jest . runAllTimers ( ) ;
199-
200- focusTarget . focus ( ) ;
201-
202- expect ( onDismiss . mock . calls . length ) . toEqual ( 0 ) ;
203139 } ) ;
140+ getByText ( 'button' ) . focus ( ) ;
141+
142+ expect ( queryByText ( 'Content' ) ) . toBeTruthy ( ) ;
143+ expect ( onDismiss ) . not . toHaveBeenCalled ( ) ;
204144 } ) ;
205145
206146 it ( 'will correctly return focus to element that spawned it' , ( ) => {
@@ -211,59 +151,40 @@ describe('Callout', () => {
211151 // Callout/popup checks active element to get what currently has focus
212152 // to know what to return focus to. By mocking the return value we can be sure
213153 // that it will have something "focused" when mounted
214- const b = jest . spyOn ( window . document , 'activeElement' , 'get' ) ;
215- b . mockReturnValue ( focusedElement as Element ) ;
216-
217- let threwException = false ;
218- let previousFocusElement ;
219- let isFocused ;
220- let restoreCalled = false ;
221- const onRestoreFocus = ( options : IPopupRestoreFocusParams ) => {
222- previousFocusElement = options . originalElement ;
223- isFocused = options . containsFocus ;
224- restoreCalled = true ;
225- } ;
226- // In order to have eventlisteners that have been added to the window to be called the JSX needs
227- // to be rendered into the real dom rather than the testutil simulated dom.
228- try {
229- ReactTestUtils . act ( ( ) => {
230- ReactDOM . render < HTMLDivElement > (
231- < div >
232- < button id = "target" style = { { top : '10px' , left : '10px' , height : '0' , width : '0px' } } >
233- target
234- </ button >
235- < Callout target = "#target" directionalHint = { DirectionalHint . topLeftEdge } onRestoreFocus = { onRestoreFocus } >
236- { /* must be a button to be focusable for the test*/ }
237- < button id = { 'inner' } > Content</ button >
238- </ Callout >
239- </ div > ,
240- realDom ,
241- ) ;
242- } ) ;
243- } catch ( e ) {
244- threwException = true ;
245- }
246- expect ( threwException ) . toEqual ( false ) ;
247-
248- ReactTestUtils . act ( ( ) => {
249- const focusTarget = document . querySelector ( '#inner' ) as HTMLDivElement ;
154+ jest . spyOn ( window . document , 'activeElement' , 'get' ) . mockReturnValue ( focusedElement as Element ) ;
155+
156+ const onRestoreFocus = jest . fn ( ) ;
157+
158+ const { getByText, unmount } = render (
159+ < div >
160+ < button id = "target" style = { { top : '10px' , left : '10px' , height : '0' , width : '0px' } } >
161+ target
162+ </ button >
163+ < Callout target = "#target" directionalHint = { DirectionalHint . topLeftEdge } onRestoreFocus = { onRestoreFocus } >
164+ { /* must be a button to be focusable for the test*/ }
165+ < button id = "inner" > Content</ button >
166+ </ Callout >
167+ </ div > ,
168+ ) ;
250169
170+ act ( ( ) => {
251171 jest . runAllTimers ( ) ;
252- // Make sure that focus is in the callout
253- focusTarget . focus ( ) ;
254172 } ) ;
255173
256- // Unmounting everything is the same as dismissing the Callout. As
257- // the tree is unmounted, popup will get unmounted first and the
258- // onRestoreFocus method will get called
259- ReactDOM . unmountComponentAtNode ( realDom ) ;
174+ // Make sure that focus is in the callout
175+ getByText ( 'Content' ) . focus ( ) ;
260176
261- expect ( restoreCalled ) . toEqual ( true ) ;
262- expect ( isFocused ) . toEqual ( true ) ;
177+ // Unmounting everything is the same as dismissing the Callout.
178+ // As the tree is unmounted, popup will get unmounted first and onRestoreFocus will get called.
179+ unmount ( ) ;
263180
264- // Just to make sure that both elements are not undefined
265- expect ( previousFocusElement ) . not . toBeFalsy ( ) ;
266- expect ( previousFocusElement ) . toEqual ( focusedElement ) ;
181+ expect ( onRestoreFocus ) . toHaveBeenCalledTimes ( 1 ) ;
182+ expect ( onRestoreFocus ) . toHaveBeenLastCalledWith (
183+ expect . objectContaining < Partial < IPopupRestoreFocusParams > > ( {
184+ originalElement : focusedElement ,
185+ containsFocus : true ,
186+ } ) ,
187+ ) ;
267188 } ) ;
268189
269190 // This behavior could be changed in the future
0 commit comments