Skip to content

Commit 88d3a04

Browse files
committed
Test
1 parent 5707101 commit 88d3a04

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails react-core
8+
* @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment
9+
*/
10+
11+
'use strict';
12+
import {
13+
insertNodesAndExecuteScripts,
14+
getVisibleChildren,
15+
} from '../test-utils/FizzTestUtils';
16+
17+
let JSDOM;
18+
let React;
19+
let Suspense;
20+
let ViewTransition;
21+
let ReactDOMClient;
22+
let clientAct;
23+
let ReactDOMFizzServer;
24+
let Stream;
25+
let document;
26+
let writable;
27+
let container;
28+
let buffer = '';
29+
let hasErrored = false;
30+
let fatalError = undefined;
31+
32+
describe('ReactDOMFizzViewTransition', () => {
33+
beforeEach(() => {
34+
jest.resetModules();
35+
JSDOM = require('jsdom').JSDOM;
36+
React = require('react');
37+
ReactDOMClient = require('react-dom/client');
38+
clientAct = require('internal-test-utils').act;
39+
ReactDOMFizzServer = require('react-dom/server');
40+
Stream = require('stream');
41+
42+
Suspense = React.Suspense;
43+
ViewTransition = React.unstable_ViewTransition;
44+
45+
// Test Environment
46+
const jsdom = new JSDOM(
47+
'<!DOCTYPE html><html><head></head><body><div id="container">',
48+
{
49+
runScripts: 'dangerously',
50+
},
51+
);
52+
document = jsdom.window.document;
53+
container = document.getElementById('container');
54+
global.window = jsdom.window;
55+
// The Fizz runtime assumes requestAnimationFrame exists so we need to polyfill it.
56+
global.requestAnimationFrame = global.window.requestAnimationFrame = cb =>
57+
setTimeout(cb);
58+
59+
buffer = '';
60+
hasErrored = false;
61+
62+
writable = new Stream.PassThrough();
63+
writable.setEncoding('utf8');
64+
writable.on('data', chunk => {
65+
buffer += chunk;
66+
});
67+
writable.on('error', error => {
68+
hasErrored = true;
69+
fatalError = error;
70+
});
71+
});
72+
73+
afterEach(() => {
74+
jest.restoreAllMocks();
75+
});
76+
77+
async function serverAct(callback) {
78+
await callback();
79+
// Await one turn around the event loop.
80+
// This assumes that we'll flush everything we have so far.
81+
await new Promise(resolve => {
82+
setImmediate(resolve);
83+
});
84+
if (hasErrored) {
85+
throw fatalError;
86+
}
87+
// JSDOM doesn't support stream HTML parser so we need to give it a proper fragment.
88+
// We also want to execute any scripts that are embedded.
89+
// We assume that we have now received a proper fragment of HTML.
90+
const bufferedContent = buffer;
91+
buffer = '';
92+
const temp = document.createElement('body');
93+
temp.innerHTML = bufferedContent;
94+
await insertNodesAndExecuteScripts(temp, container, null);
95+
jest.runAllTimers();
96+
}
97+
98+
// @gate enableViewTransition
99+
it('emits annotations for view transitions', async () => {
100+
function App() {
101+
return (
102+
<div>
103+
<ViewTransition>
104+
<div />
105+
</ViewTransition>
106+
<ViewTransition name="foo" update="bar">
107+
<div />
108+
</ViewTransition>
109+
<ViewTransition update={{something: 'a', default: 'baz'}}>
110+
<div />
111+
</ViewTransition>
112+
<ViewTransition name="outer" update="bar" share="pair">
113+
<ViewTransition>
114+
<div />
115+
</ViewTransition>
116+
</ViewTransition>
117+
</div>
118+
);
119+
}
120+
121+
await serverAct(async () => {
122+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
123+
pipe(writable);
124+
});
125+
126+
expect(getVisibleChildren(container)).toEqual(
127+
<div>
128+
<div vt-update="auto" />
129+
<div vt-name="foo" vt-update="bar" vt-share="auto" />
130+
<div vt-update="baz" />
131+
<div vt-name="outer" vt-update="auto" vt-share="pair" />
132+
</div>,
133+
);
134+
135+
// Hydration should not yield any errors.
136+
await clientAct(async () => {
137+
ReactDOMClient.hydrateRoot(container, <App />);
138+
});
139+
});
140+
141+
// @gate enableViewTransition
142+
it('emits enter/exit annotations for view transitions inside Suspense', async () => {
143+
let resolve;
144+
const promise = new Promise(r => (resolve = r));
145+
function Suspend() {
146+
return React.use(promise);
147+
}
148+
function App() {
149+
const fallback = (
150+
<ViewTransition>
151+
<div>
152+
<ViewTransition>
153+
<span>Loading</span>
154+
</ViewTransition>
155+
</div>
156+
</ViewTransition>
157+
);
158+
return (
159+
<div>
160+
<Suspense fallback={fallback}>
161+
<ViewTransition>
162+
<Suspend />
163+
</ViewTransition>
164+
</Suspense>
165+
</div>
166+
);
167+
}
168+
169+
await serverAct(async () => {
170+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
171+
pipe(writable);
172+
});
173+
174+
expect(getVisibleChildren(container)).toEqual(
175+
<div>
176+
<div vt-update="auto" vt-exit="auto">
177+
<span vt-update="auto">Loading</span>
178+
</div>
179+
</div>,
180+
);
181+
182+
await serverAct(async () => {
183+
await resolve(
184+
<div>
185+
<ViewTransition>
186+
<span>Content</span>
187+
</ViewTransition>
188+
</div>,
189+
);
190+
});
191+
192+
expect(getVisibleChildren(container)).toEqual(
193+
<div>
194+
<div vt-update="auto" vt-enter="auto">
195+
<span vt-update="auto">Content</span>
196+
</div>
197+
</div>,
198+
);
199+
200+
// Hydration should not yield any errors.
201+
await clientAct(async () => {
202+
ReactDOMClient.hydrateRoot(container, <App />);
203+
});
204+
});
205+
206+
// @gate enableViewTransition
207+
it('can emit both enter and exit on the same node', async () => {
208+
let resolve;
209+
const promise = new Promise(r => (resolve = r));
210+
function Suspend() {
211+
return React.use(promise);
212+
}
213+
function App() {
214+
const fallback = (
215+
<Suspense fallback={null}>
216+
<ViewTransition enter="hello" exit="goodbye">
217+
<div>
218+
<ViewTransition>
219+
<span>Loading</span>
220+
</ViewTransition>
221+
</div>
222+
</ViewTransition>
223+
</Suspense>
224+
);
225+
return (
226+
<div>
227+
<Suspense fallback={fallback}>
228+
<ViewTransition enter="hi">
229+
<Suspend />
230+
</ViewTransition>
231+
</Suspense>
232+
</div>
233+
);
234+
}
235+
236+
await serverAct(async () => {
237+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
238+
pipe(writable);
239+
});
240+
241+
expect(getVisibleChildren(container)).toEqual(
242+
<div>
243+
<div vt-update="auto" vt-enter="hello" vt-exit="goodbye">
244+
<span vt-update="auto">Loading</span>
245+
</div>
246+
</div>,
247+
);
248+
249+
await serverAct(async () => {
250+
await resolve(
251+
<div>
252+
<ViewTransition>
253+
<span>Content</span>
254+
</ViewTransition>
255+
</div>,
256+
);
257+
});
258+
259+
expect(getVisibleChildren(container)).toEqual(
260+
<div>
261+
<div vt-update="auto" vt-enter="hi">
262+
<span vt-update="auto">Content</span>
263+
</div>
264+
</div>,
265+
);
266+
267+
// Hydration should not yield any errors.
268+
await clientAct(async () => {
269+
ReactDOMClient.hydrateRoot(container, <App />);
270+
});
271+
});
272+
273+
// @gate enableViewTransition
274+
it('emits annotations for view transitions outside Suspense', async () => {
275+
let resolve;
276+
const promise = new Promise(r => (resolve = r));
277+
function Suspend() {
278+
return React.use(promise);
279+
}
280+
function App() {
281+
const fallback = (
282+
<div>
283+
<ViewTransition>
284+
<span>Loading</span>
285+
</ViewTransition>
286+
</div>
287+
);
288+
return (
289+
<div>
290+
<ViewTransition>
291+
<Suspense fallback={fallback}>
292+
<Suspend />
293+
</Suspense>
294+
</ViewTransition>
295+
</div>
296+
);
297+
}
298+
299+
await serverAct(async () => {
300+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<App />);
301+
pipe(writable);
302+
});
303+
304+
expect(getVisibleChildren(container)).toEqual(
305+
<div>
306+
<div vt-name="«R0»" vt-update="auto" vt-share="auto">
307+
<span vt-update="auto">Loading</span>
308+
</div>
309+
</div>,
310+
);
311+
312+
await serverAct(async () => {
313+
await resolve(
314+
<div>
315+
<ViewTransition>
316+
<span>Content</span>
317+
</ViewTransition>
318+
</div>,
319+
);
320+
});
321+
322+
expect(getVisibleChildren(container)).toEqual(
323+
<div>
324+
<div vt-name="«R0»" vt-update="auto" vt-share="auto">
325+
<span vt-update="auto">Content</span>
326+
</div>
327+
</div>,
328+
);
329+
330+
// Hydration should not yield any errors.
331+
await clientAct(async () => {
332+
ReactDOMClient.hydrateRoot(container, <App />);
333+
});
334+
});
335+
});

0 commit comments

Comments
 (0)