Skip to content

Commit b669649

Browse files
authored
Merge pull request #270 from alexandr-g/react-18
feat: upgrade to react 18, react-router v6, react-redux v8, react-testing-library
2 parents 7144094 + 12e1db4 commit b669649

26 files changed

+747
-783
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
name: Run E2E tests on deployment success
2323
if: github.event.deployment_status.state == 'success'
2424
runs-on: ubuntu-latest
25-
container: cypress/included:8.7.0
25+
container: cypress/included:9.5.4
2626
env:
2727
NPM_RC: ${{ secrets.NPM_TOKEN }}
2828
TERM: xterm

.github/workflows/main.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: CI
33
on: [push]
44

55
env:
6-
NODE_VERSION: 14.x
6+
NODE_VERSION: 16.x
77

88
jobs:
99
install:
@@ -13,10 +13,10 @@ jobs:
1313
- name: CI
1414
uses: actions/checkout@v3
1515
- name: Use Node ${{ env.NODE_VERSION }}
16-
uses: actions/setup-node@v2
16+
uses: actions/setup-node@v3
1717
with:
1818
node-version: ${{ env.NODE_VERSION }}
19-
- uses: actions/cache@v2
19+
- uses: actions/cache@v3
2020
with:
2121
path: '**/node_modules'
2222
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/yarn.lock') }}
@@ -32,7 +32,7 @@ jobs:
3232
steps:
3333
- name: Running lint
3434
uses: actions/checkout@v3
35-
- uses: actions/cache@v2
35+
- uses: actions/cache@v3
3636
with:
3737
path: '**/node_modules'
3838
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/yarn.lock') }}
@@ -48,7 +48,7 @@ jobs:
4848
steps:
4949
- name: Running tests
5050
uses: actions/checkout@v3
51-
- uses: actions/cache@v2
51+
- uses: actions/cache@v3
5252
with:
5353
path: '**/node_modules'
5454
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/yarn.lock') }}
@@ -68,7 +68,7 @@ jobs:
6868
steps:
6969
- name: Build
7070
uses: actions/checkout@v3
71-
- uses: actions/cache@v2
71+
- uses: actions/cache@v3
7272
with:
7373
path: '**/node_modules'
7474
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/yarn.lock') }}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
# A quick start Redux + TypeScript Create React App template
77

