Skip to content

Make RSC tests await act #1734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from 'node:path';
import { globalIgnores } from 'eslint/config';
import jest from 'eslint-plugin-jest';
import prettierRecommended from 'eslint-plugin-prettier/recommended';
import testingLibrary from 'eslint-plugin-testing-library';
import globals from 'globals';
import tsEslint from 'typescript-eslint';
import { includeIgnoreFile } from '@eslint/compat';
Expand Down Expand Up @@ -198,11 +199,17 @@ const config = tsEslint.config([
{
files: ['node_package/tests/**', '**/*.test.{js,jsx,ts,tsx}'],

extends: [jest.configs['flat/recommended'], jest.configs['flat/style']],
extends: [
jest.configs['flat/recommended'],
jest.configs['flat/style'],
testingLibrary.configs['flat/dom'],
],

rules: {
// Allows Jest mocks before import
'import/first': 'off',
// Avoiding these methods complicates tests and isn't useful for our purposes
'testing-library/no-node-access': 'off',
},
},
// must be the last config in the array
Expand Down
12 changes: 4 additions & 8 deletions node_package/tests/SuspenseHydration.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,17 +167,15 @@ async function renderAndHydrate() {
hydrate();
await writeFirstChunk();
});
await waitFor(() => {
expect(screen.queryByText('Loading...')).toBeInTheDocument();
});
expect(await screen.findByText('Loading...')).toBeInTheDocument();
expect(onContainerHydrated).toHaveBeenCalled();

// The async component is not hydrated until the second chunk is written to the document
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
expect(onAsyncComponentHydrated).not.toHaveBeenCalled();
expect(screen.queryByText('Loading...')).toBeInTheDocument();
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(screen.queryByText('Hello World')).not.toBeInTheDocument();
});

Expand All @@ -189,18 +187,16 @@ async function renderAndHydrate() {
hydrate();
await writeFirstChunk();
});
await waitFor(() => {
expect(screen.queryByText('Loading...')).toBeInTheDocument();
});
expect(await screen.findByText('Loading...')).toBeInTheDocument();

await act(async () => {
const secondChunk = await writeSecondChunk();
expect(secondChunk).toContain('script');
});
await waitFor(() => {
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
expect(screen.queryByText('Hello World')).toBeInTheDocument();
});
expect(screen.getByText('Hello World')).toBeInTheDocument();

expect(onContainerHydrated).toHaveBeenCalled();
expect(onAsyncComponentHydrated).toHaveBeenCalled();
Expand Down
17 changes: 8 additions & 9 deletions node_package/tests/registerServerComponent.client.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { clear as clearComponentRegistry } from '../src/ComponentRegistry.ts';
enableFetchMocks();

// React Server Components tests require React 19 and only run with Node version 18 (`newest` in our CI matrix)
(getNodeVersion() >= 18 ? describe : describe.skip)('RSCClientRoot', () => {
(getNodeVersion() >= 18 ? describe : describe.skip)('registerServerComponent', () => {
let container;
const mockDomNodeId = 'test-container';

Expand Down Expand Up @@ -70,11 +70,10 @@ enableFetchMocks();
};

// Execute the render
const render = () =>
act(async () => {
const Component = ReactOnRails.getComponent('TestComponent');
await Component.component({}, railsContext, mockDomNodeId);
});
const render = async () => {
const Component = ReactOnRails.getComponent('TestComponent');
await Component.component({}, railsContext, mockDomNodeId);
};

return {
render,
Expand All @@ -92,7 +91,7 @@ enableFetchMocks();

await act(async () => {
pushFirstChunk();
render();
await render();
});
expect(window.fetch).toHaveBeenCalledWith('/rsc-render/TestComponent?props=%7B%7D');
expect(window.fetch).toHaveBeenCalledTimes(1);
Expand All @@ -113,7 +112,7 @@ enableFetchMocks();
const { render, pushFirstChunk, pushSecondChunk, endStream } = await mockRSCRequest();

await act(async () => {
render();
await render();
pushFirstChunk();
});
expect(consoleSpy).toHaveBeenCalledWith(
Expand Down Expand Up @@ -143,7 +142,7 @@ enableFetchMocks();
const { render, pushFirstChunk, pushSecondChunk, endStream } = await mockRSCRequest('/rsc-render/');

await act(async () => {
render();
await render();
pushFirstChunk();
pushSecondChunk();
endStream();
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-testing-library": "^7.5.3",
"globals": "^16.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
Expand All @@ -71,7 +72,7 @@
"redux": "^4.2.1",
"ts-jest": "^29.2.5",
"typescript": "^5.8.3",
"typescript-eslint": "^8.29.1"
"typescript-eslint": "^8.35.0"
},
"peerDependencies": {
"react": ">= 16",
Expand Down
8 changes: 2 additions & 6 deletions script/convert
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,12 @@ gsub_file_content("../Gemfile.development_dependencies", /gem "shakapacker", "[^
gsub_file_content("../spec/dummy/package.json", /"shakapacker": "[^"]*",/, '"shakapacker": "6.6.0",')

# The below packages don't work on the oldest supported Node version and aren't needed there anyway
gsub_file_content("../package.json", /"eslint": "[^"]*",/, "")
gsub_file_content("../package.json", /"eslint-plugin-jest": "[^"]*",/, "")
gsub_file_content("../package.json", /"[^"]*eslint[^"]*": "[^"]*",?/, "")
gsub_file_content("../package.json", /"globals": "[^"]*",/, "")
gsub_file_content("../package.json", /"knip": "[^"]*",/, "")
gsub_file_content("../package.json", /"publint": "[^"]*",/, "")
gsub_file_content("../package.json", /"typescript-eslint": "[^"]*",?/, "")
gsub_file_content("../package.json", %r{"@arethetypeswrong/cli": "[^"]*",}, "")
gsub_file_content("../package.json", %r{"@eslint/compat": "[^"]*",}, "")
gsub_file_content("../package.json", %r{"@testing-library/dom": "[^"]*",}, "")
gsub_file_content("../package.json", %r{"@testing-library/react": "[^"]*",}, "")
gsub_file_content("../package.json", %r{"@testing-library/[^"]*": "[^"]*",}, "")

# Clean up any trailing commas before closing braces
gsub_file_content("../package.json", /,(\s*})/, "\\1")
Expand Down
Loading
Loading