diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 328ea622e..0631fa00c 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -11,6 +11,7 @@ const jestConfig = { '!**/node_modules/**', '!**/vendor/**', ], + setupFilesAfterEnv: ['./jest.setup.js'], }; // 只对本仓库内的 pkg 做 mapping diff --git a/packages/utils/jest.setup.js b/packages/utils/jest.setup.js new file mode 100644 index 000000000..7b0828bfa --- /dev/null +++ b/packages/utils/jest.setup.js @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/packages/utils/package.json b/packages/utils/package.json index c2433f152..67529a422 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -9,7 +9,7 @@ "main": "lib/index.js", "module": "es/index.js", "scripts": { - "test": "build-scripts test --config build.test.json", + "test": "build-scripts test --config build.test.json --jest-coverage", "build": "build-scripts build" }, "dependencies": { @@ -21,8 +21,11 @@ }, "devDependencies": { "@alib/build-scripts": "^0.1.18", + "@testing-library/jest-dom": "^6.1.4", + "@testing-library/react": "^11.2.7", "@types/node": "^13.7.1", - "@types/react": "^16" + "@types/react": "^16", + "react-dom": "^16.14.0" }, "publishConfig": { "access": "public", diff --git a/packages/utils/src/check-types/is-action-content-object.ts b/packages/utils/src/check-types/is-action-content-object.ts index 4e9a6545a..8fe31b5bd 100644 --- a/packages/utils/src/check-types/is-action-content-object.ts +++ b/packages/utils/src/check-types/is-action-content-object.ts @@ -1,5 +1,6 @@ import { IPublicTypeActionContentObject } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isActionContentObject(obj: any): obj is IPublicTypeActionContentObject { - return obj && typeof obj === 'object'; + return isObject(obj); } diff --git a/packages/utils/src/check-types/is-custom-view.ts b/packages/utils/src/check-types/is-custom-view.ts index 89ea9f10e..4cf921d9c 100644 --- a/packages/utils/src/check-types/is-custom-view.ts +++ b/packages/utils/src/check-types/is-custom-view.ts @@ -2,7 +2,9 @@ import { isValidElement } from 'react'; import { isReactComponent } from '../is-react'; import { IPublicTypeCustomView } from '@alilc/lowcode-types'; - export function isCustomView(obj: any): obj is IPublicTypeCustomView { - return obj && (isValidElement(obj) || isReactComponent(obj)); + if (!obj) { + return false; + } + return isValidElement(obj) || isReactComponent(obj); } diff --git a/packages/utils/src/check-types/is-drag-any-object.ts b/packages/utils/src/check-types/is-drag-any-object.ts index 34f140f4a..8711b4e33 100644 --- a/packages/utils/src/check-types/is-drag-any-object.ts +++ b/packages/utils/src/check-types/is-drag-any-object.ts @@ -1,5 +1,9 @@ import { IPublicEnumDragObjectType } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isDragAnyObject(obj: any): boolean { - return obj && obj.type !== IPublicEnumDragObjectType.NodeData && obj.type !== IPublicEnumDragObjectType.Node; + if (!isObject(obj)) { + return false; + } + return obj.type !== IPublicEnumDragObjectType.NodeData && obj.type !== IPublicEnumDragObjectType.Node; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-drag-node-data-object.ts b/packages/utils/src/check-types/is-drag-node-data-object.ts index 4b08f67fa..aa62f5b1c 100644 --- a/packages/utils/src/check-types/is-drag-node-data-object.ts +++ b/packages/utils/src/check-types/is-drag-node-data-object.ts @@ -1,5 +1,9 @@ import { IPublicEnumDragObjectType, IPublicTypeDragNodeDataObject } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isDragNodeDataObject(obj: any): obj is IPublicTypeDragNodeDataObject { - return obj && obj.type === IPublicEnumDragObjectType.NodeData; + if (!isObject(obj)) { + return false; + } + return obj.type === IPublicEnumDragObjectType.NodeData; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-drag-node-object.ts b/packages/utils/src/check-types/is-drag-node-object.ts index 1b6c131e9..3a29ec967 100644 --- a/packages/utils/src/check-types/is-drag-node-object.ts +++ b/packages/utils/src/check-types/is-drag-node-object.ts @@ -1,5 +1,9 @@ import { IPublicEnumDragObjectType, IPublicModelNode, IPublicTypeDragNodeObject } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isDragNodeObject(obj: any): obj is IPublicTypeDragNodeObject { - return obj && obj.type === IPublicEnumDragObjectType.Node; + if (!isObject(obj)) { + return false; + } + return obj.type === IPublicEnumDragObjectType.Node; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-dynamic-setter.ts b/packages/utils/src/check-types/is-dynamic-setter.ts index 43a8cb8be..35f8ff389 100644 --- a/packages/utils/src/check-types/is-dynamic-setter.ts +++ b/packages/utils/src/check-types/is-dynamic-setter.ts @@ -1,7 +1,10 @@ +import { isFunction } from '../is-function'; import { isReactClass } from '../is-react'; import { IPublicTypeDynamicSetter } from '@alilc/lowcode-types'; - export function isDynamicSetter(obj: any): obj is IPublicTypeDynamicSetter { - return obj && typeof obj === 'function' && !isReactClass(obj); + if (!isFunction(obj)) { + return false; + } + return !isReactClass(obj); } diff --git a/packages/utils/src/check-types/is-function.ts b/packages/utils/src/check-types/is-function.ts new file mode 100644 index 000000000..d7d3b4c27 --- /dev/null +++ b/packages/utils/src/check-types/is-function.ts @@ -0,0 +1,3 @@ +export function isFunction(obj: any): obj is Function { + return obj && typeof obj === 'function'; +} \ No newline at end of file diff --git a/packages/utils/src/check-types/is-i18n-data.ts b/packages/utils/src/check-types/is-i18n-data.ts index f4a7b6f7a..793295d24 100644 --- a/packages/utils/src/check-types/is-i18n-data.ts +++ b/packages/utils/src/check-types/is-i18n-data.ts @@ -1,8 +1,9 @@ - -// type checks - -import { IPublicTypeI18nData } from "@alilc/lowcode-types"; +import { IPublicTypeI18nData } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isI18nData(obj: any): obj is IPublicTypeI18nData { - return obj && obj.type === 'i18n'; + if (!isObject(obj)) { + return false; + } + return obj.type === 'i18n'; } diff --git a/packages/utils/src/check-types/is-isfunction.ts b/packages/utils/src/check-types/is-isfunction.ts index a6d5da900..64b867663 100644 --- a/packages/utils/src/check-types/is-isfunction.ts +++ b/packages/utils/src/check-types/is-isfunction.ts @@ -1,10 +1,26 @@ +import { IPublicTypeJSFunction } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; + +interface InnerJsFunction { + type: 'JSExpression'; + source: string; + value: string; + extType: 'function'; +} + /** * 内部版本 的 { type: 'JSExpression', source: '', value: '', extType: 'function' } 能力上等同于 JSFunction */ -export function isInnerJsFunction(data: any) { - return data && data.type === 'JSExpression' && data.extType === 'function'; +export function isInnerJsFunction(data: any): data is InnerJsFunction { + if (!isObject(data)) { + return false; + } + return data.type === 'JSExpression' && data.extType === 'function'; } -export function isJSFunction(data: any): boolean { - return typeof data === 'object' && data && data.type === 'JSFunction' || isInnerJsFunction(data); +export function isJSFunction(data: any): data is IPublicTypeJSFunction { + if (!isObject(data)) { + return false; + } + return data.type === 'JSFunction' || isInnerJsFunction(data); } diff --git a/packages/utils/src/check-types/is-jsblock.ts b/packages/utils/src/check-types/is-jsblock.ts index 4c3b28f52..858f5c09c 100644 --- a/packages/utils/src/check-types/is-jsblock.ts +++ b/packages/utils/src/check-types/is-jsblock.ts @@ -1,4 +1,9 @@ +import { IPublicTypeJSBlock } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; -export function isJSBlock(data: any): boolean { - return data && data.type === 'JSBlock'; +export function isJSBlock(data: any): data is IPublicTypeJSBlock { + if (!isObject(data)) { + return false; + } + return data.type === 'JSBlock'; } diff --git a/packages/utils/src/check-types/is-jsexpression.ts b/packages/utils/src/check-types/is-jsexpression.ts index 949d0f00d..16b8f4ac2 100644 --- a/packages/utils/src/check-types/is-jsexpression.ts +++ b/packages/utils/src/check-types/is-jsexpression.ts @@ -1,4 +1,5 @@ import { IPublicTypeJSExpression } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; /** * 为了避免把 { type: 'JSExpression', extType: 'function' } 误判为表达式,故增加如下逻辑。 @@ -11,5 +12,8 @@ import { IPublicTypeJSExpression } from '@alilc/lowcode-types'; * @returns */ export function isJSExpression(data: any): data is IPublicTypeJSExpression { - return data && data.type === 'JSExpression' && data.extType !== 'function'; + if (!isObject(data)) { + return false; + } + return data.type === 'JSExpression' && data.extType !== 'function'; } diff --git a/packages/utils/src/check-types/is-jsslot.ts b/packages/utils/src/check-types/is-jsslot.ts index 1a8f09def..1fb1d819d 100644 --- a/packages/utils/src/check-types/is-jsslot.ts +++ b/packages/utils/src/check-types/is-jsslot.ts @@ -1,5 +1,9 @@ import { IPublicTypeJSSlot } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isJSSlot(data: any): data is IPublicTypeJSSlot { - return data && data.type === 'JSSlot'; + if (!isObject(data)) { + return false; + } + return data.type === 'JSSlot'; } diff --git a/packages/utils/src/check-types/is-location-children-detail.ts b/packages/utils/src/check-types/is-location-children-detail.ts index c6d2819d3..cc093c4e4 100644 --- a/packages/utils/src/check-types/is-location-children-detail.ts +++ b/packages/utils/src/check-types/is-location-children-detail.ts @@ -1,5 +1,9 @@ import { IPublicTypeLocationChildrenDetail, IPublicTypeLocationDetailType } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isLocationChildrenDetail(obj: any): obj is IPublicTypeLocationChildrenDetail { - return obj && obj.type === IPublicTypeLocationDetailType.Children; + if (!isObject(obj)) { + return false; + } + return obj.type === IPublicTypeLocationDetailType.Children; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-location-data.ts b/packages/utils/src/check-types/is-location-data.ts index 8bb310346..dabd493fa 100644 --- a/packages/utils/src/check-types/is-location-data.ts +++ b/packages/utils/src/check-types/is-location-data.ts @@ -1,5 +1,9 @@ import { IPublicTypeLocationData } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isLocationData(obj: any): obj is IPublicTypeLocationData { - return obj && obj.target && obj.detail; + if (!isObject(obj)) { + return false; + } + return 'target' in obj && 'detail' in obj; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-lowcode-project-schema.ts b/packages/utils/src/check-types/is-lowcode-project-schema.ts index 0fffaea2f..230911f0f 100644 --- a/packages/utils/src/check-types/is-lowcode-project-schema.ts +++ b/packages/utils/src/check-types/is-lowcode-project-schema.ts @@ -1,6 +1,15 @@ -import { IPublicTypeComponentSchema, IPublicTypeProjectSchema } from "@alilc/lowcode-types"; -import { isComponentSchema } from "./is-component-schema"; +import { IPublicTypeComponentSchema, IPublicTypeProjectSchema } from '@alilc/lowcode-types'; +import { isComponentSchema } from './is-component-schema'; +import { isObject } from '../is-object'; export function isLowcodeProjectSchema(data: any): data is IPublicTypeProjectSchema { - return data && data.componentsTree && data.componentsTree.length && isComponentSchema(data.componentsTree[0]); + if (!isObject(data)) { + return false; + } + + if (!('componentsTree' in data) || data.componentsTree.length === 0) { + return false; + } + + return isComponentSchema(data.componentsTree[0]); } diff --git a/packages/utils/src/check-types/is-node-schema.ts b/packages/utils/src/check-types/is-node-schema.ts index bfc3ff3f2..253c05a08 100644 --- a/packages/utils/src/check-types/is-node-schema.ts +++ b/packages/utils/src/check-types/is-node-schema.ts @@ -1,5 +1,9 @@ import { IPublicTypeNodeSchema } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isNodeSchema(data: any): data is IPublicTypeNodeSchema { - return data && data.componentName && !data.isNode; + if (!isObject(data)) { + return false; + } + return 'componentName' in data && !data.isNode; } diff --git a/packages/utils/src/check-types/is-node.ts b/packages/utils/src/check-types/is-node.ts index 14c2e2f74..b4690ddff 100644 --- a/packages/utils/src/check-types/is-node.ts +++ b/packages/utils/src/check-types/is-node.ts @@ -1,5 +1,9 @@ import { IPublicModelNode } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isNode(node: any): node is Node { - return node && node.isNode; + if (!isObject(node)) { + return false; + } + return node.isNode; } \ No newline at end of file diff --git a/packages/utils/src/check-types/is-object.ts b/packages/utils/src/check-types/is-object.ts new file mode 100644 index 000000000..56ceb7d97 --- /dev/null +++ b/packages/utils/src/check-types/is-object.ts @@ -0,0 +1,3 @@ +export function isObject(obj: any): boolean { + return obj && typeof obj === 'object'; +} \ No newline at end of file diff --git a/packages/utils/src/check-types/is-procode-component-type.ts b/packages/utils/src/check-types/is-procode-component-type.ts index 5c768dd94..46618dcd5 100644 --- a/packages/utils/src/check-types/is-procode-component-type.ts +++ b/packages/utils/src/check-types/is-procode-component-type.ts @@ -1,6 +1,10 @@ import { IPublicTypeComponentMap, IPublicTypeProCodeComponent } from '@alilc/lowcode-types'; - +import { isObject } from '../is-object'; export function isProCodeComponentType(desc: IPublicTypeComponentMap): desc is IPublicTypeProCodeComponent { + if (!isObject(desc)) { + return false; + } + return 'package' in desc; } diff --git a/packages/utils/src/check-types/is-project-schema.ts b/packages/utils/src/check-types/is-project-schema.ts index b228d481e..d217acd9e 100644 --- a/packages/utils/src/check-types/is-project-schema.ts +++ b/packages/utils/src/check-types/is-project-schema.ts @@ -1,5 +1,9 @@ -import { IPublicTypeProjectSchema } from "@alilc/lowcode-types"; +import { IPublicTypeProjectSchema } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isProjectSchema(data: any): data is IPublicTypeProjectSchema { - return data && data.componentsTree; + if (!isObject(data)) { + return false; + } + return 'componentsTree' in data; } diff --git a/packages/utils/src/check-types/is-setter-config.ts b/packages/utils/src/check-types/is-setter-config.ts index d631d237f..98d835f32 100644 --- a/packages/utils/src/check-types/is-setter-config.ts +++ b/packages/utils/src/check-types/is-setter-config.ts @@ -1,7 +1,10 @@ import { IPublicTypeSetterConfig } from '@alilc/lowcode-types'; import { isCustomView } from './is-custom-view'; - +import { isObject } from '../is-object'; export function isSetterConfig(obj: any): obj is IPublicTypeSetterConfig { - return obj && typeof obj === 'object' && 'componentName' in obj && !isCustomView(obj); + if (!isObject(obj)) { + return false; + } + return 'componentName' in obj && !isCustomView(obj); } diff --git a/packages/utils/src/check-types/is-setting-field.ts b/packages/utils/src/check-types/is-setting-field.ts index 67a183a95..0d6e21d84 100644 --- a/packages/utils/src/check-types/is-setting-field.ts +++ b/packages/utils/src/check-types/is-setting-field.ts @@ -1,5 +1,10 @@ -import { IPublicModelSettingField } from "@alilc/lowcode-types"; +import { IPublicModelSettingField } from '@alilc/lowcode-types'; +import { isObject } from '../is-object'; export function isSettingField(obj: any): obj is IPublicModelSettingField { - return obj && obj.isSettingField; + if (!isObject(obj)) { + return false; + } + + return 'isSettingField' in obj && obj.isSettingField; } diff --git a/packages/utils/src/clone-enumerable-property.ts b/packages/utils/src/clone-enumerable-property.ts index 414f8dccd..eb09e177f 100644 --- a/packages/utils/src/clone-enumerable-property.ts +++ b/packages/utils/src/clone-enumerable-property.ts @@ -11,8 +11,8 @@ const excludePropertyNames = [ 'arguments', ]; -export function cloneEnumerableProperty(target: any, origin: any) { - const compExtraPropertyNames = Object.keys(origin).filter(d => !excludePropertyNames.includes(d)); +export function cloneEnumerableProperty(target: any, origin: any, excludes = excludePropertyNames) { + const compExtraPropertyNames = Object.keys(origin).filter(d => !excludes.includes(d)); compExtraPropertyNames.forEach((d: string) => { (target as any)[d] = origin[d]; diff --git a/packages/utils/src/is-object.ts b/packages/utils/src/is-object.ts index 50b580e5a..c8d764458 100644 --- a/packages/utils/src/is-object.ts +++ b/packages/utils/src/is-object.ts @@ -1,4 +1,4 @@ -export function isObject(value: any): value is Record { +export function isObject(value: any): value is Record { return value !== null && typeof value === 'object'; } diff --git a/packages/utils/src/is-react.ts b/packages/utils/src/is-react.ts index b19f043f1..1d6c939ea 100644 --- a/packages/utils/src/is-react.ts +++ b/packages/utils/src/is-react.ts @@ -2,27 +2,49 @@ import { ComponentClass, Component, FunctionComponent, ComponentType, createElem import { cloneEnumerableProperty } from './clone-enumerable-property'; const hasSymbol = typeof Symbol === 'function' && Symbol.for; -const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; -const REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; +export const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; +export const REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; export function isReactClass(obj: any): obj is ComponentClass { - return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component); + if (!obj) { + return false; + } + if (obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component)) { + return true; + } + return false; } export function acceptsRef(obj: any): boolean { - return obj?.prototype?.isReactComponent || isForwardOrMemoForward(obj); + if (!obj) { + return false; + } + if (obj?.prototype?.isReactComponent || isForwardOrMemoForward(obj)) { + return true; + } + + return false; } export function isForwardRefType(obj: any): boolean { - return obj?.$$typeof && obj?.$$typeof === REACT_FORWARD_REF_TYPE; + if (!obj || !obj?.$$typeof) { + return false; + } + return obj?.$$typeof === REACT_FORWARD_REF_TYPE; } -function isMemoType(obj: any): boolean { - return obj?.$$typeof && obj.$$typeof === REACT_MEMO_TYPE; +export function isMemoType(obj: any): boolean { + if (!obj || !obj?.$$typeof) { + return false; + } + return obj.$$typeof === REACT_MEMO_TYPE; } export function isForwardOrMemoForward(obj: any): boolean { - return obj?.$$typeof && ( + if (!obj || !obj?.$$typeof) { + return false; + } + return ( // React.forwardRef(..) isForwardRefType(obj) || // React.memo(React.forwardRef(..)) diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index 4c8f375ec..3eb43eedb 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -88,7 +88,7 @@ const shouldOutput = ( const output = (logLevel: string, bizName: string) => { return (...args: any[]) => { - return outputFunction[logLevel].apply(console, getLogArgs(args, bizName, logLevel)); + return outputFunction[logLevel]?.apply(console, getLogArgs(args, bizName, logLevel)); }; }; @@ -142,7 +142,6 @@ const defaultOptions: Options = { bizName: '*', }; - class Logger { bizName: string; targetBizName: string; @@ -192,7 +191,6 @@ class Logger { } } - export { Logger }; export function getLogger(config: { level: Level; bizName: string }): Logger { diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 89f121065..28833ef32 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -2,6 +2,9 @@ import { isI18NObject } from './is-object'; import { get } from 'lodash'; import { IPublicEnumTransformStage, IPublicModelComponentMeta } from '@alilc/lowcode-types'; +import { Logger } from './logger'; + +const logger = new Logger({ level: 'warn', bizName: 'utils' }); interface Variable { type: 'variable'; @@ -10,7 +13,10 @@ interface Variable { } export function isVariable(obj: any): obj is Variable { - return obj && obj.type === 'variable'; + if (!obj || typeof obj !== 'object') { + return false; + } + return obj.type === 'variable'; } export function isUseI18NSetter(prototype: any, propName: string) { @@ -103,12 +109,15 @@ export function invariant(check: any, message: string, thing?: any) { export function deprecate(fail: any, message: string, alterative?: string) { if (fail) { - console.warn(`Deprecation: ${message}` + (alterative ? `, use ${alterative} instead.` : '')); + logger.warn(`Deprecation: ${message}` + (alterative ? `, use ${alterative} instead.` : '')); } } export function isRegExp(obj: any): obj is RegExp { - return obj && obj.test && obj.exec && obj.compile; + if (!obj || typeof obj !== 'object') { + return false; + } + return 'test' in obj && 'exec' in obj && 'compile' in obj; } /** diff --git a/packages/utils/src/navtive-selection.ts b/packages/utils/src/navtive-selection.ts index 76f51f48a..b8e525773 100644 --- a/packages/utils/src/navtive-selection.ts +++ b/packages/utils/src/navtive-selection.ts @@ -1,4 +1,5 @@ -let nativeSelectionEnabled = true; +export let nativeSelectionEnabled = true; + const preventSelection = (e: Event) => { if (nativeSelectionEnabled) { return null; diff --git a/packages/utils/src/script.ts b/packages/utils/src/script.ts index 25159b198..c4c476fac 100644 --- a/packages/utils/src/script.ts +++ b/packages/utils/src/script.ts @@ -1,4 +1,7 @@ import { createDefer } from './create-defer'; +import { Logger } from './logger'; + +const logger = new Logger({ level: 'warn', bizName: 'utils' }); export function evaluate(script: string, scriptType?: string) { const scriptEl = document.createElement('script'); @@ -53,7 +56,7 @@ export function newFunction(args: string, code: string) { // eslint-disable-next-line no-new-func return new Function(args, code); } catch (e) { - console.warn('Caught error, Cant init func'); + logger.warn('Caught error, Cant init func'); return null; } } diff --git a/packages/utils/src/svg-icon.tsx b/packages/utils/src/svg-icon.tsx index f75724b06..2513f7bca 100644 --- a/packages/utils/src/svg-icon.tsx +++ b/packages/utils/src/svg-icon.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import React, { ReactNode } from 'react'; const SizePresets: any = { xsmall: 8, diff --git a/packages/utils/test/src/__snapshots__/is-react.test.tsx.snap b/packages/utils/test/src/__snapshots__/is-react.test.tsx.snap new file mode 100644 index 000000000..14ef39453 --- /dev/null +++ b/packages/utils/test/src/__snapshots__/is-react.test.tsx.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`wrapReactClass should render the FunctionComponent with props 1`] = ` + + Child Text + +`; diff --git a/packages/utils/test/src/build-components/buildComponents.test.ts b/packages/utils/test/src/build-components/buildComponents.test.ts deleted file mode 100644 index e854890da..000000000 --- a/packages/utils/test/src/build-components/buildComponents.test.ts +++ /dev/null @@ -1,342 +0,0 @@ - -import { buildComponents } from "../../../src/build-components"; - -function Button() {}; - -function WrapButton() {}; - -function ButtonGroup() {}; - -function WrapButtonGroup() {}; - -ButtonGroup.Button = Button; - -Button.displayName = "Button"; -ButtonGroup.displayName = "ButtonGroup"; -ButtonGroup.prototype.isReactComponent = true; -Button.prototype.isReactComponent = true; - -jest.mock('../../../src/is-react', () => { - const original = jest.requireActual('../../../src/is-react'); - return { - ...original, - wrapReactClass(view) { - return view; - } - } -}) - -describe('build-component', () => { - it('basic button', () => { - expect( - buildComponents( - { - '@alilc/button': { - Button, - } - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - destructuring: true, - exportName: 'Button', - subName: 'Button', - } - }, - () => {}, - )) - .toEqual({ - Button, - }); - }); - - it('component is a __esModule', () => { - expect( - buildComponents( - { - '@alilc/button': { - __esModule: true, - default: Button, - } - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - } - }, - () => {}, - )) - .toEqual({ - Button, - }); - }) - - it('basic warp button', () => { - expect( - buildComponents( - { - '@alilc/button': { - WrapButton, - } - }, - { - WrapButton: { - componentName: 'WrapButton', - package: '@alilc/button', - destructuring: true, - exportName: 'WrapButton', - subName: 'WrapButton', - } - }, - () => {}, - )) - .toEqual({ - WrapButton, - }); - }); - - it('destructuring is false button', () => { - expect( - buildComponents( - { - '@alilc/button': Button - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - destructuring: false, - } - }, - () => {}, - )) - .toEqual({ - Button, - }); - }); - - it('Button and ButtonGroup', () => { - expect( - buildComponents( - { - '@alilc/button': { - Button, - ButtonGroup, - } - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - destructuring: true, - exportName: 'Button', - subName: 'Button', - }, - ButtonGroup: { - componentName: 'ButtonGroup', - package: '@alilc/button', - destructuring: true, - exportName: 'ButtonGroup', - subName: 'ButtonGroup', - } - }, - () => {}, - )) - .toEqual({ - Button, - ButtonGroup, - }); - }); - - it('ButtonGroup and ButtonGroup.Button', () => { - expect( - buildComponents( - { - '@alilc/button': { - ButtonGroup, - } - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - destructuring: true, - exportName: 'ButtonGroup', - subName: 'ButtonGroup.Button', - }, - ButtonGroup: { - componentName: 'ButtonGroup', - package: '@alilc/button', - destructuring: true, - exportName: 'ButtonGroup', - subName: 'ButtonGroup', - } - }, - () => {}, - )) - .toEqual({ - Button, - ButtonGroup, - }); - }); - - it('ButtonGroup.default and ButtonGroup.Button', () => { - expect( - buildComponents( - { - '@alilc/button': ButtonGroup, - }, - { - Button: { - componentName: 'Button', - package: '@alilc/button', - destructuring: true, - exportName: 'Button', - subName: 'Button', - }, - ButtonGroup: { - componentName: 'ButtonGroup', - package: '@alilc/button', - destructuring: true, - exportName: 'default', - subName: 'default', - } - }, - () => {}, - )) - .toEqual({ - Button, - ButtonGroup, - }); - }); - - it('no npm component', () => { - expect( - buildComponents( - { - '@alilc/button': Button, - }, - { - Button: null, - }, - () => {}, - )) - .toEqual({}); - }); - - it('no npm component and global button', () => { - window.Button = Button; - expect( - buildComponents( - {}, - { - Button: null, - }, - () => {}, - )) - .toEqual({ - Button, - }); - window.Button = null; - }); - - it('componentsMap value is component funtion', () => { - expect( - buildComponents( - {}, - { - Button, - }, - () => {}, - )) - .toEqual({ - Button, - }); - }); - - - it('componentsMap value is component', () => { - expect( - buildComponents( - {}, - { - Button: WrapButton, - }, - () => {}, - )) - .toEqual({ - Button: WrapButton, - }); - }); - - it('componentsMap value is mix component', () => { - expect( - buildComponents( - {}, - { - Button: { - WrapButton, - Button, - ButtonGroup, - }, - }, - () => {}, - )) - .toEqual({ - Button: { - WrapButton, - Button, - ButtonGroup, - }, - }); - }); - - it('componentsMap value is Lowcode Component', () => { - expect( - buildComponents( - {}, - { - Button: { - componentName: 'Component', - schema: {}, - }, - }, - (component) => { - return component as any; - }, - )) - .toEqual({ - Button: { - componentsMap: [], - componentsTree: [ - { - componentName: 'Component', - schema: {}, - } - ], - version: "", - }, - }); - }) -}); - -describe('build div component', () => { - it('build div component', () => { - const components = buildComponents( - { - '@alilc/div': 'div' - }, - { - div: { - componentName: 'div', - package: '@alilc/div' - } - }, - () => {}, - ); - - expect(components['div']).not.toBeNull(); - }) -}) \ No newline at end of file diff --git a/packages/utils/test/src/build-components/buildComponents.test.tsx b/packages/utils/test/src/build-components/buildComponents.test.tsx new file mode 100644 index 000000000..a50a68b39 --- /dev/null +++ b/packages/utils/test/src/build-components/buildComponents.test.tsx @@ -0,0 +1,616 @@ +import React from 'react'; +import { + accessLibrary, + generateHtmlComp, + getSubComponent, + buildComponents, + getProjectUtils, +} from "../../../src/build-components"; + +function Button() {}; + +function WrapButton() {}; + +function ButtonGroup() {}; + +function WrapButtonGroup() {}; + +ButtonGroup.Button = Button; + +Button.displayName = "Button"; +ButtonGroup.displayName = "ButtonGroup"; +ButtonGroup.prototype.isReactComponent = true; +Button.prototype.isReactComponent = true; + +jest.mock('../../../src/is-react', () => { + const original = jest.requireActual('../../../src/is-react'); + return { + ...original, + wrapReactClass(view) { + return view; + } + } +}); + +describe('accessLibrary', () => { + it('should return a library object when given a library object', () => { + const libraryObject = { key: 'value' }; + const result = accessLibrary(libraryObject); + expect(result).toEqual(libraryObject); + }); + + it('should generate an HTML component when given a string library name', () => { + const libraryName = 'div'; + const result = accessLibrary(libraryName); + + // You can write more specific assertions to validate the generated component + expect(result).toBeDefined(); + }); + + // Add more test cases to cover other scenarios +}); + +describe('generateHtmlComp', () => { + it('should generate an HTML component for valid HTML tags', () => { + const htmlTags = ['a', 'img', 'div', 'span', 'svg']; + htmlTags.forEach((tag) => { + const result = generateHtmlComp(tag); + + // You can write more specific assertions to validate the generated component + expect(result).toBeDefined(); + }); + }); + + it('should return undefined for an invalid HTML tag', () => { + const invalidTag = 'invalidtag'; + const result = generateHtmlComp(invalidTag); + expect(result).toBeUndefined(); + }); + + // Add more test cases to cover other scenarios +}); + +describe('getSubComponent', () => { + it('should return the root library if paths are empty', () => { + const library = { component: 'RootComponent' }; + const paths = []; + const result = getSubComponent(library, paths); + expect(result).toEqual(library); + }); + + it('should return the specified sub-component', () => { + const library = { + components: { + Button: 'ButtonComponent', + Text: 'TextComponent', + }, + }; + const paths = ['components', 'Button']; + const result = getSubComponent(library, paths); + expect(result).toEqual('ButtonComponent'); + }); + + it('should handle missing keys in the path', () => { + const library = { + components: { + Button: 'ButtonComponent', + }, + }; + const paths = ['components', 'Text']; + const result = getSubComponent(library, paths); + expect(result).toEqual({ + Button: 'ButtonComponent', + }); + }); + + it('should handle exceptions and return null', () => { + const library = 'ButtonComponent'; + const paths = ['components', 'Button']; + // Simulate an exception by providing a non-object in place of 'ButtonComponent' + const result = getSubComponent(library, paths); + expect(result).toBeNull(); + }); + + it('should handle the "default" key as the first path element', () => { + const library = { + default: 'DefaultComponent', + }; + const paths = ['default']; + const result = getSubComponent(library, paths); + expect(result).toEqual('DefaultComponent'); + }); +}); + +describe('getProjectUtils', () => { + it('should return an empty object when given empty metadata and library map', () => { + const libraryMap = {}; + const utilsMetadata = []; + const result = getProjectUtils(libraryMap, utilsMetadata); + expect(result).toEqual({}); + }); + + it('should return project utilities based on metadata and library map', () => { + const libraryMap = { + 'package1': 'library1', + 'package2': 'library2', + }; + + const utilsMetadata = [ + { + name: 'util1', + npm: { + package: 'package1', + }, + }, + { + name: 'util2', + npm: { + package: 'package2', + }, + }, + ]; + + global['library1'] = { name: 'library1' }; + global['library2'] = { name: 'library2' }; + + const result = getProjectUtils(libraryMap, utilsMetadata); + + // Define the expected output based on the mocked accessLibrary + const expectedOutput = { + 'util1': { name: 'library1' }, + 'util2': { name: 'library2' }, + }; + + expect(result).toEqual(expectedOutput); + + global['library1'] = null; + global['library1'] = null; + }); + + it('should handle metadata with destructuring', () => { + const libraryMap = { + 'package1': { destructuring: true, util1: 'library1', util2: 'library2' }, + }; + + const utilsMetadata = [ + { + name: 'util1', + npm: { + package: 'package1', + destructuring: true, + }, + }, + ]; + + const result = getProjectUtils(libraryMap, utilsMetadata); + + // Define the expected output based on the mocked accessLibrary + const expectedOutput = { + 'util1': 'library1', + 'util2': 'library2', + }; + + expect(result).toEqual(expectedOutput); + }); +}); + +describe('buildComponents', () => { + it('should create components from component map with React components', () => { + const libraryMap = {}; + const componentsMap = { + Button: () => , + Text: () =>

