Skip to content

Add tests for React 16 to CI #1732

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 2 commits into from
Apr 27, 2025
Merged

Add tests for React 16 to CI #1732

merged 2 commits into from
Apr 27, 2025

Conversation

alexeyr-ci2
Copy link
Collaborator

@alexeyr-ci2 alexeyr-ci2 commented Apr 13, 2025

Summary

Make sure we don't accidentally break React 16 support.

Closes #1679.

Pull Request checklist

  • Add/update test to cover these changes
  • [ ] Update documentation
  • [ ] Update CHANGELOG file

This change is Reviewable

Summary by CodeRabbit

  • New Features
    • Added new client-side React 16 app entry points, including manual render and Redux-based apps, with support for hydration and hot module replacement.
  • Chores
    • Updated configuration to disable deprecated React warnings for React 16 apps and exclude these files from certain analysis tools.
    • Enhanced script to automate React 16 downgrades, dependency pinning, and configuration adjustments for development and testing environments.

Copy link
Contributor

coderabbitai bot commented Apr 13, 2025

Walkthrough

This set of changes introduces new configuration and client-side code to support React 16 in the codebase. ESLint and Knip configurations are updated to accommodate files under app-react16 directories, disabling deprecated React rule checks and excluding these files from Knip analysis. The script/convert script is enhanced to automate the downgrade of dependencies and settings to React 16, including renaming files and updating configuration files for compatibility. Three new client entry points are added for React 16 applications, enabling manual rendering, Redux integration, and shared Redux store usage, with support for hydration and hot module replacement.

Changes

Files/Paths Change Summary
eslint.config.ts Added an override to disable react/no-deprecated for files under any app-react16 directory.
knip.ts Updated the Knip config to ignore all files under any app-react16 directory in the spec/dummy workspace.
script/convert Refactored file renaming logic, added a move helper, automated downgrades for React and related dependencies, updated configuration files for React 16 compatibility, adjusted Jest and Babel settings, and renamed/moved files under app-react16 directories.
spec/dummy/client/app-react16/startup/ManualRenderApp.jsx Introduced a new React component module for manual rendering/hydration based on a prerender flag.
spec/dummy/client/app-react16/startup/ReduxApp.client.jsx Added a new Redux-based React client entry point supporting store creation, hydration, and hot module replacement.
spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx Added a new client entry for rendering a Redux-connected React app using a shared store, with hydration and HMR support.

Sequence Diagram(s)

sequenceDiagram
    participant Script as convert script
    participant FS as Filesystem
    participant Config as Config Files
    participant App as React16 App Entry

    Script->>FS: Move/rename app-react16 files (remove -react16 suffix)
    Script->>Config: Update dependencies to React 16 in package.json, tsconfig, babel.config.js, etc.
    Script->>Config: Pin shakapacker to v6.6.0 in Gemfile and package.json
    Script->>Config: Adjust Jest and TypeScript settings for React 16
    App->>DOM: Render or hydrate React16 app (ManualRenderApp/ReduxApp/ReduxSharedStoreApp)
    App->>Redux: Create or use Redux store as needed
    App->>HMR: Support hot module replacement for containers/reducers
Loading

Assessment against linked issues

