1
- /*
2
- Normally, you could call userEvent.setup to enable clipboard mocking, but
3
- userEvent doesn't expose a teardown function. It also modifies the global
4
- scope for the whole test file, so enabling just one userEvent session will
5
- make a mock clipboard exist for all other tests, even though you didn't tell
6
- them to set up a session. The mock also assumes that the clipboard API will
7
- always be available, which is not true on HTTP-only connections
8
-
9
- Since these tests need to split hairs and differentiate between HTTP and HTTPS
10
- connections, setting up a single userEvent is disastrous. It will make all the
11
- tests pass, even if they shouldn't. Have to avoid that by creating a custom
12
- clipboard mock.
13
- */
1
+ /**
2
+ * @file This is a very weird test setup.
3
+ *
4
+ * There are two main things that it's fighting against to insure that the
5
+ * clipboard functionality is working as expected:
6
+ * 1. userEvent.setup's default global behavior
7
+ * 2. The fact that we need to reuse the same set of test cases for two separate
8
+ * contexts (secure and insecure), each with their own version of global
9
+ * state.
10
+ *
11
+ * The goal of this file is to provide a shared set of test behavior that can
12
+ * be imported into two separate test files (one for HTTP, one for HTTPS),
13
+ * without any risk of global state conflicts.
14
+ *
15
+ * ---
16
+ * For (1), normally you could call userEvent.setup to enable clipboard mocking,
17
+ * but userEvent doesn't expose a teardown function. It also modifies the global
18
+ * scope for the whole test file, so enabling just one userEvent session will
19
+ * make a mock clipboard exist for all other tests, even though you didn't tell
20
+ * them to set up a session. The mock also assumes that the clipboard API will
21
+ * always be available, which is not true on HTTP-only connections
22
+ *
23
+ * Since these tests need to split hairs and differentiate between HTTP and
24
+ * HTTPS connections, setting up a single userEvent is disastrous. It will make
25
+ * all the tests pass, even if they shouldn't. Have to avoid that by creating a
26
+ * custom clipboard mock.
27
+ *
28
+ * ---
29
+ * For (2), we're fighting against Jest's default behavior, which is to treat
30
+ * the test file as the main boundary for test environments, with each test case
31
+ * able to run in parallel. That works if you have one single global state, but
32
+ * we need two separate versions of the global state, while repeating the exact
33
+ * same test cases for each one.
34
+ *
35
+ * If both tests were to be placed in the same file, Jest would not isolate them
36
+ * and would let their setup steps interfere with each other. This leads to one
37
+ * of two things:
38
+ * 1. One of the global mocks overrides the other, making it so that one
39
+ * connection type always fails
40
+ * 2. The two just happen not to conflict each other, through some convoluted
41
+ * order of operations involving closure, but you have no idea why the code
42
+ * is working, and it's impossible to debug.
43
+ */
14
44
import { type UseClipboardResult , useClipboard } from "./useClipboard" ;
15
45
import { act , renderHook } from "@testing-library/react" ;
16
46
@@ -72,6 +102,8 @@ function renderUseClipboard(textToCopy: string) {
72
102
) ;
73
103
}
74
104
105
+ type ScheduleConfig = Readonly < { isHttps : boolean } > ;
106
+
75
107
/**
76
108
* Unconventional test setup, but we need two separate instances of the
77
109
* MockClipboard (one for HTTP and one for HTTPS).
@@ -80,7 +112,7 @@ function renderUseClipboard(textToCopy: string) {
80
112
* else you get shared mutable state, and test cases interfering with each
81
113
* other. Test isolation is especially important for this test file
82
114
*/
83
- function scheduleTests ( isHttps : boolean ) {
115
+ export function scheduleClipboardTests ( { isHttps } : ScheduleConfig ) {
84
116
const mockClipboardInstance = makeMockClipboard ( isHttps ) ;
85
117
86
118
beforeAll ( ( ) => {
@@ -148,13 +180,3 @@ function scheduleTests(isHttps: boolean) {
148
180
await jest . runAllTimersAsync ( ) ;
149
181
} ) ;
150
182
}
151
-
152
- describe ( useClipboard . name , ( ) => {
153
- describe ( "HTTP (non-secure) connections" , ( ) => {
154
- scheduleTests ( false ) ;
155
- } ) ;
156
-
157
- describe ( "HTTPS connections" , ( ) => {
158
- scheduleTests ( true ) ;
159
- } ) ;
160
- } ) ;
0 commit comments