8-
An opinionated quick start [Create React App](https://github.com/facebook/create-react-app) (CRA) _template_ with configured **Redux**, **TypeScript**, **React Router**, **Enzyme** and custom **ESlint** configuration.
8+
An opinionated quick start [Create React App](https://github.com/facebook/create-react-app) (CRA) _template_ with configured **Redux**, **TypeScript**, **React Router**, **React Testing Library** and custom **ESlint** configuration.
99

1010
Original Create React App README available [here](./README_CRA.md)
1111

@@ -53,7 +53,7 @@ The template provides basic Redux configuration with [feature based](https://red
5353

5454
## Testing
5555

56-
Testing is done with [Enzyme](https://airbnb.io/enzyme/).
56+
Testing is done with [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/).
5757

5858
## [Prettier](https://prettier.io/)
5959

cypress.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { defineConfig } from 'cypress'
2+
3+
export default defineConfig({
4+
video: false,
5+
retries: 1,
6+
e2e: {
7+
// We've imported your old cypress plugins here.
8+
// You may want to clean this up later by importing these.
9+
setupNodeEvents: function (on, config) {
10+
return require('./cypress/plugins/index.js')(on, config)
11+
},
12+
baseUrl: 'http://localhost:3000',
13+
},
14+
})

cypress.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

cypress/integration/about.spec.js renamed to cypress/e2e/about.cy.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable no-undef */
21
describe('About page', () => {
32
beforeEach(() => {
43
cy.visit('/about')
@@ -12,7 +11,7 @@ describe('About page', () => {
1211

1312
it('should navigate to the about page and back via header link', () => {
1413
cy.get('h1').contains('About')
15-
cy.get('[cy-data="home-nav-link"] > .active').click()
14+
cy.get('[cy-data="home-nav-link"]').click()
1615
cy.get('h1').contains('Redux + TypeScript')
1716
})
1817
})

cypress/integration/counter.spec.js renamed to cypress/e2e/counter.cy.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable no-undef */
21
describe('counter', () => {
32
beforeEach(() => {
43
cy.visit('/')

cypress/support/e2e.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import './commands'

package.json

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
"license": "MIT",
55
"author": "Alexander Grischuk <[email protected]",
66
"engines": {
7-
"node": ">=10"
7+
"node": ">=16.0.0 <20.0.0"
88
},
99
"main": "template.json",
10-
"description": "A quick start Create React App template with React Router, Redux, TypeScript, Enzyme and custom ESlint configurations",
10+
"description": "A quick start Create React App template with React Router, Redux, TypeScript, React Testing Library and custom ESlint configurations",
1111
"keywords": [
1212
"react",
1313
"create-react-app",
1414
"cra-template",
1515
"template",
16-
"enzyme",
16+
"react-testing-library",
1717
"eslint",
1818
"redux",
1919
"react-redux",
@@ -35,18 +35,14 @@
3535
"url": "https://github.com/alexandr-g/cra-template-typescript-redux/issues"
3636
},
3737
"dependencies": {
38-
"@types/enzyme": "^3.10.4",
39-
"@types/jest": "^27.0.0",
38+
"@testing-library/react": "^14.0.0",
4039
"@types/node": "^14.0.0",
41-
"@types/react": "^17.0.0",
42-
"@types/react-dom": "^17.0.0",
43-
"@types/react-redux": "^7.1.5",
44-
"@types/react-router-dom": "^5.1.3",
40+
"@types/react": "^18.2.6",
41+
"@types/react-dom": "^18.2.4",
42+
"@types/react-router-dom": "^5.3.3",
4543
"@types/redux-mock-store": "^1.0.1",
4644
"@typescript-eslint/eslint-plugin": "^5.59.5",
4745
"@typescript-eslint/parser": "5.9.1",
48-
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.0",
49-
"enzyme": "^3.11.0",
5046
"eslint": "^8.6.0",
5147
"eslint-config-airbnb-typescript": "^16.1.0",
5248
"eslint-config-prettier": "^8.0.0",
@@ -56,12 +52,12 @@
5652
"eslint-plugin-react": "^7.21.5",
5753
"eslint-plugin-react-hooks": "^4.0.8",
5854
"prettier": "^2.0.0",
59-
"react": "^17.0.0",
60-
"react-dom": "^17.0.0",
61-
"react-redux": "^7.1.3",
62-
"react-router-dom": "^5.1.2",
63-
"react-scripts": "5.0.0",
64-
"redux": "^4.0.5",
55+
"react": "^18.2.0",
56+
"react-dom": "^18.2.0",
57+
"react-redux": "^8.0.5",
58+
"react-router-dom": "^6.11.1",
59+
"react-scripts": "^5.0.1",
60+
"redux": "^4.2.1",
6561
"redux-devtools-extension": "^2.13.8",
6662
"redux-mock-store": "^1.5.4",
6763
"typescript": "~4.5.0"
@@ -78,7 +74,7 @@
7874
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
7975
"fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
8076
"format": "prettier --write src/**/*.{ts,tsx}",
81-
"e2e": "cypress run --spec 'cypress/integration/*'"
77+
"e2e": "cypress run --env device=web"
8278
},
8379
"eslintConfig": {
8480
"extends": "react-app"
@@ -110,12 +106,14 @@
110106
]
111107
},
112108
"devDependencies": {
109+
"@babel/runtime": "^7.21.5",
113110
"@semantic-release/changelog": "6.0.1",
114111
"@semantic-release/git": "10.0.1",
115112
"@semantic-release/github": "8.0.2",
113+
"@testing-library/jest-dom": "^5.16.5",
116114
"codecov": "3.8.3",
117115
"commitizen": "4.2.4",
118-
"cypress": "9.4.1",
116+
"cypress": "^12.12.0",
119117
"cz-conventional-changelog": "3.3.0",
120118
"eslint-plugin-cypress": "2.12.1",
121119
"semantic-release": "18.0.1"

src/App.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { BrowserRouter, Switch, Route } from 'react-router-dom'
2+
import { BrowserRouter, Routes, Route } from 'react-router-dom'
33

44
import { Navbar } from './components/Navbar'
55
import { About } from './pages/About'
@@ -10,10 +10,10 @@ const App: React.FC = () => {
1010
<BrowserRouter>
1111
<Navbar />
1212
<div className="container">
13-
<Switch>
14-
<Route path="/" component={Home} exact />
15-
<Route path="/about" component={About} />
16-
</Switch>
13+
<Routes>
14+
<Route path="/" element={<Home />} />
15+
<Route path="/about" element={<About />} />
16+
</Routes>
1717
</div>
1818
</BrowserRouter>
1919
)

src/components/counter/Counter.spec.tsx

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React from 'react'
1+
import { render, screen, fireEvent } from '@testing-library/react'
22
import { Provider } from 'react-redux'
3-
import { mount } from 'enzyme'
43
import configureStore from 'redux-mock-store'
54

65
import { actionTypes } from '../../features/counter'
@@ -17,47 +16,43 @@ describe('Counter', () => {
1716
// Add jest mock spy to watch for store.dispatch method. See https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname for more info
1817
jest.spyOn(store, 'dispatch')
1918

20-
it('renders without crashing.', () => {
21-
const wrapper = mount(
19+
test('renders without crashing.', () => {
20+
render(
2221
<Provider store={store}>
2322
<Counter />
2423
</Provider>
2524
)
2625

27-
const countValue = wrapper.find('strong').text()
28-
expect(countValue).toBe('42')
26+
const countValue = screen.getByText('42')
27+
expect(countValue).toBeInTheDocument()
2928
})
3029

31-
it('should be possible to increment counter.', () => {
32-
const wrapper = mount(
30+
test('should be possible to increment counter.', () => {
31+
render(
3332
<Provider store={store}>
3433
<Counter />
3534
</Provider>
3635
)
3736

38-
wrapper
39-
.find('button')
40-
.filter({ 'data-qa': 'increment-counter' })
41-
.simulate('click')
37+
const incrementButton = screen.getByRole('button', { name: 'increment' })
38+
fireEvent.click(incrementButton)
4239

43-
expect(store.dispatch).toBeCalledTimes(1)
40+
expect(store.dispatch).toHaveBeenCalledTimes(1)
4441

45-
expect(store.dispatch).toBeCalledWith({
42+
expect(store.dispatch).toHaveBeenCalledWith({
4643
type: actionTypes.INCREMENT_COUNTER,
4744
})
4845
})
4946

50-
it('should be possible to decrement counter.', () => {
51-
const wrapper = mount(
47+
test('should be possible to decrement counter.', () => {
48+
render(
5249
<Provider store={store}>
5350
<Counter />
5451
</Provider>
5552
)
5653

57-
wrapper
58-
.find('button')
59-
.filter({ 'data-qa': 'decrement-counter' })
60-
.simulate('click')
54+
const decrementButton = screen.getByRole('button', { name: 'decrement' })
55+
fireEvent.click(decrementButton)
6156

6257
expect(store.dispatch).toHaveBeenCalledTimes(1)
6358

src/features/counter/counterReducer.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import counterReducer from './counterReducer'
33
import { CounterActionTypes } from './types'
44

55
describe('features > counter > counterReducer', () => {
6-
it(`increments value, if ${INCREMENT_COUNTER} action is provided`, () => {
6+
test(`increments value, if ${INCREMENT_COUNTER} action is provided`, () => {
77
const initialState = {
88
value: 0,
99
}
@@ -19,7 +19,7 @@ describe('features > counter > counterReducer', () => {
1919
expect(counterReducer(initialState, action)).toEqual(expectedState)
2020
})
2121

22-
it(`increments value, if ${DECREMENT_COUNTER} action is provided`, () => {
22+
test(`increments value, if ${DECREMENT_COUNTER} action is provided`, () => {
2323
const initialState = {
2424
value: 0,
2525
}

src/index.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import React from 'react'
2-
import ReactDOM from 'react-dom'
1+
import { createRoot } from 'react-dom/client'
32
import { Provider } from 'react-redux'
43
import store from './store'
54

65
import './index.css'
76

87
import App from './App'
98

10-
ReactDOM.render(
9+
const container = document.getElementById('root') as HTMLDivElement
10+
const root = createRoot(container!)
11+
12+
root.render(
1113
<Provider store={store}>
1214
<App />
13-
</Provider>,
14-
document.getElementById('root')
15+
</Provider>
1516
)

src/pages/About.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { Fragment } from 'react'
2-
import { useHistory } from 'react-router-dom'
2+
import { useNavigate } from 'react-router-dom'
33

44
export const About: React.FC = () => {
5-
const history = useHistory()
5+
const navigate = useNavigate()
66

77
return (
88
<Fragment>
@@ -17,7 +17,7 @@ export const About: React.FC = () => {
1717
type="button"
1818
className="btn"
1919
cy-data="go-back-button"
20-
onClick={() => history.push('/')}
20+
onClick={() => navigate('/')}
2121
>
2222
Go back
2323
</button>

src/pages/Home.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ export const Home: React.FC = () => {
77
<h1>Redux + TypeScript</h1>
88
<p>
99
Hello and welcome! :) This app was generated by the Create React App
10-
template and bootstrapped with Redux, React Router, TypeScript, ESlint,
11-
Prettier for you. Take a look around ;)
10+
template and bootstrapped with Redux, React Router, React Testing
11+
Library, TypeScript, ESlint, Prettier for you. Take a look around ;)
1212
</p>
1313
<Counter />
1414
</Fragment>

src/setupTests.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
import { configure } from 'enzyme'
2-
import Adapter from '@wojtekmaj/enzyme-adapter-react-17'
3-
4-
configure({ adapter: new Adapter() })
1+
import '@testing-library/jest-dom'

0 commit comments

Comments
 (0)