Text

, + }; + + const createComponent = (schema) => { + // Mock createComponent function + return schema.componentsTree.map((component) => component.component); + }; + + const result = buildComponents(libraryMap, componentsMap, createComponent); + + expect(result.Button).toBeDefined(); + expect(result.Text).toBeDefined(); + }); + + it('should create components from component map with component schemas', () => { + const libraryMap = {}; + const componentsMap = { + Button: { + componentsTree: [ + { + componentName: 'Component' + } + ] + }, + Text: { + componentsTree: [ + { + componentName: 'Component' + } + ] + }, + }; + + const createComponent = (schema) => { + // Mock createComponent function + return schema.componentsTree.map((component) => component.component); + }; + + const result = buildComponents(libraryMap, componentsMap, createComponent); + + expect(result.Button).toBeDefined(); + expect(result.Text).toBeDefined(); + }); + + it('should create components from component map with React components and schemas', () => { + const libraryMap = {}; + const componentsMap = { + Button: () => , + Text: { + type: 'ComponentSchema', + // Add component schema properties here + }, + }; + + const createComponent = (schema) => { + // Mock createComponent function + return schema.componentsTree.map((component) => component.component); + }; + + const result = buildComponents(libraryMap, componentsMap, createComponent); + + expect(result.Button).toBeDefined(); + expect(result.Text).toBeDefined(); + }); + + it('should create components from component map with library mappings', () => { + const libraryMap = { + 'libraryName1': 'library1', + 'libraryName2': 'library2', + }; + const componentsMap = { + Button: { + package: 'libraryName1', + version: '1.0', + exportName: 'ButtonComponent', + }, + Text: { + package: 'libraryName2', + version: '2.0', + exportName: 'TextComponent', + }, + }; + + const createComponent = (schema) => { + // Mock createComponent function + return schema.componentsTree.map((component) => component.component); + }; + + global['library1'] = () => ; + global['library2'] = () => () =>

TextComponent

; + + const result = buildComponents(libraryMap, componentsMap, createComponent); + + expect(result.Button).toBeDefined(); + expect(result.Text).toBeDefined(); + + global['library1'] = null; + global['library2'] = null; + }); +}); + +describe('build-component', () => { + it('basic button', () => { + expect( + buildComponents( + { + '@alilc/button': { + Button, + } + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + destructuring: true, + exportName: 'Button', + subName: 'Button', + } + }, + () => {}, + )) + .toEqual({ + Button, + }); + }); + + it('component is a __esModule', () => { + expect( + buildComponents( + { + '@alilc/button': { + __esModule: true, + default: Button, + } + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + } + }, + () => {}, + )) + .toEqual({ + Button, + }); + }) + + it('basic warp button', () => { + expect( + buildComponents( + { + '@alilc/button': { + WrapButton, + } + }, + { + WrapButton: { + componentName: 'WrapButton', + package: '@alilc/button', + destructuring: true, + exportName: 'WrapButton', + subName: 'WrapButton', + } + }, + () => {}, + )) + .toEqual({ + WrapButton, + }); + }); + + it('destructuring is false button', () => { + expect( + buildComponents( + { + '@alilc/button': Button + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + destructuring: false, + } + }, + () => {}, + )) + .toEqual({ + Button, + }); + }); + + it('Button and ButtonGroup', () => { + expect( + buildComponents( + { + '@alilc/button': { + Button, + ButtonGroup, + } + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + destructuring: true, + exportName: 'Button', + subName: 'Button', + }, + ButtonGroup: { + componentName: 'ButtonGroup', + package: '@alilc/button', + destructuring: true, + exportName: 'ButtonGroup', + subName: 'ButtonGroup', + } + }, + () => {}, + )) + .toEqual({ + Button, + ButtonGroup, + }); + }); + + it('ButtonGroup and ButtonGroup.Button', () => { + expect( + buildComponents( + { + '@alilc/button': { + ButtonGroup, + } + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + destructuring: true, + exportName: 'ButtonGroup', + subName: 'ButtonGroup.Button', + }, + ButtonGroup: { + componentName: 'ButtonGroup', + package: '@alilc/button', + destructuring: true, + exportName: 'ButtonGroup', + subName: 'ButtonGroup', + } + }, + () => {}, + )) + .toEqual({ + Button, + ButtonGroup, + }); + }); + + it('ButtonGroup.default and ButtonGroup.Button', () => { + expect( + buildComponents( + { + '@alilc/button': ButtonGroup, + }, + { + Button: { + componentName: 'Button', + package: '@alilc/button', + destructuring: true, + exportName: 'Button', + subName: 'Button', + }, + ButtonGroup: { + componentName: 'ButtonGroup', + package: '@alilc/button', + destructuring: true, + exportName: 'default', + subName: 'default', + } + }, + () => {}, + )) + .toEqual({ + Button, + ButtonGroup, + }); + }); + + it('no npm component', () => { + expect( + buildComponents( + { + '@alilc/button': Button, + }, + { + Button: null, + }, + () => {}, + )) + .toEqual({}); + }); + + it('no npm component and global button', () => { + window.Button = Button; + expect( + buildComponents( + {}, + { + Button: null, + }, + () => {}, + )) + .toEqual({ + Button, + }); + window.Button = null; + }); + + it('componentsMap value is component funtion', () => { + expect( + buildComponents( + {}, + { + Button, + }, + () => {}, + )) + .toEqual({ + Button, + }); + }); + + + it('componentsMap value is component', () => { + expect( + buildComponents( + {}, + { + Button: WrapButton, + }, + () => {}, + )) + .toEqual({ + Button: WrapButton, + }); + }); + + it('componentsMap value is mix component', () => { + expect( + buildComponents( + {}, + { + Button: { + WrapButton, + Button, + ButtonGroup, + }, + }, + () => {}, + )) + .toEqual({ + Button: { + WrapButton, + Button, + ButtonGroup, + }, + }); + }); + + it('componentsMap value is Lowcode Component', () => { + expect( + buildComponents( + {}, + { + Button: { + componentName: 'Component', + schema: {}, + }, + }, + (component) => { + return component as any; + }, + )) + .toEqual({ + Button: { + componentsMap: [], + componentsTree: [ + { + componentName: 'Component', + schema: {}, + } + ], + version: "", + }, + }); + }) +}); + +describe('build div component', () => { + it('build div component', () => { + const components = buildComponents( + { + '@alilc/div': 'div' + }, + { + div: { + componentName: 'div', + package: '@alilc/div' + } + }, + () => {}, + ); + + expect(components['div']).not.toBeNull(); + }) +}) \ No newline at end of file diff --git a/packages/utils/test/src/check-types/is-action-content-object.test.ts b/packages/utils/test/src/check-types/is-action-content-object.test.ts new file mode 100644 index 000000000..08b95788d --- /dev/null +++ b/packages/utils/test/src/check-types/is-action-content-object.test.ts @@ -0,0 +1,20 @@ +import { isActionContentObject } from '../../../src/check-types/is-action-content-object'; + +describe('isActionContentObject', () => { + test('should return true for an object', () => { + const obj = { prop: 'value' }; + expect(isActionContentObject(obj)).toBe(true); + }); + + test('should return false for a non-object', () => { + expect(isActionContentObject('not an object')).toBe(false); + expect(isActionContentObject(123)).toBe(false); + expect(isActionContentObject(null)).toBe(false); + expect(isActionContentObject(undefined)).toBe(false); + }); + + test('should return false for an empty object', () => { + const obj = {}; + expect(isActionContentObject(obj)).toBe(true); + }); +}); diff --git a/packages/utils/test/src/check-types/is-custom-view.test.tsx b/packages/utils/test/src/check-types/is-custom-view.test.tsx new file mode 100644 index 000000000..62c08780e --- /dev/null +++ b/packages/utils/test/src/check-types/is-custom-view.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { isCustomView } from '../../../src/check-types/is-custom-view'; +import { IPublicTypeCustomView } from '@alilc/lowcode-types'; + +describe('isCustomView', () => { + test('should return true when obj is a valid React element', () => { + const obj: IPublicTypeCustomView =
Hello, World!
; + expect(isCustomView(obj)).toBe(true); + }); + + test('should return true when obj is a valid React component', () => { + const MyComponent: React.FC = () =>
Hello, World!
; + const obj: IPublicTypeCustomView = MyComponent; + expect(isCustomView(obj)).toBe(true); + }); + + test('should return false when obj is null or undefined', () => { + expect(isCustomView(null)).toBe(false); + expect(isCustomView(undefined)).toBe(false); + }); + + test('should return false when obj is not a valid React element or component', () => { + const obj: IPublicTypeCustomView = 'not a valid object'; + expect(isCustomView(obj)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-dom-text.test.ts b/packages/utils/test/src/check-types/is-dom-text.test.ts new file mode 100644 index 000000000..50dce0fb7 --- /dev/null +++ b/packages/utils/test/src/check-types/is-dom-text.test.ts @@ -0,0 +1,13 @@ +import { isDOMText } from '../../../src/check-types/is-dom-text'; + +describe('isDOMText', () => { + it('should return true when the input is a string', () => { + const result = isDOMText('Hello World'); + expect(result).toBe(true); + }); + + it('should return false when the input is not a string', () => { + const result = isDOMText(123); + expect(result).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-drag-any-object.test.ts b/packages/utils/test/src/check-types/is-drag-any-object.test.ts new file mode 100644 index 000000000..6a835f2be --- /dev/null +++ b/packages/utils/test/src/check-types/is-drag-any-object.test.ts @@ -0,0 +1,32 @@ +import { isDragAnyObject } from '../../../src/check-types/is-drag-any-object'; +import { IPublicEnumDragObjectType } from '@alilc/lowcode-types'; + +describe('isDragAnyObject', () => { + it('should return false if obj is null', () => { + const result = isDragAnyObject(null); + expect(result).toBe(false); + }); + + it('should return false if obj is number', () => { + const result = isDragAnyObject(2); + expect(result).toBe(false); + }); + + it('should return false if obj.type is NodeData', () => { + const obj = { type: IPublicEnumDragObjectType.NodeData }; + const result = isDragAnyObject(obj); + expect(result).toBe(false); + }); + + it('should return false if obj.type is Node', () => { + const obj = { type: IPublicEnumDragObjectType.Node }; + const result = isDragAnyObject(obj); + expect(result).toBe(false); + }); + + it('should return true if obj.type is anything else', () => { + const obj = { type: 'SomeOtherType' }; + const result = isDragAnyObject(obj); + expect(result).toBe(true); + }); +}); diff --git a/packages/utils/test/src/check-types/is-drag-node-data-object.test.ts b/packages/utils/test/src/check-types/is-drag-node-data-object.test.ts new file mode 100644 index 000000000..92867843a --- /dev/null +++ b/packages/utils/test/src/check-types/is-drag-node-data-object.test.ts @@ -0,0 +1,29 @@ +import { IPublicEnumDragObjectType, IPublicTypeDragNodeDataObject } from '@alilc/lowcode-types'; +import { isDragNodeDataObject } from '../../../src/check-types/is-drag-node-data-object'; + +describe('isDragNodeDataObject', () => { + test('should return true for valid IPublicTypeDragNodeDataObject', () => { + const obj: IPublicTypeDragNodeDataObject = { + type: IPublicEnumDragObjectType.NodeData, + // 其他属性... + }; + + expect(isDragNodeDataObject(obj)).toBe(true); + }); + + test('should return false for invalid IPublicTypeDragNodeDataObject', () => { + const obj: any = { + type: 'InvalidType', + // 其他属性... + }; + + expect(isDragNodeDataObject(obj)).toBe(false); + }); + + test('should return false for null or undefined', () => { + expect(isDragNodeDataObject(null)).toBe(false); + expect(isDragNodeDataObject(undefined)).toBe(false); + }); + + // 可以添加更多测试用例... +}); diff --git a/packages/utils/test/src/check-types/is-drag-node-object.test.ts b/packages/utils/test/src/check-types/is-drag-node-object.test.ts new file mode 100644 index 000000000..3561c8788 --- /dev/null +++ b/packages/utils/test/src/check-types/is-drag-node-object.test.ts @@ -0,0 +1,36 @@ +import { IPublicEnumDragObjectType } from '@alilc/lowcode-types'; +import { isDragNodeObject } from '../../../src/check-types/is-drag-node-object'; + +describe('isDragNodeObject', () => { + it('should return true if the object is of IPublicTypeDragNodeObject type and has type IPublicEnumDragObjectType.Node', () => { + const obj = { + type: IPublicEnumDragObjectType.Node, + //... other properties + }; + + expect(isDragNodeObject(obj)).toBe(true); + }); + + it('should return false if the object is not of IPublicTypeDragNodeObject type', () => { + const obj = { + type: IPublicEnumDragObjectType.OtherType, + //... other properties + }; + + expect(isDragNodeObject(obj)).toBe(false); + }); + + it('should return false if the object is of IPublicTypeDragNodeObject type but type is not IPublicEnumDragObjectType.Node', () => { + const obj = { + type: IPublicEnumDragObjectType.OtherType, + //... other properties + }; + + expect(isDragNodeObject(obj)).toBe(false); + }); + + it('should return false if the object is null or undefined', () => { + expect(isDragNodeObject(null)).toBe(false); + expect(isDragNodeObject(undefined)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-dynamic-setter.test.ts b/packages/utils/test/src/check-types/is-dynamic-setter.test.ts new file mode 100644 index 000000000..72f55367d --- /dev/null +++ b/packages/utils/test/src/check-types/is-dynamic-setter.test.ts @@ -0,0 +1,28 @@ +import { Component } from 'react'; +import { isDynamicSetter } from '../../../src/check-types/is-dynamic-setter'; + +describe('isDynamicSetter', () => { + it('returns true if input is a dynamic setter function', () => { + const dynamicSetter = (value: any) => { + // some implementation + }; + + expect(isDynamicSetter(dynamicSetter)).toBeTruthy(); + }); + + it('returns false if input is not a dynamic setter function', () => { + expect(isDynamicSetter('not a function')).toBeFalsy(); + expect(isDynamicSetter(null)).toBeFalsy(); + expect(isDynamicSetter(undefined)).toBeFalsy(); + expect(isDynamicSetter(2)).toBeFalsy(); + expect(isDynamicSetter(0)).toBeFalsy(); + }); + + it('returns false if input is a React class', () => { + class ReactClass extends Component { + // some implementation + } + + expect(isDynamicSetter(ReactClass)).toBeFalsy(); + }); +}); diff --git a/packages/utils/test/src/check-types/is-i18n-data.test.ts b/packages/utils/test/src/check-types/is-i18n-data.test.ts new file mode 100644 index 000000000..2e903a2ed --- /dev/null +++ b/packages/utils/test/src/check-types/is-i18n-data.test.ts @@ -0,0 +1,27 @@ +import { isI18nData } from '../../../src/check-types/is-i18n-data'; +import { IPublicTypeI18nData } from "@alilc/lowcode-types"; + +describe('isI18nData', () => { + it('should return true for valid i18n data', () => { + const i18nData: IPublicTypeI18nData = { + type: 'i18n', + // add any other required properties here + }; + + expect(isI18nData(i18nData)).toBe(true); + }); + + it('should return false for invalid i18n data', () => { + const invalidData = { + type: 'some-other-type', + // add any other properties here + }; + + expect(isI18nData(invalidData)).toBe(false); + }); + + it('should return false for undefined or null', () => { + expect(isI18nData(undefined)).toBe(false); + expect(isI18nData(null)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-isfunction.test.ts b/packages/utils/test/src/check-types/is-isfunction.test.ts new file mode 100644 index 000000000..515428228 --- /dev/null +++ b/packages/utils/test/src/check-types/is-isfunction.test.ts @@ -0,0 +1,61 @@ +import { isInnerJsFunction, isJSFunction } from '../../../src/check-types/is-isfunction'; + +describe('isInnerJsFunction', () => { + test('should return true for valid input', () => { + const data = { + type: 'JSExpression', + source: '', + value: '', + extType: 'function' + }; + + expect(isInnerJsFunction(data)).toBe(true); + }); + + test('should return false for invalid input', () => { + const data = { + type: 'JSExpression', + source: '', + value: '', + extType: 'object' + }; + + expect(isInnerJsFunction(data)).toBe(false); + expect(isInnerJsFunction(null)).toBe(false); + expect(isInnerJsFunction(undefined)).toBe(false); + expect(isInnerJsFunction(1)).toBe(false); + expect(isInnerJsFunction(0)).toBe(false); + expect(isInnerJsFunction('string')).toBe(false); + expect(isInnerJsFunction('')).toBe(false); + }); +}); + +describe('isJSFunction', () => { + test('should return true for valid input', () => { + const data = { + type: 'JSFunction', + }; + + expect(isJSFunction(data)).toBe(true); + }); + + test('should return true for inner js function', () => { + const data = { + type: 'JSExpression', + source: '', + value: '', + extType: 'function' + }; + + expect(isJSFunction(data)).toBe(true); + }); + + test('should return false for invalid input', () => { + expect(isJSFunction(null)).toBe(false); + expect(isJSFunction(undefined)).toBe(false); + expect(isJSFunction('string')).toBe(false); + expect(isJSFunction('')).toBe(false); + expect(isJSFunction(0)).toBe(false); + expect(isJSFunction(2)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-jsblock.test.ts b/packages/utils/test/src/check-types/is-jsblock.test.ts new file mode 100644 index 000000000..e44e9eb70 --- /dev/null +++ b/packages/utils/test/src/check-types/is-jsblock.test.ts @@ -0,0 +1,22 @@ +import { isJSBlock } from '../../../src/check-types/is-jsblock'; + +describe('isJSBlock', () => { + it('should return false if data is null or undefined', () => { + expect(isJSBlock(null)).toBe(false); + expect(isJSBlock(undefined)).toBe(false); + }); + + it('should return false if data is not an object', () => { + expect(isJSBlock('JSBlock')).toBe(false); + expect(isJSBlock(123)).toBe(false); + expect(isJSBlock(true)).toBe(false); + }); + + it('should return false if data.type is not "JSBlock"', () => { + expect(isJSBlock({ type: 'InvalidType' })).toBe(false); + }); + + it('should return true if data is an object and data.type is "JSBlock"', () => { + expect(isJSBlock({ type: 'JSBlock' })).toBe(true); + }); +}); diff --git a/packages/utils/test/src/check-types/is-jsexpression.test.ts b/packages/utils/test/src/check-types/is-jsexpression.test.ts new file mode 100644 index 000000000..dd8509a3b --- /dev/null +++ b/packages/utils/test/src/check-types/is-jsexpression.test.ts @@ -0,0 +1,39 @@ +import { isJSExpression } from '../../../src/check-types/is-jsexpression'; + +describe('isJSExpression', () => { + it('should return true if the input is a valid JSExpression object', () => { + const validJSExpression = { + type: 'JSExpression', + extType: 'variable', + }; + + const result = isJSExpression(validJSExpression); + + expect(result).toBe(true); + }); + + it('should return false if the input is not a valid JSExpression object', () => { + const invalidJSExpression = { + type: 'JSExpression', + extType: 'function', + }; + + const result = isJSExpression(invalidJSExpression); + + expect(result).toBe(false); + }); + + it('should return false if the input is null', () => { + const result = isJSExpression(null); + + expect(result).toBe(false); + }); + + it('should return false if the input is undefined', () => { + const result = isJSExpression(undefined); + + expect(result).toBe(false); + }); + + // 添加其他需要的测试 +}); diff --git a/packages/utils/test/src/check-types/is-jsslot.test.ts b/packages/utils/test/src/check-types/is-jsslot.test.ts new file mode 100644 index 000000000..5c130cddf --- /dev/null +++ b/packages/utils/test/src/check-types/is-jsslot.test.ts @@ -0,0 +1,37 @@ +import { isJSSlot } from '../../../src/check-types/is-jsslot'; +import { IPublicTypeJSSlot } from '@alilc/lowcode-types'; + +describe('isJSSlot', () => { + it('should return true when input is of type IPublicTypeJSSlot', () => { + const input: IPublicTypeJSSlot = { + type: 'JSSlot', + // other properties of IPublicTypeJSSlot + }; + + const result = isJSSlot(input); + + expect(result).toBe(true); + }); + + it('should return false when input is not of type IPublicTypeJSSlot', () => { + const input = { + type: 'OtherType', + // other properties + }; + + const result = isJSSlot(input); + + expect(result).toBe(false); + }); + + it('should return false when input is null or undefined', () => { + const input1 = null; + const input2 = undefined; + + const result1 = isJSSlot(input1); + const result2 = isJSSlot(input2); + + expect(result1).toBe(false); + expect(result2).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-location-children-detail.test.ts b/packages/utils/test/src/check-types/is-location-children-detail.test.ts new file mode 100644 index 000000000..f209e8e63 --- /dev/null +++ b/packages/utils/test/src/check-types/is-location-children-detail.test.ts @@ -0,0 +1,27 @@ +import { isLocationChildrenDetail } from '../../../src/check-types/is-location-children-detail'; +import { IPublicTypeLocationChildrenDetail, IPublicTypeLocationDetailType } from '@alilc/lowcode-types'; + +describe('isLocationChildrenDetail', () => { + it('should return true when obj is IPublicTypeLocationChildrenDetail', () => { + const obj: IPublicTypeLocationChildrenDetail = { + type: IPublicTypeLocationDetailType.Children, + // 添加其他必要的属性 + }; + + expect(isLocationChildrenDetail(obj)).toBe(true); + }); + + it('should return false when obj is not IPublicTypeLocationChildrenDetail', () => { + const obj = { + type: 'other', + // 添加其他必要的属性 + }; + + expect(isLocationChildrenDetail(obj)).toBe(false); + expect(isLocationChildrenDetail(null)).toBe(false); + expect(isLocationChildrenDetail(undefined)).toBe(false); + expect(isLocationChildrenDetail('string')).toBe(false); + expect(isLocationChildrenDetail(0)).toBe(false); + expect(isLocationChildrenDetail(2)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-location-data.test.ts b/packages/utils/test/src/check-types/is-location-data.test.ts new file mode 100644 index 000000000..ba2e2c8be --- /dev/null +++ b/packages/utils/test/src/check-types/is-location-data.test.ts @@ -0,0 +1,44 @@ +import { isLocationData } from '../../../src/check-types/is-location-data'; +import { IPublicTypeLocationData } from '@alilc/lowcode-types'; + +describe('isLocationData', () => { + it('should return true when obj is valid location data', () => { + const obj: IPublicTypeLocationData = { + target: 'some target', + detail: 'some detail', + }; + + const result = isLocationData(obj); + + expect(result).toBe(true); + }); + + it('should return false when obj is missing target or detail', () => { + const obj1 = { + target: 'some target', + // missing detail + }; + + const obj2 = { + // missing target + detail: 'some detail', + }; + + const result1 = isLocationData(obj1); + const result2 = isLocationData(obj2); + + expect(result1).toBe(false); + expect(result2).toBe(false); + }); + + it('should return false when obj is null or undefined', () => { + const obj1 = null; + const obj2 = undefined; + + const result1 = isLocationData(obj1); + const result2 = isLocationData(obj2); + + expect(result1).toBe(false); + expect(result2).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-lowcode-component-type.test.ts b/packages/utils/test/src/check-types/is-lowcode-component-type.test.ts new file mode 100644 index 000000000..35b76f00b --- /dev/null +++ b/packages/utils/test/src/check-types/is-lowcode-component-type.test.ts @@ -0,0 +1,21 @@ +import { isLowCodeComponentType } from '../../../src/check-types/is-lowcode-component-type'; +import { IPublicTypeLowCodeComponent, IPublicTypeProCodeComponent } from '@alilc/lowcode-types'; + +describe('isLowCodeComponentType', () => { + test('should return true for a low code component type', () => { + const desc: IPublicTypeLowCodeComponent = { + // create a valid low code component description + }; + + expect(isLowCodeComponentType(desc)).toBe(true); + }); + + test('should return false for a pro code component type', () => { + const desc: IPublicTypeProCodeComponent = { + // create a valid pro code component description + package: 'pro-code' + }; + + expect(isLowCodeComponentType(desc)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-lowcode-project-schema.test.ts b/packages/utils/test/src/check-types/is-lowcode-project-schema.test.ts new file mode 100644 index 000000000..bb750ed88 --- /dev/null +++ b/packages/utils/test/src/check-types/is-lowcode-project-schema.test.ts @@ -0,0 +1,42 @@ +import { isLowcodeProjectSchema } from "../../../src/check-types/is-lowcode-project-schema"; + +describe("isLowcodeProjectSchema", () => { + it("should return false when data is null", () => { + const result = isLowcodeProjectSchema(null); + expect(result).toBe(false); + }); + + it("should return false when data is undefined", () => { + const result = isLowcodeProjectSchema(undefined); + expect(result).toBe(false); + }); + + it("should return false when data is not an object", () => { + const result = isLowcodeProjectSchema("not an object"); + expect(result).toBe(false); + }); + + it("should return false when componentsTree is missing", () => { + const data = { someKey: "someValue" }; + const result = isLowcodeProjectSchema(data); + expect(result).toBe(false); + }); + + it("should return false when componentsTree is an empty array", () => { + const data = { componentsTree: [] }; + const result = isLowcodeProjectSchema(data); + expect(result).toBe(false); + }); + + it("should return false when the first element of componentsTree is not a component schema", () => { + const data = { componentsTree: [{}] }; + const result = isLowcodeProjectSchema(data); + expect(result).toBe(false); + }); + + it("should return true when all conditions are met", () => { + const data = { componentsTree: [{ prop: "value", componentName: 'Component' }] }; + const result = isLowcodeProjectSchema(data); + expect(result).toBe(true); + }); +}); diff --git a/packages/utils/test/src/check-types/is-node-schema.test.ts b/packages/utils/test/src/check-types/is-node-schema.test.ts new file mode 100644 index 000000000..b5a4e39ac --- /dev/null +++ b/packages/utils/test/src/check-types/is-node-schema.test.ts @@ -0,0 +1,43 @@ +import { isNodeSchema } from '../../../src/check-types/is-node-schema'; + +describe('isNodeSchema', () => { + // 测试正常情况 + it('should return true for valid IPublicTypeNodeSchema', () => { + const validData = { + componentName: 'Component', + isNode: false, + }; + expect(isNodeSchema(validData)).toBe(true); + }); + + // 测试 null 或 undefined + it('should return false for null or undefined', () => { + expect(isNodeSchema(null)).toBe(false); + expect(isNodeSchema(undefined)).toBe(false); + }); + + // 测试没有componentName属性的情况 + it('should return false if componentName is missing', () => { + const invalidData = { + isNode: false, + }; + expect(isNodeSchema(invalidData)).toBe(false); + }); + + // 测试isNode为true的情况 + it('should return false if isNode is true', () => { + const invalidData = { + componentName: 'Component', + isNode: true, + }; + expect(isNodeSchema(invalidData)).toBe(false); + }); + + // 测试其他数据类型的情况 + it('should return false for other data types', () => { + expect(isNodeSchema('string')).toBe(false); + expect(isNodeSchema(123)).toBe(false); + expect(isNodeSchema([])).toBe(false); + expect(isNodeSchema({})).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-node.test.ts b/packages/utils/test/src/check-types/is-node.test.ts new file mode 100644 index 000000000..d6d8dfc03 --- /dev/null +++ b/packages/utils/test/src/check-types/is-node.test.ts @@ -0,0 +1,19 @@ +import { isNode } from '../../../src/check-types/is-node'; + +describe('isNode', () => { + it('should return true for a valid node', () => { + const node = { isNode: true }; + expect(isNode(node)).toBeTruthy(); + }); + + it('should return false for an invalid node', () => { + const node = { isNode: false }; + expect(isNode(node)).toBeFalsy(); + }); + + it('should return false for an undefined node', () => { + expect(isNode(undefined)).toBeFalsy(); + }); + + // Add more test cases if needed +}); diff --git a/packages/utils/test/src/check-types/is-procode-component-type.test.ts b/packages/utils/test/src/check-types/is-procode-component-type.test.ts new file mode 100644 index 000000000..58f435b98 --- /dev/null +++ b/packages/utils/test/src/check-types/is-procode-component-type.test.ts @@ -0,0 +1,13 @@ +import { isProCodeComponentType } from '../../../src/check-types/is-procode-component-type'; + +describe('isProCodeComponentType', () => { + it('should return true if the given desc object contains "package" property', () => { + const desc = { package: 'packageName' }; + expect(isProCodeComponentType(desc)).toBe(true); + }); + + it('should return false if the given desc object does not contain "package" property', () => { + const desc = { name: 'componentName' }; + expect(isProCodeComponentType(desc)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-project-schema.test.ts b/packages/utils/test/src/check-types/is-project-schema.test.ts new file mode 100644 index 000000000..0ec3f4740 --- /dev/null +++ b/packages/utils/test/src/check-types/is-project-schema.test.ts @@ -0,0 +1,28 @@ +import { IPublicTypeProjectSchema } from "@alilc/lowcode-types"; +import { isProjectSchema } from "../../../src/check-types/is-project-schema"; + +describe("isProjectSchema", () => { + it("should return true if data has componentsTree property", () => { + const data: IPublicTypeProjectSchema = { + // ... + componentsTree: { + // ... + }, + }; + expect(isProjectSchema(data)).toBe(true); + }); + + it("should return false if data does not have componentsTree property", () => { + const data = { + // ... + }; + expect(isProjectSchema(data)).toBe(false); + }); + + it("should return false if data is null or undefined", () => { + expect(isProjectSchema(null)).toBe(false); + expect(isProjectSchema(undefined)).toBe(false); + }); + + // 更多的测试用例... +}); diff --git a/packages/utils/test/src/check-types/is-setter-config.test.ts b/packages/utils/test/src/check-types/is-setter-config.test.ts new file mode 100644 index 000000000..eee234658 --- /dev/null +++ b/packages/utils/test/src/check-types/is-setter-config.test.ts @@ -0,0 +1,26 @@ +import { isSetterConfig } from '../../../src/check-types/is-setter-config'; + +describe('isSetterConfig', () => { + test('should return true for valid setter config', () => { + const config = { + componentName: 'MyComponent', + // Add other required properties here + }; + + expect(isSetterConfig(config)).toBe(true); + }); + + test('should return false for invalid setter config', () => { + const config = { + // Missing componentName property + }; + + expect(isSetterConfig(config)).toBe(false); + expect(isSetterConfig(null)).toBe(false); + expect(isSetterConfig(undefined)).toBe(false); + expect(isSetterConfig(0)).toBe(false); + expect(isSetterConfig(2)).toBe(false); + }); + + // Add more test cases for different scenarios you want to cover +}); diff --git a/packages/utils/test/src/check-types/is-setting-field.test.ts b/packages/utils/test/src/check-types/is-setting-field.test.ts new file mode 100644 index 000000000..5f9bbd623 --- /dev/null +++ b/packages/utils/test/src/check-types/is-setting-field.test.ts @@ -0,0 +1,18 @@ +import { isSettingField } from "../../../src/check-types/is-setting-field"; + +describe("isSettingField", () => { + it("should return true for an object that has isSettingField property", () => { + const obj = { isSettingField: true }; + expect(isSettingField(obj)).toBe(true); + }); + + it("should return false for an object that does not have isSettingField property", () => { + const obj = { foo: "bar" }; + expect(isSettingField(obj)).toBe(false); + }); + + it("should return false for a falsy value", () => { + const obj = null; + expect(isSettingField(obj)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/check-types/is-title-config.test.ts b/packages/utils/test/src/check-types/is-title-config.test.ts new file mode 100644 index 000000000..4aa6d219c --- /dev/null +++ b/packages/utils/test/src/check-types/is-title-config.test.ts @@ -0,0 +1,18 @@ +import { isTitleConfig } from '../../../src/check-types/is-title-config'; + +describe('isTitleConfig', () => { + it('should return true for valid config object', () => { + const config = { title: 'My Title' }; + expect(isTitleConfig(config)).toBe(true); + }); + + it('should return false for invalid config object', () => { + const config = { title: 'My Title', type: 'i18n' , i18nData: {} }; + expect(isTitleConfig(config)).toBe(false); + }); + + it('should return false for non-object input', () => { + const config = 'invalid'; + expect(isTitleConfig(config)).toBe(false); + }); +}); diff --git a/packages/utils/test/src/clone-deep.test.ts b/packages/utils/test/src/clone-deep.test.ts new file mode 100644 index 000000000..58fabc6f6 --- /dev/null +++ b/packages/utils/test/src/clone-deep.test.ts @@ -0,0 +1,30 @@ +import { cloneDeep } from '../../src/clone-deep'; + +describe('cloneDeep', () => { + it('should clone null', () => { + const src = null; + expect(cloneDeep(src)).toBeNull(); + }); + + it('should clone undefined', () => { + const src = undefined; + expect(cloneDeep(src)).toBeUndefined(); + }); + + it('should clone an array', () => { + const src = [1, 2, 3, 4]; + expect(cloneDeep(src)).toEqual(src); + }); + + it('should clone an object', () => { + const src = { name: 'John', age: 25 }; + expect(cloneDeep(src)).toEqual(src); + }); + + it('should deep clone nested objects', () => { + const src = { person: { name: 'John', age: 25 } }; + const cloned = cloneDeep(src); + expect(cloned).toEqual(src); + expect(cloned.person).not.toBe(src.person); + }); +}); \ No newline at end of file diff --git a/packages/utils/test/src/clone-enumerable-property.test.ts b/packages/utils/test/src/clone-enumerable-property.test.ts new file mode 100644 index 000000000..2eff09e44 --- /dev/null +++ b/packages/utils/test/src/clone-enumerable-property.test.ts @@ -0,0 +1,30 @@ +import { cloneEnumerableProperty } from '../../src/clone-enumerable-property'; + +describe('cloneEnumerableProperty', () => { + test('should clone enumerable properties from origin to target', () => { + // Arrange + const target = {}; + const origin = { prop1: 1, prop2: 'hello', prop3: true }; + + // Act + const result = cloneEnumerableProperty(target, origin); + + // Assert + expect(result).toBe(target); + expect(result).toEqual(origin); + }); + + test('should exclude properties specified in excludePropertyNames', () => { + // Arrange + const target = {}; + const origin = { prop1: 1, prop2: 'hello', prop3: true }; + const excludePropertyNames = ['prop2']; + + // Act + const result = cloneEnumerableProperty(target, origin, excludePropertyNames); + + // Assert + expect(result).toBe(target); + expect(result).toEqual({ prop1: 1, prop3: true }); + }); +}); \ No newline at end of file diff --git a/packages/utils/test/src/create-content.test.tsx b/packages/utils/test/src/create-content.test.tsx new file mode 100644 index 000000000..c41fb0f0d --- /dev/null +++ b/packages/utils/test/src/create-content.test.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { createContent } from '../../src/create-content'; + +const MyComponent = () => { + return
MyComponent
+} +describe('createContent', () => { + test('should return the same content if it is a valid React element', () => { + const content =
Hello
; + const result = createContent(content); + + expect(result).toEqual(content); + }); + + test('should clone the element with props if props are provided', () => { + const content =
; + const props = { className: 'my-class' }; + const result = createContent(content, props); + + expect(result.props).toEqual(props); + }); + + test('should create an element with props if the content is a React component', () => { + const content = MyComponent; + const props = { className: 'my-class' }; + const result = createContent(content, props); + + expect(result.type).toEqual(content); + expect(result.props).toEqual(props); + }); + + test('should return the content if it is not a React element or a React component', () => { + const content = 'Hello'; + const result = createContent(content); + + expect(result).toEqual(content); + }); +}); diff --git a/packages/utils/test/src/create-defer.test.ts b/packages/utils/test/src/create-defer.test.ts new file mode 100644 index 000000000..c6ab9207a --- /dev/null +++ b/packages/utils/test/src/create-defer.test.ts @@ -0,0 +1,16 @@ +import { createDefer } from '../../src/create-defer'; + +describe('createDefer', () => { + it('should resolve with given value', async () => { + const defer = createDefer(); + defer.resolve(42); + const result = await defer.promise(); + expect(result).toBe(42); + }); + + it('should reject with given reason', async () => { + const defer = createDefer(); + defer.reject('error'); + await expect(defer.promise()).rejects.toEqual('error'); + }); +}); diff --git a/packages/utils/test/src/is-object.test.ts b/packages/utils/test/src/is-object.test.ts new file mode 100644 index 000000000..7ae984b8f --- /dev/null +++ b/packages/utils/test/src/is-object.test.ts @@ -0,0 +1,45 @@ +import { isObject, isI18NObject } from '../../src/is-object'; + +describe('isObject', () => { + it('should return true for an object', () => { + const obj = { key: 'value' }; + const result = isObject(obj); + expect(result).toBe(true); + }); + + it('should return false for null', () => { + const result = isObject(null); + expect(result).toBe(false); + }); + + it('should return false for a non-object value', () => { + const value = 42; // Not an object + const result = isObject(value); + expect(result).toBe(false); + }); +}); + +describe('isI18NObject', () => { + it('should return true for an I18N object', () => { + const i18nObject = { type: 'i18n', data: 'some data' }; + const result = isI18NObject(i18nObject); + expect(result).toBe(true); + }); + + it('should return false for a non-I18N object', () => { + const nonI18nObject = { type: 'other', data: 'some data' }; + const result = isI18NObject(nonI18nObject); + expect(result).toBe(false); + }); + + it('should return false for null', () => { + const result = isI18NObject(null); + expect(result).toBe(false); + }); + + it('should return false for a non-object value', () => { + const value = 42; // Not an object + const result = isI18NObject(value); + expect(result).toBe(false); + }); +}); diff --git a/packages/utils/test/src/is-react.test.ts b/packages/utils/test/src/is-react.test.ts deleted file mode 100644 index 74c88c933..000000000 --- a/packages/utils/test/src/is-react.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import { isReactComponent, wrapReactClass } from "../../src/is-react"; - -class reactDemo extends React.Component { - -} - -const reactMemo = React.memo(reactDemo); - -const reactForwardRef = React.forwardRef((props, ref): any => { - return ''; -}); - -describe('is-react-ut', () => { - it('isReactComponent', () => { - expect(isReactComponent(null)).toBeFalsy(); - expect(isReactComponent(() => {})).toBeTruthy(); - expect(isReactComponent({ - $$typeof: Symbol.for('react.memo') - })).toBeTruthy(); - expect(isReactComponent({ - $$typeof: Symbol.for('react.forward_ref') - })).toBeTruthy(); - expect(isReactComponent(reactDemo)).toBeTruthy(); - expect(isReactComponent(reactMemo)).toBeTruthy(); - expect(isReactComponent(reactForwardRef)).toBeTruthy(); - - }); - - it('wrapReactClass', () => { - const wrap = wrapReactClass(() => {}); - expect(isReactComponent(wrap)).toBeTruthy(); - - const fun = () => {}; - fun.displayName = 'mock'; - expect(wrapReactClass(fun).displayName).toBe('mock'); - }) -}) \ No newline at end of file diff --git a/packages/utils/test/src/is-react.test.tsx b/packages/utils/test/src/is-react.test.tsx new file mode 100644 index 000000000..9ed2bd6c3 --- /dev/null +++ b/packages/utils/test/src/is-react.test.tsx @@ -0,0 +1,316 @@ +import React, { Component, createElement } from "react"; +import { + isReactComponent, + wrapReactClass, + isForwardOrMemoForward, + isMemoType, + isForwardRefType, + acceptsRef, + isReactClass, + REACT_MEMO_TYPE, + REACT_FORWARD_REF_TYPE, + } from "../../src/is-react"; + +class reactDemo extends React.Component { + +} + +const reactMemo = React.memo(reactDemo); + +const reactForwardRef = React.forwardRef((props, ref): any => { + return ''; +}); + +describe('is-react-ut', () => { + it('isReactComponent', () => { + expect(isReactComponent(null)).toBeFalsy(); + expect(isReactComponent(() => {})).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.memo') + })).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.forward_ref') + })).toBeTruthy(); + expect(isReactComponent(reactDemo)).toBeTruthy(); + expect(isReactComponent(reactMemo)).toBeTruthy(); + expect(isReactComponent(reactForwardRef)).toBeTruthy(); + + }); + + it('wrapReactClass', () => { + const wrap = wrapReactClass(() => {}); + expect(isReactComponent(wrap)).toBeTruthy(); + + const fun = () => {}; + fun.displayName = 'mock'; + expect(wrapReactClass(fun).displayName).toBe('mock'); + }) +}) + +describe('wrapReactClass', () => { + it('should wrap a FunctionComponent', () => { + // Create a mock FunctionComponent + const MockComponent: React.FunctionComponent = (props) => { + return
{props.children}
; + }; + + // Wrap the FunctionComponent using wrapReactClass + const WrappedComponent = wrapReactClass(MockComponent); + const instance = new WrappedComponent(); + + // Check if the WrappedComponent extends Component + expect(instance instanceof React.Component).toBe(true); + }); + + it('should render the FunctionComponent with props', () => { + // Create a mock FunctionComponent + const MockComponent: React.FunctionComponent = (props) => { + return
{props.children}
; + }; + + MockComponent.displayName = 'FunctionComponent'; + + // Wrap the FunctionComponent using wrapReactClass + const WrappedComponent = wrapReactClass(MockComponent); + + // Create some test props + const testProps = { prop1: 'value1', prop2: 'value2' }; + + // Render the WrappedComponent with test props + const rendered = createElement(WrappedComponent, testProps, 'Child Text'); + + // Check if the WrappedComponent renders the FunctionComponent with props + expect(rendered).toMatchSnapshot(); + }); +}); + +describe('isReactComponent', () => { + it('should identify a class component as a React component', () => { + class ClassComponent extends React.Component { + render() { + return
Class Component
; + } + } + + expect(isReactComponent(ClassComponent)).toBe(true); + }); + + it('should identify a functional component as a React component', () => { + const FunctionalComponent = () => { + return
Functional Component
; + }; + + expect(isReactComponent(FunctionalComponent)).toBe(true); + }); + + it('should identify a forward ref component as a React component', () => { + const ForwardRefComponent = React.forwardRef((props, ref) => { + return
Forward Ref Component
; + }); + + expect(isReactComponent(ForwardRefComponent)).toBe(true); + }); + + it('should identify a memo component as a React component', () => { + const MemoComponent = React.memo(() => { + return
Memo Component
; + }); + + expect(isReactComponent(MemoComponent)).toBe(true); + }); + + it('should return false for non-React components', () => { + const plainObject = { prop: 'value' }; + const notAComponent = 'Not a component'; + + expect(isReactComponent(plainObject)).toBe(false); + expect(isReactComponent(notAComponent)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(isReactComponent(nullValue)).toBe(false); + expect(isReactComponent(undefinedValue)).toBe(false); + }); +}); + +describe('isForwardOrMemoForward', () => { + it('should return true for a forwardRef component', () => { + const forwardRefComponent = React.forwardRef(() => { + return
ForwardRef Component
; + }); + + expect(isForwardOrMemoForward(forwardRefComponent)).toBe(true); + }); + + it('should return true for a memoized forwardRef component', () => { + const forwardRefComponent = React.forwardRef(() => { + return
ForwardRef Component
; + }); + + const memoizedComponent = React.memo(forwardRefComponent); + + expect(isForwardOrMemoForward(memoizedComponent)).toBe(true); + }); + + it('should return false for a memoized component that is not a forwardRef', () => { + const memoizedComponent = React.memo(() => { + return
Memoized Component
; + }); + + expect(isForwardOrMemoForward(memoizedComponent)).toBe(false); + }); + + it('should return false for a plain object', () => { + const plainObject = { prop: 'value' }; + + expect(isForwardOrMemoForward(plainObject)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(isForwardOrMemoForward(nullValue)).toBe(false); + expect(isForwardOrMemoForward(undefinedValue)).toBe(false); + }); +}); + +describe('isMemoType', () => { + it('should return true for an object with $$typeof matching REACT_MEMO_TYPE', () => { + const memoTypeObject = { $$typeof: REACT_MEMO_TYPE }; + + expect(isMemoType(memoTypeObject)).toBe(true); + }); + + it('should return false for an object with $$typeof not matching REACT_MEMO_TYPE', () => { + const otherTypeObject = { $$typeof: Symbol.for('other.type') }; + + expect(isMemoType(otherTypeObject)).toBe(false); + }); + + it('should return false for an object with no $$typeof property', () => { + const noTypeObject = { key: 'value' }; + + expect(isMemoType(noTypeObject)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(isMemoType(nullValue)).toBe(false); + expect(isMemoType(undefinedValue)).toBe(false); + }); +}); + +describe('isForwardRefType', () => { + it('should return true for an object with $$typeof matching REACT_FORWARD_REF_TYPE', () => { + const forwardRefTypeObject = { $$typeof: REACT_FORWARD_REF_TYPE }; + + expect(isForwardRefType(forwardRefTypeObject)).toBe(true); + }); + + it('should return false for an object with $$typeof not matching REACT_FORWARD_REF_TYPE', () => { + const otherTypeObject = { $$typeof: Symbol.for('other.type') }; + + expect(isForwardRefType(otherTypeObject)).toBe(false); + }); + + it('should return false for an object with no $$typeof property', () => { + const noTypeObject = { key: 'value' }; + + expect(isForwardRefType(noTypeObject)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(isForwardRefType(nullValue)).toBe(false); + expect(isForwardRefType(undefinedValue)).toBe(false); + }); +}); + +describe('acceptsRef', () => { + it('should return true for an object with isReactComponent in its prototype', () => { + const objWithIsReactComponent = { + prototype: { + isReactComponent: true, + }, + }; + + expect(acceptsRef(objWithIsReactComponent)).toBe(true); + }); + + it('should return true for an object that is forwardRef or memoized forwardRef', () => { + const forwardRefObject = React.forwardRef(() => { + return null; + }); + + const memoizedForwardRefObject = React.memo(forwardRefObject); + + expect(acceptsRef(forwardRefObject)).toBe(true); + expect(acceptsRef(memoizedForwardRefObject)).toBe(true); + }); + + it('should return false for an object without isReactComponent in its prototype', () => { + const objWithoutIsReactComponent = { + prototype: { + someOtherProperty: true, + }, + }; + + expect(acceptsRef(objWithoutIsReactComponent)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(acceptsRef(nullValue)).toBe(false); + expect(acceptsRef(undefinedValue)).toBe(false); + }); +}); + +describe('isReactClass', () => { + it('should return true for an object with isReactComponent in its prototype', () => { + class ReactClassComponent extends Component { + render() { + return null; + } + } + + expect(isReactClass(ReactClassComponent)).toBe(true); + }); + + it('should return true for an object with Component in its prototype chain', () => { + class CustomComponent extends Component { + render() { + return null; + } + } + + expect(isReactClass(CustomComponent)).toBe(true); + }); + + it('should return false for an object without isReactComponent in its prototype', () => { + class NonReactComponent { + render() { + return null; + } + } + + expect(isReactClass(NonReactComponent)).toBe(false); + }); + + it('should return false for null or undefined', () => { + const nullValue = null; + const undefinedValue = undefined; + + expect(isReactClass(nullValue)).toBe(false); + expect(isReactClass(undefinedValue)).toBe(false); + }); +}); \ No newline at end of file diff --git a/packages/utils/test/src/is-shaken.test.ts b/packages/utils/test/src/is-shaken.test.ts new file mode 100644 index 000000000..35a27af5f --- /dev/null +++ b/packages/utils/test/src/is-shaken.test.ts @@ -0,0 +1,45 @@ +import { isShaken } from '../../src/is-shaken'; + +describe('isShaken', () => { + it('should return true if e1 has shaken property', () => { + const e1: any = { shaken: true }; + const e2: MouseEvent | DragEvent = { target: null } as MouseEvent | DragEvent; + + expect(isShaken(e1, e2)).toBe(true); + }); + + it('should return true if e1.target and e2.target are different', () => { + const e1: MouseEvent | DragEvent = { target: {} } as MouseEvent | DragEvent; + const e2: MouseEvent | DragEvent = { target: {} } as MouseEvent | DragEvent; + + expect(isShaken(e1, e2)).toBe(true); + }); + + it('should return false if e1 and e2 targets are the same and distance is less than SHAKE_DISTANCE', () => { + const target = {}; + const e1: MouseEvent | DragEvent = { target: target } as MouseEvent | DragEvent; + const e2: MouseEvent | DragEvent = { target: target } as MouseEvent | DragEvent; + + // Assuming SHAKE_DISTANCE is 100 + e1.clientY = 50; + e2.clientY = 50; + + e1.clientX = 60; + e2.clientX = 60; + + expect(isShaken(e1, e2)).toBe(false); + }); + + it('should return true if e1 and e2 targets are the same and distance is greater than SHAKE_DISTANCE', () => { + const e1: MouseEvent | DragEvent = { target: {} } as MouseEvent | DragEvent; + const e2: MouseEvent | DragEvent = { target: {} } as MouseEvent | DragEvent; + + // Assuming SHAKE_DISTANCE is 100 + e1.clientY = 50; + e1.clientX = 50; + e2.clientY = 200; + e2.clientX = 200; + + expect(isShaken(e1, e2)).toBe(true); + }); +}); diff --git a/packages/utils/test/src/misc.test.ts b/packages/utils/test/src/misc.test.ts index 8873dc4c8..251466150 100644 --- a/packages/utils/test/src/misc.test.ts +++ b/packages/utils/test/src/misc.test.ts @@ -1,4 +1,321 @@ -import { shouldUseVariableSetter } from '../../src/misc'; +import { + isVariable, + isUseI18NSetter, + convertToI18NObject, + isString, + waitForThing, + arrShallowEquals, + isFromVC, + executePendingFn, + compatStage, + invariant, + isRegExp, + shouldUseVariableSetter, +} from '../../src/misc'; +import { IPublicModelComponentMeta } from '@alilc/lowcode-types'; + +describe('isVariable', () => { + it('should return true for a variable object', () => { + const variable = { type: 'variable', variable: 'foo', value: 'bar' }; + const result = isVariable(variable); + expect(result).toBe(true); + }); + + it('should return false for non-variable objects', () => { + const obj = { type: 'object' }; + const result = isVariable(obj); + expect(result).toBe(false); + }); +}); + +describe('isUseI18NSetter', () => { + it('should return true for a property with I18nSetter', () => { + const prototype = { options: { configure: [{ name: 'propName', setter: { type: { displayName: 'I18nSetter' } } }] } }; + const propName = 'propName'; + const result = isUseI18NSetter(prototype, propName); + expect(result).toBe(true); + }); + + it('should return false for a property without I18nSetter', () => { + const prototype = { options: { configure: [{ name: 'propName', setter: { type: { displayName: 'OtherSetter' } } }] } }; + const propName = 'propName'; + const result = isUseI18NSetter(prototype, propName); + expect(result).toBe(false); + }); +}); + +describe('convertToI18NObject', () => { + it('should return the input if it is already an I18N object', () => { + const i18nObject = { type: 'i18n', use: 'en', en: 'Hello' }; + const result = convertToI18NObject(i18nObject); + expect(result).toEqual(i18nObject); + }); + + it('should convert a string to an I18N object', () => { + const inputString = 'Hello'; + const result = convertToI18NObject(inputString); + const expectedOutput = { type: 'i18n', use: 'zh-CN', 'zh-CN': inputString }; + expect(result).toEqual(expectedOutput); + }); +}); + +describe('isString', () => { + it('should return true for a string', () => { + const stringValue = 'Hello, world!'; + const result = isString(stringValue); + expect(result).toBe(true); + }); + + it('should return true for an empty string', () => { + const emptyString = ''; + const result = isString(emptyString); + expect(result).toBe(true); + }); + + it('should return false for a number', () => { + const numberValue = 42; // Not a string + const result = isString(numberValue); + expect(result).toBe(false); + }); + + it('should return false for an object', () => { + const objectValue = { key: 'value' }; // Not a string + const result = isString(objectValue); + expect(result).toBe(false); + }); + + it('should return false for null', () => { + const result = isString(null); + expect(result).toBe(false); + }); + + it('should return false for undefined', () => { + const undefinedValue = undefined; + const result = isString(undefinedValue); + expect(result).toBe(false); + }); + + it('should return false for a boolean', () => { + const booleanValue = true; // Not a string + const result = isString(booleanValue); + expect(result).toBe(false); + }); +}); + +describe('waitForThing', () => { + it('should resolve immediately if the thing is available', async () => { + const obj = { prop: 'value' }; + const path = 'prop'; + const result = await waitForThing(obj, path); + expect(result).toBe('value'); + }); + + it('should resolve after a delay if the thing becomes available', async () => { + const obj = { prop: undefined }; + const path = 'prop'; + const delay = 100; // Adjust the delay as needed + setTimeout(() => { + obj.prop = 'value'; + }, delay); + + const result = await waitForThing(obj, path); + expect(result).toBe('value'); + }); +}); + +describe('arrShallowEquals', () => { + it('should return true for two empty arrays', () => { + const arr1 = []; + const arr2 = []; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(true); + }); + + it('should return true for two arrays with the same elements in the same order', () => { + const arr1 = [1, 2, 3]; + const arr2 = [1, 2, 3]; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(true); + }); + + it('should return true for two arrays with the same elements in a different order', () => { + const arr1 = [1, 2, 3]; + const arr2 = [3, 2, 1]; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(true); + }); + + it('should return false for two arrays with different lengths', () => { + const arr1 = [1, 2, 3]; + const arr2 = [1, 2]; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(false); + }); + + it('should return false for one array and a non-array', () => { + const arr1 = [1, 2, 3]; + const nonArray = 'not an array'; + const result = arrShallowEquals(arr1, nonArray); + expect(result).toBe(false); + }); + + it('should return false for two arrays with different elements', () => { + const arr1 = [1, 2, 3]; + const arr2 = [3, 4, 5]; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(false); + }); + + it('should return true for arrays with duplicate elements', () => { + const arr1 = [1, 2, 2, 3]; + const arr2 = [2, 3, 3, 1]; + const result = arrShallowEquals(arr1, arr2); + expect(result).toBe(true); + }); +}); + +describe('isFromVC', () => { + it('should return true when advanced configuration is present', () => { + // Create a mock meta object with advanced configuration + const meta: IPublicModelComponentMeta = { + getMetadata: () => ({ configure: { advanced: true } }), + }; + + const result = isFromVC(meta); + + expect(result).toBe(true); + }); + + it('should return false when advanced configuration is not present', () => { + // Create a mock meta object without advanced configuration + const meta: IPublicModelComponentMeta = { + getMetadata: () => ({ configure: { advanced: false } }), + }; + + const result = isFromVC(meta); + + expect(result).toBe(false); + }); + + it('should return false when meta is undefined', () => { + const meta: IPublicModelComponentMeta | undefined = undefined; + + const result = isFromVC(meta); + + expect(result).toBe(false); + }); + + it('should return false when meta does not have configure information', () => { + // Create a mock meta object without configure information + const meta: IPublicModelComponentMeta = { + getMetadata: () => ({}), + }; + + const result = isFromVC(meta); + + expect(result).toBe(false); + }); + + it('should return false when configure.advanced is not present', () => { + // Create a mock meta object with incomplete configure information + const meta: IPublicModelComponentMeta = { + getMetadata: () => ({ configure: {} }), + }; + + const result = isFromVC(meta); + + expect(result).toBe(false); + }); +}); + +describe('executePendingFn', () => { + it('should execute the provided function after the specified timeout', async () => { + // Mock the function to execute + const fn = jest.fn(); + + // Call executePendingFn with the mocked function and a short timeout + executePendingFn(fn, 100); + + // Ensure the function has not been called immediately + expect(fn).not.toHaveBeenCalled(); + + // Wait for the specified timeout + await new Promise(resolve => setTimeout(resolve, 100)); + + // Ensure the function has been called after the timeout + expect(fn).toHaveBeenCalled(); + }); + + it('should execute the provided function with a default timeout if not specified', async () => { + // Mock the function to execute + const fn = jest.fn(); + + // Call executePendingFn with the mocked function without specifying a timeout + executePendingFn(fn); + + // Ensure the function has not been called immediately + expect(fn).not.toHaveBeenCalled(); + + // Wait for the default timeout (2000 milliseconds) + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Ensure the function has been called after the default timeout + expect(fn).toHaveBeenCalled(); + }); +}); + +describe('compatStage', () => { + it('should convert a number to an enum stage', () => { + const result = compatStage(3); + expect(result).toBe('save'); + }); + + it('should warn about the deprecated usage', () => { + const warnSpy = jest.spyOn(console, 'warn'); + const result = compatStage(2); + expect(result).toBe('serilize'); + expect(warnSpy).toHaveBeenCalledWith( + 'stage 直接指定为数字的使用方式已经过时,将在下一版本移除,请直接使用 IPublicEnumTransformStage.Render|Serilize|Save|Clone|Init|Upgrade' + ); + warnSpy.mockRestore(); + }); + + it('should return the enum stage if it is already an enum', () => { + const result = compatStage('render'); + expect(result).toBe('render'); + }); +}); + +describe('invariant', () => { + it('should not throw an error if the check is true', () => { + expect(() => invariant(true, 'Test invariant', 'thing')).not.toThrow(); + }); + + it('should throw an error if the check is false', () => { + expect(() => invariant(false, 'Test invariant', 'thing')).toThrowError( + "Invariant failed: Test invariant in 'thing'" + ); + }); +}); + +describe('isRegExp', () => { + it('should return true for a valid RegExp', () => { + const regex = /test/; + const result = isRegExp(regex); + expect(result).toBe(true); + }); + + it('should return false for a non-RegExp object', () => { + const nonRegExp = { test: /test/ }; + const result = isRegExp(nonRegExp); + expect(result).toBe(false); + }); + + it('should return false for null', () => { + const result = isRegExp(null); + expect(result).toBe(false); + }); +}); it('shouldUseVariableSetter', () => { expect(shouldUseVariableSetter(false, true)).toBeFalsy(); diff --git a/packages/utils/test/src/navtive-selection.test.ts b/packages/utils/test/src/navtive-selection.test.ts new file mode 100644 index 000000000..f45d0e1b2 --- /dev/null +++ b/packages/utils/test/src/navtive-selection.test.ts @@ -0,0 +1,18 @@ +import { setNativeSelection, nativeSelectionEnabled } from '../../src/navtive-selection'; + +describe('setNativeSelection', () => { + beforeEach(() => { + // 在每个测试运行之前重置nativeSelectionEnabled的值 + setNativeSelection(true); + }); + + test('should enable native selection', () => { + setNativeSelection(true); + expect(nativeSelectionEnabled).toBe(true); + }); + + test('should disable native selection', () => { + setNativeSelection(false); + expect(nativeSelectionEnabled).toBe(false); + }); +}); diff --git a/packages/utils/test/src/schema.test.ts b/packages/utils/test/src/schema.test.ts index 138dd7a82..8d03f5811 100644 --- a/packages/utils/test/src/schema.test.ts +++ b/packages/utils/test/src/schema.test.ts @@ -1,4 +1,132 @@ -import { compatibleLegaoSchema } from '../../src/schema'; +import { + compatibleLegaoSchema, + getNodeSchemaById, + applyActivities, +} from '../../src/schema'; +import { ActivityType } from '@alilc/lowcode-types'; + +describe('compatibleLegaoSchema', () => { + it('should handle null input', () => { + const result = compatibleLegaoSchema(null); + expect(result).toBeNull(); + }); + + it('should convert Legao schema to JSExpression', () => { + // Create your test input + const legaoSchema = { + type: 'LegaoType', + source: 'LegaoSource', + compiled: 'LegaoCompiled', + }; + const result = compatibleLegaoSchema(legaoSchema); + + // Define the expected output + const expectedOutput = { + type: 'JSExpression', + value: 'LegaoCompiled', + extType: 'function', + }; + + // Assert that the result matches the expected output + expect(result).toEqual(expectedOutput); + }); + + // Add more test cases for other scenarios +}); + +describe('getNodeSchemaById', () => { + it('should find a node in the schema', () => { + // Create your test schema and node ID + const testSchema = { + id: 'root', + children: [ + { + id: 'child1', + children: [ + { + id: 'child1.1', + }, + ], + }, + ], + }; + const nodeId = 'child1.1'; + + const result = getNodeSchemaById(testSchema, nodeId); + + // Define the expected output + const expectedOutput = { + id: 'child1.1', + }; + + // Assert that the result matches the expected output + expect(result).toEqual(expectedOutput); + }); + + // Add more test cases for other scenarios +}); + +describe('applyActivities', () => { + it('should apply ADD activity', () => { + // Create your test schema and activities + const testSchema = { + id: 'root', + children: [ + { + id: 'child1', + children: [ + { + id: 'child1.1', + }, + ], + }, + ], + }; + const activities = [ + { + type: ActivityType.ADDED, + payload: { + location: { + parent: { + nodeId: 'child1', + index: 0, + }, + }, + schema: { + id: 'newChild', + }, + }, + }, + ]; + + const result = applyActivities(testSchema, activities); + + // Define the expected output + const expectedOutput = { + id: 'root', + children: [ + { + id: 'child1', + children: [ + { + id: 'newChild', + }, + { + id: 'child1.1', + }, + ], + }, + ], + }; + + // Assert that the result matches the expected output + expect(result).toEqual(expectedOutput); + }); + + // Add more test cases for other activity types and scenarios +}); + + describe('Schema Ut', () => { it('props', () => { const schema = { diff --git a/packages/utils/test/src/script.test.ts b/packages/utils/test/src/script.test.ts new file mode 100644 index 000000000..d3d4ffd59 --- /dev/null +++ b/packages/utils/test/src/script.test.ts @@ -0,0 +1,47 @@ +import { + evaluate, + evaluateExpression, + newFunction, +} from '../../src/script'; + +describe('evaluate', () => { + test('should evaluate the given script', () => { + const script = 'console.log("Hello, world!");'; + global.console = { log: jest.fn() }; + + evaluate(script); + + expect(global.console.log).toHaveBeenCalledWith('Hello, world!'); + }); +}); + +describe('evaluateExpression', () => { + test('should evaluate the given expression', () => { + const expr = 'return 1 + 2'; + + const result = evaluateExpression(expr); + + expect(result).toBe(3); + }); +}); + +describe('newFunction', () => { + test('should create a new function with the given arguments and code', () => { + const args = 'a, b'; + const code = 'return a + b'; + + const result = newFunction(args, code); + + expect(result).toBeInstanceOf(Function); + expect(result(1, 2)).toBe(3); + }); + + test('should return null if an error occurs', () => { + const args = 'a, b'; + const code = 'return a +;'; // Invalid code + + const result = newFunction(args, code); + + expect(result).toBeNull(); + }); +}); diff --git a/packages/utils/test/src/svg-icon.test.tsx b/packages/utils/test/src/svg-icon.test.tsx new file mode 100644 index 000000000..bbb6e18b7 --- /dev/null +++ b/packages/utils/test/src/svg-icon.test.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { SVGIcon, IconProps } from '../../src/svg-icon'; + +describe('SVGIcon', () => { + it('should render SVG element with correct size', () => { + const iconProps: IconProps = { + size: 'small', + viewBox: '0 0 24 24', + }; + + const { container } = render(); + + const svgElement = container.querySelector('svg'); + + expect(svgElement).toHaveAttribute('width', '12'); + expect(svgElement).toHaveAttribute('height', '12'); + }); + + it('should render SVG element with custom size', () => { + const iconProps: IconProps = { + size: 24, + viewBox: '0 0 24 24', + }; + + const { container } = render(); + + const svgElement = container.querySelector('svg'); + + expect(svgElement).toHaveAttribute('width', '24'); + expect(svgElement).toHaveAttribute('height', '24'); + }); + + // Add more tests for other scenarios if needed +}); diff --git a/packages/utils/test/src/transaction-manager.test.ts b/packages/utils/test/src/transaction-manager.test.ts new file mode 100644 index 000000000..42c7fa8bf --- /dev/null +++ b/packages/utils/test/src/transaction-manager.test.ts @@ -0,0 +1,58 @@ +import { transactionManager } from '../../src/transaction-manager'; +import { IPublicEnumTransitionType } from '@alilc/lowcode-types'; + +const type = IPublicEnumTransitionType.REPAINT; + +describe('TransactionManager', () => { + let fn1: jest.Mock; + let fn2: jest.Mock; + + beforeEach(() => { + fn1 = jest.fn(); + fn2 = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('executeTransaction should emit startTransaction and endTransaction events', () => { + const startTransactionSpy = jest.spyOn(transactionManager.emitter, 'emit'); + const endTransactionSpy = jest.spyOn(transactionManager.emitter, 'emit'); + + transactionManager.executeTransaction(() => { + // Perform some action within the transaction + }); + + expect(startTransactionSpy).toHaveBeenCalledWith(`[${type}]startTransaction`); + expect(endTransactionSpy).toHaveBeenCalledWith(`[${type}]endTransaction`); + }); + + test('onStartTransaction should register the provided function for startTransaction event', () => { + const offSpy = jest.spyOn(transactionManager.emitter, 'off'); + + const offFunction = transactionManager.onStartTransaction(fn1); + + expect(transactionManager.emitter.listenerCount(`[${type}]startTransaction`)).toBe(1); + expect(offSpy).not.toHaveBeenCalled(); + + offFunction(); + + expect(transactionManager.emitter.listenerCount(`[${type}]startTransaction`)).toBe(0); + expect(offSpy).toHaveBeenCalledWith(`[${type}]startTransaction`, fn1); + }); + + test('onEndTransaction should register the provided function for endTransaction event', () => { + const offSpy = jest.spyOn(transactionManager.emitter, 'off'); + + const offFunction = transactionManager.onEndTransaction(fn2); + + expect(transactionManager.emitter.listenerCount(`[${type}]endTransaction`)).toBe(1); + expect(offSpy).not.toHaveBeenCalled(); + + offFunction(); + + expect(transactionManager.emitter.listenerCount(`[${type}]endTransaction`)).toBe(0); + expect(offSpy).toHaveBeenCalledWith(`[${type}]endTransaction`, fn2); + }); +}); diff --git a/packages/utils/test/src/unique-id.test.ts b/packages/utils/test/src/unique-id.test.ts new file mode 100644 index 000000000..2b4b6e9e0 --- /dev/null +++ b/packages/utils/test/src/unique-id.test.ts @@ -0,0 +1,11 @@ +import { uniqueId } from '../../src/unique-id'; + +test('uniqueId should return a unique id with prefix', () => { + const id = uniqueId('test'); + expect(id.startsWith('test')).toBeTruthy(); +}); + +test('uniqueId should return a unique id without prefix', () => { + const id = uniqueId(); + expect(id).not.toBeFalsy(); +});