Skip to content

Commit dc0c0c3

Browse files
make RSCClientRoot tests compatible with React 19
1 parent ff7fbfa commit dc0c0c3

File tree

8 files changed

+64
-70
lines changed

8 files changed

+64
-70
lines changed

.github/workflows/package-js-tests.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Setup Node
2121
uses: actions/setup-node@v4
2222
with:
23-
node-version: ${{ matrix.versions == 'oldest' && '18' || '20' }}
23+
node-version: ${{ matrix.versions == 'oldest' && '16' || '20' }}
2424
- name: Print system information
2525
run: |
2626
echo "Linux release: "; cat /etc/issue
@@ -43,7 +43,3 @@ jobs:
4343
sudo yarn global add yalc
4444
- name: Run JS unit tests for Renderer package
4545
run: yarn test
46-
# TODO: Remove this once we made these tests compatible with React 19
47-
- name: Run JS unit tests for Renderer package with React 18 (for tests not compatible with React 19)
48-
if: matrix.versions == 'newest'
49-
run: yarn test:react-18

jest.config.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1+
const nodeVersion = parseInt(process.version.slice(1), 10);
2+
13
module.exports = {
24
preset: 'ts-jest/presets/js-with-ts',
35
testEnvironment: 'jsdom',
46
setupFiles: ['<rootDir>/node_package/tests/jest.setup.js'],
5-
// TODO: Remove this once we made RSCClientRoot compatible with React 19
6-
moduleNameMapper: process.env.USE_REACT_18
7+
// React Server Components tests are not compatible with Experimental React 18 and React 19
8+
// That only run with node version 18 and above
9+
moduleNameMapper: nodeVersion < 18
710
? {
8-
'^react$': '<rootDir>/node_modules/react-18',
9-
'^react/(.*)$': '<rootDir>/node_modules/react-18/$1',
10-
'^react-dom$': '<rootDir>/node_modules/react-dom-18',
11-
'^react-dom/(.*)$': '<rootDir>/node_modules/react-dom-18/$1',
12-
}
13-
: {
1411
'react-server-dom-webpack/client': '<rootDir>/node_package/tests/emptyForTesting.js',
1512
'^@testing-library/dom$': '<rootDir>/node_package/tests/emptyForTesting.js',
1613
'^@testing-library/react$': '<rootDir>/node_package/tests/emptyForTesting.js',
17-
},
14+
}
15+
: {},
1816
};

