-
-
Notifications
You must be signed in to change notification settings - Fork 636
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,26 +8,47 @@ def gsub_file_content(path, old_content, new_content) | |
File.binwrite(path, content) | ||
end | ||
|
||
old_config = File.expand_path("../spec/dummy/config/shakapacker.yml", __dir__) | ||
new_config = File.expand_path("../spec/dummy/config/webpacker.yml", __dir__) | ||
def move(old_path, new_path) | ||
old_path = File.expand_path(old_path, __dir__) | ||
new_path = File.expand_path(new_path, __dir__) | ||
File.rename(old_path, new_path) | ||
end | ||
|
||
File.rename(old_config, new_config) | ||
move("../spec/dummy/config/shakapacker.yml", "../spec/dummy/config/webpacker.yml") | ||
|
||
# Shakapacker | ||
gsub_file_content("../Gemfile.development_dependencies", /gem "shakapacker", "[^"]*"/, 'gem "shakapacker", "6.6.0"') | ||
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", /"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", /"knip": "[^"]*",/, "") | ||
gsub_file_content("../package.json", /"publint": "[^"]*",/, "") | ||
|
||
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",') | ||
gsub_file_content("../package.json", /"react-dom": "[^"]*",/, '"react-dom": "16.14.0",') | ||
gsub_file_content("../spec/dummy/package.json", /"react": "[^"]*",/, '"react": "16.14.0",') | ||
gsub_file_content("../spec/dummy/package.json", /"react-dom": "[^"]*",/, '"react-dom": "16.14.0",') | ||
gsub_file_content( | ||
"../package.json", | ||
"jest node_package/tests", | ||
'jest node_package/tests --testPathIgnorePatterns=\".*(RSC|stream|serverRenderReactComponent).*\"' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of this we can either:
Any preference? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If what you have works for now, I say we use it. |
||
) | ||
gsub_file_content("../tsconfig.json", "react-jsx", "react") | ||
gsub_file_content("../spec/dummy/babel.config.js", "runtime: 'automatic'", "runtime: 'classic'") | ||
# https://rescript-lang.org/docs/react/latest/migrate-react#configuration | ||
gsub_file_content("../spec/dummy/rescript.json", '"version": 4', '"version": 4, "mode": "classic"') | ||
# Find all files under app-react16 and replace the React 19 versions | ||
Dir.glob(File.expand_path("../spec/dummy/**/app-react16/**/*.*", __dir__)).each do |file| | ||
move(file, file.gsub("-react16", "")) | ||
end | ||
alexeyr-ci2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
gsub_file_content("../spec/dummy/config/webpack/commonWebpackConfig.js", /generateWebpackConfig(\(\))?/, | ||
"webpackConfig") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
|
||
export default (props, _railsContext, domNodeId) => { | ||
const reactElement = ( | ||
<div> | ||
<h1 id="manual-render">Manual Render Example</h1> | ||
<p>If you can see this, you can register renderer functions.</p> | ||
</div> | ||
); | ||
|
||
const domNode = document.getElementById(domNodeId); | ||
if (props.prerender) { | ||
ReactDOM.hydrate(reactElement, domNode); | ||
} else { | ||
ReactDOM.render(reactElement, domNode); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,57 @@ | ||||||||||||||||||
// Top level component for client side. | ||||||||||||||||||
// Compare this to the ./ServerApp.jsx file which is used for server side rendering. | ||||||||||||||||||
// NOTE: these are basically the same, but they are shown here | ||||||||||||||||||
|
||||||||||||||||||
import React from 'react'; | ||||||||||||||||||
import { combineReducers, applyMiddleware, createStore } from 'redux'; | ||||||||||||||||||
import { Provider } from 'react-redux'; | ||||||||||||||||||
import thunkMiddleware from 'redux-thunk'; | ||||||||||||||||||
import ReactDOM from 'react-dom'; | ||||||||||||||||||
|
||||||||||||||||||
import reducers from '../../app/reducers/reducersIndex'; | ||||||||||||||||||
import composeInitialState from '../../app/store/composeInitialState'; | ||||||||||||||||||
|
||||||||||||||||||
import HelloWorldContainer from '../../app/components/HelloWorldContainer'; | ||||||||||||||||||
|
||||||||||||||||||
/* | ||||||||||||||||||
* Export a function that takes the props and returns a ReactComponent. | ||||||||||||||||||
* This is used for the client rendering hook after the page html is rendered. | ||||||||||||||||||
* React will see that the state is the same and not do anything. | ||||||||||||||||||
* | ||||||||||||||||||
*/ | ||||||||||||||||||
export default (props, railsContext, domNodeId) => { | ||||||||||||||||||
const render = props.prerender ? ReactDOM.hydrate : ReactDOM.render; | ||||||||||||||||||
// eslint-disable-next-line no-param-reassign | ||||||||||||||||||
delete props.prerender; | ||||||||||||||||||
|
||||||||||||||||||
const combinedReducer = combineReducers(reducers); | ||||||||||||||||||
const combinedProps = composeInitialState(props, railsContext); | ||||||||||||||||||
|
||||||||||||||||||
// This is where we'll put in the middleware for the async function. Placeholder. | ||||||||||||||||||
// store will have helloWorldData as a top level property | ||||||||||||||||||
const store = createStore(combinedReducer, combinedProps, applyMiddleware(thunkMiddleware)); | ||||||||||||||||||
|
||||||||||||||||||
// renderApp is a function required for hot reloading. see | ||||||||||||||||||
// https://github.com/retroalgic/react-on-rails-hot-minimal/blob/master/client/src/entry.js | ||||||||||||||||||
|
||||||||||||||||||
// Provider uses this.props.children, so we're not typical React syntax. | ||||||||||||||||||
// This allows redux to add additional props to the HelloWorldContainer. | ||||||||||||||||||
const renderApp = (Komponent) => { | ||||||||||||||||||
const element = ( | ||||||||||||||||||
<Provider store={store}> | ||||||||||||||||||
<Komponent /> | ||||||||||||||||||
</Provider> | ||||||||||||||||||
); | ||||||||||||||||||
|
||||||||||||||||||
render(element, document.getElementById(domNodeId)); | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
renderApp(HelloWorldContainer); | ||||||||||||||||||
|
||||||||||||||||||
if (module.hot) { | ||||||||||||||||||
module.hot.accept(['../reducers/reducersIndex', '../components/HelloWorldContainer'], () => { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Suggested change
|
||||||||||||||||||
store.replaceReducer(combineReducers(reducers)); | ||||||||||||||||||
renderApp(HelloWorldContainer); | ||||||||||||||||||
}); | ||||||||||||||||||
} | ||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Top level component for the client side. | ||
// Compare this to the ./ReduxSharedStoreApp.server.jsx file which is used for server side rendering. | ||
|
||
import React from 'react'; | ||
import { Provider } from 'react-redux'; | ||
import ReactOnRails from 'react-on-rails/client'; | ||
import ReactDOM from 'react-dom'; | ||
|
||
import HelloWorldContainer from '../../app/components/HelloWorldContainer'; | ||
|
||
/* | ||
* Export a function that returns a ReactComponent, depending on a store named SharedReduxStore. | ||
* This is used for the client rendering hook after the page html is rendered. | ||
* React will see that the state is the same and not do anything. | ||
*/ | ||
export default (props, _railsContext, domNodeId) => { | ||
const render = props.prerender ? ReactDOM.hydrate : ReactDOM.render; | ||
// eslint-disable-next-line no-param-reassign | ||
delete props.prerender; | ||
|
||
// This is where we get the existing store. | ||
const store = ReactOnRails.getStore('SharedReduxStore'); | ||
|
||
// renderApp is a function required for hot reloading. see | ||
// https://github.com/retroalgic/react-on-rails-hot-minimal/blob/master/client/src/entry.js | ||
|
||
// Provider uses this.props.children, so we're not typical React syntax. | ||
// This allows redux to add additional props to the HelloWorldContainer. | ||
const renderApp = (Component) => { | ||
const element = ( | ||
<Provider store={store}> | ||
<Component /> | ||
</Provider> | ||
); | ||
render(element, document.getElementById(domNodeId)); | ||
}; | ||
|
||
renderApp(HelloWorldContainer); | ||
|
||
if (module.hot) { | ||
module.hot.accept(['../components/HelloWorldContainer'], () => { | ||
renderApp(HelloWorldContainer); | ||
}); | ||
} | ||
Comment on lines
+40
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify HMR path for the HelloWorldContainer component The hot module replacement configuration is accepting changes from 🏁 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 - 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change isn't needed because the file will be copied to There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ✏️ Learnings added
|
||
}; |
There was a problem hiding this comment.
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 missingmemo
(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.