Objective Addressed Explanation
Add tests for the lowest supported React version in CI matrix (#1679)

Suggested reviewers

  • alexeyr-ci
  • Judahmeek

Poem

A hop, a skip, a React sixteen leap,
Old rules set aside, no deprecation to keep.
Redux and hydration, with HMR in tow,
The convert script guides where new changes go.
With configs aligned and dependencies set,
This bunny’s delighted—lowest React’s met! 🐇✨

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/react-16-tests branch 5 times, most recently from c8f58d1 to 4034118 Compare April 13, 2025 07:27
gsub_file_content(
"../package.json",
"jest node_package/tests",
'jest node_package/tests --testPathIgnorePatterns=\".*(RSC|stream|serverRenderReactComponent).*\"'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this we can either:

  1. Include something like #react18 in the description of these tests and filter them out;
  2. Use https://www.npmjs.com/package/jest-runner-groups.

Any preference?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If what you have works for now, I say we use it.

@alexeyr-ci alexeyr-ci force-pushed the alexeyr/react-16-tests branch 2 times, most recently from d44422d to a824b57 Compare April 13, 2025 09:03
@alexeyr-ci alexeyr-ci force-pushed the alexeyr/react-16-tests branch 2 times, most recently from d20da7f to 0fe51fd Compare April 18, 2025 12:07
@alexeyr-ci alexeyr-ci force-pushed the alexeyr/react-16-tests branch from 18d85d0 to 4d1f174 Compare April 18, 2025 12:50
@alexeyr-ci2 alexeyr-ci2 marked this pull request as ready for review April 18, 2025 12:50

gsub_file_content("../spec/dummy/package.json", /"shakapacker": "[^"]*",/, '"shakapacker": "6.6.0",')
# Switch to the oldest supported React version
gsub_file_content("../package.json", /"react": "[^"]*",/, '"react": "16.14.0",')
Copy link
Collaborator Author

@alexeyr-ci2 alexeyr-ci2 Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

16.0.0 doesn't work due to missing createContext, 16.3.0 doesn't work due to missing memo (I think both are in the dummy app specifically, not in the NPM package, so it doesn't mean much for its compatibility).

I could try to bisect the earliest working version, but I don't see much point; I think it's enough to show we can work with some 16.x.y.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
script/convert (1)

39-43: Consider alternative approaches for filtering tests.

The current approach of modifying the Jest command with ignore patterns works, but as previously suggested, alternative approaches like test groups or descriptive tags might be more maintainable long-term.

If you choose to implement test groups with jest-runner-groups, you'd need to:

  1. Add jest-runner-groups to your dependencies
  2. Configure test groups in your Jest config
  3. Mark tests with group annotations instead of relying on filename patterns
🧹 Nitpick comments (3)
spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx (1)

17-19: Consider avoiding the delete operator for better performance

The delete operator can impact performance. Consider using object destructuring instead.

- const render = props.prerender ? ReactDOM.hydrate : ReactDOM.render;
- // eslint-disable-next-line no-param-reassign
- delete props.prerender;
+ const { prerender, ...restProps } = props;
+ const render = prerender ? ReactDOM.hydrate : ReactDOM.render;
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

spec/dummy/client/app-react16/startup/ReduxApp.client.jsx (2)

25-25: Consider replacing delete with undefined assignment.

The delete operator can impact performance. While not critical in this non-loop context, using an undefined assignment is generally preferred.

-  delete props.prerender;
+  props.prerender = undefined;
🧰 Tools
🪛 Biome (1.9.4)

[error] 25-25: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


39-39: Consider using standard naming convention for components.

React component variables conventionally start with a capital letter. While Komponent works, Component would better follow React naming conventions.

-  const renderApp = (Komponent) => {
+  const renderApp = (Component) => {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d09352e and 4d1f174.

📒 Files selected for processing (6)
  • eslint.config.ts (1 hunks)
  • knip.ts (1 hunks)
  • script/convert (1 hunks)
  • spec/dummy/client/app-react16/startup/ManualRenderApp.jsx (1 hunks)
  • spec/dummy/client/app-react16/startup/ReduxApp.client.jsx (1 hunks)
  • spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
spec/dummy/client/app-react16/startup/ReduxApp.client.jsx (3)
spec/dummy/client/app-react16/startup/ManualRenderApp.jsx (1)
  • props (4-18)
spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx (4)
  • props (16-45)
  • render (17-17)
  • store (22-22)
  • renderApp (29-36)
spec/dummy/client/app/components/HelloWorldContainer.jsx (1)
  • HelloWorldContainer (10-12)
🪛 Biome (1.9.4)
spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx

[error] 19-19: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

spec/dummy/client/app-react16/startup/ReduxApp.client.jsx

[error] 25-25: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: dummy-app-integration-tests (newest)
  • GitHub Check: dummy-app-integration-tests (oldest)
  • GitHub Check: build
  • GitHub Check: rspec-package-tests (oldest)
  • GitHub Check: rspec-package-tests (newest)
🔇 Additional comments (7)
knip.ts (1)

57-57: LGTM: Properly ignores React 16 test files

This change excludes the React 16 test files from Knip's analysis, which is appropriate since these files intentionally use older React patterns that might trigger unnecessary warnings.

eslint.config.ts (1)

187-192: LGTM: Appropriate ESLint configuration for React 16 files

Disabling the react/no-deprecated rule specifically for React 16 test files is the correct approach. This allows the tests to use APIs that might be deprecated in newer React versions while maintaining strict linting for the rest of the codebase.

spec/dummy/client/app-react16/startup/ManualRenderApp.jsx (1)

1-18: LGTM: Clean implementation of React 16 manual rendering

This component correctly implements the React on Rails pattern for manual rendering with React 16, using the appropriate ReactDOM.render and ReactDOM.hydrate methods based on whether the content was prerendered.

spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx (2)

1-46: Overall implementation looks good

The component correctly implements a Redux-connected React component with shared store and hot module replacement support for React 16.

🧰 Tools
🪛 Biome (1.9.4)

[error] 19-19: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


9-9:

✅ Verification successful

Verify HelloWorldContainer compatibility with React 16

The component is importing HelloWorldContainer from outside the React 16 directory. Ensure that this component is compatible with React 16.


🏁 Script executed:

#!/bin/bash
# Check if HelloWorldContainer uses any React APIs not compatible with React 16
rg -A 5 -B 5 "createRoot|hydrateRoot|useId|useDeferredValue|useTransition|useSyncExternalStore" client/app/components/HelloWorldContainer.jsx

Length of output: 229


🏁 Script executed:

#!/bin/bash
# Find HelloWorldContainer file(s) and search for React 16 incompatible APIs
files=$(fd HelloWorldContainer.* -t f)

if [ -z "$files" ]; then
  echo "No HelloWorldContainer file found."
  exit 1
fi

for file in $files; do
  echo "Inspecting $file..."
  rg -A 5 -B 5 'createRoot|hydrateRoot|useId|useDeferredValue|useTransition|useSyncExternalStore' "$file" || echo "No matches in $file"
  echo
done

Length of output: 1693


HelloWorldContainer compatibility verified for React 16

No React 18‑only APIs (createRoot, hydrateRoot, useId, useDeferredValue, useTransition, useSyncExternalStore) are used in the imported HelloWorldContainer (spec/dummy/client/app/components/HelloWorldContainer.jsx). This component is safe to use with React 16.

script/convert (2)

11-15: Well-designed path handling function.

The move method properly handles path expansion, making the file operations more robust when dealing with relative paths.


34-38: Appropriate React version pinning for compatibility testing.

Pinning React and ReactDOM to version 16.14.0 aligns perfectly with the PR objective of ensuring React 16 support.

Comment on lines +40 to +44
if (module.hot) {
module.hot.accept(['../components/HelloWorldContainer'], () => {
renderApp(HelloWorldContainer);
});
}
Copy link
Contributor

@coderabbitai coderabbitai bot Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify HMR path for the HelloWorldContainer component

The hot module replacement configuration is accepting changes from ../components/HelloWorldContainer, but the import path is ../../app/components/HelloWorldContainer. This mismatch might prevent hot reloading from working correctly.


🏁 Script executed:

#!/bin/bash
# Check the directory structure to understand the correct path for HMR
find client -name "HelloWorldContainer*" | sort

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Locate the HelloWorldContainer component file
find spec -type f -iname "HelloWorldContainer.*" -print

# Show import and HMR usage in the startup file
rg "HelloWorldContainer" -n spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx

Length of output: 517


Update HMR path to match import for HelloWorldContainer

The hot‑reload hook is currently watching

module.hot.accept(['../components/HelloWorldContainer'], 

but the component is actually imported via

import HelloWorldContainer from '../../app/components/HelloWorldContainer';

From
spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx
you need to adjust the HMR path to the same relative location:

- if (module.hot) {
-   module.hot.accept(['../components/HelloWorldContainer'], () => {
-     renderApp(HelloWorldContainer);
-   });
- }
+ if (module.hot) {
+   module.hot.accept(['../../app/components/HelloWorldContainer'], () => {
+     renderApp(HelloWorldContainer);
+   });
+ }

This will ensure hot‑module replacement picks up changes to your container.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change isn't needed because the file will be copied to /app first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexeyr-ci2 Thank you for the clarification. I understand now - since the file gets copied from app-react16/startup/ to /app/startup/ during the conversion process, the relative path for HMR will actually be correct at runtime. The current path ../components/HelloWorldContainer is properly configured for where the file will exist when executed, not its current location in the source.


✏️ Learnings added
Learnt from: alexeyr-ci2
PR: shakacode/react_on_rails#1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.813Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

renderApp(HelloWorldContainer);

if (module.hot) {
module.hot.accept(['../reducers/reducersIndex', '../components/HelloWorldContainer'], () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect HMR module paths.

The hot module replacement paths don't match the actual import paths, which will break HMR for reducers and components.

-    module.hot.accept(['../reducers/reducersIndex', '../components/HelloWorldContainer'], () => {
+    module.hot.accept(['../../app/reducers/reducersIndex', '../../app/components/HelloWorldContainer'], () => {
      store.replaceReducer(combineReducers(reducers));
      renderApp(HelloWorldContainer);
    });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
module.hot.accept(['../reducers/reducersIndex', '../components/HelloWorldContainer'], () => {
module.hot.accept(
['../../app/reducers/reducersIndex', '../../app/components/HelloWorldContainer'],
() => {
store.replaceReducer(combineReducers(reducers));
renderApp(HelloWorldContainer);
}
);

gsub_file_content(
"../package.json",
"jest node_package/tests",
'jest node_package/tests --testPathIgnorePatterns=\".*(RSC|stream|serverRenderReactComponent).*\"'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If what you have works for now, I say we use it.

@alexeyr-ci2 alexeyr-ci2 merged commit 3161d91 into master Apr 27, 2025
11 checks passed
@alexeyr-ci2 alexeyr-ci2 deleted the alexeyr/react-16-tests branch April 27, 2025 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add tests for the lowest supported React version in CI matrix
3 participants