Skip to content

Commit 3d1994c

Browse files
committed
add jest
1 parent 2881d2d commit 3d1994c

File tree

7 files changed

+250
-6
lines changed

7 files changed

+250
-6
lines changed

admin-ui/__mocks__/fileMock.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'test-file-stub';

admin-ui/jest.config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Config } from 'jest';
2+
3+
const config: Config = {
4+
preset: 'ts-jest',
5+
testEnvironment: 'jsdom',
6+
transform: {
7+
'^.+\\.(ts|tsx)$': 'ts-jest',
8+
'^.+\\.(js|jsx)$': 'babel-jest',
9+
},
10+
moduleNameMapper: {
11+
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
12+
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
13+
'^@/(.*)$': '<rootDir>/src/$1'
14+
},
15+
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
16+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
17+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
18+
};
19+
20+
export default config;

admin-ui/package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
"scripts": {
3434
"start": "webpack serve --config webpack.config.mock.js --open",
3535
"dev": "webpack serve --config webpack.config.dev.js --open",
36-
"build": "webpack --mode production --config webpack.config.prod.js"
36+
"build": "webpack --mode production --config webpack.config.prod.js",
37+
"test": "jest",
38+
"test:watch": "jest --watch"
3739
},
3840
"eslintConfig": {
3941
"extends": [
@@ -53,19 +55,28 @@
5355
]
5456
},
5557
"devDependencies": {
58+
"@testing-library/dom": "^10.4.0",
59+
"@testing-library/jest-dom": "^6.6.3",
60+
"@testing-library/react": "^16.1.0",
61+
"@types/jest": "^29.5.14",
5662
"@types/lodash": "^4.17.7",
5763
"@types/lodash-es": "^4.17.12",
5864
"clean-webpack-plugin": "^4.0.0",
5965
"copy-webpack-plugin": "^12.0.2",
6066
"css-loader": "^7.1.2",
6167
"express": "^4.21.0",
6268
"html-webpack-plugin": "^5.6.0",
69+
"identity-obj-proxy": "^3.0.0",
70+
"jest": "^29.7.0",
71+
"jest-environment-jsdom": "^29.7.0",
6372
"mockjs": "^1.1.0",
6473
"monaco-editor-webpack-plugin": "^7.1.0",
6574
"sass": "^1.78.0",
6675
"sass-loader": "^16.0.1",
6776
"style-loader": "^4.0.0",
77+
"ts-jest": "^29.2.5",
6878
"ts-loader": "^9.5.1",
79+
"ts-node": "^10.9.2",
6980
"webpack": "^5.94.0",
7081
"webpack-cli": "^5.1.4",
7182
"webpack-dev-server": "^5.1.0",

admin-ui/react-jest.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# React Unit Test for Jest
2+
3+
1. dependencies on devDependencies
4+
```
5+
@testing-library/dom
6+
@testing-library/jest-dom
7+
@testing-library/react
8+
@types/jest
9+
jest
10+
ts-jest
11+
jest-environment-jsdom
12+
identity-obj-proxy
13+
ts-node
14+
```
15+
2. add jest.config.ts and add jest script in package.json
16+
17+
```
18+
//rootDir/jest.config.ts
19+
import type { Config } from 'jest';
20+
21+
const config: Config = {
22+
preset: 'ts-jest',
23+
testEnvironment: 'jsdom',
24+
transform: {
25+
'^.+\\.(ts|tsx)$': 'ts-jest',
26+
'^.+\\.(js|jsx)$': 'babel-jest',
27+
},
28+
moduleNameMapper: {
29+
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
30+
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
31+
'^@/(.*)$': '<rootDir>/src/$1'
32+
},
33+
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
34+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
35+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
36+
};
37+
38+
export default config;
39+
40+
41+
```
42+
43+
on jest.config.ts will setting up the jest configuration for the project.
44+
some important configuration is moduleNameMapper, this setting configuration for style,asset,and project path alias.
45+
setupFilesAfterEnv is the file that will be executed before the test run, in this case jest.setup.ts
46+
testRegex is regex for the test file, in this case the test file should be end with .test.tsx or .spec.tsx
47+
48+
49+
on `package.json` add jest script
50+
```
51+
"scripts":{
52+
"test": "jest",
53+
"test:watch": "jest --watch"
54+
... other scripts
55+
}
56+
```
57+
58+
3. add jest.setup.ts and fileMock.js in src folder
59+
```
60+
// rootDir/src/jest.setup.ts
61+
import '@testing-library/jest-dom';
62+
```
63+
64+
```
65+
66+
// rootDir/__mocks__/fileMock.js
67+
module.exports = 'test-file-stub';
68+
69+
```
70+
71+
4. create test file in src folder, example my component file in `rootDir/src/pages/welcome/index.tsx` then the test file should be in `rootDir/src/pages/welcome/__tests__/index.test.tsx`
72+
```
73+
74+
import React from 'react';
75+
import Welcome from '../index';
76+
import {render} from "@testing-library/react";
77+
import store from "@/store/Redux";
78+
import {Provider} from "react-redux";
79+
80+
// test description for the component
81+
describe('Welcome Component', () => {
82+
// test case for the component
83+
test('renders with default initial value', () => {
84+
// render the component
85+
render(
86+
<Provider store={store}>
87+
<Welcome />
88+
</Provider>
89+
);
90+
// get the element that will be tested
91+
const countElement = document.querySelector('.App-header p');
92+
// assert the element is exist
93+
expect(countElement).toBeInTheDocument();
94+
// assert the element text content
95+
expect(countElement).toHaveTextContent('hi , Redux counter: 0, Roles:');
96+
// log the element text content
97+
console.log(countElement?.textContent);
98+
});
99+
});
100+
101+
```
102+
103+
5. test router component
104+
105+
> specify the router path in the test file
106+
```
107+
import { render, fireEvent } from '@testing-library/react';
108+
import { MemoryRouter, Routes, Route } from 'react-router-dom';
109+
import UserProfile from '../UserProfile';
110+
111+
describe('UserProfile Component', () => {
112+
test('renders user profile with correct ID', () => {
113+
114+
// use memory router and setting initial router path
115+
const { getByTestId } = render(
116+
<MemoryRouter initialEntries={['/user/123']}>
117+
<Routes>
118+
<Route path="/user/:id" element={<UserProfile />} />
119+
</Routes>
120+
</MemoryRouter>
121+
);
122+
123+
expect(getByTestId('user-id')).toHaveTextContent('User ID: 123');
124+
});
125+
});
126+
```
127+
> or use `BrowserRouter` and `render` function
128+
```
129+
import { render, fireEvent } from '@testing-library/react';
130+
import { createMemoryHistory } from 'history';
131+
import { Router } from 'react-router-dom';
132+
import App from '../App';
133+
134+
describe('App Navigation', () => {
135+
test('full app navigation', () => {
136+
const history = createMemoryHistory();
137+
const { getByText } = render(
138+
<Router location={history.location} navigator={history}>
139+
<App />
140+
</Router>
141+
);
142+
fireEvent.click(getByText('Go to Profile'));
143+
expect(history.location.pathname).toBe('/profile');
144+
});
145+
});
146+
```
147+
148+
6. test axios api calling
149+
```
150+
151+
import { render, waitFor } from '@testing-library/react';
152+
import axios from 'axios';
153+
import { UserProfile } from '../UserProfile';
154+
155+
jest.mock('axios');
156+
const mockedAxios = axios as jest.Mocked<typeof axios>;
157+
158+
describe('UserProfile Component', () => {
159+
// test case for the component
160+
test('loads all user data correctly - URL based', async () => {
161+
// mock the axios get function
162+
mockedAxios.get.mockImplementation((url) => {
163+
if (url === '/api/users/123') {
164+
return Promise.resolve({
165+
data: { name: 'John Doe', email: 'john@example.com' }
166+
});
167+
}
168+
if (url === '/api/users/123/posts') {
169+
return Promise.resolve({
170+
data: [{ id: 1, title: 'Post 1' }]
171+
});
172+
}
173+
if (url === '/api/users/123/followers') {
174+
return Promise.resolve({
175+
data: [{ id: 1, name: 'Follower 1' }]
176+
});
177+
}
178+
return Promise.reject(new Error('Not found'));
179+
});
180+
181+
const { getByTestId } = render(<UserProfile userId="123" />);
182+
183+
await waitFor(() => {
184+
expect(getByTestId('user-info')).toHaveTextContent('John Doe');
185+
expect(getByTestId('user-posts')).toHaveTextContent('Post 1');
186+
expect(getByTestId('user-followers')).toHaveTextContent('Follower 1');
187+
});
188+
});
189+
190+
```

admin-ui/src/jest.setup.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@testing-library/jest-dom';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import Welcome from '../index';
3+
import {render} from "@testing-library/react";
4+
import store from "@/store/Redux";
5+
import {Provider} from "react-redux";
6+
7+
// test description for the component
8+
describe('Welcome Component', () => {
9+
// test case for the component
10+
test('renders with default initial value', () => {
11+
// render the component
12+
render(
13+
<Provider store={store}>
14+
<Welcome />
15+
</Provider>
16+
);
17+
// get the element that will be tested
18+
const countElement = document.querySelector('.App-header p');
19+
// assert the element is exist
20+
expect(countElement).toBeInTheDocument();
21+
// assert the element text content
22+
expect(countElement).toHaveTextContent('hi , Redux counter: 0, Roles:');
23+
// log the element text content
24+
console.log(countElement?.textContent);
25+
});
26+
});

admin-ui/src/setupTests.ts

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

0 commit comments

Comments
 (0)