Skip to content

Commit d6b31ea

Browse files
authored
Merge pull request #85 from codingapi/dev
Dev
2 parents 5b97427 + aac1966 commit d6b31ea

File tree

67 files changed

+2047
-534
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2047
-534
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright 2021-2022 copyright codingapi
189+
Copyright 2021-2025 copyright codingapi
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

admin-ui/__mocks__/axios.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// __mocks__/axios.ts
2+
const mockAxios = {
3+
create: jest.fn(() => ({
4+
interceptors: {
5+
request: {
6+
use: jest.fn(),
7+
eject: jest.fn()
8+
},
9+
response: {
10+
use: jest.fn(),
11+
eject: jest.fn()
12+
}
13+
},
14+
get: jest.fn(),
15+
post: jest.fn(),
16+
put: jest.fn(),
17+
delete: jest.fn(),
18+
patch: jest.fn()
19+
})),
20+
interceptors: {
21+
request: {
22+
use: jest.fn(),
23+
eject: jest.fn()
24+
},
25+
response: {
26+
use: jest.fn(),
27+
eject: jest.fn()
28+
}
29+
},
30+
get: jest.fn(),
31+
post: jest.fn(),
32+
put: jest.fn(),
33+
delete: jest.fn(),
34+
patch: jest.fn()
35+
};
36+
37+
export default mockAxios;

admin-ui/__mocks__/fileMock.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'test-file-stub';

admin-ui/__mocks__/monaco-editor.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
editor: {
3+
create: jest.fn(() => ({
4+
dispose: jest.fn(),
5+
getValue: jest.fn(() => ''),
6+
setValue: jest.fn(),
7+
})),
8+
},
9+
};

admin-ui/jest.config.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
useESM: true,
9+
}],
10+
'^.+\\.(js|jsx|mjs)$': ['babel-jest', {
11+
presets: [
12+
['@babel/preset-env', {
13+
targets: {
14+
node: 'current',
15+
},
16+
}],
17+
'@babel/preset-react',
18+
'@babel/preset-typescript'
19+
],
20+
}],
21+
},
22+
moduleNameMapper: {
23+
'^monaco-editor$': '<rootDir>/__mocks__/monaco-editor.js',
24+
"@logicflow": "<rootDir>/node_modules/@logicflow/core/dist/index.min.js",
25+
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
26+
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
27+
'^@/(.*)$': '<rootDir>/src/$1'
28+
},
29+
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
30+
testMatch: [
31+
"**/__test__/**/*.[jt]s?(x)",
32+
"**/__tests__/**/*.[jt]s?(x)",
33+
"**/?(*.)+(spec|test).[jt]s?(x)"
34+
],
35+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
36+
transformIgnorePatterns: [
37+
'node_modules/(?!(lodash-es|@ant-design|@logicflow|other-esm-modules)/)'
38+
],
39+
};
40+
41+
export default config;

admin-ui/package.json

+21-2
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,25 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@ag-grid-community/locale": "^33.0.3",
67
"@ant-design/icons": "^5.4.0",
7-
"@ant-design/pro-components": "^2.7.19",
8+
"@ant-design/pro-components": "^2.8.2",
89
"@babel/standalone": "^7.25.6",
910
"@dnd-kit/core": "^6.2.0",
1011
"@dnd-kit/sortable": "^9.0.0",
12+
"@handsontable/react-wrapper": "^15.0.0",
1113
"@logicflow/core": "^2.0.5",
1214
"@logicflow/extension": "^2.0.9",
1315
"@reduxjs/toolkit": "^2.2.7",
1416
"@types/babel__standalone": "^7.1.7",
1517
"@types/node": "^16.18.108",
1618
"@types/react": "^18.3.5",
1719
"@types/react-dom": "^18.3.0",
20+
"ag-grid-react": "^33.0.3",
1821
"antd": "^5.20.6",
1922
"axios": "^1.7.7",
2023
"base64-js": "^1.5.1",
24+
"handsontable": "^15.0.0",
2125
"jszip": "^3.10.1",
2226
"lodash": "^4.17.21",
2327
"moment": "^2.30.1",
@@ -33,7 +37,9 @@
3337
"scripts": {
3438
"start": "webpack serve --config webpack.config.mock.js --open",
3539
"dev": "webpack serve --config webpack.config.dev.js --open",
36-
"build": "webpack --mode production --config webpack.config.prod.js"
40+
"build": "webpack --mode production --config webpack.config.prod.js",
41+
"test": "jest",
42+
"test:watch": "jest --watchAll"
3743
},
3844
"eslintConfig": {
3945
"extends": [
@@ -53,19 +59,32 @@
5359
]
5460
},
5561
"devDependencies": {
62+
"@babel/preset-env": "^7.26.0",
63+
"@babel/preset-react": "^7.26.3",
64+
"@babel/preset-typescript": "^7.26.0",
65+
"@testing-library/dom": "^10.4.0",
66+
"@testing-library/jest-dom": "^6.6.3",
67+
"@testing-library/react": "^16.1.0",
68+
"@types/jest": "^29.5.14",
5669
"@types/lodash": "^4.17.7",
5770
"@types/lodash-es": "^4.17.12",
71+
"babel-jest": "^29.7.0",
5872
"clean-webpack-plugin": "^4.0.0",
5973
"copy-webpack-plugin": "^12.0.2",
6074
"css-loader": "^7.1.2",
6175
"express": "^4.21.0",
6276
"html-webpack-plugin": "^5.6.0",
77+
"identity-obj-proxy": "^3.0.0",
78+
"jest": "^29.7.0",
79+
"jest-environment-jsdom": "^29.7.0",
6380
"mockjs": "^1.1.0",
6481
"monaco-editor-webpack-plugin": "^7.1.0",
6582
"sass": "^1.78.0",
6683
"sass-loader": "^16.0.1",
6784
"style-loader": "^4.0.0",
85+
"ts-jest": "^29.2.5",
6886
"ts-loader": "^9.5.1",
87+
"ts-node": "^10.9.2",
6988
"webpack": "^5.94.0",
7089
"webpack-cli": "^5.1.4",
7190
"webpack-dev-server": "^5.1.0",

admin-ui/react-jest.md

+190
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: '[email protected]' }
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/api/salary.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const users = async () => {
2+
const data = [];
3+
for(let i=0;i<500;i++){
4+
data.push({
5+
id:i,
6+
name:`张三${i}`
7+
});
8+
}
9+
return data
10+
}

admin-ui/src/components/Flow/flow/data.ts

+14
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,20 @@ export class FlowData extends FlowWorkData {
268268
return false;
269269
}
270270

271+
// 是否是退回状态
272+
isReject(){
273+
const historyRecords = this.data.historyRecords || [];
274+
const currentRecord = this.data.flowRecord;
275+
if(currentRecord && historyRecords.length>0){
276+
const preId = currentRecord.preId;
277+
const preRecord = historyRecords.find((item:any)=>item.id===preId);
278+
if(preRecord){
279+
return preRecord.flowSourceDirection === 'REJECT';
280+
}
281+
}
282+
return false;
283+
}
284+
271285
// 是否是结束节点
272286
private isFinished() {
273287
if (this.data.flowRecord) {

0 commit comments

Comments
 (0)