knip.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ const config: KnipConfig = {
3434
'react-server-dom-webpack',
3535
'cross-fetch',
3636
'jsdom',
37-
'react-18',
38-
'react-dom-18',
3937
],
4038
},
4139
'spec/dummy': {

node_package/tests/RSCClientRoot.test.jsx

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ window.__webpack_chunk_load__ = jest.fn();
1010

1111
import * as React from 'react';
1212
import { enableFetchMocks } from 'jest-fetch-mock';
13-
import { render, waitFor, screen } from '@testing-library/react';
13+
import { render, screen, act } from '@testing-library/react';
1414
import '@testing-library/jest-dom';
1515
import path from 'path';
1616
import fs from 'fs';
@@ -20,8 +20,11 @@ import RSCClientRoot, { resetRenderCache } from '../src/RSCClientRoot';
2020

2121
enableFetchMocks();
2222

23-
// TODO: Remove this once we made these tests compatible with React 19
24-
(process.env.USE_REACT_18 ? describe : describe.skip)('RSCClientRoot', () => {
23+
const nodeVersion = parseInt(process.version.slice(1), 10);
24+
25+
// React Server Components tests are not compatible with Experimental React 18 and React 19
26+
// That only run with node version 18 and above
27+
(nodeVersion >= 18 ? describe : describe.skip)('RSCClientRoot', () => {
2528
beforeEach(() => {
2629
jest.clearAllMocks();
2730

@@ -41,7 +44,7 @@ enableFetchMocks();
4144
}).toThrow('React.use is not defined');
4245
});
4346

44-
const mockRSCRequest = (rscPayloadGenerationUrlPath = 'rsc-render') => {
47+
const mockRSCRequest = async (rscPayloadGenerationUrlPath = 'rsc-render') => {
4548
const chunksDirectory = path.join(
4649
__dirname,
4750
'fixtures',
@@ -59,7 +62,7 @@ enableFetchMocks();
5962
rscPayloadGenerationUrlPath,
6063
};
6164

62-
const { rerender } = render(<RSCClientRoot {...props} />);
65+
const { rerender } = await act(async () => render(<RSCClientRoot {...props} />));
6366

6467
return {
6568
rerender: () => rerender(<RSCClientRoot {...props} />),
@@ -71,20 +74,24 @@ enableFetchMocks();
7174
};
7275

7376
it('fetches and caches component data', async () => {
74-
const { rerender, pushFirstChunk, pushSecondChunk, endStream } = mockRSCRequest();
77+
const { rerender, pushFirstChunk, pushSecondChunk, endStream } = await mockRSCRequest();
7578

7679
expect(window.fetch).toHaveBeenCalledWith('/rsc-render/TestComponent');
7780
expect(window.fetch).toHaveBeenCalledTimes(1);
7881
expect(screen.queryByText('StaticServerComponent')).not.toBeInTheDocument();
7982

80-
pushFirstChunk();
81-
await waitFor(() => expect(screen.getByText('StaticServerComponent')).toBeInTheDocument());
83+
await act(async () => {
84+
pushFirstChunk();
85+
});
86+
expect(screen.getByText('StaticServerComponent')).toBeInTheDocument();
8287
expect(screen.getByText('Loading AsyncComponent...')).toBeInTheDocument();
8388
expect(screen.queryByText('AsyncComponent')).not.toBeInTheDocument();
8489

85-
pushSecondChunk();
86-
endStream();
87-
await waitFor(() => expect(screen.getByText('AsyncComponent')).toBeInTheDocument());
90+
await act(async () => {
91+
pushSecondChunk();
92+
endStream();
93+
});
94+
expect(screen.getByText('AsyncComponent')).toBeInTheDocument();
8895
expect(screen.queryByText('Loading AsyncComponent...')).not.toBeInTheDocument();
8996

9097
// Second render - should use cache
@@ -96,15 +103,27 @@ enableFetchMocks();
96103

97104
it('replays console logs', async () => {
98105
const consoleSpy = jest.spyOn(console, 'log');
99-
const { rerender, pushFirstChunk, pushSecondChunk, endStream } = mockRSCRequest();
100-
101-
pushFirstChunk();
102-
await waitFor(() => expect(consoleSpy).toHaveBeenCalledWith('[SERVER] Console log at first chunk'));
106+
const { rerender, pushFirstChunk, pushSecondChunk, endStream } = await mockRSCRequest();
107+
108+
await act(async () => {
109+
pushFirstChunk();
110+
});
111+
expect(consoleSpy).toHaveBeenCalledWith(
112+
expect.stringContaining('Console log at first chunk'),
113+
expect.anything(), expect.anything(), expect.anything()
114+
);
103115
expect(consoleSpy).toHaveBeenCalledTimes(1);
104116

105-
pushSecondChunk();
106-
await waitFor(() => expect(consoleSpy).toHaveBeenCalledWith('[SERVER] Console log at second chunk'));
107-
endStream();
117+
await act(async () => {
118+
pushSecondChunk();
119+
});
120+
expect(consoleSpy).toHaveBeenCalledWith(
121+
expect.stringContaining('Console log at second chunk'),
122+
expect.anything(), expect.anything(), expect.anything()
123+
);
124+
await act(async () => {
125+
endStream();
126+
});
108127
expect(consoleSpy).toHaveBeenCalledTimes(2);
109128

110129
// On rerender, console logs should not be replayed again
@@ -113,15 +132,17 @@ enableFetchMocks();
113132
});
114133

115134
it('strips leading and trailing slashes from rscPayloadGenerationUrlPath', async () => {
116-
const { pushFirstChunk, pushSecondChunk, endStream } = mockRSCRequest('/rsc-render/');
135+
const { pushFirstChunk, pushSecondChunk, endStream } = await mockRSCRequest('/rsc-render/');
117136

118-
pushFirstChunk();
119-
pushSecondChunk();
120-
endStream();
137+
await act(async () => {
138+
pushFirstChunk();
139+
pushSecondChunk();
140+
endStream();
141+
});
121142

122-
await waitFor(() => expect(window.fetch).toHaveBeenCalledWith('/rsc-render/TestComponent'));
143+
expect(window.fetch).toHaveBeenCalledWith('/rsc-render/TestComponent');
123144
expect(window.fetch).toHaveBeenCalledTimes(1);
124145

125-
await waitFor(() => expect(screen.getByText('StaticServerComponent')).toBeInTheDocument());
146+
expect(screen.getByText('StaticServerComponent')).toBeInTheDocument();
126147
});
127148
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"html": "1:\"$Sreact.suspense\"\n0:D{\"name\":\"StaticServerComponent\",\"env\":\"Server\"}\n2:D{\"name\":\"AsyncComponent\",\"env\":\"Server\"}\n0:[\"$\",\"div\",null,{\"children\":[[\"$\",\"h1\",null,{\"children\":\"StaticServerComponent\"}],[\"$\",\"p\",null,{\"children\":\"This is a static server component\"}],[\"$\",\"$1\",null,{\"fallback\":[\"$\",\"div\",null,{\"children\":\"Loading AsyncComponent...\"}],\"children\":\"$L2\"}]]}]\n",
3-
"consoleReplayScript": "\n\u003cscript id=\"consoleReplayLog\"\u003e\nconsole.log.apply(console, [\"[SERVER] Console log at first chunk\"]);\n\u003c/script\u003e",
2+
"html": "3:\"$Sreact.suspense\"\n1:{\"name\":\"SimpleComponentForTesting\",\"env\":\"Server\",\"key\":null,\"owner\":null,\"props\":{\"helloWorldData\":{\"name\":\"Mr. Server Side Rendering\",\"\u003cscript\u003ewindow.alert('xss1');\u003c/script\u003e\":\"\u003cscript\u003ewindow.alert(\\\"xss2\\\");\u003c/script\u003e\"}}}\n0:D\"$1\"\n2:W[\"log\",[[\"SimpleComponentForTesting\",\"webpack://react_on_rails_pro_dummy/./client/app/ror-auto-load-components/SimpleComponentForTesting.jsx?\",35,11]],\"$1\",\"Server\",\"Console log at first chunk\"]\n5:{\"name\":\"AsyncComponent\",\"env\":\"Server\",\"key\":null,\"owner\":\"$1\",\"props\":{}}\n4:D\"$5\"\n0:[\"$\",\"div\",null,{\"children\":[[\"$\",\"h1\",null,{\"children\":\"StaticServerComponent\"},\"$1\"],[\"$\",\"p\",null,{\"children\":\"This is a static server component\"},\"$1\"],[\"$\",\"$3\",null,{\"fallback\":[\"$\",\"div\",null,{\"children\":\"Loading AsyncComponent...\"},\"$1\"],\"children\":\"$L4\"},\"$1\"]]},\"$1\"]\n",
3+
"consoleReplayScript": "",
44
"hasErrors": false,
55
"isShellReady": true
66
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"html": "2:[\"$\",\"div\",null,{\"children\":\"AsyncComponent\"}]\n",
3-
"consoleReplayScript": "\n\u003cscript id=\"consoleReplayLog\"\u003e\nconsole.log.apply(console, [\"[SERVER] Console log at second chunk\"]);\n\u003c/script\u003e",
2+
"html": "6:W[\"log\",[[\"AsyncComponent\",\"webpack://react_on_rails_pro_dummy/./client/app/ror-auto-load-components/SimpleComponentForTesting.jsx?\",24,11]],\"$5\",\"Server\",\"Console log at second chunk\"]\n4:[\"$\",\"div\",null,{\"children\":\"AsyncComponent\"},\"$5\"]\n",
3+
"consoleReplayScript": "",
44
"hasErrors": false,
55
"isShellReady": true
66
}

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@
4646
"prettier": "^2.8.8",
4747
"prop-types": "^15.8.1",
4848
"react": "^19.0.0",
49-
"react-18": "npm:[email protected]",
5049
"react-dom": "^19.0.0",
51-
"react-dom-18": "npm:[email protected]",
52-
"react-server-dom-webpack": "18.3.0-canary-670811593-20240322",
50+
"react-server-dom-webpack": "19.0.0",
5351
"redux": "^4.2.1",
5452
"ts-jest": "^29.2.5",
5553
"typescript": "^5.6.2",
@@ -66,7 +64,6 @@
6664
],
6765
"scripts": {
6866
"test": "jest node_package/tests",
69-
"test:react-18": "USE_REACT_18=true jest node_package/tests/RSCClientRoot.test.jsx",
7067
"clean": "rm -rf node_package/lib",
7168
"start": "nps",
7269
"prepack": "nps build.prepack",

yarn.lock

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5815,18 +5815,6 @@ randombytes@^2.1.0:
58155815
dependencies:
58165816
safe-buffer "^5.1.0"
58175817

5818-
"react-18@npm:[email protected]":
5819-
version "18.3.0-canary-670811593-20240322"
5820-
resolved "https://registry.yarnpkg.com/react/-/react-18.3.0-canary-670811593-20240322.tgz#3735250b45468d313ed36121324452bb5a732e9b"
5821-
integrity sha512-EI6+q3tOT+0z4OkB2sz842Ra/n/yz7b3jOJhSK1HQwi4Ng29VJzLGngWmSuxQ94YfdE3EBhpUKDfgNgzoKM9Vg==
5822-
5823-
"react-dom-18@npm:[email protected]":
5824-
version "18.3.0-canary-670811593-20240322"
5825-
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.0-canary-670811593-20240322.tgz#ac677b164fd83050272bf985e740ed4ca65337be"
5826-
integrity sha512-AHxCnyDzZueXIHY4WA2Uba1yaL7/vbjhO3D3TWPQeruKD5MwgD0/xExZi0T104gBr6Thv6MEsLSxFjBAHhHKKg==
5827-
dependencies:
5828-
scheduler "0.24.0-canary-670811593-20240322"
5829-
58305818
react-dom@^19.0.0:
58315819
version "19.0.0"
58325820
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0.tgz#43446f1f01c65a4cd7f7588083e686a6726cfb57"
@@ -5849,13 +5837,14 @@ react-is@^18.0.0:
58495837
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
58505838
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
58515839

5852-
react-server-dom-webpack@18.3.0-canary-670811593-20240322:
5853-
version "18.3.0-canary-670811593-20240322"
5854-
resolved "https://registry.yarnpkg.com/react-server-dom-webpack/-/react-server-dom-webpack-18.3.0-canary-670811593-20240322.tgz#e9b99b1f0179357e5acbf2fbacaee88dd1e8bf3b"
5855-
integrity sha512-YaCk3AvvOXcOo0FL7SlAY2GVBeuZKFQ/5FfAtE48IjpI6MvXTwMBu3QVnT/Ukk9Y4M9GzpIbLtuc8hPjfFAOaw==
5840+
react-server-dom-webpack@19.0.0:
5841+
version "19.0.0"
5842+
resolved "https://registry.yarnpkg.com/react-server-dom-webpack/-/react-server-dom-webpack-19.0.0.tgz#c60819b6cb54e317e675ddc0c5959ff915b789d0"
5843+
integrity sha512-hLug9KEXLc8vnU9lDNe2b2rKKDaqrp5gNiES4uyu2Up3FZfZJZmdwLFXlWzdA9gTB/6/cWduSB2K1Lfag2pSvw==
58565844
dependencies:
58575845
acorn-loose "^8.3.0"
58585846
neo-async "^2.6.1"
5847+
webpack-sources "^3.2.0"
58595848

58605849
react@^19.0.0:
58615850
version "19.0.0"
@@ -6110,11 +6099,6 @@ saxes@^6.0.0:
61106099
dependencies:
61116100
xmlchars "^2.2.0"
61126101

6113-
6114-
version "0.24.0-canary-670811593-20240322"
6115-
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.24.0-canary-670811593-20240322.tgz#45c5c45f18a127ab4e3c805dd466bc231b20adf3"
6116-
integrity sha512-IGX6Fq969h1L0X7jV0sJ/EdI4fr+mRetbBNJl55nn+/RsCuQSVwgKnZG6Q3NByixDNbkRI8nRmWuhOm8NQowGQ==
6117-
61186102
scheduler@^0.25.0:
61196103
version "0.25.0"
61206104
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0.tgz#336cd9768e8cceebf52d3c80e3dcf5de23e7e015"
@@ -6870,7 +6854,7 @@ webidl-conversions@^7.0.0:
68706854
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
68716855
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
68726856

6873-
webpack-sources@^3.2.3:
6857+
webpack-sources@^3.2.0, webpack-sources@^3.2.3:
68746858
version "3.2.3"
68756859
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
68766860
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==

0 commit comments

Comments
 (0)