Skip to content

Dev #85

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 16 commits into from
Jan 2, 2025
Merged

Dev #85

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2021-2022 copyright codingapi
Copyright 2021-2025 copyright codingapi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
37 changes: 37 additions & 0 deletions admin-ui/__mocks__/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// __mocks__/axios.ts
const mockAxios = {
create: jest.fn(() => ({
interceptors: {
request: {
use: jest.fn(),
eject: jest.fn()
},
response: {
use: jest.fn(),
eject: jest.fn()
}
},
get: jest.fn(),
post: jest.fn(),
put: jest.fn(),
delete: jest.fn(),
patch: jest.fn()
})),
interceptors: {
request: {
use: jest.fn(),
eject: jest.fn()
},
response: {
use: jest.fn(),
eject: jest.fn()
}
},
get: jest.fn(),
post: jest.fn(),
put: jest.fn(),
delete: jest.fn(),
patch: jest.fn()
};

export default mockAxios;
1 change: 1 addition & 0 deletions admin-ui/__mocks__/fileMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'test-file-stub';
9 changes: 9 additions & 0 deletions admin-ui/__mocks__/monaco-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
editor: {
create: jest.fn(() => ({
dispose: jest.fn(),
getValue: jest.fn(() => ''),
setValue: jest.fn(),
})),
},
};
41 changes: 41 additions & 0 deletions admin-ui/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Config } from 'jest';

const config: Config = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
transform: {
'^.+\\.(ts|tsx)$': ['ts-jest', {
useESM: true,
}],
'^.+\\.(js|jsx|mjs)$': ['babel-jest', {
presets: [
['@babel/preset-env', {
targets: {
node: 'current',
},
}],
'@babel/preset-react',
'@babel/preset-typescript'
],
}],
},
moduleNameMapper: {
'^monaco-editor$': '<rootDir>/__mocks__/monaco-editor.js',
"@logicflow": "<rootDir>/node_modules/@logicflow/core/dist/index.min.js",
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
'^@/(.*)$': '<rootDir>/src/$1'
},
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
testMatch: [
"**/__test__/**/*.[jt]s?(x)",
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)"
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
'node_modules/(?!(lodash-es|@ant-design|@logicflow|other-esm-modules)/)'
],
};

