@zat-design/sisyphus-react 3.13.18-beta.8 → 3.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.js +11 -1
- package/es/ProForm/components/base/DatePicker/index.js +16 -32
- package/es/ProForm/components/combination/Group/hooks/index.js +2 -7
- package/jest.config.js +46 -4
- package/lib/ProForm/components/base/DatePicker/index.js +16 -32
- package/lib/ProForm/components/combination/Group/hooks/index.js +2 -7
- package/package.json +2 -2
- package/tests/__mocks__/fileMock.js +1 -0
- package/tests/__mocks__/zatUtils.js +27 -0
- package/tests/setup.ts +477 -0
- package/tests/test-utils.tsx +81 -0
- package/es/ProForm/components/base/DatePicker/useDateLimit.d.ts +0 -9
- package/es/ProForm/components/base/DatePicker/useDateLimit.js +0 -15
- package/lib/ProForm/components/base/DatePicker/useDateLimit.d.ts +0 -9
- package/lib/ProForm/components/base/DatePicker/useDateLimit.js +0 -22
package/babel.config.js
CHANGED
@@ -1,3 +1,13 @@
|
|
1
1
|
module.exports = {
|
2
|
-
presets: [
|
2
|
+
presets: [
|
3
|
+
['@babel/preset-env', { loose: true }],
|
4
|
+
['@babel/preset-react', { runtime: 'automatic' }],
|
5
|
+
'@babel/preset-typescript'
|
6
|
+
],
|
7
|
+
plugins: [
|
8
|
+
['@babel/plugin-proposal-decorators', { legacy: true }],
|
9
|
+
['@babel/plugin-proposal-class-properties', { loose: true }],
|
10
|
+
['@babel/plugin-transform-private-methods', { loose: true }],
|
11
|
+
['@babel/plugin-transform-private-property-in-object', { loose: true }],
|
12
|
+
]
|
3
13
|
};
|
@@ -1,10 +1,9 @@
|
|
1
1
|
import "antd/es/date-picker/style";
|
2
2
|
import _DatePicker from "antd/es/date-picker";
|
3
3
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
4
|
-
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
5
4
|
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
6
5
|
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
7
|
-
import
|
6
|
+
import _omit from "lodash/omit";
|
8
7
|
import _isObject from "lodash/isObject";
|
9
8
|
import _isString from "lodash/isString";
|
10
9
|
import _isFunction from "lodash/isFunction";
|
@@ -25,18 +24,11 @@ var DatePicker = function DatePicker(props) {
|
|
25
24
|
viewEmpty = _ref.viewEmpty,
|
26
25
|
valueType = _ref.valueType;
|
27
26
|
var isView = typeof props.isView === 'boolean' ? props.isView : isViewCon; // 组件可直接接收isView参数, 优先级高
|
28
|
-
var initialConfig = useProConfig('DatePicker');
|
29
|
-
var
|
27
|
+
var initialConfig = useProConfig('DatePicker') || {};
|
28
|
+
var nextFormat = format;
|
29
|
+
var nextClassName = classNames(_defineProperty({
|
30
30
|
'full-form-item': true
|
31
31
|
}, className, !!className));
|
32
|
-
var _viewFormat = Array.isArray(format) ? format[0] : format;
|
33
|
-
var tempFormat = Array.isArray(format) ? format : [format];
|
34
|
-
if (_isFunction(format)) {
|
35
|
-
tempFormat = [];
|
36
|
-
}
|
37
|
-
var _format = _uniq([].concat(_toConsumableArray(tempFormat), ['YYYY-MM-DD', 'YYYYMMDD', 'YYYY/MM/DD', 'YYYY_MM_DD', 'YYYY.MM.DD'])).filter(function (formatKey) {
|
38
|
-
return !!formatKey;
|
39
|
-
});
|
40
32
|
if (isView) {
|
41
33
|
var value = rest.value;
|
42
34
|
var viewChildren = null;
|
@@ -47,38 +39,30 @@ var DatePicker = function DatePicker(props) {
|
|
47
39
|
} else if (_isFunction(format)) {
|
48
40
|
viewChildren = format(value);
|
49
41
|
} else if (_isString(value)) {
|
50
|
-
viewChildren = moment(value).format(
|
42
|
+
viewChildren = moment(value).format(format);
|
51
43
|
}
|
52
44
|
return _jsx(Container, {
|
53
45
|
viewEmpty: viewEmpty,
|
54
46
|
children: viewChildren
|
55
47
|
});
|
56
48
|
}
|
57
|
-
var _defaultShowTime = {
|
58
|
-
format: 'HH:mm:ss'
|
59
|
-
};
|
60
49
|
// dateTime模式下默认开启time选择
|
61
|
-
var
|
50
|
+
var restProps = _objectSpread({}, rest);
|
62
51
|
// showTime默认值受valueType属性影响
|
63
|
-
if (
|
64
|
-
|
65
|
-
|
66
|
-
|
52
|
+
if (restProps.showTime === false) {
|
53
|
+
restProps.showTime = false;
|
54
|
+
} else if (valueType === 'dateTime') {
|
55
|
+
restProps.showTime = true;
|
56
|
+
nextFormat = 'YYYY-MM-DD HH:mm:ss';
|
67
57
|
}
|
68
58
|
// 字符串时间格式兼容
|
69
|
-
if (_isString(
|
70
|
-
|
71
|
-
}
|
72
|
-
if (_rest.showTime === true) {
|
73
|
-
_rest.showTime = _objectSpread({}, _defaultShowTime);
|
74
|
-
}
|
75
|
-
if (_isObject(_rest.showTime)) {
|
76
|
-
_rest.showTime = Object.assign(_defaultShowTime, _rest.showTime);
|
59
|
+
if (_isString(restProps.value)) {
|
60
|
+
restProps.value = moment(restProps.value);
|
77
61
|
}
|
78
62
|
return _jsx(_DatePicker, _objectSpread(_objectSpread(_objectSpread({}, initialConfig), {}, {
|
79
|
-
format:
|
80
|
-
},
|
81
|
-
className:
|
63
|
+
format: nextFormat
|
64
|
+
}, _omit(restProps, ['otherProps'])), {}, {
|
65
|
+
className: nextClassName
|
82
66
|
}));
|
83
67
|
};
|
84
68
|
export default DatePicker;
|
@@ -165,14 +165,9 @@ export var useTransformColumns = function useTransformColumns(params) {
|
|
165
165
|
getValueProps: transform === null || transform === void 0 ? void 0 : transform.getValueProps,
|
166
166
|
fieldProps: _objectSpread(_objectSpread(_objectSpread({}, column === null || column === void 0 ? void 0 : column.fieldProps), reactiveProps === null || reactiveProps === void 0 ? void 0 : reactiveProps.fieldProps), {}, {
|
167
167
|
onChange: handleChange,
|
168
|
-
onBlur: handleBlur
|
169
|
-
}, names ? {
|
170
|
-
value: form.getFieldValue(columnName)
|
171
|
-
} // 独立字段模式
|
172
|
-
: {
|
168
|
+
onBlur: handleBlur,
|
173
169
|
value: value === null || value === void 0 ? void 0 : value[index]
|
174
|
-
}
|
175
|
-
)
|
170
|
+
})
|
176
171
|
});
|
177
172
|
});
|
178
173
|
};
|
package/jest.config.js
CHANGED
@@ -1,8 +1,50 @@
|
|
1
|
-
const React = require('react');
|
2
|
-
|
3
1
|
module.exports = {
|
4
2
|
setupFiles: ['jest-canvas-mock'],
|
5
|
-
|
6
|
-
|
3
|
+
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
|
4
|
+
testEnvironment: 'jsdom',
|
5
|
+
testEnvironmentOptions: {
|
6
|
+
url: 'http://localhost',
|
7
7
|
},
|
8
|
+
moduleNameMapper: {
|
9
|
+
'^@/(.*)$': '<rootDir>/src/$1',
|
10
|
+
'^@zat-design/sisyphus-react$': '<rootDir>/src/index.ts',
|
11
|
+
'\\.(css|less|scss)$': 'identity-obj-proxy',
|
12
|
+
'\\.(png|jpg|jpeg|gif|svg)$': '<rootDir>/tests/__mocks__/fileMock.js',
|
13
|
+
'@zat-design/utils': '<rootDir>/tests/__mocks__/zatUtils.js',
|
14
|
+
},
|
15
|
+
collectCoverageFrom: [
|
16
|
+
'src/**/*.{ts,tsx}',
|
17
|
+
'!src/**/*.d.ts',
|
18
|
+
'!src/**/demos/**',
|
19
|
+
'!src/**/test/**',
|
20
|
+
'!src/old/**',
|
21
|
+
'!src/style/**',
|
22
|
+
'!src/**/symbolIcon.js',
|
23
|
+
'!src/index.ts',
|
24
|
+
'!src/tokens.ts',
|
25
|
+
'!src/utils/**',
|
26
|
+
'!src/locale/**',
|
27
|
+
],
|
28
|
+
testMatch: [
|
29
|
+
'<rootDir>/src/**/__tests__/**/*.{ts,tsx}',
|
30
|
+
'<rootDir>/src/**/*.(test|spec).{ts,tsx}',
|
31
|
+
],
|
32
|
+
testTimeout: 15000,
|
33
|
+
transform: {
|
34
|
+
'^.+\\.(ts|tsx)$': 'babel-jest',
|
35
|
+
'^.+\\.(js|jsx)$': 'babel-jest',
|
36
|
+
},
|
37
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
38
|
+
transformIgnorePatterns: [
|
39
|
+
'node_modules/(?!(antd|@ant-design|@pansy|rc-.+|@babel/runtime|@zat-design)/)',
|
40
|
+
],
|
41
|
+
coverageThreshold: {
|
42
|
+
global: {
|
43
|
+
branches: 80,
|
44
|
+
functions: 80,
|
45
|
+
lines: 80,
|
46
|
+
statements: 80,
|
47
|
+
},
|
48
|
+
},
|
49
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
8
50
|
};
|
@@ -8,10 +8,9 @@ exports.default = void 0;
|
|
8
8
|
require("antd/es/date-picker/style");
|
9
9
|
var _datePicker = _interopRequireDefault(require("antd/es/date-picker"));
|
10
10
|
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
|
11
|
-
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
12
11
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
13
12
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
14
|
-
var
|
13
|
+
var _omit2 = _interopRequireDefault(require("lodash/omit"));
|
15
14
|
var _isObject2 = _interopRequireDefault(require("lodash/isObject"));
|
16
15
|
var _isString2 = _interopRequireDefault(require("lodash/isString"));
|
17
16
|
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
|
@@ -32,18 +31,11 @@ var DatePicker = function DatePicker(props) {
|
|
32
31
|
viewEmpty = _ref.viewEmpty,
|
33
32
|
valueType = _ref.valueType;
|
34
33
|
var isView = typeof props.isView === 'boolean' ? props.isView : isViewCon; // 组件可直接接收isView参数, 优先级高
|
35
|
-
var initialConfig = (0, _ProConfigProvider.useProConfig)('DatePicker');
|
36
|
-
var
|
34
|
+
var initialConfig = (0, _ProConfigProvider.useProConfig)('DatePicker') || {};
|
35
|
+
var nextFormat = format;
|
36
|
+
var nextClassName = (0, _classnames.default)((0, _defineProperty2.default)({
|
37
37
|
'full-form-item': true
|
38
38
|
}, className, !!className));
|
39
|
-
var _viewFormat = Array.isArray(format) ? format[0] : format;
|
40
|
-
var tempFormat = Array.isArray(format) ? format : [format];
|
41
|
-
if ((0, _isFunction2.default)(format)) {
|
42
|
-
tempFormat = [];
|
43
|
-
}
|
44
|
-
var _format = (0, _uniq2.default)([].concat((0, _toConsumableArray2.default)(tempFormat), ['YYYY-MM-DD', 'YYYYMMDD', 'YYYY/MM/DD', 'YYYY_MM_DD', 'YYYY.MM.DD'])).filter(function (formatKey) {
|
45
|
-
return !!formatKey;
|
46
|
-
});
|
47
39
|
if (isView) {
|
48
40
|
var value = rest.value;
|
49
41
|
var viewChildren = null;
|
@@ -54,38 +46,30 @@ var DatePicker = function DatePicker(props) {
|
|
54
46
|
} else if ((0, _isFunction2.default)(format)) {
|
55
47
|
viewChildren = format(value);
|
56
48
|
} else if ((0, _isString2.default)(value)) {
|
57
|
-
viewChildren = (0, _moment.default)(value).format(
|
49
|
+
viewChildren = (0, _moment.default)(value).format(format);
|
58
50
|
}
|
59
51
|
return (0, _jsxRuntime.jsx)(_Container.default, {
|
60
52
|
viewEmpty: viewEmpty,
|
61
53
|
children: viewChildren
|
62
54
|
});
|
63
55
|
}
|
64
|
-
var _defaultShowTime = {
|
65
|
-
format: 'HH:mm:ss'
|
66
|
-
};
|
67
56
|
// dateTime模式下默认开启time选择
|
68
|
-
var
|
57
|
+
var restProps = (0, _objectSpread2.default)({}, rest);
|
69
58
|
// showTime默认值受valueType属性影响
|
70
|
-
if (
|
71
|
-
|
72
|
-
|
73
|
-
|
59
|
+
if (restProps.showTime === false) {
|
60
|
+
restProps.showTime = false;
|
61
|
+
} else if (valueType === 'dateTime') {
|
62
|
+
restProps.showTime = true;
|
63
|
+
nextFormat = 'YYYY-MM-DD HH:mm:ss';
|
74
64
|
}
|
75
65
|
// 字符串时间格式兼容
|
76
|
-
if ((0, _isString2.default)(
|
77
|
-
|
78
|
-
}
|
79
|
-
if (_rest.showTime === true) {
|
80
|
-
_rest.showTime = (0, _objectSpread2.default)({}, _defaultShowTime);
|
81
|
-
}
|
82
|
-
if ((0, _isObject2.default)(_rest.showTime)) {
|
83
|
-
_rest.showTime = Object.assign(_defaultShowTime, _rest.showTime);
|
66
|
+
if ((0, _isString2.default)(restProps.value)) {
|
67
|
+
restProps.value = (0, _moment.default)(restProps.value);
|
84
68
|
}
|
85
69
|
return (0, _jsxRuntime.jsx)(_datePicker.default, (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, initialConfig), {}, {
|
86
|
-
format:
|
87
|
-
},
|
88
|
-
className:
|
70
|
+
format: nextFormat
|
71
|
+
}, (0, _omit2.default)(restProps, ['otherProps'])), {}, {
|
72
|
+
className: nextClassName
|
89
73
|
}));
|
90
74
|
};
|
91
75
|
var _default = exports.default = DatePicker;
|
@@ -173,14 +173,9 @@ var useTransformColumns = exports.useTransformColumns = function useTransformCol
|
|
173
173
|
getValueProps: transform === null || transform === void 0 ? void 0 : transform.getValueProps,
|
174
174
|
fieldProps: (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, column === null || column === void 0 ? void 0 : column.fieldProps), reactiveProps === null || reactiveProps === void 0 ? void 0 : reactiveProps.fieldProps), {}, {
|
175
175
|
onChange: handleChange,
|
176
|
-
onBlur: handleBlur
|
177
|
-
}, names ? {
|
178
|
-
value: form.getFieldValue(columnName)
|
179
|
-
} // 独立字段模式
|
180
|
-
: {
|
176
|
+
onBlur: handleBlur,
|
181
177
|
value: value === null || value === void 0 ? void 0 : value[index]
|
182
|
-
}
|
183
|
-
)
|
178
|
+
})
|
184
179
|
});
|
185
180
|
});
|
186
181
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@zat-design/sisyphus-react",
|
3
|
-
"version": "3.13.18
|
3
|
+
"version": "3.13.18",
|
4
4
|
"license": "MIT",
|
5
5
|
"main": "lib/index.js",
|
6
6
|
"module": "es/index.js",
|
@@ -17,7 +17,7 @@
|
|
17
17
|
"docs:deploy": "gh-pages -d docs-dist",
|
18
18
|
"lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
|
19
19
|
"lint-staged": "lint-staged",
|
20
|
-
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx",
|
20
|
+
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx --ignore-pattern '**/__tests__/**' --ignore-pattern '**/*.test.*' --ignore-pattern '**/*.spec.*'",
|
21
21
|
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
|
22
22
|
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
|
23
23
|
"lint:prettier": "prettier --check \"**/*\" --end-of-line auto",
|
@@ -0,0 +1 @@
|
|
1
|
+
module.exports = 'test-file-stub';
|
@@ -0,0 +1,27 @@
|
|
1
|
+
// Mock @zat-design/utils
|
2
|
+
module.exports = {
|
3
|
+
tools: {
|
4
|
+
isValidUrl: jest.fn(() => true),
|
5
|
+
formatCurrency: jest.fn((value) => `¥${value}`),
|
6
|
+
formatDate: jest.fn((date) => new Date(date).toLocaleDateString()),
|
7
|
+
},
|
8
|
+
transforms: {
|
9
|
+
transformDate: jest.fn((value, params) => {
|
10
|
+
if (!value) return '';
|
11
|
+
return new Date(value).toLocaleDateString();
|
12
|
+
}),
|
13
|
+
transformSwitch: jest.fn((value) => value ? '是' : '否'),
|
14
|
+
transformMoney: jest.fn((value) => `¥${value || 0}`),
|
15
|
+
transformPercent: jest.fn((value) => `${(value || 0) * 100}%`),
|
16
|
+
},
|
17
|
+
validate: {
|
18
|
+
isEmail: jest.fn((email) => /\S+@\S+\.\S+/.test(email)),
|
19
|
+
isPhone: jest.fn((phone) => /^1[3-9]\d{9}$/.test(phone)),
|
20
|
+
isIdCard: jest.fn(() => true),
|
21
|
+
},
|
22
|
+
validateNameMap: {
|
23
|
+
email: 'isEmail',
|
24
|
+
phone: 'isPhone',
|
25
|
+
idCard: 'isIdCard',
|
26
|
+
},
|
27
|
+
};
|
package/tests/setup.ts
ADDED
@@ -0,0 +1,477 @@
|
|
1
|
+
import '@testing-library/jest-dom';
|
2
|
+
import 'jest-canvas-mock';
|
3
|
+
import React from 'react';
|
4
|
+
|
5
|
+
// Ensure JSDOM document.body exists and setup proper DOM environment
|
6
|
+
if (typeof document !== 'undefined') {
|
7
|
+
// Ensure documentElement exists
|
8
|
+
if (!document.documentElement) {
|
9
|
+
const html = document.createElement('html');
|
10
|
+
document.appendChild(html);
|
11
|
+
}
|
12
|
+
|
13
|
+
// Ensure head exists
|
14
|
+
if (!document.head) {
|
15
|
+
const head = document.createElement('head');
|
16
|
+
document.documentElement.appendChild(head);
|
17
|
+
}
|
18
|
+
|
19
|
+
// Ensure body exists
|
20
|
+
if (!document.body) {
|
21
|
+
const body = document.createElement('body');
|
22
|
+
document.documentElement.appendChild(body);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
// Mock testing library environment setup
|
27
|
+
Object.defineProperty(global, 'MutationObserver', {
|
28
|
+
writable: true,
|
29
|
+
value: jest.fn().mockImplementation(() => ({
|
30
|
+
observe: jest.fn(),
|
31
|
+
disconnect: jest.fn(),
|
32
|
+
takeRecords: jest.fn(),
|
33
|
+
})),
|
34
|
+
});
|
35
|
+
|
36
|
+
// Setup test container div
|
37
|
+
beforeEach(() => {
|
38
|
+
if (typeof document !== 'undefined' && document.body) {
|
39
|
+
try {
|
40
|
+
// Clean up any existing test containers
|
41
|
+
const existingContainers = document.querySelectorAll('[data-testid="test-container"]');
|
42
|
+
existingContainers.forEach(container => {
|
43
|
+
if (container.parentNode) {
|
44
|
+
try {
|
45
|
+
container.parentNode.removeChild(container);
|
46
|
+
} catch (e) {
|
47
|
+
// Ignore cleanup errors
|
48
|
+
}
|
49
|
+
}
|
50
|
+
});
|
51
|
+
|
52
|
+
// Create a fresh test container
|
53
|
+
const testContainer = document.createElement('div');
|
54
|
+
testContainer.setAttribute('data-testid', 'test-container');
|
55
|
+
testContainer.id = 'test-container';
|
56
|
+
document.body.appendChild(testContainer);
|
57
|
+
} catch (e) {
|
58
|
+
// Ignore setup errors - React Testing Library will handle this
|
59
|
+
}
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
// 抑制控制台错误
|
64
|
+
const originalError = console.error;
|
65
|
+
beforeAll(() => {
|
66
|
+
console.error = (...args: any[]) => {
|
67
|
+
if (
|
68
|
+
args.find(
|
69
|
+
arg =>
|
70
|
+
typeof arg === 'string' &&
|
71
|
+
(
|
72
|
+
arg.includes('Warning: ReactDOM.render is deprecated') ||
|
73
|
+
arg.includes('Warning: componentWillReceiveProps') ||
|
74
|
+
arg.includes('validateDOMNesting') ||
|
75
|
+
arg.includes('Warning: componentWillMount') ||
|
76
|
+
arg.includes('Warning: React.createFactory() is deprecated') ||
|
77
|
+
arg.includes('Warning: findDOMNode is deprecated') ||
|
78
|
+
arg.includes('act(...)') ||
|
79
|
+
arg.includes('useLayoutEffect does nothing on the server') ||
|
80
|
+
arg.includes('Invalid hook call') ||
|
81
|
+
arg.includes('Hooks can only be called inside') ||
|
82
|
+
arg.includes('Warning: Each child in a list should have a unique') ||
|
83
|
+
arg.includes('Warning: validateDOMNesting')
|
84
|
+
)
|
85
|
+
)
|
86
|
+
) {
|
87
|
+
return;
|
88
|
+
}
|
89
|
+
originalError.call(console, ...args);
|
90
|
+
};
|
91
|
+
});
|
92
|
+
|
93
|
+
afterAll(() => {
|
94
|
+
console.error = originalError;
|
95
|
+
});
|
96
|
+
|
97
|
+
// Mock window APIs
|
98
|
+
Object.defineProperty(window, 'matchMedia', {
|
99
|
+
writable: true,
|
100
|
+
value: jest.fn().mockImplementation(query => ({
|
101
|
+
matches: false,
|
102
|
+
media: query,
|
103
|
+
onchange: null,
|
104
|
+
addListener: jest.fn(),
|
105
|
+
removeListener: jest.fn(),
|
106
|
+
addEventListener: jest.fn(),
|
107
|
+
removeEventListener: jest.fn(),
|
108
|
+
dispatchEvent: jest.fn(),
|
109
|
+
})),
|
110
|
+
});
|
111
|
+
|
112
|
+
// Mock ahooks hooks - 基于ahooks官方测试实践改进
|
113
|
+
jest.mock('ahooks', () => ({
|
114
|
+
useSetState: jest.fn((initialState) => {
|
115
|
+
const mockState = { current: initialState || {} };
|
116
|
+
const mockSetState = jest.fn((patch) => {
|
117
|
+
if (typeof patch === 'function') {
|
118
|
+
mockState.current = patch(mockState.current);
|
119
|
+
} else {
|
120
|
+
mockState.current = { ...mockState.current, ...patch };
|
121
|
+
}
|
122
|
+
return mockState.current;
|
123
|
+
});
|
124
|
+
return [mockState.current, mockSetState];
|
125
|
+
}),
|
126
|
+
|
127
|
+
useRequest: jest.fn((service, options = {}) => {
|
128
|
+
const mockState = {
|
129
|
+
loading: false,
|
130
|
+
data: null,
|
131
|
+
error: null,
|
132
|
+
params: [],
|
133
|
+
};
|
134
|
+
|
135
|
+
const mockActions = {
|
136
|
+
run: jest.fn((...params) => {
|
137
|
+
mockState.loading = true;
|
138
|
+
mockState.params = params;
|
139
|
+
// 模拟异步操作
|
140
|
+
setTimeout(() => {
|
141
|
+
mockState.loading = false;
|
142
|
+
mockState.data = 'mock-data';
|
143
|
+
}, 100);
|
144
|
+
return Promise.resolve('mock-data');
|
145
|
+
}),
|
146
|
+
refresh: jest.fn(() => {
|
147
|
+
mockState.loading = true;
|
148
|
+
setTimeout(() => {
|
149
|
+
mockState.loading = false;
|
150
|
+
}, 100);
|
151
|
+
return Promise.resolve('mock-data');
|
152
|
+
}),
|
153
|
+
cancel: jest.fn(() => {
|
154
|
+
mockState.loading = false;
|
155
|
+
}),
|
156
|
+
mutate: jest.fn((data) => {
|
157
|
+
mockState.data = data;
|
158
|
+
}),
|
159
|
+
};
|
160
|
+
|
161
|
+
const result = {
|
162
|
+
...mockState,
|
163
|
+
...mockActions,
|
164
|
+
};
|
165
|
+
|
166
|
+
// 如果提供了service函数,模拟调用
|
167
|
+
if (service && typeof service === 'function') {
|
168
|
+
result.run = jest.fn((...params) => {
|
169
|
+
mockState.loading = true;
|
170
|
+
mockState.params = params;
|
171
|
+
|
172
|
+
return new Promise((resolve, reject) => {
|
173
|
+
setTimeout(() => {
|
174
|
+
try {
|
175
|
+
const serviceResult = service(...params);
|
176
|
+
if (serviceResult instanceof Promise) {
|
177
|
+
serviceResult.then(resolve).catch(reject);
|
178
|
+
} else {
|
179
|
+
resolve(serviceResult);
|
180
|
+
}
|
181
|
+
} catch (error) {
|
182
|
+
reject(error);
|
183
|
+
} finally {
|
184
|
+
mockState.loading = false;
|
185
|
+
}
|
186
|
+
}, 100);
|
187
|
+
});
|
188
|
+
});
|
189
|
+
}
|
190
|
+
|
191
|
+
// 处理options
|
192
|
+
if (options.manual !== true) {
|
193
|
+
// 自动执行
|
194
|
+
setTimeout(() => {
|
195
|
+
result.run();
|
196
|
+
}, 0);
|
197
|
+
}
|
198
|
+
|
199
|
+
return result;
|
200
|
+
}),
|
201
|
+
|
202
|
+
useDebounce: jest.fn((value, delay) => value),
|
203
|
+
|
204
|
+
useThrottle: jest.fn((value, delay) => value),
|
205
|
+
|
206
|
+
useLocalStorageState: jest.fn((key, defaultValue) => {
|
207
|
+
const [state, setState] = [defaultValue, jest.fn()];
|
208
|
+
return [state, setState];
|
209
|
+
}),
|
210
|
+
|
211
|
+
useSessionStorageState: jest.fn((key, defaultValue) => {
|
212
|
+
const [state, setState] = [defaultValue, jest.fn()];
|
213
|
+
return [state, setState];
|
214
|
+
}),
|
215
|
+
|
216
|
+
useDeepCompareEffect: jest.fn((effect, deps) => {
|
217
|
+
if (effect && typeof effect === 'function') {
|
218
|
+
effect();
|
219
|
+
}
|
220
|
+
}),
|
221
|
+
|
222
|
+
useUpdateEffect: jest.fn((effect, deps) => {
|
223
|
+
if (effect && typeof effect === 'function') {
|
224
|
+
effect();
|
225
|
+
}
|
226
|
+
}),
|
227
|
+
|
228
|
+
useSize: jest.fn(() => ({ width: 100, height: 100 })),
|
229
|
+
|
230
|
+
useInViewport: jest.fn(() => [true, jest.fn()]),
|
231
|
+
|
232
|
+
useMemoizedFn: jest.fn((fn) => fn),
|
233
|
+
|
234
|
+
useMount: jest.fn((fn) => {
|
235
|
+
if (fn && typeof fn === 'function') {
|
236
|
+
fn();
|
237
|
+
}
|
238
|
+
}),
|
239
|
+
|
240
|
+
useUnmount: jest.fn((fn) => {
|
241
|
+
if (fn && typeof fn === 'function') {
|
242
|
+
fn();
|
243
|
+
}
|
244
|
+
}),
|
245
|
+
|
246
|
+
useDebounceEffect: jest.fn((effect, deps, wait) => {
|
247
|
+
if (effect && typeof effect === 'function') {
|
248
|
+
effect();
|
249
|
+
}
|
250
|
+
}),
|
251
|
+
|
252
|
+
useThrottleEffect: jest.fn((effect, deps, wait) => {
|
253
|
+
if (effect && typeof effect === 'function') {
|
254
|
+
effect();
|
255
|
+
}
|
256
|
+
}),
|
257
|
+
}));
|
258
|
+
|
259
|
+
Object.defineProperty(window, 'ResizeObserver', {
|
260
|
+
writable: true,
|
261
|
+
value: jest.fn().mockImplementation(() => ({
|
262
|
+
observe: jest.fn(),
|
263
|
+
unobserve: jest.fn(),
|
264
|
+
disconnect: jest.fn(),
|
265
|
+
})),
|
266
|
+
});
|
267
|
+
|
268
|
+
Object.defineProperty(window, 'IntersectionObserver', {
|
269
|
+
writable: true,
|
270
|
+
value: jest.fn().mockImplementation(() => ({
|
271
|
+
observe: jest.fn(),
|
272
|
+
unobserve: jest.fn(),
|
273
|
+
disconnect: jest.fn(),
|
274
|
+
})),
|
275
|
+
});
|
276
|
+
|
277
|
+
// Mock getBoundingClientRect
|
278
|
+
Element.prototype.getBoundingClientRect = jest.fn(() => ({
|
279
|
+
width: 120,
|
280
|
+
height: 120,
|
281
|
+
top: 0,
|
282
|
+
left: 0,
|
283
|
+
bottom: 0,
|
284
|
+
right: 0,
|
285
|
+
x: 0,
|
286
|
+
y: 0,
|
287
|
+
toJSON: jest.fn(),
|
288
|
+
}));
|
289
|
+
|
290
|
+
// Mock scrollIntoView
|
291
|
+
Element.prototype.scrollIntoView = jest.fn();
|
292
|
+
|
293
|
+
// Mock getComputedStyle
|
294
|
+
window.getComputedStyle = jest.fn(() => ({
|
295
|
+
getPropertyValue: jest.fn(() => ''),
|
296
|
+
setProperty: jest.fn(),
|
297
|
+
removeProperty: jest.fn(),
|
298
|
+
} as any));
|
299
|
+
|
300
|
+
// Mock URL methods
|
301
|
+
global.URL.createObjectURL = jest.fn(() => 'mock-url');
|
302
|
+
global.URL.revokeObjectURL = jest.fn();
|
303
|
+
|
304
|
+
// Mock fetch
|
305
|
+
global.fetch = jest.fn(() =>
|
306
|
+
Promise.resolve({
|
307
|
+
ok: true,
|
308
|
+
json: () => Promise.resolve({}),
|
309
|
+
blob: () => Promise.resolve(new Blob()),
|
310
|
+
text: () => Promise.resolve(''),
|
311
|
+
} as Response)
|
312
|
+
);
|
313
|
+
|
314
|
+
// Mock String methods to prevent format.match and format.replace errors
|
315
|
+
String.prototype.match = String.prototype.match || jest.fn(() => []);
|
316
|
+
String.prototype.replace = String.prototype.replace || jest.fn((pattern, replacement) => {
|
317
|
+
if (typeof pattern === 'string') {
|
318
|
+
return this.split(pattern).join(replacement);
|
319
|
+
}
|
320
|
+
return this;
|
321
|
+
});
|
322
|
+
|
323
|
+
// Mock FileReader
|
324
|
+
class MockFileReader {
|
325
|
+
result: any = null;
|
326
|
+
error: any = null;
|
327
|
+
readyState: number = 0;
|
328
|
+
onload: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null = null;
|
329
|
+
onerror: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null = null;
|
330
|
+
onloadend: ((this: FileReader, ev: ProgressEvent<FileReader>) => any) | null = null;
|
331
|
+
|
332
|
+
readAsDataURL = jest.fn(() => {
|
333
|
+
this.readyState = 2;
|
334
|
+
this.result = 'data:image/png;base64,test';
|
335
|
+
setTimeout(() => {
|
336
|
+
if (this.onload) this.onload({} as ProgressEvent<FileReader>);
|
337
|
+
if (this.onloadend) this.onloadend({} as ProgressEvent<FileReader>);
|
338
|
+
}, 0);
|
339
|
+
});
|
340
|
+
|
341
|
+
readAsText = jest.fn(() => {
|
342
|
+
this.readyState = 2;
|
343
|
+
this.result = 'test content';
|
344
|
+
setTimeout(() => {
|
345
|
+
if (this.onload) this.onload({} as ProgressEvent<FileReader>);
|
346
|
+
if (this.onloadend) this.onloadend({} as ProgressEvent<FileReader>);
|
347
|
+
}, 0);
|
348
|
+
});
|
349
|
+
|
350
|
+
abort = jest.fn();
|
351
|
+
addEventListener = jest.fn();
|
352
|
+
removeEventListener = jest.fn();
|
353
|
+
dispatchEvent = jest.fn();
|
354
|
+
}
|
355
|
+
|
356
|
+
global.FileReader = MockFileReader as any;
|
357
|
+
|
358
|
+
// Mock Blob
|
359
|
+
global.Blob = jest.fn().mockImplementation(() => ({
|
360
|
+
size: 1024,
|
361
|
+
type: 'image/png',
|
362
|
+
slice: jest.fn(),
|
363
|
+
stream: jest.fn(),
|
364
|
+
text: jest.fn(() => Promise.resolve('mock text')),
|
365
|
+
arrayBuffer: jest.fn(() => Promise.resolve(new ArrayBuffer(8))),
|
366
|
+
}));
|
367
|
+
|
368
|
+
// Store original createElement
|
369
|
+
const originalCreateElement = document.createElement;
|
370
|
+
|
371
|
+
// Simple mock for specific elements only when needed
|
372
|
+
document.createElement = jest.fn((tagName: string) => {
|
373
|
+
try {
|
374
|
+
return originalCreateElement.call(document, tagName);
|
375
|
+
} catch (e) {
|
376
|
+
// Fallback for special cases
|
377
|
+
const mockElement = {
|
378
|
+
tagName: tagName.toUpperCase(),
|
379
|
+
style: {},
|
380
|
+
className: '',
|
381
|
+
textContent: '',
|
382
|
+
innerHTML: '',
|
383
|
+
addEventListener: jest.fn(),
|
384
|
+
removeEventListener: jest.fn(),
|
385
|
+
dispatchEvent: jest.fn(),
|
386
|
+
setAttribute: jest.fn(),
|
387
|
+
getAttribute: jest.fn(),
|
388
|
+
removeAttribute: jest.fn(),
|
389
|
+
appendChild: jest.fn(),
|
390
|
+
removeChild: jest.fn(),
|
391
|
+
insertBefore: jest.fn(),
|
392
|
+
querySelector: jest.fn(),
|
393
|
+
querySelectorAll: jest.fn(() => []),
|
394
|
+
parentNode: null,
|
395
|
+
childNodes: [],
|
396
|
+
children: [],
|
397
|
+
};
|
398
|
+
|
399
|
+
// Add specific properties for different elements
|
400
|
+
if (tagName === 'canvas') {
|
401
|
+
Object.assign(mockElement, {
|
402
|
+
getContext: jest.fn(() => ({
|
403
|
+
drawImage: jest.fn(),
|
404
|
+
getImageData: jest.fn(),
|
405
|
+
putImageData: jest.fn(),
|
406
|
+
toDataURL: jest.fn(() => 'data:image/png;base64,test'),
|
407
|
+
})),
|
408
|
+
toDataURL: jest.fn(() => 'data:image/png;base64,test'),
|
409
|
+
width: 100,
|
410
|
+
height: 100,
|
411
|
+
});
|
412
|
+
} else if (tagName === 'a') {
|
413
|
+
Object.assign(mockElement, {
|
414
|
+
click: jest.fn(),
|
415
|
+
download: '',
|
416
|
+
href: '',
|
417
|
+
});
|
418
|
+
}
|
419
|
+
|
420
|
+
return mockElement as any;
|
421
|
+
}
|
422
|
+
});
|
423
|
+
|
424
|
+
// Store original methods
|
425
|
+
const originalAppendChild = document.body.appendChild;
|
426
|
+
const originalRemoveChild = document.body.removeChild;
|
427
|
+
|
428
|
+
// Mock but still allow actual functionality for testing
|
429
|
+
document.body.appendChild = jest.fn((node) => {
|
430
|
+
try {
|
431
|
+
// Check if node is a valid DOM element
|
432
|
+
if (node && typeof node === 'object' && node.nodeType !== undefined) {
|
433
|
+
return originalAppendChild.call(document.body, node);
|
434
|
+
}
|
435
|
+
// For non-DOM elements, just return the node
|
436
|
+
return node;
|
437
|
+
} catch (e) {
|
438
|
+
// If original fails, just return the node
|
439
|
+
return node;
|
440
|
+
}
|
441
|
+
});
|
442
|
+
|
443
|
+
document.body.removeChild = jest.fn((node) => {
|
444
|
+
try {
|
445
|
+
// Check if node is a valid DOM element
|
446
|
+
if (node && typeof node === 'object' && node.nodeType !== undefined) {
|
447
|
+
return originalRemoveChild.call(document.body, node);
|
448
|
+
}
|
449
|
+
// For non-DOM elements, just return the node
|
450
|
+
return node;
|
451
|
+
} catch (e) {
|
452
|
+
// If original fails, just return the node
|
453
|
+
return node;
|
454
|
+
}
|
455
|
+
});
|
456
|
+
|
457
|
+
// Mock getElementsByTagName to prevent ProIcon DOM access issues
|
458
|
+
const originalGetElementsByTagName = document.getElementsByTagName;
|
459
|
+
document.getElementsByTagName = jest.fn((tagName: string) => {
|
460
|
+
if (tagName === 'script') {
|
461
|
+
// Return empty collection for script tags to prevent ProIcon issues
|
462
|
+
return [];
|
463
|
+
}
|
464
|
+
try {
|
465
|
+
return originalGetElementsByTagName.call(document, tagName);
|
466
|
+
} catch (e) {
|
467
|
+
// Fallback for testing
|
468
|
+
return [];
|
469
|
+
}
|
470
|
+
});
|
471
|
+
|
472
|
+
// Mock ProIcon to prevent DOM manipulation
|
473
|
+
jest.mock('../src/ProIcon', () => ({
|
474
|
+
__esModule: true,
|
475
|
+
default: jest.fn(() => null),
|
476
|
+
ProIcon: jest.fn(() => null),
|
477
|
+
}));
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, RenderOptions } from '@testing-library/react';
|
3
|
+
import { ConfigProvider } from 'antd';
|
4
|
+
import zhCN from 'antd/locale/zh_CN';
|
5
|
+
|
6
|
+
// 确保document.body存在
|
7
|
+
const ensureDocumentSetup = () => {
|
8
|
+
if (typeof document !== 'undefined') {
|
9
|
+
if (!document.body) {
|
10
|
+
document.body = document.createElement('body');
|
11
|
+
document.documentElement.appendChild(document.body);
|
12
|
+
}
|
13
|
+
|
14
|
+
// 创建一个测试容器
|
15
|
+
let testContainer = document.getElementById('test-container');
|
16
|
+
if (!testContainer) {
|
17
|
+
testContainer = document.createElement('div');
|
18
|
+
testContainer.id = 'test-container';
|
19
|
+
document.body.appendChild(testContainer);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
};
|
23
|
+
|
24
|
+
// Mock ProConfigProvider
|
25
|
+
const MockProConfigProvider = ({ children }: { children: React.ReactNode }) => {
|
26
|
+
const mockValue = {
|
27
|
+
state: {
|
28
|
+
ProTable: {},
|
29
|
+
ProForm: {},
|
30
|
+
ProEnum: {},
|
31
|
+
ProSelect: {},
|
32
|
+
storage: {},
|
33
|
+
theme: {},
|
34
|
+
locale: {},
|
35
|
+
},
|
36
|
+
setState: jest.fn(),
|
37
|
+
};
|
38
|
+
|
39
|
+
const MockContext = React.createContext(mockValue);
|
40
|
+
return React.createElement(MockContext.Provider, { value: mockValue }, children);
|
41
|
+
};
|
42
|
+
|
43
|
+
// Mock FieldProvider
|
44
|
+
const MockFieldProvider = ({ children }: { children: React.ReactNode }) => {
|
45
|
+
const mockValue = {
|
46
|
+
readonly: false,
|
47
|
+
disabled: false,
|
48
|
+
};
|
49
|
+
|
50
|
+
const MockFieldContext = React.createContext(mockValue);
|
51
|
+
return React.createElement(MockFieldContext.Provider, { value: mockValue }, children);
|
52
|
+
};
|
53
|
+
|
54
|
+
// All providers wrapper
|
55
|
+
const AllTheProviders = ({ children }: { children: React.ReactNode }) => {
|
56
|
+
ensureDocumentSetup();
|
57
|
+
|
58
|
+
return (
|
59
|
+
<ConfigProvider locale={zhCN}>
|
60
|
+
<MockProConfigProvider>
|
61
|
+
<MockFieldProvider>
|
62
|
+
{children}
|
63
|
+
</MockFieldProvider>
|
64
|
+
</MockProConfigProvider>
|
65
|
+
</ConfigProvider>
|
66
|
+
);
|
67
|
+
};
|
68
|
+
|
69
|
+
const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => {
|
70
|
+
ensureDocumentSetup();
|
71
|
+
|
72
|
+
return render(ui, {
|
73
|
+
wrapper: AllTheProviders,
|
74
|
+
container: document.getElementById('test-container') || document.body,
|
75
|
+
...options
|
76
|
+
});
|
77
|
+
};
|
78
|
+
|
79
|
+
export * from '@testing-library/react';
|
80
|
+
export { customRender as render };
|
81
|
+
export { AllTheProviders, ensureDocumentSetup };
|
@@ -1,9 +0,0 @@
|
|
1
|
-
import { DurationInputArg1, DurationInputArg2, Moment } from 'moment';
|
2
|
-
interface Props {
|
3
|
-
range?: [Moment?, Moment?];
|
4
|
-
limit?: [DurationInputArg1, DurationInputArg2];
|
5
|
-
}
|
6
|
-
export declare const useDateLimit: (props: Props) => {
|
7
|
-
onCalendarChange: (val: any) => void;
|
8
|
-
};
|
9
|
-
export {};
|
@@ -1,15 +0,0 @@
|
|
1
|
-
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
2
|
-
import { useState } from 'react';
|
3
|
-
export var useDateLimit = function useDateLimit(props) {
|
4
|
-
var range = props.range,
|
5
|
-
limit = props.limit;
|
6
|
-
var _useState = useState(),
|
7
|
-
_useState2 = _slicedToArray(_useState, 2),
|
8
|
-
dates = _useState2[0],
|
9
|
-
setDates = _useState2[1];
|
10
|
-
return {
|
11
|
-
onCalendarChange: function onCalendarChange(val) {
|
12
|
-
return setDates(val);
|
13
|
-
}
|
14
|
-
};
|
15
|
-
};
|
@@ -1,9 +0,0 @@
|
|
1
|
-
import { DurationInputArg1, DurationInputArg2, Moment } from 'moment';
|
2
|
-
interface Props {
|
3
|
-
range?: [Moment?, Moment?];
|
4
|
-
limit?: [DurationInputArg1, DurationInputArg2];
|
5
|
-
}
|
6
|
-
export declare const useDateLimit: (props: Props) => {
|
7
|
-
onCalendarChange: (val: any) => void;
|
8
|
-
};
|
9
|
-
export {};
|
@@ -1,22 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
5
|
-
value: true
|
6
|
-
});
|
7
|
-
exports.useDateLimit = void 0;
|
8
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
9
|
-
var _react = require("react");
|
10
|
-
var useDateLimit = exports.useDateLimit = function useDateLimit(props) {
|
11
|
-
var range = props.range,
|
12
|
-
limit = props.limit;
|
13
|
-
var _useState = (0, _react.useState)(),
|
14
|
-
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
15
|
-
dates = _useState2[0],
|
16
|
-
setDates = _useState2[1];
|
17
|
-
return {
|
18
|
-
onCalendarChange: function onCalendarChange(val) {
|
19
|
-
return setDates(val);
|
20
|
-
}
|
21
|
-
};
|
22
|
-
};
|