|
2 | 2 |
|
3 | 3 | import ReactExports, { StrictMode, Suspense } from 'react' |
4 | 4 | import { act, fireEvent, render, screen } from '@testing-library/react' |
5 | | -import { afterEach, beforeEach, expect, it, vi } from 'vitest' |
| 5 | +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' |
6 | 6 | import { proxy, useSnapshot } from 'valtio' |
7 | | - |
8 | | -beforeEach(() => { |
9 | | - vi.useFakeTimers() |
10 | | -}) |
11 | | - |
12 | | -afterEach(() => { |
13 | | - vi.useRealTimers() |
14 | | -}) |
15 | | - |
16 | | -const sleep = (ms: number) => |
17 | | - new Promise((resolve) => { |
18 | | - setTimeout(resolve, ms) |
19 | | - }) |
| 7 | +import { sleep } from './utils' |
20 | 8 |
|
21 | 9 | const { use } = ReactExports |
22 | 10 | const use2 = <T,>(x: T): Awaited<T> => |
23 | 11 | x instanceof Promise ? use(x) : (x as Awaited<T>) |
24 | 12 |
|
25 | | -it.skipIf(typeof use === 'undefined')('delayed increment', async () => { |
26 | | - const state = proxy<any>({ count: 0 }) |
27 | | - const delayedIncrement = () => { |
28 | | - const nextCount = state.count + 1 |
29 | | - state.count = sleep(300).then(() => nextCount) |
30 | | - } |
31 | | - |
32 | | - const Counter = () => { |
33 | | - const snap = useSnapshot(state) |
34 | | - return ( |
35 | | - <> |
36 | | - <div>count: {use2(snap.count)}</div> |
37 | | - <button onClick={delayedIncrement}>button</button> |
38 | | - </> |
39 | | - ) |
40 | | - } |
41 | | - |
42 | | - render( |
43 | | - <StrictMode> |
44 | | - <Suspense fallback="loading"> |
45 | | - <Counter /> |
46 | | - </Suspense> |
47 | | - </StrictMode>, |
48 | | - ) |
| 13 | +describe('async', () => { |
| 14 | + beforeEach(() => { |
| 15 | + vi.useFakeTimers() |
| 16 | + }) |
49 | 17 |
|
50 | | - expect(screen.getByText('count: 0')).toBeInTheDocument() |
| 18 | + afterEach(() => { |
| 19 | + vi.useRealTimers() |
| 20 | + }) |
51 | 21 |
|
52 | | - fireEvent.click(screen.getByText('button')) |
53 | | - await act(() => vi.advanceTimersByTimeAsync(0)) |
54 | | - expect(screen.getByText('loading')).toBeInTheDocument() |
55 | | - await act(() => vi.advanceTimersByTimeAsync(300)) |
56 | | - expect(screen.getByText('count: 1')).toBeInTheDocument() |
57 | | -}) |
| 22 | + it.skipIf(typeof use === 'undefined')('delayed increment', async () => { |
| 23 | + const state = proxy<any>({ count: 0 }) |
| 24 | + const delayedIncrement = () => { |
| 25 | + const nextCount = state.count + 1 |
| 26 | + state.count = sleep(300).then(() => nextCount) |
| 27 | + } |
| 28 | + |
| 29 | + const Counter = () => { |
| 30 | + const snap = useSnapshot(state) |
| 31 | + return ( |
| 32 | + <> |
| 33 | + <div>count: {use2(snap.count)}</div> |
| 34 | + <button onClick={delayedIncrement}>button</button> |
| 35 | + </> |
| 36 | + ) |
| 37 | + } |
58 | 38 |
|
59 | | -it.skipIf(typeof use === 'undefined')('delayed object', async () => { |
60 | | - const state = proxy<any>({ object: { text: 'none' } }) |
61 | | - const delayedObject = () => { |
62 | | - state.object = sleep(300).then(() => ({ text: 'hello' })) |
63 | | - } |
64 | | - |
65 | | - const Counter = () => { |
66 | | - const snap = useSnapshot(state) |
67 | | - return ( |
68 | | - <> |
69 | | - <div>text: {use2(snap.object).text}</div> |
70 | | - <button onClick={delayedObject}>button</button> |
71 | | - </> |
| 39 | + render( |
| 40 | + <StrictMode> |
| 41 | + <Suspense fallback="loading"> |
| 42 | + <Counter /> |
| 43 | + </Suspense> |
| 44 | + </StrictMode>, |
72 | 45 | ) |
73 | | - } |
74 | | - |
75 | | - render( |
76 | | - <StrictMode> |
77 | | - <Suspense fallback="loading"> |
78 | | - <Counter /> |
79 | | - </Suspense> |
80 | | - </StrictMode>, |
81 | | - ) |
82 | 46 |
|
83 | | - expect(screen.getByText('text: none')).toBeInTheDocument() |
| 47 | + expect(screen.getByText('count: 0')).toBeInTheDocument() |
84 | 48 |
|
85 | | - fireEvent.click(screen.getByText('button')) |
86 | | - await act(() => vi.advanceTimersByTimeAsync(0)) |
87 | | - expect(screen.getByText('loading')).toBeInTheDocument() |
88 | | - await act(() => vi.advanceTimersByTimeAsync(300)) |
89 | | - expect(screen.getByText('text: hello')).toBeInTheDocument() |
90 | | -}) |
| 49 | + fireEvent.click(screen.getByText('button')) |
| 50 | + await act(() => vi.advanceTimersByTimeAsync(0)) |
| 51 | + expect(screen.getByText('loading')).toBeInTheDocument() |
| 52 | + await act(() => vi.advanceTimersByTimeAsync(300)) |
| 53 | + expect(screen.getByText('count: 1')).toBeInTheDocument() |
| 54 | + }) |
91 | 55 |
|
92 | | -it.skipIf(typeof use === 'undefined')( |
93 | | - 'delayed object update fulfilled', |
94 | | - async () => { |
95 | | - const state = proxy<any>({ |
96 | | - object: sleep(300).then(() => ({ text: 'counter', count: 0 })), |
97 | | - }) |
98 | | - const updateObject = () => { |
99 | | - state.object = state.object.then((v: any) => |
100 | | - sleep(300).then(() => ({ ...v, count: v.count + 1 })), |
101 | | - ) |
| 56 | + it.skipIf(typeof use === 'undefined')('delayed object', async () => { |
| 57 | + const state = proxy<any>({ object: { text: 'none' } }) |
| 58 | + const delayedObject = () => { |
| 59 | + state.object = sleep(300).then(() => ({ text: 'hello' })) |
102 | 60 | } |
103 | 61 |
|
104 | 62 | const Counter = () => { |
105 | 63 | const snap = useSnapshot(state) |
106 | 64 | return ( |
107 | 65 | <> |
108 | 66 | <div>text: {use2(snap.object).text}</div> |
109 | | - <div>count: {use2(snap.object).count}</div> |
110 | | - <button onClick={updateObject}>button</button> |
| 67 | + <button onClick={delayedObject}>button</button> |
111 | 68 | </> |
112 | 69 | ) |
113 | 70 | } |
114 | 71 |
|
115 | | - // eslint-disable-next-line testing-library/no-unnecessary-act |
116 | | - await act(() => |
117 | | - render( |
118 | | - <StrictMode> |
119 | | - <Suspense fallback="loading"> |
120 | | - <Counter /> |
121 | | - </Suspense> |
122 | | - </StrictMode>, |
123 | | - ), |
| 72 | + render( |
| 73 | + <StrictMode> |
| 74 | + <Suspense fallback="loading"> |
| 75 | + <Counter /> |
| 76 | + </Suspense> |
| 77 | + </StrictMode>, |
124 | 78 | ) |
125 | 79 |
|
126 | | - expect(screen.getByText('loading')).toBeInTheDocument() |
127 | | - await act(() => vi.advanceTimersByTimeAsync(300)) |
128 | | - expect(screen.getByText('text: counter')).toBeInTheDocument() |
129 | | - expect(screen.getByText('count: 0')).toBeInTheDocument() |
| 80 | + expect(screen.getByText('text: none')).toBeInTheDocument() |
130 | 81 |
|
131 | 82 | fireEvent.click(screen.getByText('button')) |
132 | 83 | await act(() => vi.advanceTimersByTimeAsync(0)) |
133 | 84 | expect(screen.getByText('loading')).toBeInTheDocument() |
134 | 85 | await act(() => vi.advanceTimersByTimeAsync(300)) |
135 | | - expect(screen.getByText('text: counter')).toBeInTheDocument() |
136 | | - expect(screen.getByText('count: 1')).toBeInTheDocument() |
137 | | - }, |
138 | | -) |
139 | | - |
140 | | -it.skipIf(typeof use === 'undefined')('delayed falsy value', async () => { |
141 | | - const state = proxy<any>({ value: true }) |
142 | | - const delayedValue = () => { |
143 | | - state.value = sleep(300).then(() => null) |
144 | | - } |
145 | | - |
146 | | - const Counter = () => { |
147 | | - const snap = useSnapshot(state) |
148 | | - return ( |
149 | | - <> |
150 | | - <div>value: {String(use2(snap.value))}</div> |
151 | | - <button onClick={delayedValue}>button</button> |
152 | | - </> |
153 | | - ) |
154 | | - } |
155 | | - |
156 | | - render( |
157 | | - <StrictMode> |
158 | | - <Suspense fallback="loading"> |
159 | | - <Counter /> |
160 | | - </Suspense> |
161 | | - </StrictMode>, |
| 86 | + expect(screen.getByText('text: hello')).toBeInTheDocument() |
| 87 | + }) |
| 88 | + |
| 89 | + it.skipIf(typeof use === 'undefined')( |
| 90 | + 'delayed object update fulfilled', |
| 91 | + async () => { |
| 92 | + const state = proxy<any>({ |
| 93 | + object: sleep(300).then(() => ({ text: 'counter', count: 0 })), |
| 94 | + }) |
| 95 | + const updateObject = () => { |
| 96 | + state.object = state.object.then((v: any) => |
| 97 | + sleep(300).then(() => ({ ...v, count: v.count + 1 })), |
| 98 | + ) |
| 99 | + } |
| 100 | + |
| 101 | + const Counter = () => { |
| 102 | + const snap = useSnapshot(state) |
| 103 | + return ( |
| 104 | + <> |
| 105 | + <div>text: {use2(snap.object).text}</div> |
| 106 | + <div>count: {use2(snap.object).count}</div> |
| 107 | + <button onClick={updateObject}>button</button> |
| 108 | + </> |
| 109 | + ) |
| 110 | + } |
| 111 | + |
| 112 | + // eslint-disable-next-line testing-library/no-unnecessary-act |
| 113 | + await act(() => |
| 114 | + render( |
| 115 | + <StrictMode> |
| 116 | + <Suspense fallback="loading"> |
| 117 | + <Counter /> |
| 118 | + </Suspense> |
| 119 | + </StrictMode>, |
| 120 | + ), |
| 121 | + ) |
| 122 | + |
| 123 | + expect(screen.getByText('loading')).toBeInTheDocument() |
| 124 | + await act(() => vi.advanceTimersByTimeAsync(300)) |
| 125 | + expect(screen.getByText('text: counter')).toBeInTheDocument() |
| 126 | + expect(screen.getByText('count: 0')).toBeInTheDocument() |
| 127 | + |
| 128 | + fireEvent.click(screen.getByText('button')) |
| 129 | + await act(() => vi.advanceTimersByTimeAsync(0)) |
| 130 | + expect(screen.getByText('loading')).toBeInTheDocument() |
| 131 | + await act(() => vi.advanceTimersByTimeAsync(300)) |
| 132 | + expect(screen.getByText('text: counter')).toBeInTheDocument() |
| 133 | + expect(screen.getByText('count: 1')).toBeInTheDocument() |
| 134 | + }, |
162 | 135 | ) |
163 | 136 |
|
164 | | - expect(screen.getByText('value: true')).toBeInTheDocument() |
| 137 | + it.skipIf(typeof use === 'undefined')('delayed falsy value', async () => { |
| 138 | + const state = proxy<any>({ value: true }) |
| 139 | + const delayedValue = () => { |
| 140 | + state.value = sleep(300).then(() => null) |
| 141 | + } |
| 142 | + |
| 143 | + const Counter = () => { |
| 144 | + const snap = useSnapshot(state) |
| 145 | + return ( |
| 146 | + <> |
| 147 | + <div>value: {String(use2(snap.value))}</div> |
| 148 | + <button onClick={delayedValue}>button</button> |
| 149 | + </> |
| 150 | + ) |
| 151 | + } |
| 152 | + |
| 153 | + render( |
| 154 | + <StrictMode> |
| 155 | + <Suspense fallback="loading"> |
| 156 | + <Counter /> |
| 157 | + </Suspense> |
| 158 | + </StrictMode>, |
| 159 | + ) |
| 160 | + |
| 161 | + expect(screen.getByText('value: true')).toBeInTheDocument() |
165 | 162 |
|
166 | | - fireEvent.click(screen.getByText('button')) |
167 | | - await act(() => vi.advanceTimersByTimeAsync(0)) |
168 | | - expect(screen.getByText('loading')).toBeInTheDocument() |
169 | | - await act(() => vi.advanceTimersByTimeAsync(300)) |
170 | | - expect(screen.getByText('value: null')).toBeInTheDocument() |
| 163 | + fireEvent.click(screen.getByText('button')) |
| 164 | + await act(() => vi.advanceTimersByTimeAsync(0)) |
| 165 | + expect(screen.getByText('loading')).toBeInTheDocument() |
| 166 | + await act(() => vi.advanceTimersByTimeAsync(300)) |
| 167 | + expect(screen.getByText('value: null')).toBeInTheDocument() |
| 168 | + }) |
171 | 169 | }) |
0 commit comments