1
1
/* eslint-disable react/prop-types */
2
2
/* eslint-disable react/button-has-type */
3
3
import React , { useState , useContext , useEffect } from 'react' ;
4
- // TODO: Convert to RTL
5
- import { shallow , mount } from 'enzyme' ;
6
- import { render , fireEvent , waitFor } from '@testing-library/react' ;
4
+ import { render , screen , fireEvent , waitFor } from '@testing-library/react' ;
7
5
import composeHooks from '../index' ;
8
6
9
7
const INITIAL_COUNT = 0 ;
@@ -27,66 +25,73 @@ const useUseState = () => useState(INITIAL_COUNT);
27
25
const TestComponent = ( { text } ) => < div > { text } </ div > ;
28
26
29
27
TestComponent . defaultProps = {
30
- text : 'Test' ,
28
+ text : 'Test'
31
29
} ;
32
30
33
31
test ( 'passes custom hooks to component' , ( ) => {
34
- const Container = composeHooks ( { useCount , useChange } ) ( TestComponent ) ;
35
- const wrapper = shallow ( < Container /> ) ;
36
- const { count , increment , decrement , value , onChange } = wrapper
37
- . find ( TestComponent )
38
- . props ( ) ;
39
- expect ( count ) . toBe ( INITIAL_COUNT ) ;
40
- expect ( value ) . toBe ( INITIAL_VALUE ) ;
41
- expect ( typeof increment ) . toBe ( 'function' ) ;
42
- expect ( typeof decrement ) . toBe ( 'function' ) ;
43
- expect ( typeof onChange ) . toBe ( 'function' ) ;
32
+ const MockComponent = jest . fn ( ( ) => < div > Test </ div > ) ;
33
+ const Container = composeHooks ( { useCount , useChange } ) ( MockComponent ) ;
34
+ render ( < Container /> ) ;
35
+ expect ( MockComponent . mock . calls [ 0 ] [ 0 ] ) . toEqual ( {
36
+ count : INITIAL_COUNT ,
37
+ value : INITIAL_VALUE ,
38
+ increment : expect . any ( Function ) ,
39
+ decrement : expect . any ( Function ) ,
40
+ onChange : expect . any ( Function )
41
+ } ) ;
44
42
} ) ;
45
43
46
44
test ( 'passes props to component' , ( ) => {
47
- const Container = composeHooks ( { useChange } ) ( TestComponent ) ;
48
- const wrapper = shallow ( < Container foo = "bar" /> ) ;
49
- const { foo } = wrapper . find ( TestComponent ) . props ( ) ;
50
- expect ( foo ) . toBe ( 'bar' ) ;
45
+ const MockComponent = jest . fn ( ( ) => < div > Test </ div > ) ;
46
+ const Container = composeHooks ( { useChange } ) ( MockComponent ) ;
47
+ render ( < Container foo = "bar" /> ) ;
48
+ expect ( MockComponent . mock . calls [ 0 ] [ 0 ] . foo ) . toBe ( 'bar' ) ;
51
49
} ) ;
52
50
53
51
test ( 'hooks work as expected' , ( ) => {
54
52
const Component = ( { value, onChange } ) => (
55
- < input value = { value } onChange = { onChange } />
53
+ < label >
54
+ Testing
55
+ < input value = { value } onChange = { onChange } />
56
+ </ label >
56
57
) ;
57
58
const Container = composeHooks ( { useChange } ) ( Component ) ;
58
- const wrapper = mount ( < Container /> ) ;
59
- expect ( wrapper . find ( 'input' ) . props ( ) . value ) . toBe ( INITIAL_VALUE ) ;
60
- wrapper . find ( 'input' ) . simulate ( 'change' , { target : { value : 'new' } } ) ;
61
- expect ( wrapper . find ( 'input' ) . props ( ) . value ) . toBe ( 'new' ) ;
59
+ render ( < Container /> ) ;
60
+ expect ( screen . getByLabelText ( / t e s t i n g / i) . value ) . toBe ( INITIAL_VALUE ) ;
61
+ fireEvent . change ( screen . getByLabelText ( / t e s t i n g / i) , {
62
+ target : { value : 'new' }
63
+ } ) ;
64
+ expect ( screen . getByLabelText ( / t e s t i n g / i) . value ) . toBe ( 'new' ) ;
62
65
} ) ;
63
66
64
67
test ( 'works with useContext' , ( ) => {
65
68
const TestContext = React . createContext ( ) ;
66
69
const Component = ( { value } ) => < div > { value } </ div > ;
67
- // eslint-disable-next-line react-hooks/rules-of-hooks
68
- const Container = composeHooks ( { value : ( ) => useContext ( TestContext ) } ) (
69
- Component
70
- ) ;
71
- const wrapper = mount (
70
+ const Container = composeHooks ( {
71
+ value : function ContextFn ( ) {
72
+ return useContext ( TestContext ) ;
73
+ }
74
+ } ) ( Component ) ;
75
+ render (
72
76
< TestContext . Provider value = "Hello" >
73
77
< Container />
74
78
</ TestContext . Provider >
75
79
) ;
76
- expect ( wrapper . text ( ) ) . toBe ( 'Hello' ) ;
80
+ expect ( screen . getByText ( / h e l l o / i ) ) . toBeTruthy ( ) ;
77
81
} ) ;
78
82
79
83
test ( 'works with custom hook that returns array' , ( ) => {
80
84
const Component = ( { simpleHook } ) => {
81
85
const [ count , setCount ] = simpleHook ;
82
86
return < button onClick = { ( ) => setCount ( c => c + 1 ) } > { count } </ button > ;
83
87
} ;
84
-
85
88
const Container = composeHooks ( { simpleHook : useUseState } ) ( Component ) ;
86
- const wrapper = mount ( < Container /> ) ;
87
- expect ( wrapper . text ( ) ) . toBe ( INITIAL_COUNT . toString ( ) ) ;
88
- wrapper . find ( 'button' ) . simulate ( 'click' ) ;
89
- expect ( wrapper . text ( ) ) . toBe ( ( INITIAL_COUNT + 1 ) . toString ( ) ) ;
89
+ render ( < Container /> ) ;
90
+ expect ( screen . getByRole ( 'button' ) . textContent ) . toBe ( INITIAL_COUNT . toString ( ) ) ;
91
+ fireEvent . click ( screen . getByRole ( 'button' ) ) ;
92
+ expect ( screen . getByRole ( 'button' ) . textContent ) . toBe (
93
+ ( INITIAL_COUNT + 1 ) . toString ( )
94
+ ) ;
90
95
} ) ;
91
96
92
97
test ( 'works with custom hook that returns single value' , ( ) => {
@@ -106,20 +111,20 @@ test('works with custom hook that returns single value', () => {
106
111
< button onClick = { ( ) => setFoo ( 'after' ) } > { bar } </ button >
107
112
) ;
108
113
const Container = composeHooks ( { setFoo : useFoo , bar : useBar } ) ( Component ) ;
109
- const wrapper = mount ( < Container /> ) ;
114
+ render ( < Container /> ) ;
110
115
expect ( outerFoo ) . toBe ( 'before' ) ;
111
- wrapper . find ( { children : 'Click me' } ) . simulate ( 'click' ) ;
116
+ fireEvent . click ( screen . getByRole ( 'button' ) ) ;
112
117
expect ( outerFoo ) . toBe ( 'after' ) ;
113
118
} ) ;
114
119
115
120
test ( 'can pass props to hooks via function' , ( ) => {
116
121
const TEST_VALUE = 'test-value' ;
117
122
const Component = ( { value } ) => value ;
118
123
const Container = composeHooks ( props => ( {
119
- useChange : ( ) => useChange ( props . initialValue ) ,
124
+ useChange : ( ) => useChange ( props . initialValue )
120
125
} ) ) ( Component ) ;
121
- const wrapper = mount ( < Container initialValue = { TEST_VALUE } /> ) ;
122
- expect ( wrapper . text ( ) ) . toBe ( TEST_VALUE ) ;
126
+ render ( < Container initialValue = { TEST_VALUE } /> ) ;
127
+ expect ( screen . getByText ( TEST_VALUE ) ) . toBeTruthy ( ) ;
123
128
} ) ;
124
129
125
130
test ( 'useEffect from custom hook' , ( ) => {
@@ -144,8 +149,8 @@ test('useEffect from custom hook', () => {
144
149
describe ( 'Edge cases' , ( ) => {
145
150
it ( 'returns component if no hooks' , ( ) => {
146
151
const Container = composeHooks ( ) ( TestComponent ) ;
147
- const wrapper = shallow ( < Container text = "some text" /> ) ;
148
- expect ( wrapper . html ( ) ) . toMatchInlineSnapshot ( `"<div> some text</div>"` ) ;
152
+ render ( < Container text = "some text" /> ) ;
153
+ expect ( screen . getByText ( / s o m e t e x t / i ) ) . toBeTruthy ( ) ;
149
154
} ) ;
150
155
151
156
it ( 'throws if no component' , ( ) => {
@@ -214,14 +219,14 @@ describe('React.memo', () => {
214
219
const { setNameOne, setNameTwo } = useContext ( TestContext ) ;
215
220
return (
216
221
< >
217
- < input
218
- onChange = { e => setNameOne ( e . target . value ) }
219
- placeholder = "Name One"
220
- / >
221
- < input
222
- onChange = { e => setNameTwo ( e . target . value ) }
223
- placeholder = "Name Two"
224
- / >
222
+ < label >
223
+ Name One
224
+ < input onChange = { e => setNameOne ( e . target . value ) } />
225
+ </ label >
226
+ < label >
227
+ Name Two
228
+ < input onChange = { e => setNameTwo ( e . target . value ) } />
229
+ </ label >
225
230
</ >
226
231
) ;
227
232
} ;
@@ -258,39 +263,39 @@ describe('React.memo', () => {
258
263
} ) ;
259
264
260
265
it ( 'renders memoized child when props update' , ( ) => {
261
- const { getByText } = render ( < Parent /> ) ;
266
+ render ( < Parent /> ) ;
262
267
expect ( rendersOne ) . toBe ( 1 ) ;
263
268
expect ( rendersTwo ) . toBe ( 1 ) ;
264
269
265
- fireEvent . click ( getByText ( 'Update Child Two Props' ) ) ;
270
+ fireEvent . click ( screen . getByText ( 'Update Child Two Props' ) ) ;
266
271
expect ( rendersOne ) . toBe ( 2 ) ;
267
272
expect ( rendersTwo ) . toBe ( 2 ) ;
268
273
} ) ;
269
274
270
275
it ( 'does NOT re-render memoized child when child 2 props update' , ( ) => {
271
- const { getByText } = render ( < Parent /> ) ;
272
- fireEvent . click ( getByText ( 'Update Child One Props' ) ) ;
276
+ render ( < Parent /> ) ;
277
+ fireEvent . click ( screen . getByText ( 'Update Child One Props' ) ) ;
273
278
expect ( rendersOne ) . toBe ( 2 ) ;
274
279
expect ( rendersTwo ) . toBe ( 1 ) ;
275
280
} ) ;
276
281
277
282
it ( 'does NOT render memoized child when non-subscribed context value updates' , ( ) => {
278
- const { getByPlaceholderText , getByTestId } = render ( < Parent /> ) ;
279
- fireEvent . change ( getByPlaceholderText ( / n a m e o n e / i) , {
280
- target : { value : 'Calvin' } ,
283
+ render ( < Parent /> ) ;
284
+ fireEvent . change ( screen . getByLabelText ( / n a m e o n e / i) , {
285
+ target : { value : 'Calvin' }
281
286
} ) ;
282
287
expect ( rendersOne ) . toBe ( 2 ) ;
283
288
expect ( rendersTwo ) . toBe ( 1 ) ;
284
289
// All updates via Context so parent should not rerender
285
290
expect ( rendersParent ) . toBe ( 1 ) ;
286
- expect ( getByTestId ( 'one' ) . textContent ) . toBe ( 'Calvin' ) ;
287
- expect ( getByTestId ( 'two' ) . textContent ) . toBe ( '' ) ;
291
+ expect ( screen . getByTestId ( 'one' ) . textContent ) . toBe ( 'Calvin' ) ;
292
+ expect ( screen . getByTestId ( 'two' ) . textContent ) . toBe ( '' ) ;
288
293
} ) ;
289
294
290
295
it ( 'renders memoized child when subscribed context value changes' , ( ) => {
291
- const { getByPlaceholderText , getByTestId } = render ( < Parent /> ) ;
292
- fireEvent . change ( getByPlaceholderText ( / n a m e t w o / i) , {
293
- target : { value : 'Hobbes' } ,
296
+ const { getByTestId } = render ( < Parent /> ) ;
297
+ fireEvent . change ( screen . getByLabelText ( / n a m e t w o / i) , {
298
+ target : { value : 'Hobbes' }
294
299
} ) ;
295
300
expect ( rendersOne ) . toBe ( 2 ) ;
296
301
expect ( rendersTwo ) . toBe ( 2 ) ;
@@ -319,13 +324,12 @@ describe('Naming collisions', () => {
319
324
} ) ;
320
325
321
326
it ( 'if prop and hook names collide, props win (not including defaultProps)' , ( ) => {
327
+ const MockComponent = jest . fn ( ( ) => < div > Test</ div > ) ;
322
328
const Container = composeHooks ( { useOne, useNumber, useBool, useNull } ) (
323
- TestComponent
329
+ MockComponent
324
330
) ;
325
331
// Check falsy values, should warn for everything but undefined
326
- const wrapper = mount (
327
- < Container text = "" number = { 0 } bool = { false } null = { null } />
328
- ) ;
332
+ render ( < Container text = "" number = { 0 } bool = { false } null = { null } /> ) ;
329
333
const [ first , second , third , fourth ] = console . warn . mock . calls ;
330
334
expect ( first [ 0 ] ) . toMatchInlineSnapshot (
331
335
`"prop 'text' exists, overriding with value: ''"`
@@ -339,26 +343,28 @@ describe('Naming collisions', () => {
339
343
expect ( fourth [ 0 ] ) . toMatchInlineSnapshot (
340
344
`"prop 'null' exists, overriding with value: 'null'"`
341
345
) ;
342
- expect ( wrapper . find ( TestComponent ) . props ( ) . text ) . toBe ( '' ) ;
343
- expect ( wrapper . find ( TestComponent ) . props ( ) . number ) . toBe ( 0 ) ;
344
- expect ( wrapper . find ( TestComponent ) . props ( ) . bool ) . toBe ( false ) ;
345
- expect ( wrapper . find ( TestComponent ) . props ( ) . null ) . toBe ( null ) ;
346
+ const firstMockCall = MockComponent . mock . calls [ 0 ] [ 0 ] ;
347
+ expect ( firstMockCall . text ) . toBe ( '' ) ;
348
+ expect ( firstMockCall . number ) . toBe ( 0 ) ;
349
+ expect ( firstMockCall . bool ) . toBe ( false ) ;
350
+ expect ( firstMockCall . null ) . toBe ( null ) ;
346
351
} ) ;
347
352
348
353
it ( 'hooks override defaultProps' , ( ) => {
349
354
const Container = composeHooks ( { useOne } ) ( TestComponent ) ;
350
355
const { container } = render ( < Container /> ) ;
351
- const { container : test } = render ( < TestComponent /> ) ;
352
- expect ( test . textContent ) . toBe ( 'Test' ) ;
353
356
expect ( container . textContent ) . toBe ( 'one' ) ;
357
+ const { container : rawContainer } = render ( < TestComponent /> ) ;
358
+ expect ( rawContainer . textContent ) . toBe ( 'Test' ) ;
354
359
} ) ;
355
360
356
361
it ( 'if multiple hook value names collide, last one wins' , ( ) => {
357
362
const Container = composeHooks ( { useOne, useTwo } ) ( TestComponent ) ;
358
- const wrapper = mount ( < Container /> ) ;
363
+ render ( < Container /> ) ;
359
364
expect ( console . warn . mock . calls [ 0 ] [ 0 ] ) . toMatchInlineSnapshot (
360
365
`"prop 'text' exists, overriding with value: 'two'"`
361
366
) ;
362
- expect ( wrapper . find ( TestComponent ) . text ( ) ) . toBe ( 'two' ) ;
367
+ expect ( screen . queryByText ( 'two' ) ) . toBeTruthy ( ) ;
368
+ expect ( screen . queryByText ( 'text' ) ) . not . toBeTruthy ( ) ;
363
369
} ) ;
364
370
} ) ;
0 commit comments