@@ -47,6 +47,7 @@ import {
47
47
useClipboard ,
48
48
} from "./useClipboard" ;
49
49
import { act , renderHook } from "@testing-library/react" ;
50
+ import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar" ;
50
51
51
52
const initialExecCommand = global . document . execCommand ;
52
53
beforeAll ( ( ) => {
@@ -62,14 +63,28 @@ afterAll(() => {
62
63
type MockClipboardEscapeHatches = Readonly < {
63
64
getMockText : ( ) => string ;
64
65
setMockText : ( newText : string ) => void ;
66
+ simulateFailure : boolean ;
67
+ setSimulateFailure : ( failureMode : boolean ) => void ;
65
68
} > ;
66
69
67
70
type MockClipboard = Readonly < Clipboard & MockClipboardEscapeHatches > ;
68
71
function makeMockClipboard ( isSecureContext : boolean ) : MockClipboard {
69
72
let mockClipboardValue = "" ;
73
+ let shouldFail = false ;
70
74
71
75
return {
76
+ get simulateFailure ( ) {
77
+ return shouldFail ;
78
+ } ,
79
+ setSimulateFailure : ( value ) => {
80
+ shouldFail = value ;
81
+ } ,
82
+
72
83
readText : async ( ) => {
84
+ if ( shouldFail ) {
85
+ throw new Error ( "Clipboard deliberately failed" ) ;
86
+ }
87
+
73
88
if ( ! isSecureContext ) {
74
89
throw new Error (
75
90
"Trying to read from clipboard outside secure context!" ,
@@ -79,6 +94,10 @@ function makeMockClipboard(isSecureContext: boolean): MockClipboard {
79
94
return mockClipboardValue ;
80
95
} ,
81
96
writeText : async ( newText ) => {
97
+ if ( shouldFail ) {
98
+ throw new Error ( "Clipboard deliberately failed" ) ;
99
+ }
100
+
82
101
if ( ! isSecureContext ) {
83
102
throw new Error ( "Trying to write to clipboard outside secure context!" ) ;
84
103
}
@@ -99,10 +118,18 @@ function makeMockClipboard(isSecureContext: boolean): MockClipboard {
99
118
} ;
100
119
}
101
120
102
- function renderUseClipboard ( textToCopy : string , displayErrors : boolean ) {
121
+ function renderUseClipboard ( inputs : UseClipboardInput ) {
103
122
return renderHook < UseClipboardResult , UseClipboardInput > (
104
123
( props ) => useClipboard ( props ) ,
105
- { initialProps : { textToCopy, displayErrors } } ,
124
+ {
125
+ initialProps : inputs ,
126
+ wrapper : ( { children } ) => (
127
+ < >
128
+ < > { children } </ >
129
+ < GlobalSnackbar />
130
+ </ >
131
+ ) ,
132
+ } ,
106
133
) ;
107
134
}
108
135
@@ -111,8 +138,8 @@ type ScheduleConfig = Readonly<{ isHttps: boolean }>;
111
138
export function scheduleClipboardTests ( { isHttps } : ScheduleConfig ) {
112
139
const mockClipboardInstance = makeMockClipboard ( isHttps ) ;
113
140
141
+ const originalNavigator = window . navigator ;
114
142
beforeAll ( ( ) => {
115
- const originalNavigator = window . navigator ;
116
143
jest . spyOn ( window , "navigator" , "get" ) . mockImplementation ( ( ) => ( {
117
144
...originalNavigator ,
118
145
clipboard : mockClipboardInstance ,
@@ -122,6 +149,10 @@ export function scheduleClipboardTests({ isHttps }: ScheduleConfig) {
122
149
// Not the biggest fan of exposing implementation details like this, but
123
150
// making any kind of mock for execCommand is really gnarly in general
124
151
global . document . execCommand = jest . fn ( ( ) => {
152
+ if ( mockClipboardInstance . simulateFailure ) {
153
+ return false ;
154
+ }
155
+
125
156
const dummyInput = document . querySelector ( "input[data-testid=dummy]" ) ;
126
157
const inputIsFocused =
127
158
dummyInput instanceof HTMLInputElement &&
@@ -157,20 +188,35 @@ export function scheduleClipboardTests({ isHttps }: ScheduleConfig) {
157
188
* Start of test cases
158
189
*/
159
190
it ( "Copies the current text to the user's clipboard" , async ( ) => {
160
- const hookText = "dogs" ;
161
- const { result } = renderUseClipboard ( hookText , false ) ;
162
- await assertClipboardTextUpdate ( result , hookText ) ;
191
+ const textToCopy = "dogs" ;
192
+ const { result } = renderUseClipboard ( { textToCopy } ) ;
193
+ await assertClipboardTextUpdate ( result , textToCopy ) ;
163
194
} ) ;
164
195
165
196
it ( "Should indicate to components not to show successful copy after a set period of time" , async ( ) => {
166
- const hookText = "cats" ;
167
- const { result } = renderUseClipboard ( hookText , false ) ;
168
- await assertClipboardTextUpdate ( result , hookText ) ;
197
+ const textToCopy = "cats" ;
198
+ const { result } = renderUseClipboard ( { textToCopy } ) ;
199
+ await assertClipboardTextUpdate ( result , textToCopy ) ;
169
200
170
201
setTimeout ( ( ) => {
171
202
expect ( result . current . showCopiedSuccess ) . toBe ( false ) ;
172
203
} , 10_000 ) ;
173
204
174
205
await jest . runAllTimersAsync ( ) ;
175
206
} ) ;
207
+
208
+ it ( "Should notify the user of an error using the provided callback" , async ( ) => {
209
+ const textToCopy = "birds" ;
210
+ const errorCallback = jest . fn ( ) ;
211
+ const { result } = renderUseClipboard ( {
212
+ textToCopy,
213
+ onError : errorCallback ,
214
+ } ) ;
215
+
216
+ mockClipboardInstance . setSimulateFailure ( true ) ;
217
+ await act ( ( ) => result . current . copyToClipboard ( ) ) ;
218
+ mockClipboardInstance . setSimulateFailure ( false ) ;
219
+
220
+ expect ( errorCallback ) . toBeCalled ( ) ;
221
+ } ) ;
176
222
}
0 commit comments