export default config;
23 changes: 21 additions & 2 deletions admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ag-grid-community/locale": "^33.0.3",
"@ant-design/icons": "^5.4.0",
"@ant-design/pro-components": "^2.7.19",
"@ant-design/pro-components": "^2.8.2",
"@babel/standalone": "^7.25.6",
"@dnd-kit/core": "^6.2.0",
"@dnd-kit/sortable": "^9.0.0",
"@handsontable/react-wrapper": "^15.0.0",
"@logicflow/core": "^2.0.5",
"@logicflow/extension": "^2.0.9",
"@reduxjs/toolkit": "^2.2.7",
"@types/babel__standalone": "^7.1.7",
"@types/node": "^16.18.108",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"ag-grid-react": "^33.0.3",
"antd": "^5.20.6",
"axios": "^1.7.7",
"base64-js": "^1.5.1",
"handsontable": "^15.0.0",
"jszip": "^3.10.1",
"lodash": "^4.17.21",
"moment": "^2.30.1",
Expand All @@ -33,7 +37,9 @@
"scripts": {
"start": "webpack serve --config webpack.config.mock.js --open",
"dev": "webpack serve --config webpack.config.dev.js --open",
"build": "webpack --mode production --config webpack.config.prod.js"
"build": "webpack --mode production --config webpack.config.prod.js",
"test": "jest",
"test:watch": "jest --watchAll"
},
"eslintConfig": {
"extends": [
Expand All @@ -53,19 +59,32 @@
]
},
"devDependencies": {
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.7",
"@types/lodash-es": "^4.17.12",
"babel-jest": "^29.7.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"express": "^4.21.0",
"html-webpack-plugin": "^5.6.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"mockjs": "^1.1.0",
"monaco-editor-webpack-plugin": "^7.1.0",
"sass": "^1.78.0",
"sass-loader": "^16.0.1",
"style-loader": "^4.0.0",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0",
Expand Down
190 changes: 190 additions & 0 deletions admin-ui/react-jest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# React Unit Test for Jest

1. dependencies on devDependencies
```
@testing-library/dom
@testing-library/jest-dom
@testing-library/react
@types/jest
jest
ts-jest
jest-environment-jsdom
identity-obj-proxy
ts-node
```
2. add jest.config.ts and add jest script in package.json

```
//rootDir/jest.config.ts
import type { Config } from 'jest';

const config: Config = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
'^.+\\.(js|jsx)$': 'babel-jest',
},
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
'^@/(.*)$': '<rootDir>/src/$1'
},
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};

export default config;


```

on jest.config.ts will setting up the jest configuration for the project.
some important configuration is moduleNameMapper, this setting configuration for style,asset,and project path alias.
setupFilesAfterEnv is the file that will be executed before the test run, in this case jest.setup.ts
testRegex is regex for the test file, in this case the test file should be end with .test.tsx or .spec.tsx


on `package.json` add jest script
```
"scripts":{
"test": "jest",
"test:watch": "jest --watch"
... other scripts
}
```

3. add jest.setup.ts and fileMock.js in src folder
```
// rootDir/src/jest.setup.ts
import '@testing-library/jest-dom';
```

```

// rootDir/__mocks__/fileMock.js
module.exports = 'test-file-stub';

```

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`
```

import React from 'react';
import Welcome from '../index';
import {render} from "@testing-library/react";
import store from "@/store/Redux";
import {Provider} from "react-redux";

// test description for the component
describe('Welcome Component', () => {
// test case for the component
test('renders with default initial value', () => {
// render the component
render(
<Provider store={store}>
<Welcome />
</Provider>
);
// get the element that will be tested
const countElement = document.querySelector('.App-header p');
// assert the element is exist
expect(countElement).toBeInTheDocument();
// assert the element text content
expect(countElement).toHaveTextContent('hi , Redux counter: 0, Roles:');
// log the element text content
console.log(countElement?.textContent);
});
});

```

5. test router component

> specify the router path in the test file
```
import { render, fireEvent } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import UserProfile from '../UserProfile';

describe('UserProfile Component', () => {
test('renders user profile with correct ID', () => {

// use memory router and setting initial router path
const { getByTestId } = render(
<MemoryRouter initialEntries={['/user/123']}>
<Routes>
<Route path="/user/:id" element={<UserProfile />} />
</Routes>
</MemoryRouter>
);

expect(getByTestId('user-id')).toHaveTextContent('User ID: 123');
});
});
```
> or use `BrowserRouter` and `render` function
```
import { render, fireEvent } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import App from '../App';

describe('App Navigation', () => {
test('full app navigation', () => {
const history = createMemoryHistory();
const { getByText } = render(
<Router location={history.location} navigator={history}>
<App />
</Router>
);
fireEvent.click(getByText('Go to Profile'));
expect(history.location.pathname).toBe('/profile');
});
});
```

6. test axios api calling
```

import { render, waitFor } from '@testing-library/react';
import axios from 'axios';
import { UserProfile } from '../UserProfile';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('UserProfile Component', () => {
// test case for the component
test('loads all user data correctly - URL based', async () => {
// mock the axios get function
mockedAxios.get.mockImplementation((url) => {
if (url === '/api/users/123') {
return Promise.resolve({
data: { name: 'John Doe', email: 'john@example.com' }
});
}
if (url === '/api/users/123/posts') {
return Promise.resolve({
data: [{ id: 1, title: 'Post 1' }]
});
}
if (url === '/api/users/123/followers') {
return Promise.resolve({
data: [{ id: 1, name: 'Follower 1' }]
});
}
return Promise.reject(new Error('Not found'));
});

const { getByTestId } = render(<UserProfile userId="123" />);

await waitFor(() => {
expect(getByTestId('user-info')).toHaveTextContent('John Doe');
expect(getByTestId('user-posts')).toHaveTextContent('Post 1');
expect(getByTestId('user-followers')).toHaveTextContent('Follower 1');
});
});

```
10 changes: 10 additions & 0 deletions admin-ui/src/api/salary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const users = async () => {
const data = [];
for(let i=0;i<500;i++){
data.push({
id:i,
name:`张三${i}`
});
}
return data
}
14 changes: 14 additions & 0 deletions admin-ui/src/components/Flow/flow/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,20 @@ export class FlowData extends FlowWorkData {
return false;
}

// 是否是退回状态
isReject(){
const historyRecords = this.data.historyRecords || [];
const currentRecord = this.data.flowRecord;
if(currentRecord && historyRecords.length>0){
const preId = currentRecord.preId;
const preRecord = historyRecords.find((item:any)=>item.id===preId);
if(preRecord){
return preRecord.flowSourceDirection === 'REJECT';
}
}
return false;
}

// 是否是结束节点
private isFinished() {
if (this.data.flowRecord) {
Expand Down
Loading