1
1
import { render , screen } from "@testing-library/react" ;
2
- import React , { act } from "react" ;
2
+ import { act , type FC } from "react" ;
3
3
import Action from "@/components/Action" ;
4
- import { Button } from "@/components/Button" ;
4
+ import { Button , type ButtonProps } from "@/components/Button" ;
5
5
import type { Mock } from "vitest" ;
6
6
import userEvent from "@/lib/dev/vitestUserEvent" ;
7
7
@@ -44,7 +44,10 @@ afterEach(() => {
44
44
vitest . useRealTimers ( ) ;
45
45
} ) ;
46
46
47
- const button = < Button data-testid = "button" /> ;
47
+ const TestButton : FC < ButtonProps > = ( p ) => (
48
+ < Button data-testid = "button" { ...p } />
49
+ ) ;
50
+
48
51
const getButton = ( ) => screen . getByTestId ( "button" ) ;
49
52
50
53
const clickTrigger = async ( ) => {
@@ -56,18 +59,30 @@ const advanceTime = async (ms: number) => {
56
59
} ;
57
60
58
61
test ( "Sync Action is called when trigger is clicked" , async ( ) => {
59
- render ( < Action action = { syncAction1 } > { button } </ Action > ) ;
62
+ render (
63
+ < Action action = { syncAction1 } >
64
+ < TestButton />
65
+ </ Action > ,
66
+ ) ;
60
67
await clickTrigger ( ) ;
61
68
expect ( syncAction1 ) . toHaveBeenCalledOnce ( ) ;
62
69
} ) ;
63
70
64
71
test ( "Action function is updated when action prop changes" , async ( ) => {
65
- const r = render ( < Action action = { syncAction1 } > { button } </ Action > ) ;
72
+ const r = render (
73
+ < Action action = { syncAction1 } >
74
+ < TestButton />
75
+ </ Action > ,
76
+ ) ;
66
77
await clickTrigger ( ) ;
67
78
expect ( syncAction1 ) . toHaveBeenCalledOnce ( ) ;
68
79
expect ( syncAction2 ) . not . toHaveBeenCalledOnce ( ) ;
69
80
70
- r . rerender ( < Action action = { syncAction2 } > { button } </ Action > ) ;
81
+ r . rerender (
82
+ < Action action = { syncAction2 } >
83
+ < TestButton />
84
+ </ Action > ,
85
+ ) ;
71
86
await clickTrigger ( ) ;
72
87
expect ( syncAction1 ) . toHaveBeenCalledOnce ( ) ;
73
88
expect ( syncAction2 ) . toHaveBeenCalledOnce ( ) ;
@@ -76,7 +91,9 @@ test("Action function is updated when action prop changes", async () => {
76
91
test ( "Nested sync actions are called when trigger is clicked" , async ( ) => {
77
92
render (
78
93
< Action action = { syncAction2 } >
79
- < Action action = { syncAction1 } > { button } </ Action >
94
+ < Action action = { syncAction1 } >
95
+ < TestButton />
96
+ </ Action >
80
97
</ Action > ,
81
98
) ;
82
99
await clickTrigger ( ) ;
@@ -88,7 +105,9 @@ test("Nested sync actions are not called when break action is used", async () =>
88
105
render (
89
106
< Action action = { syncAction2 } >
90
107
< Action break >
91
- < Action action = { syncAction1 } > { button } </ Action >
108
+ < Action action = { syncAction1 } >
109
+ < TestButton />
110
+ </ Action >
92
111
</ Action >
93
112
</ Action > ,
94
113
) ;
@@ -102,7 +121,9 @@ test("Nested sync actions are not called when skipped", async () => {
102
121
< Action action = { syncAction2 } >
103
122
< Action action = { syncAction2 } >
104
123
< Action skip >
105
- < Action action = { syncAction1 } > { button } </ Action >
124
+ < Action action = { syncAction1 } >
125
+ < TestButton />
126
+ </ Action >
106
127
</ Action >
107
128
</ Action >
108
129
</ Action > ,
@@ -117,7 +138,9 @@ test("Nested sync actions are not called when multiple skipped", async () => {
117
138
< Action action = { syncAction2 } >
118
139
< Action action = { syncAction2 } >
119
140
< Action skip = { 2 } >
120
- < Action action = { syncAction1 } > { button } </ Action >
141
+ < Action action = { syncAction1 } >
142
+ < TestButton />
143
+ </ Action >
121
144
</ Action >
122
145
</ Action >
123
146
</ Action > ,
@@ -130,7 +153,9 @@ test("Nested sync actions are not called when multiple skipped", async () => {
130
153
test ( "When nested sync actions, the inner action is called first" , async ( ) => {
131
154
render (
132
155
< Action action = { syncAction2 } >
133
- < Action action = { syncAction1 } > { button } </ Action >
156
+ < Action action = { syncAction1 } >
157
+ < TestButton />
158
+ </ Action >
134
159
</ Action > ,
135
160
) ;
136
161
@@ -139,7 +164,11 @@ test("When nested sync actions, the inner action is called first", async () => {
139
164
} ) ;
140
165
141
166
test ( "Button is enabled again when async action has completed" , async ( ) => {
142
- render ( < Action action = { asyncAction1 } > { button } </ Action > ) ;
167
+ render (
168
+ < Action action = { asyncAction1 } >
169
+ < TestButton />
170
+ </ Action > ,
171
+ ) ;
143
172
await clickTrigger ( ) ;
144
173
await advanceTime ( asyncActionDuration ) ;
145
174
expect ( getButton ( ) ) . not . toBeDisabled ( ) ;
@@ -148,7 +177,9 @@ test("Button is enabled again when async action has completed", async () => {
148
177
test ( "When nested async actions, the outer action is called after the first has completed" , async ( ) => {
149
178
render (
150
179
< Action action = { asyncAction2 } >
151
- < Action action = { asyncAction1 } > { button } </ Action >
180
+ < Action action = { asyncAction1 } >
181
+ < TestButton />
182
+ </ Action >
152
183
</ Action > ,
153
184
) ;
154
185
await clickTrigger ( ) ;
@@ -181,20 +212,35 @@ describe("Feedback", () => {
181
212
test ( "is shown when sync action succeeds" , async ( ) => {
182
213
render (
183
214
< Action action = { syncAction1 } showFeedback >
184
- { button }
215
+ < TestButton />
185
216
</ Action > ,
186
217
) ;
187
218
await clickTrigger ( ) ;
188
219
expectIconInDom ( "check" ) ;
189
220
} ) ;
190
221
222
+ test ( "is shown when set in props" , async ( ) => {
223
+ const dom = render (
224
+ < Action action = { syncAction1 } showFeedback >
225
+ < TestButton isSucceeded />
226
+ </ Action > ,
227
+ ) ;
228
+ expectIconInDom ( "check" ) ;
229
+ dom . rerender (
230
+ < Action action = { syncAction1 } showFeedback >
231
+ < TestButton isFailed />
232
+ </ Action > ,
233
+ ) ;
234
+ expectIconInDom ( "x" ) ;
235
+ } ) ;
236
+
191
237
test ( "is shown when sync action fails" , async ( ) => {
192
238
syncAction1 . mockImplementation ( ( ) => {
193
239
throw new Error ( "Whoops" ) ;
194
240
} ) ;
195
241
render (
196
242
< Action action = { syncAction1 } showFeedback >
197
- { button }
243
+ < TestButton />
198
244
</ Action > ,
199
245
) ;
200
246
await clickTrigger ( ) ;
@@ -204,7 +250,7 @@ describe("Feedback", () => {
204
250
test ( "is hidden after some time" , async ( ) => {
205
251
render (
206
252
< Action action = { syncAction1 } showFeedback >
207
- { button }
253
+ < TestButton />
208
254
</ Action > ,
209
255
) ;
210
256
await clickTrigger ( ) ;
@@ -222,14 +268,31 @@ describe("Pending state", () => {
222
268
} ) ;
223
269
224
270
test ( "is shown when async action is pending" , async ( ) => {
225
- render ( < Action action = { asyncAction1 } > { button } </ Action > ) ;
271
+ render (
272
+ < Action action = { asyncAction1 } >
273
+ < TestButton />
274
+ </ Action > ,
275
+ ) ;
226
276
await clickTrigger ( ) ;
227
277
await advanceTime ( 1000 ) ;
228
278
expectIconInDom ( "loader-2" ) ;
229
279
} ) ;
230
280
281
+ test ( "is shown when set in props" , async ( ) => {
282
+ render (
283
+ < Action action = { asyncAction1 } >
284
+ < TestButton isPending />
285
+ </ Action > ,
286
+ ) ;
287
+ expectIconInDom ( "loader-2" ) ;
288
+ } ) ;
289
+
231
290
test ( "is not shown when sync action is executed" , async ( ) => {
232
- render ( < Action action = { syncAction1 } > { button } </ Action > ) ;
291
+ render (
292
+ < Action action = { syncAction1 } >
293
+ < TestButton />
294
+ </ Action > ,
295
+ ) ;
233
296
await clickTrigger ( ) ;
234
297
await advanceTime ( 1000 ) ;
235
298
expectNoIconInDom ( ) ;
@@ -238,7 +301,7 @@ describe("Pending state", () => {
238
301
test ( "is hidden after some time" , async ( ) => {
239
302
render (
240
303
< Action action = { asyncAction1 } showFeedback >
241
- { button }
304
+ < TestButton />
242
305
</ Action > ,
243
306
) ;
244
307
await clickTrigger ( ) ;
0 commit comments