Skip to content

Commit 65643b0

Browse files
committed
test
1 parent 39eb6c8 commit 65643b0

File tree

2 files changed

+183
-8
lines changed

2 files changed

+183
-8
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,11 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
2222
import type {UpdateQueue} from './ReactUpdateQueue.new';
2323
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
2424
import type {Wakeable} from 'shared/ReactTypes';
25-
import type {
26-
OffscreenState,
27-
OffscreenInstance,
28-
} from './ReactFiberOffscreenComponent';
25+
import type {OffscreenState} from './ReactFiberOffscreenComponent';
2926
import type {HookFlags} from './ReactHookEffectTags';
3027
import type {Cache} from './ReactFiberCacheComponent.new';
3128
import type {RootState} from './ReactFiberRoot.new';
32-
import type {
33-
Transition,
34-
PendingSuspenseBoundaries,
35-
} from './ReactFiberTracingMarkerComponent.new';
29+
import type {Transition} from './ReactFiberTracingMarkerComponent.new';
3630

3731
import {
3832
enableCreateEventHandleAPI,

packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ let ReactNoop;
1313
let Scheduler;
1414
let act;
1515

16+
let getCacheForType;
1617
let useState;
18+
let Suspense;
1719
let startTransition;
1820

21+
let caches;
22+
let seededCache;
23+
1924
describe('ReactInteractionTracing', () => {
2025
beforeEach(() => {
2126
jest.resetModules();
@@ -28,13 +33,121 @@ describe('ReactInteractionTracing', () => {
2833

2934
useState = React.useState;
3035
startTransition = React.startTransition;
36+
Suspense = React.Suspense;
37+
38+
getCacheForType = React.unstable_getCacheForType;
39+
40+
caches = [];
41+
seededCache = null;
3142
});
3243

44+
function createTextCache() {
45+
if (seededCache !== null) {
46+
const cache = seededCache;
47+
seededCache = null;
48+
return cache;
49+
}
50+
51+
const data = new Map();
52+
const cache = {
53+
data,
54+
resolve(text) {
55+
const record = data.get(text);
56+
57+
if (record === undefined) {
58+
const newRecord = {
59+
status: 'resolved',
60+
value: text,
61+
};
62+
data.set(text, newRecord);
63+
} else if (record.status === 'pending') {
64+
const thenable = record.value;
65+
record.status = 'resolved';
66+
record.value = text;
67+
thenable.pings.forEach(t => t());
68+
}
69+
},
70+
reject(text, error) {
71+
const record = data.get(text);
72+
if (record === undefined) {
73+
const newRecord = {
74+
status: 'rejected',
75+
value: error,
76+
};
77+
data.set(text, newRecord);
78+
} else if (record.status === 'pending') {
79+
const thenable = record.value;
80+
record.status = 'rejected';
81+
record.value = error;
82+
thenable.pings.forEach(t => t());
83+
}
84+
},
85+
};
86+
caches.push(cache);
87+
return cache;
88+
}
89+
90+
function readText(text) {
91+
const textCache = getCacheForType(createTextCache);
92+
const record = textCache.data.get(text);
93+
if (record !== undefined) {
94+
switch (record.status) {
95+
case 'pending':
96+
Scheduler.unstable_yieldValue(`Suspend [${text}]`);
97+
throw record.value;
98+
case 'rejected':
99+
Scheduler.unstable_yieldValue(`Error [${text}]`);
100+
throw record.value;
101+
case 'resolved':
102+
return record.value;
103+
}
104+
} else {
105+
Scheduler.unstable_yieldValue(`Suspend [${text}]`);
106+
107+
const thenable = {
108+
pings: [],
109+
then(resolve) {
110+
if (newRecord.status === 'pending') {
111+
thenable.pings.push(resolve);
112+
} else {
113+
Promise.resolve().then(() => resolve(newRecord.value));
114+
}
115+
},
116+
};
117+
118+
const newRecord = {
119+
status: 'pending',
120+
value: thenable,
121+
};
122+
textCache.data.set(text, newRecord);
123+
124+
throw thenable;
125+
}
126+
}
127+
128+
function AsyncText({text}) {
129+
const fullText = readText(text);
130+
Scheduler.unstable_yieldValue(fullText);
131+
return fullText;
132+
}
133+
33134
function Text({text}) {
34135
Scheduler.unstable_yieldValue(text);
35136
return text;
36137
}
37138

139+
function resolveMostRecentTextCache(text) {
140+
if (caches.length === 0) {
141+
throw Error('Cache does not exist');
142+
} else {
143+
// Resolve the most recently created cache. An older cache can by
144+
// resolved with `caches[index].resolve(text)`.
145+
caches[caches.length - 1].resolve(text);
146+
}
147+
}
148+
149+
const resolveText = resolveMostRecentTextCache;
150+
38151
function advanceTimers(ms) {
39152
// Note: This advances Jest's virtual time but not React's. Use
40153
// ReactNoop.expire for that.
@@ -98,4 +211,72 @@ describe('ReactInteractionTracing', () => {
98211
});
99212
});
100213
});
214+
215+
// @gate enableTransitionTracing
216+
it('should correctly trace interactions for async roots', async () => {
217+
const transitionCallbacks = {
218+
onTransitionStart: (name, startTime) => {
219+
Scheduler.unstable_yieldValue(
220+
`onTransitionStart(${name}, ${startTime})`,
221+
);
222+
},
223+
onTransitionComplete: (name, startTime, endTime) => {
224+
Scheduler.unstable_yieldValue(
225+
`onTransitionComplete(${name}, ${startTime}, ${endTime})`,
226+
);
227+
},
228+
};
229+
let navigateToPageTwo;
230+
function App() {
231+
const [navigate, setNavigate] = useState(false);
232+
navigateToPageTwo = () => {
233+
setNavigate(true);
234+
};
235+
236+
return (
237+
<div>
238+
{navigate ? (
239+
<Suspense
240+
fallback={<Text text="Loading..." />}
241+
name="suspense page">
242+
<AsyncText text="Page Two" />
243+
</Suspense>
244+
) : (
245+
<Text text="Page One" />
246+
)}
247+
</div>
248+
);
249+
}
250+
251+
const root = ReactNoop.createRoot({transitionCallbacks});
252+
await act(async () => {
253+
root.render(<App />);
254+
ReactNoop.expire(1000);
255+
await advanceTimers(1000);
256+
257+
expect(Scheduler).toFlushAndYield(['Page One']);
258+
});
259+
260+
await act(async () => {
261+
startTransition(() => navigateToPageTwo(), {name: 'page transition'});
262+
263+
ReactNoop.expire(1000);
264+
await advanceTimers(1000);
265+
266+
expect(Scheduler).toFlushAndYield([
267+
'Suspend [Page Two]',
268+
'Loading...',
269+
'onTransitionStart(page transition, 1000)',
270+
]);
271+
272+
ReactNoop.expire(1000);
273+
await advanceTimers(1000);
274+
await resolveText('Page Two');
275+
276+
expect(Scheduler).toFlushAndYield([
277+
'Page Two',
278+
'onTransitionComplete(page transition, 1000, 3000)',
279+
]);
280+
});
281+
});
101282
});

0 commit comments

Comments
 (0)