17
let DiscreteEventPriority;
18
let startTransition;
19
let waitForMicrotasks;
20
+let Scheduler;
21
+let assertLog;
22
23
describe('isomorphic act()', () => {
24
beforeEach(() => {
25
React = require('react');
26
+ Scheduler = require('scheduler');
27
28
ReactNoop = require('react-noop-renderer');
29
DiscreteEventPriority =
34
startTransition = React.startTransition;
35
36
waitForMicrotasks = require('internal-test-utils').waitForMicrotasks;
37
+ assertLog = require('internal-test-utils').assertLog;
38
});
39
40
beforeEach(() => {
45
jest.restoreAllMocks();
46
});
47
48
+ function Text({text}) {
49
+ Scheduler.log(text);
50
+ return text;
51
+ }
52
+
53
// @gate __DEV__
54
test('bypasses queueMicrotask', async () => {
55
const root = ReactNoop.createRoot();
141
const root = ReactNoop.createLegacyRoot();
142
143
await act(async () => {
135
- // These updates are batched. This replicates the behavior of the original
136
- // `act` implementation, for compatibility.
137
- root.render('A');
138
- root.render('B');
139
- // Nothing has rendered yet.
140
- expect(root).toMatchRenderedOutput(null);
144
+ queueMicrotask(() => {
145
+ Scheduler.log('Current tree in microtask: ' + root.getChildrenAsJSX());
146
+ root.render(<Text text="C" />);
147
+ });
148
+ root.render(<Text text="A" />);
149
+ root.render(<Text text="B" />);
150
+
151
await null;
142
- // Updates are flushed after the first await.
143
- expect(root).toMatchRenderedOutput('B');
152
+ assertLog([
153
+ // A and B should render in a single batch _before_ the microtask queue
154
+ // has run. This replicates the behavior of the original `act`
155
+ // implementation, for compatibility.
156
+ 'B',
157
+ 'Current tree in microtask: B',
158
+
159
+ // C isn't scheduled until a microtask, so it's rendered separately.
160
+ 'C',
161
+ ]);
162
+
163
+ // Subsequent updates should also render in separate batches.
164
+ root.render(<Text text="D" />);
165
+ root.render(<Text text="E" />);
166
+ assertLog(['D', 'E']);
167
+ });
168
+ });
169
145
- // Subsequent updates in the same scope aren't batched.
146
- root.render('C');
147
- expect(root).toMatchRenderedOutput('C');
170
+ // @gate __DEV__
171
+ test('in legacy mode, in an async scope, updates are batched until the first `await` (regression test: batchedUpdates)', async () => {
172
+ const root = ReactNoop.createLegacyRoot();
173
+
174
+ await act(async () => {
175
+ queueMicrotask(() => {
176
+ Scheduler.log('Current tree in microtask: ' + root.getChildrenAsJSX());
177
+ root.render(<Text text="C" />);
178
+ });
179
+
180
+ // This is a regression test. The presence of `batchedUpdates` would cause
181
+ // these updates to not flush until a microtask. The correct behavior is
182
+ // that they flush before the microtask queue, regardless of whether
183
+ // they are wrapped with `batchedUpdates`.
184
+ ReactNoop.batchedUpdates(() => {
185
+ root.render(<Text text="A" />);
186
+ root.render(<Text text="B" />);
187
+ });
188
+
189
+ await null;
190
+ assertLog([
191
+ // A and B should render in a single batch _before_ the microtask queue
192
+ // has run. This replicates the behavior of the original `act`
193
+ // implementation, for compatibility.
194
+ 'B',
195
+ 'Current tree in microtask: B',
196
+
197
+ // C isn't scheduled until a microtask, so it's rendered separately.
198
+ 'C',
199
+ ]);
200
+
201
+ // Subsequent updates should also render in separate batches.
202
+ root.render(<Text text="D" />);
203
+ root.render(<Text text="E" />);
204
+ assertLog(['D', 'E']);
205
});
206
});
207