From d1702161c1710c02fabbc8f7d2203c08d9a0d6a2 Mon Sep 17 00:00:00 2001 From: 1ncounter <1ncounter.100@gmail.com> Date: Tue, 9 Jul 2024 15:15:23 +0800 Subject: [PATCH] feat: add some init codes --- packages/designer/package.json | 8 +- .../src/models/document/document-model.ts | 4 +- packages/designer/src/models/node/node.ts | 6 +- .../tests/__mocks__/document-model.ts | 9 - packages/designer/tests/__mocks__/node.ts | 9 - packages/designer/tests/bugs/misc.ts.bak | 55 - .../tests/bugs/prop-variable-jse.test.ts | 72 - packages/designer/tests/bugs/why.md | 6 - .../bem-tools/drag-resize-engine.test.ts | 127 -- .../bem-tools/manager.test.tsx | 50 - .../tests/builtin-simulator/host.test.ts | 510 ------- .../tests/builtin-simulator/renderer.test.tsx | 9 - .../resource-consumer.test.ts | 60 - .../utils/parse-metadata.test.ts | 177 --- .../builtin-simulator/utils/path.test.ts | 78 -- .../builtin-simulator/utils/throttle.test.ts | 22 - .../tests/builtin-simulator/viewport.test.ts | 180 --- .../tests/designer/active-tracker.test.ts | 41 - .../tests/designer/builtin-hotkey.test.ts | 395 ------ .../designer/tests/designer/designer.test.ts | 500 ------- .../designer/tests/designer/detecting.test.ts | 26 - .../designer/tests/designer/dragon.test.ts | 369 ----- .../designer/tests/designer/location.test.ts | 209 --- .../designer/tests/designer/scroller.test.ts | 136 -- .../__snapshots__/setting-field.test.ts.snap | 79 -- .../designer/setting/setting-field.test.ts | 281 ---- .../setting/setting-prop-entry.test.ts | 230 ---- .../setting/setting-top-entry.test.ts | 196 --- .../__snapshots__/document-model.test.ts.snap | 1215 ----------------- .../document-model/document-model.test.ts | 313 ----- .../__snapshots__/history.test.ts.snap | 9 - .../tests/document/history/history.test.ts | 343 ----- .../tests/document/history/session.test.ts | 57 - .../document/node/modal-nodes-manager.test.ts | 112 -- .../tests/document/node/node-children.test.ts | 250 ---- .../tests/document/node/node.add.test.ts | 588 -------- .../tests/document/node/node.dragdrop.test.ts | 56 - .../tests/document/node/node.modify.test.ts | 456 ------- .../tests/document/node/node.remove.test.ts | 123 -- .../designer/tests/document/node/node.test.ts | 664 --------- .../value-to-source.test.ts.snap | 37 - .../tests/document/node/props/prop.test.ts | 697 ---------- .../tests/document/node/props/props.test.ts | 313 ----- .../node/props/value-to-source.test.ts | 29 - .../designer/tests/document/selection.test.ts | 263 ---- .../fixtures/component-metadata/abcgroup.ts | 280 ---- .../fixtures/component-metadata/abcitem.ts | 280 ---- .../fixtures/component-metadata/abcnode.ts | 280 ---- .../fixtures/component-metadata/abcoption.ts | 280 ---- .../fixtures/component-metadata/button.ts | 308 ----- .../fixtures/component-metadata/dialog.ts | 277 ---- .../tests/fixtures/component-metadata/div.ts | 283 ---- .../fixtures/component-metadata/div10.ts | 22 - .../tests/fixtures/component-metadata/div2.ts | 280 ---- .../tests/fixtures/component-metadata/div3.ts | 282 ---- .../tests/fixtures/component-metadata/div4.ts | 272 ---- .../tests/fixtures/component-metadata/div5.ts | 283 ---- .../tests/fixtures/component-metadata/div6.ts | 283 ---- .../tests/fixtures/component-metadata/div7.ts | 276 ---- .../tests/fixtures/component-metadata/div8.ts | 12 - .../tests/fixtures/component-metadata/div9.ts | 8 - .../tests/fixtures/component-metadata/form.ts | 279 ---- .../fixtures/component-metadata/other.ts | 279 ---- .../tests/fixtures/component-metadata/page.ts | 279 ---- .../fixtures/component-metadata/page2.ts | 279 ---- .../component-metadata/root-content.ts | 279 ---- .../component-metadata/root-footer.ts | 279 ---- .../component-metadata/root-header.ts | 279 ---- .../designer/tests/fixtures/disable-raf.ts | 3 - .../tests/fixtures/schema/form-with-modal.ts | 1021 -------------- .../designer/tests/fixtures/schema/form.ts | 993 -------------- .../designer/tests/fixtures/schema/setting.ts | 90 -- .../designer/tests/fixtures/silent-console.ts | 6 - .../tests/fixtures/unhandled-rejection.ts | 7 - packages/designer/tests/fixtures/window.ts | 28 - .../tests/main/meta/component-meta.test.ts | 252 ---- .../designer/tests/main/simulator.test.ts | 7 - .../tests/plugin/plugin-manager.test.ts | 529 ------- .../tests/plugin/plugin-utils.test.ts | 85 -- .../designer/tests/plugin/sequencify.test.ts | 128 -- .../tests/project/project-methods.test.ts | 181 --- .../designer/tests/project/project.test.ts | 305 ----- .../designer/tests/utils-ut/invariant.test.ts | 8 - packages/designer/tests/utils-ut/misc.test.ts | 164 --- packages/designer/tests/utils-ut/slot.test.ts | 43 - packages/designer/tests/utils/bom.ts | 111 -- packages/designer/tests/utils/event.ts | 8 - packages/designer/tests/utils/index.ts | 5 - packages/designer/tests/utils/misc.ts | 25 - packages/designer/tests/utils/renderer.ts | 8 - .../configuration/configurationModel.spec.ts | 59 + packages/engine-core/package.json | 3 +- packages/engine-core/src/command/command.ts | 34 + .../src/command/commandRegistry.ts | 115 ++ .../engine-core/src/command/commandService.ts | 20 + .../src/configuration/configuration.ts | 81 ++ .../src/configuration/configurationModel.ts | 406 ++++++ .../configuration/configurationRegistry.ts | 301 ++++ .../src/configuration/configurationService.ts | 63 + .../engine-core/src/configuration/index.ts | 3 + packages/engine-core/src/extension/index.ts | 1 + .../engine-core/src/extension/registry.ts | 39 + packages/engine-core/src/index.ts | 2 + .../src/lifeCycle/lifeCycleService.ts | 0 .../src}/plugin/context.ts | 6 +- .../src}/plugin/index.ts | 0 .../src}/plugin/manager.ts | 0 .../src}/plugin/runtime.ts | 0 .../src}/plugin/types.ts | 0 .../src}/plugin/utils.ts | 0 packages/engine-core/src/resource.ts | 5 + .../src/workbench/layout/layoutService.ts | 6 + .../src/workbench/window/windowService.ts | 0 .../engine-core/src/workspace/workspace.ts | 12 + packages/engine/package.json | 1 + packages/engine/src/common/command.ts | 50 - packages/engine/src/main.ts | 10 +- .../src/services/command/commandService.ts | 8 - .../services/configuration/configuration.ts | 154 --- .../configuration/configurationService.ts | 8 - packages/engine/src/themeService.ts | 28 + packages/plugin-command/package.json | 3 - packages/plugin-designer/package.json | 6 +- packages/plugin-outline-pane/package.json | 8 +- .../src/runtime/createComponent.tsx | 8 +- .../react-renderer/src/runtime/elements.tsx | 36 +- .../src/runtime/hooks/useReactiveStore.tsx | 12 +- .../src/runtime/reactiveState.ts | 8 +- .../src.bak/README.md | 1 - .../builtin-components/builtin-components.ts | 413 ------ .../src.bak/builtin-components/leaf.tsx | 24 - .../src.bak/builtin-components/slot.tsx | 58 - .../react-simulator-renderer/src.bak/host.ts | 4 - .../react-simulator-renderer/src.bak/index.ts | 17 - .../src.bak/locale/en-US.json | 4 - .../src.bak/locale/index.ts | 21 - .../src.bak/locale/zh-CN.json | 4 - .../src.bak/renderer-view.tsx | 270 ---- .../src.bak/renderer.less | 95 -- .../src.bak/renderer.ts | 637 --------- .../src.bak/utils/get-client-rects.ts | 13 - .../src.bak/utils/is-dom-node.ts | 3 - .../src.bak/utils/misc.ts | 38 - .../src.bak/utils/react-find-dom-nodes.ts | 41 - .../src.bak/utils/url.ts | 74 - .../src/services/code-runtime/codeRuntime.ts | 37 +- .../code-runtime/codeRuntimeService.ts | 8 +- .../src/services/code-runtime/codeScope.ts | 18 +- .../src/services/extension/boosts.ts | 10 +- .../src/services/extension/plugin.ts | 4 +- .../src/services/model/componentTreeModel.ts | 48 +- .../model/componentTreeModelService.ts | 12 +- .../src/services/package/loader.ts | 6 +- .../src/services/package/managementService.ts | 30 +- .../src/services/runtimeIntlService.ts | 4 +- .../src/services/runtimeUtilService.ts | 34 +- .../src/services/schema/schemaService.ts | 8 +- .../src/services/schema/validation.ts | 6 +- .../src/services/widget/widget.ts | 10 +- packages/renderer-core/src/types.ts | 6 +- packages/renderer-core/src/utils/value.ts | 12 +- packages/renderer-router/src/matcher.ts | 4 +- packages/renderer-router/src/router.ts | 6 +- packages/renderer-router/src/types.ts | 18 +- packages/shared/src/abilities/event.ts | 82 +- .../src/abilities/instantiation/decorators.ts | 34 + .../src/abilities/instantiation/index.ts | 60 +- .../instantiation/instantiationService.ts | 46 + packages/shared/src/abilities/storage.ts | 6 +- packages/shared/src/signals.ts | 4 +- packages/shared/src/types/common.ts | 9 + packages/shared/src/types/index.ts | 14 +- packages/shared/src/types/json.ts | 7 + packages/shared/src/types/material.ts | 15 - packages/shared/src/types/specs/asset-spec.ts | 87 ++ .../shared/src/types/specs/lowcode-spec.ts | 44 +- .../shared/src/types/specs/material-spec.ts | 51 +- packages/shared/src/types/specs/runtime.ts | 8 +- packages/shared/src/utils/callback.ts | 30 - packages/shared/src/utils/index.ts | 3 +- packages/shared/src/utils/node.ts | 39 +- .../shared/src/utils/type-guards/index.ts | 2 - .../shared/src/utils/type-guards/material.ts | 10 - packages/shared/src/utils/type-guards/spec.ts | 26 - packages/shared/src/utils/types/index.ts | 2 + packages/shared/src/utils/types/json.ts | 17 + packages/shared/src/utils/types/spec.ts | 42 + 187 files changed, 1753 insertions(+), 21414 deletions(-) delete mode 100644 packages/designer/tests/__mocks__/document-model.ts delete mode 100644 packages/designer/tests/__mocks__/node.ts delete mode 100644 packages/designer/tests/bugs/misc.ts.bak delete mode 100644 packages/designer/tests/bugs/prop-variable-jse.test.ts delete mode 100644 packages/designer/tests/bugs/why.md delete mode 100644 packages/designer/tests/builtin-simulator/bem-tools/drag-resize-engine.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx delete mode 100644 packages/designer/tests/builtin-simulator/host.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/renderer.test.tsx delete mode 100644 packages/designer/tests/builtin-simulator/resource-consumer.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/utils/path.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/utils/throttle.test.ts delete mode 100644 packages/designer/tests/builtin-simulator/viewport.test.ts delete mode 100644 packages/designer/tests/designer/active-tracker.test.ts delete mode 100644 packages/designer/tests/designer/builtin-hotkey.test.ts delete mode 100644 packages/designer/tests/designer/designer.test.ts delete mode 100644 packages/designer/tests/designer/detecting.test.ts delete mode 100644 packages/designer/tests/designer/dragon.test.ts delete mode 100644 packages/designer/tests/designer/location.test.ts delete mode 100644 packages/designer/tests/designer/scroller.test.ts delete mode 100644 packages/designer/tests/designer/setting/__snapshots__/setting-field.test.ts.snap delete mode 100644 packages/designer/tests/designer/setting/setting-field.test.ts delete mode 100644 packages/designer/tests/designer/setting/setting-prop-entry.test.ts delete mode 100644 packages/designer/tests/designer/setting/setting-top-entry.test.ts delete mode 100644 packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap delete mode 100644 packages/designer/tests/document/document-model/document-model.test.ts delete mode 100644 packages/designer/tests/document/history/__snapshots__/history.test.ts.snap delete mode 100644 packages/designer/tests/document/history/history.test.ts delete mode 100644 packages/designer/tests/document/history/session.test.ts delete mode 100644 packages/designer/tests/document/node/modal-nodes-manager.test.ts delete mode 100644 packages/designer/tests/document/node/node-children.test.ts delete mode 100644 packages/designer/tests/document/node/node.add.test.ts delete mode 100644 packages/designer/tests/document/node/node.dragdrop.test.ts delete mode 100644 packages/designer/tests/document/node/node.modify.test.ts delete mode 100644 packages/designer/tests/document/node/node.remove.test.ts delete mode 100644 packages/designer/tests/document/node/node.test.ts delete mode 100644 packages/designer/tests/document/node/props/__snapshots__/value-to-source.test.ts.snap delete mode 100644 packages/designer/tests/document/node/props/prop.test.ts delete mode 100644 packages/designer/tests/document/node/props/props.test.ts delete mode 100644 packages/designer/tests/document/node/props/value-to-source.test.ts delete mode 100644 packages/designer/tests/document/selection.test.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/abcgroup.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/abcitem.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/abcnode.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/abcoption.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/button.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/dialog.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div10.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div2.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div3.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div4.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div5.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div6.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div7.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div8.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/div9.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/form.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/other.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/page.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/page2.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/root-content.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/root-footer.ts delete mode 100644 packages/designer/tests/fixtures/component-metadata/root-header.ts delete mode 100644 packages/designer/tests/fixtures/disable-raf.ts delete mode 100644 packages/designer/tests/fixtures/schema/form-with-modal.ts delete mode 100644 packages/designer/tests/fixtures/schema/form.ts delete mode 100644 packages/designer/tests/fixtures/schema/setting.ts delete mode 100644 packages/designer/tests/fixtures/silent-console.ts delete mode 100644 packages/designer/tests/fixtures/unhandled-rejection.ts delete mode 100644 packages/designer/tests/fixtures/window.ts delete mode 100644 packages/designer/tests/main/meta/component-meta.test.ts delete mode 100644 packages/designer/tests/main/simulator.test.ts delete mode 100644 packages/designer/tests/plugin/plugin-manager.test.ts delete mode 100644 packages/designer/tests/plugin/plugin-utils.test.ts delete mode 100644 packages/designer/tests/plugin/sequencify.test.ts delete mode 100644 packages/designer/tests/project/project-methods.test.ts delete mode 100644 packages/designer/tests/project/project.test.ts delete mode 100644 packages/designer/tests/utils-ut/invariant.test.ts delete mode 100644 packages/designer/tests/utils-ut/misc.test.ts delete mode 100644 packages/designer/tests/utils-ut/slot.test.ts delete mode 100644 packages/designer/tests/utils/bom.ts delete mode 100644 packages/designer/tests/utils/event.ts delete mode 100644 packages/designer/tests/utils/index.ts delete mode 100644 packages/designer/tests/utils/misc.ts delete mode 100644 packages/designer/tests/utils/renderer.ts create mode 100644 packages/engine-core/__tests__/configuration/configurationModel.spec.ts create mode 100644 packages/engine-core/src/command/command.ts create mode 100644 packages/engine-core/src/command/commandRegistry.ts create mode 100644 packages/engine-core/src/command/commandService.ts create mode 100644 packages/engine-core/src/configuration/configuration.ts create mode 100644 packages/engine-core/src/configuration/configurationModel.ts create mode 100644 packages/engine-core/src/configuration/configurationRegistry.ts create mode 100644 packages/engine-core/src/configuration/configurationService.ts create mode 100644 packages/engine-core/src/configuration/index.ts create mode 100644 packages/engine-core/src/extension/index.ts create mode 100644 packages/engine-core/src/extension/registry.ts create mode 100644 packages/engine-core/src/lifeCycle/lifeCycleService.ts rename packages/{engine/src/common => engine-core/src}/plugin/context.ts (93%) rename packages/{engine/src/common => engine-core/src}/plugin/index.ts (100%) rename packages/{engine/src/common => engine-core/src}/plugin/manager.ts (100%) rename packages/{engine/src/common => engine-core/src}/plugin/runtime.ts (100%) rename packages/{engine/src/common => engine-core/src}/plugin/types.ts (100%) rename packages/{engine/src/common => engine-core/src}/plugin/utils.ts (100%) create mode 100644 packages/engine-core/src/resource.ts create mode 100644 packages/engine-core/src/workbench/layout/layoutService.ts create mode 100644 packages/engine-core/src/workbench/window/windowService.ts create mode 100644 packages/engine-core/src/workspace/workspace.ts delete mode 100644 packages/engine/src/common/command.ts delete mode 100644 packages/engine/src/services/command/commandService.ts delete mode 100644 packages/engine/src/services/configuration/configuration.ts delete mode 100644 packages/engine/src/services/configuration/configurationService.ts create mode 100644 packages/engine/src/themeService.ts delete mode 100644 packages/react-simulator-renderer/src.bak/README.md delete mode 100644 packages/react-simulator-renderer/src.bak/builtin-components/builtin-components.ts delete mode 100644 packages/react-simulator-renderer/src.bak/builtin-components/leaf.tsx delete mode 100644 packages/react-simulator-renderer/src.bak/builtin-components/slot.tsx delete mode 100644 packages/react-simulator-renderer/src.bak/host.ts delete mode 100644 packages/react-simulator-renderer/src.bak/index.ts delete mode 100644 packages/react-simulator-renderer/src.bak/locale/en-US.json delete mode 100644 packages/react-simulator-renderer/src.bak/locale/index.ts delete mode 100644 packages/react-simulator-renderer/src.bak/locale/zh-CN.json delete mode 100644 packages/react-simulator-renderer/src.bak/renderer-view.tsx delete mode 100644 packages/react-simulator-renderer/src.bak/renderer.less delete mode 100644 packages/react-simulator-renderer/src.bak/renderer.ts delete mode 100644 packages/react-simulator-renderer/src.bak/utils/get-client-rects.ts delete mode 100644 packages/react-simulator-renderer/src.bak/utils/is-dom-node.ts delete mode 100644 packages/react-simulator-renderer/src.bak/utils/misc.ts delete mode 100644 packages/react-simulator-renderer/src.bak/utils/react-find-dom-nodes.ts delete mode 100644 packages/react-simulator-renderer/src.bak/utils/url.ts create mode 100644 packages/shared/src/abilities/instantiation/decorators.ts create mode 100644 packages/shared/src/abilities/instantiation/instantiationService.ts create mode 100644 packages/shared/src/types/common.ts create mode 100644 packages/shared/src/types/json.ts delete mode 100644 packages/shared/src/types/material.ts delete mode 100644 packages/shared/src/utils/callback.ts delete mode 100644 packages/shared/src/utils/type-guards/index.ts delete mode 100644 packages/shared/src/utils/type-guards/material.ts delete mode 100644 packages/shared/src/utils/type-guards/spec.ts create mode 100644 packages/shared/src/utils/types/index.ts create mode 100644 packages/shared/src/utils/types/json.ts create mode 100644 packages/shared/src/utils/types/spec.ts diff --git a/packages/designer/package.json b/packages/designer/package.json index 84a555fdc..e8122030b 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -30,10 +30,8 @@ }, "license": "MIT", "dependencies": { - "@alilc/lowcode-core": "workspace:*", + "@alilc/lowcode-engine-core": "workspace:*", "@alilc/lowcode-shared": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", "@alifd/next": "^1.27.8", "classnames": "^2.5.1", "lodash-es": "^4.17.20", @@ -55,10 +53,8 @@ }, "peerDependencies": { "@alifd/next": "^1.27.8", - "@alilc/lowcode-core": "workspace:*", + "@alilc/lowcode-engine-core": "workspace:*", "@alilc/lowcode-shared": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/designer/src/models/document/document-model.ts b/packages/designer/src/models/document/document-model.ts index 641fb209e..bd1b7431a 100644 --- a/packages/designer/src/models/document/document-model.ts +++ b/packages/designer/src/models/document/document-model.ts @@ -1,8 +1,8 @@ -import { signal, uniqueId, type Spec } from '@alilc/lowcode-shared'; +import { signal, uniqueId, ComponentTreeRoot } from '@alilc/lowcode-shared'; import { type Project } from '../project'; import { History } from './history'; -export interface DocumentSchema extends Spec.ComponentTreeRoot { +export interface DocumentSchema extends ComponentTreeRoot { id: string; } diff --git a/packages/designer/src/models/node/node.ts b/packages/designer/src/models/node/node.ts index c1095189c..0301e5929 100644 --- a/packages/designer/src/models/node/node.ts +++ b/packages/designer/src/models/node/node.ts @@ -1,8 +1,8 @@ -import { Spec } from '@alilc/lowcode-shared'; +import { ComponentNode } from '@alilc/lowcode-shared'; import { type ComponentMeta } from '../component-meta'; import { type Prop } from './prop'; -export interface Node { +export interface Node { /** * 节点 id * node id @@ -353,6 +353,6 @@ export interface Node { }; } -export function createNode(nodeSchema: Schema): Node { +export function createNode(nodeSchema: Schema): Node { return {}; } diff --git a/packages/designer/tests/__mocks__/document-model.ts b/packages/designer/tests/__mocks__/document-model.ts deleted file mode 100644 index 0eb1910fc..000000000 --- a/packages/designer/tests/__mocks__/document-model.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class DocumentModel { - a = 1; - c = {}; - constructor() { - const b = { x: { y: 2 } }; - const c: number = 2; - this.a = b?.x?.y; - } -} diff --git a/packages/designer/tests/__mocks__/node.ts b/packages/designer/tests/__mocks__/node.ts deleted file mode 100644 index 109467ead..000000000 --- a/packages/designer/tests/__mocks__/node.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class Node2 { - a = 1; - c = {}; - constructor() { - const b = { x: { y: 2 } }; - const c: number = 2; - this.a = b?.x?.y; - } -} diff --git a/packages/designer/tests/bugs/misc.ts.bak b/packages/designer/tests/bugs/misc.ts.bak deleted file mode 100644 index d4001a05e..000000000 --- a/packages/designer/tests/bugs/misc.ts.bak +++ /dev/null @@ -1,55 +0,0 @@ -import set from 'lodash/set'; -import cloneDeep from 'lodash/cloneDeep'; -import '../fixtures/window'; -import { Project } from '../../src/project/project'; -// import { Node } from '../../../src/document/node/node'; -import { Designer } from '../../src/designer/designer'; -import formSchema from '../fixtures/schema/form'; -import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - }; - }, - transformProps(props) { return props; }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -it.todo('在同一个节点下,相同名称的 slot 只能有一个', () => { - const project = new Project(designer, { - componentsTree: [ - formSchema, - ], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap.size).toBe(expectedNodeCnt); - ids.forEach(id => { - expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName); - }); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); -}); diff --git a/packages/designer/tests/bugs/prop-variable-jse.test.ts b/packages/designer/tests/bugs/prop-variable-jse.test.ts deleted file mode 100644 index 0f32f0b57..000000000 --- a/packages/designer/tests/bugs/prop-variable-jse.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Editor } from '@alilc/lowcode-editor-core'; -import { IPublicEnumTransformStage } from '@alilc/lowcode-types'; -import { isPlainObject, isVariable, isJSBlock } from '@alilc/lowcode-utils'; -import '../fixtures/window'; -import { Designer } from '../../src/designer/designer'; -import { DocumentModel } from '../../src/document/document-model'; -import { Project } from '../../src/project/project'; -import formSchema from '../fixtures/schema/form'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - -/** - * bug 背景: - * Prop 在每次 setValue 时都会调用 dispose 方法用于重新计算子 Prop,我认为在 Node 未完成初始化之前的 dispose 都是 - * 无意义的,所以增加了判断条件来调用 dispose,结果导致了 variable 结果没有正确转成 JSExpression 结构。 - * - * 因为 propsReducer 的 Init / Upgrade 阶段依然可以更改 props,且此时的 Node 也未完成初始化,不调用 dispose 则导致新的 Prop 结构无法生效 - */ - -function upgradePropsReducer(props: any): any { - if (!props || !isPlainObject(props)) { - return props; - } - - if (isJSBlock(props)) { - if (props.value.componentName === 'Slot') { - return { - type: 'JSSlot', - title: (props.value.props as any)?.slotTitle, - name: (props.value.props as any)?.slotName, - value: props.value.children, - }; - } else { - return props.value; - } - } - if (isVariable(props)) { - return { - type: 'JSExpression', - value: props.variable, - mock: props.value, - }; - } - const newProps: any = {}; - Object.keys(props).forEach((key) => { - if (/^__slot__/.test(key) && props[key] === true) { - return; - } - newProps[key] = upgradePropsReducer(props[key]); - }); - return newProps; -} - -describe('Node 方法测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - - it('原始 prop 值是 variable 结构,通过一个 propsReducer 转成了 JSExpression 结构', () => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - designer.addPropsReducer(upgradePropsReducer, IPublicEnumTransformStage.Upgrade); - project = designer.project; - doc = new DocumentModel(project, formSchema); - - const form = doc.getNode('form'); - expect(form.getPropValue('dataSource')).toEqual({ - type: 'JSExpression', - value: 'state.formData', - }) - }); -}); diff --git a/packages/designer/tests/bugs/why.md b/packages/designer/tests/bugs/why.md deleted file mode 100644 index 519dee1b5..000000000 --- a/packages/designer/tests/bugs/why.md +++ /dev/null @@ -1,6 +0,0 @@ -背景: -在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。 -至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。 - -做法: -Bugs 文件夹每个文件记录一个 bug 修复的场景测试~ \ No newline at end of file diff --git a/packages/designer/tests/builtin-simulator/bem-tools/drag-resize-engine.test.ts b/packages/designer/tests/builtin-simulator/bem-tools/drag-resize-engine.test.ts deleted file mode 100644 index ccdc4b2b6..000000000 --- a/packages/designer/tests/builtin-simulator/bem-tools/drag-resize-engine.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import '../../fixtures/window'; -import { Editor, globalContext } from '@alilc/lowcode-editor-core'; -import { Project } from '../../../src/project/project'; -import { DocumentModel } from '../../../src/document/document-model'; -import { Designer } from '../../../src/designer/designer'; -import DragResizeEngine from '../../../src/builtin-simulator/bem-tools/drag-resize-engine'; -import formSchema from '../../fixtures/schema/form'; -import { fireEvent, createEvent } from '@testing-library/react'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -describe('DragResizeEngine 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let resizeEngine: DragResizeEngine; - - beforeAll(() => { - editor = new Editor(); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = project.createDocument(formSchema); - doc.open(); - resizeEngine = new DragResizeEngine(designer); - }); - - afterEach(() => { - project.unload(); - project.mountSimulator(undefined); - designer.purge(); - resizeEngine = null; - designer = null; - project = null; - }); - - it('from', () => { - const resizeStartMockFn = jest.fn(); - const resizeMockFn = jest.fn(); - const resizeEndMockFn = jest.fn(); - - const offResizeStart = resizeEngine.onResizeStart(resizeStartMockFn); - const offResize = resizeEngine.onResize(resizeMockFn); - const offResizeEnd = resizeEngine.onResizeEnd(resizeEndMockFn); - const boostedNode = doc.getNode('node_k1ow3cbn'); - const mockBoostFn = jest - .fn((e) => { - return boostedNode; - }); - - // do nothing - const noop = resizeEngine.from(); - noop(); - - const offFrom = resizeEngine.from(document, 'e', mockBoostFn); - - const mouseDownEvt = createEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - fireEvent(document, mouseDownEvt); - - expect(resizeStartMockFn).toHaveBeenCalledTimes(1); - expect(resizeStartMockFn.mock.calls[0][0]).toBe(mouseDownEvt); - expect(resizeStartMockFn.mock.calls[0][1]).toBe('e'); - expect(resizeStartMockFn.mock.calls[0][2]).toBe(boostedNode); - expect(resizeEngine.isDragResizing()).toBeTruthy(); - - const mouseMoveEvt1 = createEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - fireEvent(document, mouseMoveEvt1); - expect(resizeMockFn).toHaveBeenCalledTimes(1); - expect(resizeMockFn.mock.calls[0][0]).toBe(mouseMoveEvt1); - expect(resizeMockFn.mock.calls[0][1]).toBe('e'); - expect(resizeMockFn.mock.calls[0][2]).toBe(boostedNode); - expect(resizeMockFn.mock.calls[0][3]).toBe(8); - expect(resizeMockFn.mock.calls[0][4]).toBe(8); - - const mouseMoveEvt2 = createEvent.mouseMove(document, { clientX: 110, clientY: 110 }, 10, 10); - fireEvent(document, mouseMoveEvt2); - expect(resizeMockFn).toHaveBeenCalledTimes(2); - expect(resizeMockFn.mock.calls[1][0]).toBe(mouseMoveEvt2); - expect(resizeMockFn.mock.calls[1][1]).toBe('e'); - expect(resizeMockFn.mock.calls[1][2]).toBe(boostedNode); - expect(resizeMockFn.mock.calls[1][3]).toBe(10); - expect(resizeMockFn.mock.calls[1][4]).toBe(10); - - const mouseUpEvt = createEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - fireEvent(document, mouseUpEvt); - - expect(resizeEndMockFn).toHaveBeenCalledTimes(1); - expect(resizeEndMockFn.mock.calls[0][0]).toBe(mouseUpEvt); - expect(resizeEndMockFn.mock.calls[0][1]).toBe('e'); - expect(resizeEndMockFn.mock.calls[0][2]).toBe(boostedNode); - expect(resizeEngine.isDragResizing()).toBeFalsy(); - - offResizeStart(); - offResize(); - offResizeEnd(); - resizeStartMockFn.mockClear(); - resizeMockFn.mockClear(); - - fireEvent.mouseMove(document, { clientX: 100, clientY: 100 }); - expect(resizeMockFn).not.toHaveBeenCalled(); - - offFrom(); - fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - expect(resizeStartMockFn).not.toHaveBeenCalled(); - }); - - it('has sensor', () => { - const mockDoc = document.createElement('iframe').contentWindow?.document; - project.mountSimulator({ - sensorAvailable: true, - contentDocument: document, - }); - - const mockBoostFn = jest - .fn((e) => { - return doc.getNode('node_k1ow3cbn'); - }); - - const offFrom = resizeEngine.from(document, 'e', mockBoostFn); - - // TODO: 想办法 mock 一个 iframe.currentDocument - fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx b/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx deleted file mode 100644 index bed1e27de..000000000 --- a/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import '../../fixtures/window'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Designer } from '../../../src/designer/designer'; -import { BemToolsManager } from '../../../src/builtin-simulator/bem-tools/manager'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -describe('Node 方法测试', () => { - let editor: Editor; - let designer: Designer; - // let project: Project; - // let doc: DocumentModel; - let manager: BemToolsManager; - - beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - // project = designer.project; - // doc = new DocumentModel(project, formSchema); - manager = new BemToolsManager(designer); - }); - - afterEach(() => { - // project.unload(); - designer.purge(); - editor = null; - designer = null; - // project = null; - }); - - it('addBemTools / removeBemTools / getAllBemTools', () => { - manager.addBemTools({ - name: 't1', - item: (props: any) => { return
; }, - }); - expect(manager.getAllBemTools().length).toBe(1); - - expect(() => { - manager.addBemTools({ - name: 't1', - item: (props: any) => { return
; }, - }); - }).toThrow(/already exists/); - - manager.removeBemTools('t2'); - expect(manager.getAllBemTools().length).toBe(1); - - manager.removeBemTools('t1'); - expect(manager.getAllBemTools().length).toBe(0); - }); -}); \ No newline at end of file diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts deleted file mode 100644 index d74c31d42..000000000 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ /dev/null @@ -1,510 +0,0 @@ -import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d'; -import '../fixtures/window'; -import { - Editor, - globalContext, - Hotkey as InnerHotkey, - Setters as InnerSetters, -} from '@alilc/lowcode-editor-core'; -import { Workspace as InnerWorkspace } from '@alilc/lowcode-workspace'; -import { - AssetType, -} from '@alilc/lowcode-utils'; -import { - IPublicEnumDragObjectType, -} from '@alilc/lowcode-types'; -import { Project } from '../../src/project/project'; -import pageMetadata from '../fixtures/component-metadata/page'; -import { Designer } from '../../src/designer/designer'; -import { DocumentModel } from '../../src/document/document-model'; -import formSchema from '../fixtures/schema/form'; -import { getMockDocument, getMockWindow, getMockEvent, delayObxTick } from '../utils'; -import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; -import { fireEvent } from '@testing-library/react'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; -import { Setters, Workspace } from '@alilc/lowcode-shell'; -import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer'; -import { - Skeleton as InnerSkeleton, -} from '@alilc/lowcode-editor-skeleton'; - -describe('Host 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let host: BuiltinSimulatorHost; - - beforeAll(() => { - editor = new Editor(); - const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => { - context.project = project; - const eventPrefix = meta?.eventPrefix || 'common'; - context.workspace = workspace; - }, - }; - const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler); - const innerWorkspace = new InnerWorkspace(() => {}, {}); - const workspace = new Workspace(innerWorkspace); - const innerSkeleton = new InnerSkeleton(editor); - editor.set('skeleton' as any, innerSkeleton); - editor.set('innerHotkey', new InnerHotkey()) - editor.set('setters', new Setters(new InnerSetters())); - editor.set('innerPlugins' as any, innerPlugins); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - !globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace'); - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - designer.createComponentMeta(pageMetadata); - doc = project.createDocument(formSchema); - host = new BuiltinSimulatorHost(designer.project, designer); - }); - - afterEach(() => { - project.unload(); - project.mountSimulator(undefined); - designer._componentMetasMap.clear(); - designer.purge(); - host.purge(); - designer = null; - project = null; - host = null; - }); - - describe('基础方法测试', () => { - it('setProps / get / set', async () => { - expect(host.currentDocument).toBe(designer.project.currentDocument); - expect(host.renderEnv).toBe('default'); - expect(host.device).toBe('default'); - expect(host.deviceClassName).toBeUndefined(); - expect(host.requestHandlersMap).toBeNull(); - host.setProps({ - renderEnv: 'rax', - device: 'mobile', - deviceClassName: 'mobile-rocks', - componentsAsset: [ - { - type: AssetType.JSText, - content: 'console.log(1)', - }, - { - type: AssetType.JSUrl, - content: '//path/to/js', - }, - ], - theme: { - type: AssetType.CSSText, - content: '.theme {font-size: 50px;}', - }, - requestHandlersMap: {}, - }); - expect(host.renderEnv).toBe('rax'); - expect(host.device).toBe('mobile'); - expect(host.deviceClassName).toBe('mobile-rocks'); - expect(host.componentsAsset).toEqual([ - { - type: AssetType.JSText, - content: 'console.log(1)', - }, - { - type: AssetType.JSUrl, - content: '//path/to/js', - }, - ]); - expect(host.theme).toEqual({ - type: AssetType.CSSText, - content: '.theme {font-size: 50px;}', - }); - expect(host.componentsMap).toEqual(designer.componentsMap); - expect(host.requestHandlersMap).toEqual({}); - - host.set('renderEnv', 'vue'); - expect(host.renderEnv).toBe('vue'); - - expect(host.getComponentContext).toThrow('Method not implemented.'); - }); - - it('connect', () => { - const mockFn = jest.fn(); - const mockRenderer = { isSimulatorRenderer: true }; - host.connect(mockRenderer, mockFn); - expect(host.renderer).toEqual(mockRenderer); - - // await delayObxTick(); - expect(mockFn).toHaveBeenCalled(); - }); - - it('mountViewport', () => { - const mockBounds = { - top: 10, - bottom: 100, - left: 10, - right: 100, - }; - host.mountViewport({ - getBoundingClientRect() { - return mockBounds; - }, - }); - expect(host.viewport.bounds).toEqual(mockBounds); - }); - - it('autorun', () => { - const mockFn = jest.fn(); - host.autorun(mockFn); - expect(mockFn).toHaveBeenCalled(); - }); - - it('purge', () => { - host.purge(); - }); - - it('isEnter', () => { - const mockBounds = { - top: 10, - bottom: 100, - left: 10, - right: 100, - }; - host.mountViewport({ - getBoundingClientRect() { - return mockBounds; - }, - }); - expect( - host.isEnter({ - globalX: 5, - globalY: 50, - }), - ).toBeFalsy(); - expect( - host.isEnter({ - globalX: 115, - globalY: 50, - }), - ).toBeFalsy(); - expect( - host.isEnter({ - globalX: 50, - globalY: 50, - }), - ).toBeTruthy(); - expect( - host.isEnter({ - globalX: 50, - globalY: 5, - }), - ).toBeFalsy(); - expect( - host.isEnter({ - globalX: 50, - globalY: 150, - }), - ).toBeFalsy(); - expect( - host.isEnter({ - globalX: 150, - globalY: 150, - }), - ).toBeFalsy(); - }); - - it('fixEvent', () => { - expect(host.fixEvent({ fixed: true, clientX: 1 })).toEqual({ fixed: true, clientX: 1 }); - }); - - it('findDOMNodes', () => { - host.connect({ - findDOMNodes: () => { - return null; - }, - }, () => {}); - expect(host.findDOMNodes()).toBeNull(); - - const mockElems = [document.createElement('div')]; - host.connect({ - findDOMNodes: () => { - return mockElems; - }, - }, () => {}); - expect(host.findDOMNodes({})).toBe(mockElems); - expect(host.findDOMNodes({}, 'xxx')).toBeNull(); - expect(host.findDOMNodes({}, 'div')).toEqual(mockElems); - }); - - it('getClosestNodeInstance', () => { - const mockFn = jest.fn(() => { - return { - node: {}, - nodeId: 'id', - docId: 'docId', - }; - }); - host.connect({ - getClosestNodeInstance: mockFn, - }, () => {}); - expect(host.getClosestNodeInstance()).toEqual({ - node: {}, - nodeId: 'id', - docId: 'docId', - }); - }); - - it('getNodeInstanceFromElement', () => { - expect(host.getNodeInstanceFromElement()).toBeNull(); - host.getClosestNodeInstance = () => { - return null; - }; - expect(host.getNodeInstanceFromElement({})).toBeNull(); - host.getClosestNodeInstance = () => { - return { - docId: project.currentDocument.id, - nodeId: 'xxx', - }; - }; - expect(host.getNodeInstanceFromElement({})).toBeTruthy(); - }); - - it('getDropContainer', () => { - host.getNodeInstanceFromElement = () => { - return { - node: doc.rootNode, - }; - }; - host.getDropContainer({ - target: {}, - dragObject: { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('page')], - }, - }); - }); - - it('getComponentInstances', () => { - const mockNode = { - document: { id: 'docId' }, - }; - host.instancesMap = { - docId: { - get() { - return [{ comp: true }, { comp2: true }]; - }, - }, - }; - expect(host.getComponentInstances(mockNode)) - .toEqual([{ comp: true }, { comp2: true }]); - - const mockInst = { inst: true }; - host.getClosestNodeInstance = () => { - return { - instance: mockInst, - }; - }; - expect(host.getComponentInstances(mockNode, { instance: mockInst })) - .toEqual([{ comp: true }, { comp2: true }]); - }); - - it('setNativeSelection / setDraggingState / setCopyState / clearState', () => { - const mockFn1 = jest.fn(); - const mockFn2 = jest.fn(); - const mockFn3 = jest.fn(); - const mockFn4 = jest.fn(); - host.connect({ - setNativeSelection: mockFn1, - setDraggingState: mockFn2, - setCopyState: mockFn3, - clearState: mockFn4, - }, () => {}); - host.setNativeSelection(true); - expect(mockFn1).toHaveBeenCalledWith(true); - host.setDraggingState(false); - expect(mockFn2).toHaveBeenCalledWith(false); - host.setCopyState(true); - expect(mockFn3).toHaveBeenCalledWith(true); - host.clearState(); - expect(mockFn4).toHaveBeenCalled(); - }); - - it('sensorAvailable / deactiveSensor', () => { - expect(host.sensorAvailable).toBeTruthy(); - host.deactiveSensor(); - expect(host.sensing).toBeFalsy(); - }); - - it('getComponent', () => { - host.connect({ - getComponent: () => { - return {}; - }, - }, () => {}); - expect(host.getComponent()).toEqual({}); - expect(host.createComponent()).toBeNull(); - expect(host.setSuspense()).toBeFalsy(); - }); - - it('setInstance', () => { - host.instancesMap = {}; - host.setInstance('docId1', 'id1', [{}]); - expect(host.instancesMap.docId1.get('id1')).toEqual([{}]); - - host.setInstance('docId1', 'id1', null); - expect(host.instancesMap.docId1.get('id1')).toBeUndefined(); - }); - }); - - describe('locate 方法', () => { - beforeEach(() => { - const mockBounds = { - top: 10, - bottom: 100, - left: 10, - right: 100, - }; - host.mountViewport({ - getBoundingClientRect() { - return mockBounds; - }, - }); - }); - it('locate,没有 nodes', () => { - expect(host.locate({ - dragObject: { - type: IPublicEnumDragObjectType.Node, - nodes: [], - }, - })).toBeUndefined(); - }); - it('locate,没有 document', () => { - project.removeDocument(doc); - expect(host.locate({ - dragObject: { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('page')], - }, - })).toBeNull(); - }); - it('notFoundComponent', () => { - expect(host.locate({ - dragObject: { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('form')], - }, - })).toBeUndefined(); - }) - it('locate', () => { - host.locate({ - dragObject: { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('page')], - }, - }); - }); - }); - - describe('事件测试', () => { - it('setupDragAndClick', () => {}); - it('setupContextMenu', async () => { - const mockDocument = getMockDocument(); - const mockWindow = getMockWindow(mockDocument); - const mockIframe = { - contentWindow: mockWindow, - contentDocument: mockDocument, - dispatchEvent() {}, - }; - - host.set('library', [ - { - package: '@ali/vc-deep', - library: 'lib', - urls: ['a.js', 'b.js'], - }, - ]); - - host.componentsConsumer.consume(() => {}); - host.injectionConsumer.consume(() => {}); - await host.mountContentFrame(mockIframe); - - host.setupContextMenu(); - host.getNodeInstanceFromElement = () => { - return { - node: { componentMeta: { componentName: 'Button', getMetadata() { return {} } }, contains() {} }, - }; - }; - const mockFn = jest.fn(); - host.designer.editor.on('designer.builtinSimulator.contextmenu', mockFn); - fireEvent.contextMenu(document, {}); - // TODO: - // expect(mockFn).toHaveBeenCalledWith({ selected: 'Button' }); - }); - }); - - it('事件测试', async () => { - const mockDocument = getMockDocument(); - const mockWindow = getMockWindow(mockDocument); - const mockIframe = { - contentWindow: mockWindow, - contentDocument: mockDocument, - dispatchEvent() {}, - }; - - // 非法分支测试 - host.mountContentFrame(); - expect(host._iframe).toBeUndefined(); - - host.set('library', [ - { - package: '@ali/vc-deep', - library: 'lib', - urls: ['a.js', 'b.js'], - }, - ]); - - host.componentsConsumer.consume(() => {}); - host.injectionConsumer.consume(() => {}); - await host.mountContentFrame(mockIframe); - - expect(host.contentWindow).toBe(mockWindow); - - mockDocument.triggerEventListener( - 'mouseover', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener( - 'mouseleave', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener( - 'mousedown', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener( - 'mouseup', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener( - 'mousemove', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener('click', getMockEvent(document.createElement('input')), host); - mockDocument.triggerEventListener( - 'dblclick', - getMockEvent(mockDocument.createElement('div')), - host, - ); - mockDocument.triggerEventListener( - 'contextmenu', - getMockEvent(mockDocument.createElement('div')), - host, - ); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/renderer.test.tsx b/packages/designer/tests/builtin-simulator/renderer.test.tsx deleted file mode 100644 index d580afcb2..000000000 --- a/packages/designer/tests/builtin-simulator/renderer.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import '../fixtures/window'; -import { getMockRenderer } from '../utils'; -import { isSimulatorRenderer } from '../../src/builtin-simulator/renderer'; - -describe('renderer 测试', () => { - it('renderer', () => { - expect(isSimulatorRenderer(getMockRenderer())).toBeTruthy(); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/resource-consumer.test.ts b/packages/designer/tests/builtin-simulator/resource-consumer.test.ts deleted file mode 100644 index c310c39d2..000000000 --- a/packages/designer/tests/builtin-simulator/resource-consumer.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import ResourceConsumer from '../../src/builtin-simulator/resource-consumer'; -import { delayObxTick, delay } from '../utils'; - -it('ResourceConsumer 测试,先消费再监听', async () => { - const con = new ResourceConsumer(() => ({ a: 1, b: 2 })); - - const mockFn = jest.fn(); - con.consume((data) => { - mockFn(data); - }); - - await delay(1000); - - expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 }); - con.consume(() => {}); - - await con.waitFirstConsume(); - - con.dispose(); -}); - -it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer', async () => { - const mockFn = jest.fn(); - const con = new ResourceConsumer(() => ({ a: 1, b: 2 }), () => { - const o = { a: 3, b: 4 }; - mockFn(o); - return o; - }); - - con.consume({ isSimulatorRenderer: true }); - - await delay(1000); - - expect(mockFn).toHaveBeenCalledWith({ a: 3, b: 4 }); - con.consume(() => {}); - - await con.waitFirstConsume(); -}); - -it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer,没有 consume', async () => { - const mockFn = jest.fn(); - const con = new ResourceConsumer(() => ({ a: 1, b: 2 })); - - con.consume({ isSimulatorRenderer: true }); -}); - -it('ResourceConsumer 测试,先监听再消费', async () => { - const con = new ResourceConsumer(() => ({ a: 1, b: 2 })); - - con.waitFirstConsume(); - - const mockFn = jest.fn(); - con.consume((data) => { - mockFn(data); - }); - - await delay(1000); - - expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 }); -}); diff --git a/packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts b/packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts deleted file mode 100644 index 64e19376e..000000000 --- a/packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import '../../fixtures/window'; -import PropTypes from 'prop-types'; -import { LowcodeTypes, parseMetadata, parseProps } from '../../../src/builtin-simulator/utils/parse-metadata'; -import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret'; - -describe('parseMetadata', () => { - it('parseMetadata', async () => { - const md1 = parseMetadata('Div'); - const md2 = parseMetadata({ componentName: 'Div' }); - }); - it('LowcodeTypes.shape', async () => { - const result = (window as any).PropTypes.shape() - expect(result).toBeDefined(); - }); -}); - -describe('LowcodeTypes basic type validators', () => { - it('should validate string types', () => { - const stringValidator = LowcodeTypes.string; - // 对 stringValidator 进行测试 - const props = { testProp: 'This is a string' }; - const propName = 'testProp'; - const componentName = 'TestComponent'; - - const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeNull(); // No error for valid string - }); - - it('should fail with a non-string type', () => { - const stringValidator = LowcodeTypes.string; - const props = { testProp: 42 }; - const propName = 'testProp'; - const componentName = 'TestComponent'; - - const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeInstanceOf(Error); // Error for non-string type - expect(result.message).toContain('Invalid prop `testProp` of type `number` supplied to `TestComponent`, expected `string`.'); - }); - - it('should pass with a valid number', () => { - const numberValidator = LowcodeTypes.number; - const props = { testProp: 42 }; - const propName = 'testProp'; - const componentName = 'TestComponent'; - - const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeNull(); // No error for valid number - }); - - it('should fail with a non-number type', () => { - const numberValidator = LowcodeTypes.number; - const props = { testProp: 'Not a number' }; - const propName = 'testProp'; - const componentName = 'TestComponent'; - - const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeInstanceOf(Error); // Error for non-number type - expect(result.message).toContain('Invalid prop `testProp` of type `string` supplied to `TestComponent`, expected `number`.'); - }); -}); - -describe('Custom type constructors', () => { - it('should create a custom type validator using define', () => { - const customType = LowcodeTypes.define(PropTypes.string, 'customType'); - const props = { testProp: 'This is a string' }; - const propName = 'testProp'; - const componentName = 'TestComponent'; - - // 测试有效值 - const validResult = customType(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(validResult).toBeNull(); // No error for valid string - - // 测试无效值 - const invalidProps = { testProp: 42 }; - const invalidResult = customType(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(invalidResult).toBeInstanceOf(Error); // Error for non-string type - - // 验证 lowcodeType 属性 - expect(customType.lowcodeType).toEqual('customType'); - - // 验证 isRequired 属性 - const requiredResult = customType.isRequired(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(requiredResult).toBeInstanceOf(Error); // Error for non-string type - }); -}); - - -describe('Advanced type constructors', () => { - describe('oneOf Type Validator', () => { - const oneOfValidator = LowcodeTypes.oneOf(['red', 'green', 'blue']); - const propName = 'color'; - const componentName = 'ColorPicker'; - - it('should pass with a valid value', () => { - const props = { color: 'red' }; - const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeNull(); // No error for valid value - }); - - it('should fail with an invalid value', () => { - const props = { color: 'yellow' }; - const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeInstanceOf(Error); // Error for invalid value - expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`yellow\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`); - }); - - it('should fail with a non-existing value', () => { - const props = { color: 'others' }; - const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret); - expect(result).toBeInstanceOf(Error); // Error for non-existing value - expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`others\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`); - }); - }); -}); - - -describe('parseProps function', () => { - it('should correctly parse propTypes and defaultProps', () => { - const component = { - propTypes: { - name: LowcodeTypes.string, - age: LowcodeTypes.number, - }, - defaultProps: { - name: 'John Doe', - age: 30, - }, - }; - const parsedProps = parseProps(component); - - // 测试结果长度 - expect(parsedProps.length).toBe(2); - - // 测试 name 属性 - const nameProp: any = parsedProps.find(prop => prop.name === 'name'); - expect(nameProp).toBeDefined(); - expect(nameProp.propType).toEqual('string'); - expect(nameProp.defaultValue).toEqual('John Doe'); - - // 测试 age 属性 - const ageProp: any = parsedProps.find(prop => prop.name === 'age'); - expect(ageProp).toBeDefined(); - expect(ageProp.propType).toEqual('number'); - expect(ageProp.defaultValue).toEqual(30); - }); -}); - -describe('parseProps function', () => { - it('should correctly parse propTypes and defaultProps', () => { - const component = { - propTypes: { - name: LowcodeTypes.string, - age: LowcodeTypes.number, - }, - defaultProps: { - name: 'John Doe', - age: 30, - }, - }; - const parsedProps = parseProps(component); - - // 测试结果长度 - expect(parsedProps.length).toBe(2); - - // 测试 name 属性 - const nameProp: any = parsedProps.find(prop => prop.name === 'name'); - expect(nameProp).toBeDefined(); - expect(nameProp.propType).toEqual('string'); - expect(nameProp.defaultValue).toEqual('John Doe'); - - // 测试 age 属性 - const ageProp: any = parsedProps.find(prop => prop.name === 'age'); - expect(ageProp).toBeDefined(); - expect(ageProp.propType).toEqual('number'); - expect(ageProp.defaultValue).toEqual(30); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/utils/path.test.ts b/packages/designer/tests/builtin-simulator/utils/path.test.ts deleted file mode 100644 index 17d63f344..000000000 --- a/packages/designer/tests/builtin-simulator/utils/path.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - generateComponentName, - getNormalizedImportPath, - isPackagePath, - toTitleCase, - makeRelativePath, - removeVersion, - resolveAbsoluatePath, - joinPath, -} from '../../../src/builtin-simulator/utils/path'; - -describe('builtin-simulator/utils/path 测试', () => { - it('isPackagePath', () => { - expect(isPackagePath('a')).toBeTruthy(); - expect(isPackagePath('@ali/a')).toBeTruthy(); - expect(isPackagePath('@alife/a')).toBeTruthy(); - expect(isPackagePath('a.b')).toBeTruthy(); - expect(isPackagePath('./a')).toBeFalsy(); - expect(isPackagePath('../a')).toBeFalsy(); - expect(isPackagePath('/a')).toBeFalsy(); - }); - - it('toTitleCase', () => { - expect(toTitleCase('a')).toBe('A'); - expect(toTitleCase('a_b')).toBe('AB'); - expect(toTitleCase('a b')).toBe('AB'); - expect(toTitleCase('a-b')).toBe('AB'); - expect(toTitleCase('a.b')).toBe('AB'); - expect(toTitleCase('a.b.cx')).toBe('ABCx'); - }); - - it('generateComponentName', () => { - expect(generateComponentName('a/index.js')).toBe('A'); - expect(generateComponentName('a_b/index.js')).toBe('AB'); - expect(generateComponentName('a_b/index.web.js')).toBe('AB'); - expect(generateComponentName('a_b/index.xxx.js')).toBe('AB'); - expect(generateComponentName('a_b')).toBe('AB'); - expect(generateComponentName('')).toBe('Component'); - }); - - it('getNormalizedImportPath', () => { - expect(getNormalizedImportPath('/a')).toBe('/a'); - expect(getNormalizedImportPath('/a/')).toBe('/a/'); - expect(getNormalizedImportPath('/a/index.js')).toBe('/a'); - expect(getNormalizedImportPath('/a/index.ts')).toBe('/a'); - expect(getNormalizedImportPath('/a/index.jsx')).toBe('/a'); - expect(getNormalizedImportPath('/a/index.tsx')).toBe('/a'); - expect(getNormalizedImportPath('/a/index.x')).toBe('/a/index.x'); - }); - - it('makeRelativePath', () => { - expect(makeRelativePath('/a/b/c', '/a/b')).toBe('c'); - expect(makeRelativePath('a/b/c', '/a/c')).toBe('a/b/c'); - expect(makeRelativePath('/a/b/c', '/a/c')).toBe('./b/c'); - expect(makeRelativePath('/a/b/c', '/a/c/d')).toBe('../b/c'); - }); - - it('resolveAbsoluatePath', () => { - expect(resolveAbsoluatePath('/a/b/c', '/a')).toBe('/a/b/c'); - expect(resolveAbsoluatePath('@ali/fe', '/a')).toBe('@ali/fe'); - expect(resolveAbsoluatePath('./a/b', '/c')).toBe('/c/a/b'); - expect(resolveAbsoluatePath('./a/b/d', '/c')).toBe('/c/a/b/d'); - expect(resolveAbsoluatePath('../a/b', '/c')).toBe('/a/b'); - expect(resolveAbsoluatePath('../a/b/d', '/c')).toBe('/a/b/d'); - expect(resolveAbsoluatePath('../../a', 'c')).toBe('../a'); - }); - - it('joinPath', () => { - expect(joinPath('/a', 'b', 'c')).toBe('/a/b/c'); - expect(joinPath('a', 'b', 'c')).toBe('./a/b/c'); - }); - - it('removeVersion', () => { - expect(removeVersion('@ali/fe')).toBe('@ali/fe'); - expect(removeVersion('@ali/fe@1.0.0/index')).toBe('@ali/fe/index'); - expect(removeVersion('haha')).toBe('haha'); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/utils/throttle.test.ts b/packages/designer/tests/builtin-simulator/utils/throttle.test.ts deleted file mode 100644 index 1a2300e43..000000000 --- a/packages/designer/tests/builtin-simulator/utils/throttle.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import '../../fixtures/disable-raf'; -import { throttle } from '../../../src/builtin-simulator/utils/throttle'; - -const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); - -const cb = jest.fn(); - -describe('throttle', () => { - it('simple', async () => { - const fn = throttle(cb, 1000); - fn(); - - expect(cb).toBeCalledTimes(1); - - await delay(200); - fn(); - - await delay(400); - fn(); - expect(cb).toBeCalledTimes(1); - }); -}); diff --git a/packages/designer/tests/builtin-simulator/viewport.test.ts b/packages/designer/tests/builtin-simulator/viewport.test.ts deleted file mode 100644 index e9972fc7c..000000000 --- a/packages/designer/tests/builtin-simulator/viewport.test.ts +++ /dev/null @@ -1,180 +0,0 @@ -import '../fixtures/window'; -import { getMockWindow, getMockElement, delay } from '../utils'; -import { Editor, globalContext } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { DocumentModel } from '../../src/document/document-model'; -import Viewport from '../../src/builtin-simulator/viewport'; -import { Designer } from '../../src/designer/designer'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - - -describe('Viewport 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let viewport: Viewport; - let viewportElem; - - beforeAll(() => { - editor = new Editor(); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - - window.DOMRect = class { - constructor(top, left, width, height) { - return { top, left, width, height }; - } - }; - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - // doc = project.createDocument(formSchema); - }); - - afterEach(() => { - project.unload(); - // project.mountSimulator(undefined); - designer.purge(); - designer = null; - project = null; - viewport = null; - }); - - it('基本函数测试', async () => { - const rect = { - width: 500, - height: 500, - top: 100, - bottom: 500, - left: 100, - right: 500, - }; - viewportElem = getMockElement('div', rect); - viewport = new Viewport(); - viewport.mount(); - expect(viewport.viewportElement).toBeUndefined(); - expect(viewport.width).toBe(1000); - expect(viewport.height).toBe(600); - expect(viewport.toGlobalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 }); - expect(viewport.toLocalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 }); - - viewport.mount(viewportElem); - expect(viewport.viewportElement).toBe(viewportElem); - - expect(viewport.bounds).toEqual(rect); - expect(viewport.contentBounds).toEqual({ top: 0, left: 0, width: 500, height: 500 }); - expect(viewport.rect).toEqual(rect); - - expect(viewport.width).toBe(500); - expect(viewport.contentWidth).toBe('100%'); - expect(viewport.height).toBe(500); - expect(viewport.contentHeight).toBe('100%'); - - await delay(100); - viewportElem.setWidth(300); - viewport.width = 300; - expect(viewport.width).toBe(300); - - await delay(100); - viewportElem.setHeight(300); - viewport.height = 300; - expect(viewport.height).toBe(300); - - viewport.contentWidth = 200; - expect(viewport.contentWidth).toBe(200); - - viewport.contentHeight = 200; - expect(viewport.contentHeight).toBe(200); - }); - - it('scale', () => { - const rect = { - width: 500, - height: 500, - top: 100, - bottom: 500, - left: 100, - right: 500, - }; - viewportElem = getMockElement('div', rect); - viewport = new Viewport(); - viewport.mount(viewportElem); - - expect(viewport.scale).toBe(1); - viewport.scale = 2; - expect(viewport.scale).toBe(2); - - expect(viewport.contentWidth).toBe(500 / 2); - expect(viewport.contentHeight).toBe(500 / 2); - - viewport.width = 300; - viewportElem.setWidth(300); - expect(viewport.contentWidth).toBe(300 / 2); - - viewport.height = 300; - viewportElem.setHeight(300); - expect(viewport.contentHeight).toBe(300 / 2); - - expect(() => { viewport.scale = NaN; }).toThrow(); - expect(() => { viewport.scale = -1; }).toThrow(); - }); - - it('setScrollTarget / scrollTarget / scrolling', async () => { - const rect = { - width: 500, - height: 500, - top: 100, - bottom: 500, - left: 100, - right: 500, - }; - viewportElem = getMockElement('div', rect); - viewport = new Viewport(); - viewport.mount(viewportElem); - - const mockWindow = getMockWindow(); - viewport.setScrollTarget(mockWindow); - // TODO: 待 mock - viewport.scrollTarget; - // expect(viewport.scrollTarget).toBe(mockWindow); - - // mock scrollTarget - // viewport._scrollTarget = { left: 0, top: 0 }; - // viewport._scrollTarget.left = 123; - // viewport._scrollTarget.top = 1234; - mockWindow.triggerEventListener('scroll'); - expect(viewport.scrolling).toBeTruthy(); - // TODO: 待 mock - viewport.scrollX; - viewport.scrollY; - // expect(viewport.scrollX).toBe(123); - // expect(viewport.scrollY).toBe(1234); - await delay(100); - expect(viewport.scrolling).toBeFalsy(); - - mockWindow.triggerEventListener('resize'); - }); - - it('toGlobalPoint / toLocalPoint', () => { - const rect = { - width: 500, - height: 500, - top: 100, - bottom: 500, - left: 100, - right: 500, - }; - viewportElem = getMockElement('div', rect); - viewport = new Viewport(); - viewport.mount(viewportElem); - - expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 200, clientY: 200 }); - expect(viewport.toLocalPoint({ clientX: 200, clientY: 200 })).toEqual({ clientX: 100, clientY: 100 }); - - viewport.scale = 2; - expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 300, clientY: 300 }); - expect(viewport.toLocalPoint({ clientX: 300, clientY: 300 })).toEqual({ clientX: 100, clientY: 100 }); - }); -}); diff --git a/packages/designer/tests/designer/active-tracker.test.ts b/packages/designer/tests/designer/active-tracker.test.ts deleted file mode 100644 index cea47a08a..000000000 --- a/packages/designer/tests/designer/active-tracker.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import '../fixtures/window'; -import { ActiveTracker } from '../../src/designer/active-tracker'; - -it('ActiveTracker 测试,Node', () => { - const tracker = new ActiveTracker(); - - const mockFn = jest.fn(); - const mockNode = { isNode: true }; - const off = tracker.onChange(mockFn); - - tracker.track(mockNode); - expect(mockFn).toHaveBeenCalledWith({ node: mockNode }); - - expect(tracker.currentNode).toBe(mockNode); - - off(); - mockFn.mockClear(); - tracker.track(mockNode); - expect(mockFn).not.toHaveBeenCalled(); -}); - -it('ActiveTracker 测试,ActiveTarget', () => { - const tracker = new ActiveTracker(); - - const mockFn = jest.fn(); - const mockNode = { isNode: true }; - const off = tracker.onChange(mockFn); - const mockTarget = { node: mockNode, detail: { isDetail: true }, instance: { isInstance: true } }; - - tracker.track(mockTarget); - expect(mockFn).toHaveBeenCalledWith(mockTarget); - - expect(tracker.currentNode).toBe(mockNode); - expect(tracker.detail).toEqual({ isDetail: true }); - expect(tracker.instance).toEqual({ isInstance: true }); - - off(); - mockFn.mockClear(); - tracker.track(mockNode); - expect(mockFn).not.toHaveBeenCalled(); -}); diff --git a/packages/designer/tests/designer/builtin-hotkey.test.ts b/packages/designer/tests/designer/builtin-hotkey.test.ts deleted file mode 100644 index 9cb068ac1..000000000 --- a/packages/designer/tests/designer/builtin-hotkey.test.ts +++ /dev/null @@ -1,395 +0,0 @@ -import '../fixtures/window'; -import { - Editor, - globalContext, - Hotkey as InnerHotkey, -} from '@alilc/lowcode-editor-core'; -import { Designer } from '../../src/designer/designer'; -import formSchema from '../fixtures/schema/form'; -import { fireEvent } from '@testing-library/react'; -import { builtinHotkey } from '../../../engine/src/inner-plugins/builtin-hotkey'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; -import { ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer'; -import { IPublicApiPlugins } from '@alilc/lowcode-types'; -import { Logger, Project, Canvas } from '@alilc/lowcode-shell'; -import { Workspace } from '@alilc/lowcode-workspace'; - -const editor = new Editor(); -const workspace = new Workspace(); - -let designer: Designer; - -// keyCode 对应表:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode -// hotkey 模块底层用的 keyCode,所以还不能用 key / code 测试 -describe('快捷键测试', () => { - let pluginManager: LowCodePluginManager; - let project: any = {}; - beforeAll(() => { - return new Promise((resolve, reject) => { - const hotkey: any = new InnerHotkey(); - const logger = new Logger({ level: 'warn', bizName: 'common' }); - const contextApiAssembler = { - assembleApis(context: ILowCodePluginContextPrivate){ - context.plugins = pluginManager as IPublicApiPlugins; - context.hotkey = hotkey; - context.logger = logger; - context.project = project; - context.canvas = new Canvas(editor); - } - }; - pluginManager = new LowCodePluginManager(contextApiAssembler).toProxy(); - pluginManager.register(builtinHotkey); - globalContext.register(editor, Editor); - globalContext.register(editor, 'editor'); - globalContext.register(workspace, 'workspace'); - pluginManager.init().then(() => { - resolve({}); - }); - }) - }); - afterAll(() => { - pluginManager.dispose(); - }); - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - editor.set('designer', designer); - designer.project.open(formSchema); - project.__proto__ = new Project(designer.project); - }); - afterEach(() => { - designer = null; - }); - - it('right', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbj')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 39 }); - - expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy(); - }); - - it('left', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 37 }); - - expect(designer.currentSelection?.selected.includes('node_k1ow3cbj')).toBeTruthy(); - }); - - it('down', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 40 }); - - expect(designer.currentSelection?.selected.includes('node_k1ow3cbo')).toBeTruthy(); - }); - - it('up', () => { - const secondCardNode = designer.currentDocument?.getNode('node_k1ow3cbm')!; - secondCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 38 }); - - expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy(); - }); - - // 跟右侧节点调换位置 - it('option + right', () => { - const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!; - firstButtonNode.select(); - - fireEvent.keyDown(document, { keyCode: 39, altKey: true }); - - expect(firstButtonNode.prevSibling?.getId()).toBe('node_k1ow3cbp'); - }); - - // 跟左侧节点调换位置 - it('option + left', () => { - const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - secondButtonNode.select(); - - fireEvent.keyDown(document, { keyCode: 37, altKey: true }); - - expect(secondButtonNode.nextSibling?.getId()).toBe('node_k1ow3cbn'); - }); - - // 向父级移动该节点 - it('option + up', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 38, altKey: true }); - }); - - // 将节点移入到兄弟节点中 - it('option + up', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 40, altKey: true }); - }); - - // 撤销 - it('command + z', async () => { - const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!; - let secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - - // 等待第一个 session 结束 - await new Promise(resolve => setTimeout(resolve, 1000)); - - firstButtonNode.remove(); - expect(secondButtonNode.getParent()?.children.size).toBe(1); - - await new Promise(resolve => setTimeout(resolve, 1000)); - - fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); - - // 重新获取一次节点,因为 documentModel.import 是全画布刷新 - secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - expect(secondButtonNode.getParent()?.children.size).toBe(2); - }); - - // 重做 - it('command + y', async () => { - const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!; - let secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - - // 等待第一个 session 结束 - await new Promise(resolve => setTimeout(resolve, 1000)); - - firstButtonNode.remove(); - expect(secondButtonNode.getParent()?.children.size).toBe(1); - - await new Promise(resolve => setTimeout(resolve, 1000)); - - fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); - - // 重新获取一次节点,因为 documentModel.import 是全画布刷新 - secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - expect(secondButtonNode.getParent()?.children.size).toBe(2); - - await new Promise(resolve => setTimeout(resolve, 1000)); - - fireEvent.keyDown(document, { keyCode: 89, metaKey: true }); - - // 重新获取一次节点,因为 documentModel.import 是全画布刷新 - secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - expect(secondButtonNode.getParent()?.children.size).toBe(1); - }); - - it('command + c', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - firstCardNode.select(); - - fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - }); - - it('command + v', async () => { - const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - secondButtonNode.select(); - - fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 86, metaKey: true }); - - await new Promise(resolve => setTimeout(resolve, 1000)); - - // clipboard 异步,先注释 - // expect(secondButtonNode.getParent()?.children.size).toBe(3); - }); - - // 撤销所有选中 - it('escape', () => { - const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - firstCardNode.select(); - - expect(designer.currentSelection!.selected.includes('node_k1ow3cbp')).toBeTruthy(); - - fireEvent.keyDown(document, { keyCode: 27 }); - - expect(designer.currentSelection!.selected.length).toBe(0); - }); - - // 删除节点 - it('delete', () => { - const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!; - const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; - firstButtonNode.select(); - - expect(secondButtonNode.prevSibling.id).toBe('node_k1ow3cbn'); - - fireEvent.keyDown(document, { keyCode: 46 }); - - expect(secondButtonNode.prevSibling).toBeNull(); - }); - - - describe('非正常分支', () => { - it('liveEditing mode', () => { - designer.project.mountSimulator({ - liveEditing: { - editing: {}, - }, - }); - editor.set('designer', designer); - designer.currentDocument?.selection.select('page'); - // nothing happened - fireEvent.keyDown(document, { keyCode: 39 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 37 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 40 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 38 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 39, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 37, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 40, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 38, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 89, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 86, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 27 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(document, { keyCode: 46 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - }); - - it('isFormEvent: true', () => { - const inputDOMNode = document.createElement('INPUT'); - document.body.appendChild(inputDOMNode); - designer.currentDocument?.selection.select('page'); - // nothing happened - - fireEvent.keyDown(inputDOMNode, { keyCode: 39 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 37 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 40 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 38 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 39, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 37, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 40, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 38, altKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 90, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 89, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 67, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 86, metaKey: true }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 27 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - - fireEvent.keyDown(inputDOMNode, { keyCode: 46 }); - expect(designer.currentDocument?.selection.selected[0]).toBe('page'); - }); - - it('doc is null', () => { - designer.currentDocument?.selection.select('page'); - designer.project.documents = []; - - fireEvent.keyDown(document, { keyCode: 39 }); - - fireEvent.keyDown(document, { keyCode: 37 }); - - fireEvent.keyDown(document, { keyCode: 40 }); - - fireEvent.keyDown(document, { keyCode: 38 }); - - fireEvent.keyDown(document, { keyCode: 39, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 37, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 40, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 38, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 89, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 86, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 27 }); - - fireEvent.keyDown(document, { keyCode: 46 }); - }); - - it('selected is []', () => { - fireEvent.keyDown(document, { keyCode: 39 }); - - fireEvent.keyDown(document, { keyCode: 37 }); - - fireEvent.keyDown(document, { keyCode: 40 }); - - fireEvent.keyDown(document, { keyCode: 38 }); - - fireEvent.keyDown(document, { keyCode: 39, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 37, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 40, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 38, altKey: true }); - - fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 89, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 86, metaKey: true }); - - fireEvent.keyDown(document, { keyCode: 27 }); - - fireEvent.keyDown(document, { keyCode: 46 }); - }); - }); -}); \ No newline at end of file diff --git a/packages/designer/tests/designer/designer.test.ts b/packages/designer/tests/designer/designer.test.ts deleted file mode 100644 index 8bca7d84a..000000000 --- a/packages/designer/tests/designer/designer.test.ts +++ /dev/null @@ -1,500 +0,0 @@ -import '../fixtures/window'; -import { Editor, globalContext, Setters } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { DocumentModel } from '../../src/document/document-model'; -import { Designer } from '../../src/designer/designer'; -import { Dragon } from '../../src/designer/dragon'; -// import { TransformStage } from '../../src/document/node/transform-stage'; -import formSchema from '../fixtures/schema/form'; -import buttonMetadata from '../fixtures/component-metadata/button'; -import pageMetadata from '../fixtures/component-metadata/page'; -import divMetadata from '../fixtures/component-metadata/div'; -import { delayObxTick } from '../utils'; -import { fireEvent } from '@testing-library/react'; -import { IPublicEnumDragObjectType, IPublicEnumTransformStage } from '@alilc/lowcode-types'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - -const mockNode = { - internalToShellNode() { - return 'mockNode'; - }, -}; - -describe('Designer 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let dragon: Dragon; - - beforeAll(() => { - editor = new Editor(); - const setters = new Setters(); - editor.set('setters', setters); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = project.createDocument(formSchema); - dragon = new Dragon(designer); - }); - - afterEach(() => { - project.unload(); - project.mountSimulator(undefined); - designer.purge(); - designer = null; - project = null; - dragon = null; - }); - - describe('onDragstart / onDrag / onDragend', () => { - it('IPublicEnumDragObjectType.Node', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - const dragStartMockFn2 = jest.fn(); - const dragMockFn2 = jest.fn(); - const dragEndMockFn2 = jest.fn(); - - const designer = new Designer({ - editor, - shellModelFactory, - onDragstart: dragStartMockFn, - onDrag: dragMockFn, - onDragend: dragEndMockFn, - }); - editor.on('designer.dragstart', dragStartMockFn2); - editor.on('designer.drag', dragMockFn2); - editor.on('designer.dragend', dragEndMockFn2); - const { dragon } = designer; - - dragon.boost( - { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockFn).toHaveBeenCalledTimes(1); - expect(dragStartMockFn2).toHaveBeenCalledTimes(1); - expect(dragMockFn).toHaveBeenCalledTimes(1); - expect(dragMockFn2).toHaveBeenCalledTimes(1); - - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockFn).toHaveBeenCalledTimes(2); - expect(dragMockFn2).toHaveBeenCalledTimes(2); - - setMockDropLocation(); - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - - expect(dragEndMockFn).toHaveBeenCalledTimes(1); - expect(dragEndMockFn2).toHaveBeenCalledTimes(1); - - function setMockDropLocation() { - const mockTarget = { - document: doc, - children: { - get(x) { - return x; - }, - insert() {}, - internalInsert() {}, - }, - }; - const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - - return designer.createLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - } - }); - - it('IPublicEnumDragObjectType.NodeData', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - const dragStartMockFn2 = jest.fn(); - const dragMockFn2 = jest.fn(); - const dragEndMockFn2 = jest.fn(); - - const designer = new Designer({ - editor, - shellModelFactory, - onDragstart: dragStartMockFn, - onDrag: dragMockFn, - onDragend: dragEndMockFn, - }); - editor.on('designer.dragstart', dragStartMockFn2); - editor.on('designer.drag', dragMockFn2); - editor.on('designer.dragend', dragEndMockFn2); - const { dragon } = designer; - - dragon.boost( - { - type: IPublicEnumDragObjectType.NodeData, - data: [{ - componentName: 'Button', - }], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockFn).toHaveBeenCalledTimes(1); - expect(dragStartMockFn2).toHaveBeenCalledTimes(1); - expect(dragMockFn).toHaveBeenCalledTimes(1); - expect(dragMockFn2).toHaveBeenCalledTimes(1); - - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockFn).toHaveBeenCalledTimes(2); - expect(dragMockFn2).toHaveBeenCalledTimes(2); - - setMockDropLocation(); - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - - expect(dragEndMockFn).toHaveBeenCalledTimes(1); - expect(dragEndMockFn2).toHaveBeenCalledTimes(1); - - function setMockDropLocation() { - const mockTarget = { - document: doc, - children: { - get(x) { - return x; - }, - insert() {}, - internalInsert() {}, - }, - }; - const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - - return designer.createLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - } - }); - }); - - it('addPropsReducer / transformProps', () => { - // 没有相应的 reducer - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Init)).toEqual({ num: 1 }); - // props 是数组 - expect(designer.transformProps([{ num: 1 }], mockNode, IPublicEnumTransformStage.Init)).toEqual([{ num: 1 }]); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Init); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Init); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Clone); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Serilize); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Render); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Save); - - designer.addPropsReducer((props, node) => { - props.num += 1; - return props; - }, IPublicEnumTransformStage.Upgrade); - - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Init)).toEqual({ num: 3 }); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Clone)).toEqual({ num: 2 }); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Serilize)).toEqual({ num: 2 }); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Render)).toEqual({ num: 2 }); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Save)).toEqual({ num: 2 }); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Upgrade)).toEqual({ num: 2 }); - - designer.addPropsReducer((props, node) => { - throw new Error('calculate error'); - }, IPublicEnumTransformStage.Upgrade); - expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Upgrade)).toEqual({ num: 2 }); - }); - - it('setProps', () => { - // 第一次设置 props - const initialProps = { - simulatorComponent: { isSimulatorComp: true }, - simulatorProps: { designMode: 'design' }, - suspensed: true, - componentMetadatas: [buttonMetadata, divMetadata], - }; - designer = new Designer({ - editor, - shellModelFactory, - ...initialProps, - }); - - expect(designer.simulatorComponent).toEqual({ isSimulatorComp: true }); - expect(designer.simulatorProps).toEqual({ designMode: 'design' }); - expect(designer.suspensed).toBeTruthy(); - expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy(); - expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy(); - const { editor: editorFromDesigner, shellModelFactory: shellModelFactoryFromDesigner, ...others } = (designer as any).props; - expect(others).toEqual(initialProps); - expect(designer.get('simulatorProps')).toEqual({ designMode: 'design' }); - expect(designer.get('suspensed')).toBeTruthy(); - expect(designer.get('xxx')).toBeUndefined(); - - // 第二次设置 props - const updatedProps = { - simulatorComponent: { isSimulatorComp2: true }, - simulatorProps: { designMode: 'live' }, - suspensed: false, - componentMetadatas: [buttonMetadata], - }; - designer.setProps(updatedProps); - - expect(designer.simulatorComponent).toEqual({ isSimulatorComp2: true }); - expect(designer.simulatorProps).toEqual({ designMode: 'live' }); - expect(designer.suspensed).toBeFalsy(); - expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy(); - expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy(); - const { editor: editorFromDesigner2, shellModelFactory: shellModelFactoryFromDesigner2, ...others2 } = (designer as any).props; - expect(others2).toEqual(updatedProps); - - // 第三次设置 props,跟第二次值一样,for 覆盖率测试 - const updatedProps2 = updatedProps; - designer.setProps(updatedProps2); - - expect(designer.simulatorComponent).toEqual({ isSimulatorComp2: true }); - expect(designer.simulatorProps).toEqual({ designMode: 'live' }); - expect(designer.suspensed).toBeFalsy(); - expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy(); - expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy(); - const { editor: editorFromDesigner3, shellModelFactory: shellModelFactoryFromDesigner3, ...others3 } = (designer as any).props; - expect(others3).toEqual(updatedProps); - }); - - describe('getSuitableInsertion', () => { - it('没有 currentDocument', () => { - project.unload(); - expect(designer.getSuitableInsertion({})).toBeNull(); - }); - - it('有选中节点,isContainer && 允许放子节点', () => { - designer.createComponentMeta(divMetadata); - designer.createComponentMeta(buttonMetadata); - designer.currentSelection?.select('node_k1ow3cbo'); - const { target, index } = designer.getSuitableInsertion( - doc.createNode({ componentName: 'Button' }), - ); - expect(target).toBe(doc.getNode('node_k1ow3cbo')); - expect(index).toBeUndefined(); - }); - - it('有选中节点,不是 isContainer', () => { - designer.createComponentMeta(divMetadata); - designer.createComponentMeta(buttonMetadata); - designer.currentSelection?.select('node_k1ow3cbn'); - const { target, index } = designer.getSuitableInsertion( - doc.createNode({ componentName: 'Button' }), - ); - expect(target).toBe(doc.getNode('node_k1ow3cbo')); - expect(index).toBe(1); - }); - - it('无选中节点', () => { - designer.createComponentMeta(pageMetadata); - const { target, index } = designer.getSuitableInsertion( - doc.createNode({ componentName: 'Button' }), - ); - expect(target).toBe(doc.getNode('page')); - expect(index).toBeUndefined(); - }); - }); - - it('getComponentMetasMap', () => { - designer.createComponentMeta({ - componentName: 'Div', - title: '容器', - docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', - devMode: 'procode', - tags: ['布局'], - }); - - expect(designer.getComponentMetasMap().get('Div')).not.toBeUndefined(); - }); - - it('refreshComponentMetasMap', () => { - designer.createComponentMeta({ - componentName: 'Div', - title: '容器', - docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', - devMode: 'procode', - tags: ['布局'], - }); - - const originalMetasMap = designer.getComponentMetasMap(); - designer.refreshComponentMetasMap(); - - expect(originalMetasMap).not.toBe(designer.getComponentMetasMap()); - }); - - describe('loadIncrementalAssets', () => { - it('components && packages', async () => { - editor.set('assets', { components: [], packages: [] }); - const fn = jest.fn(); - - project.mountSimulator({ - setupComponents: fn, - }); - await designer.loadIncrementalAssets({ - components: [{ - componentName: 'Div2', - title: '容器', - docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', - devMode: 'proCode', - tags: ['布局'], - }], - packages: [], - }); - - const comps = editor.get('assets').components; - expect(comps).toHaveLength(1); - expect(fn).toHaveBeenCalled(); - }); - - it('no components && packages', async () => { - editor.set('assets', { components: [], packages: [] }); - const fn = jest.fn(); - - project.mountSimulator({ - setupComponents: fn, - }); - await designer.loadIncrementalAssets({}); - - expect(fn).not.toHaveBeenCalled(); - }); - }); - - it('createLocation / clearLocation', () => { - const mockTarget = { - document: doc, - children: { - get(x) { - return x; - }, - insert() {}, - internalInsert() {}, - }, - }; - const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - - const loc = designer.createLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(designer.dropLocation).toBe(loc); - - const doc2 = project.createDocument({ componentName: 'Page' }); - designer.createLocation({ - target: { - document: doc2, - children: { - get(x) { - return x; - }, - insert() {}, - internalInsert() {}, - }, - }, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - designer.clearLocation(); - expect(designer.dropLocation).toBeUndefined(); - }); - - it('autorun', async () => { - const mockFn = jest.fn(); - designer.autorun(() => { - mockFn(); - }, true); - - await delayObxTick(); - - expect(mockFn).toHaveBeenCalled(); - }); - - it('suspensed', () => { - designer.suspensed = true; - expect(designer.suspensed).toBeTruthy(); - designer.suspensed = false; - expect(designer.suspensed).toBeFalsy(); - }); - - it('schema', () => { - // TODO: matchSnapshot - designer.schema; - designer.setSchema({ - componentsTree: [ - { - componentName: 'Page', - props: {}, - }, - ], - }); - }); - - it('createOffsetObserver / clearOobxList / touchOffsetObserver', () => { - project.mountSimulator({ - computeComponentInstanceRect() {}, - }); - designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} }); - expect(designer.oobxList).toHaveLength(1); - designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} }); - expect(designer.oobxList).toHaveLength(2); - - designer.clearOobxList(true); - expect(designer.oobxList).toHaveLength(0); - - const obx = designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} }); - obx.pid = 'xxx'; - obx.compute = () => {}; - expect(designer.oobxList).toHaveLength(1); - - designer.touchOffsetObserver(); - expect(designer.oobxList).toHaveLength(1); - }); -}); diff --git a/packages/designer/tests/designer/detecting.test.ts b/packages/designer/tests/designer/detecting.test.ts deleted file mode 100644 index 7cc4c88e8..000000000 --- a/packages/designer/tests/designer/detecting.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Detecting } from '../../src/designer/detecting'; - -it('Detecting 测试', () => { - const fn = jest.fn(); - const detecting = new Detecting(); - detecting.onDetectingChange(fn); - - expect(detecting.enable).toBeTruthy(); - - const mockNode = { document }; - detecting.capture(mockNode); - expect(fn).toHaveBeenCalledWith(detecting.current); - expect(detecting.current).toBe(mockNode); - - detecting.release({}); - detecting.release(mockNode); - expect(detecting.current).toBeNull(); - - detecting.capture(mockNode); - detecting.leave(document); - expect(detecting.current).toBeNull(); - - detecting.capture(mockNode); - detecting.enable = false; - expect(detecting.current).toBeNull(); -}); diff --git a/packages/designer/tests/designer/dragon.test.ts b/packages/designer/tests/designer/dragon.test.ts deleted file mode 100644 index 1041f0425..000000000 --- a/packages/designer/tests/designer/dragon.test.ts +++ /dev/null @@ -1,369 +0,0 @@ -import '../fixtures/window'; -import { Editor, globalContext } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { DocumentModel } from '../../src/document/document-model'; -import { Designer } from '../../src/designer/designer'; -import { - Dragon, - isDragNodeObject, - isDragNodeDataObject, - isDragAnyObject, - isLocateEvent, - isShaken, - setShaken, - isInvalidPoint, - isSameAs, -} from '../../src/designer/dragon'; -import { IPublicEnumDragObjectType } from '@alilc/lowcode-types'; -import formSchema from '../fixtures/schema/form'; -import { fireEvent } from '@testing-library/react'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - -describe('Dragon 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let dragon: Dragon; - - beforeAll(() => { - editor = new Editor(); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = project.createDocument(formSchema); - dragon = new Dragon(designer); - }); - - afterEach(() => { - project.unload(); - project.mountSimulator(undefined); - designer.purge(); - designer = null; - project = null; - dragon = null; - }); - - it.skip('drag NodeData', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - dragon.onDragstart((e) => { - console.log('start', e, e.originalEvent, e.originalEvent.clientX); - }); - - dragon.onDrag((e) => { - console.log('drag', e, e.originalEvent, e.originalEvent.clientX); - }); - - dragon.onDragend((e) => { - console.log('end', e, e.originalEvent); - }); - - dragon.boost( - { - type: IPublicEnumDragObjectType.NodeData, - data: [{ componentName: 'Button' }], - }, - new Event('dragstart', { clientX: 100, clientY: 100 }), - ); - - fireEvent.dragOver(document, { clientX: 108, clientY: 108 }); - fireEvent.dragEnd(document, { clientX: 118, clientY: 118 }); - }); - - it.skip('drag Node', () => { - console.log(new MouseEvent('mousedown', { clientX: 1 }).clientX); - // console.log(new Event('mousedown', { clientX: 1 }).clientX); - // console.log(new Event('drag', { clientX: 1 }).clientX); - // console.log(new CustomEvent('drag', { clientX: 1 }).clientX); - console.log(document.createEvent('dragstart', { clientX: 1 }).clientX); - }); - - it('mouse NodeData', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - const offDragStart = dragon.onDragstart(dragStartMockFn); - - const offDrag = dragon.onDrag(dragMockFn); - - const offDragEnd = dragon.onDragend(dragEndMockFn); - - dragon.boost( - { - type: IPublicEnumDragObjectType.NodeData, - data: [{ componentName: 'Button' }], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - - expect(dragStartMockFn).toHaveBeenCalledTimes(1); - expect(dragMockFn).toHaveBeenCalledTimes(2); - expect(dragEndMockFn).toHaveBeenCalledTimes(1); - }); - - it('mouse Node', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - const offDragStart = dragon.onDragstart(dragStartMockFn); - const offDrag = dragon.onDrag(dragMockFn); - const offDragEnd = dragon.onDragend(dragEndMockFn); - - dragon.boost( - { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - // mouseDown 模式正常不会触发 dragStart 事件,除非 shaken 型 - expect(dragStartMockFn).not.toHaveBeenCalled(); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockFn).toHaveBeenCalledTimes(1); - expect(dragMockFn).toHaveBeenCalledTimes(1); - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockFn).toHaveBeenCalledTimes(2); - expect(dragon.dragging).toBeTruthy(); - - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - - expect(dragEndMockFn).toHaveBeenCalledTimes(1); - - offDragStart(); - offDrag(); - offDragEnd(); - dragMockFn.mockClear(); - - dragon.boost( - { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - - expect(dragMockFn).not.toHaveBeenCalled(); - }); - - it('mouse Node & esc', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - const offDragStart = dragon.onDragstart(dragStartMockFn); - const offDrag = dragon.onDrag(dragMockFn); - const offDragEnd = dragon.onDragend(dragEndMockFn); - - dragon.boost( - { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.keyDown(document, { keyCode: 27 }); - expect(dragon.designer.dropLocation).toBeUndefined(); - }); - - it('mouse Node & copy', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - const offDragStart = dragon.onDragstart(dragStartMockFn); - const offDrag = dragon.onDrag(dragMockFn); - const offDragEnd = dragon.onDragend(dragEndMockFn); - - dragon.boost( - { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - const mockFn1 = jest.fn(); - project.mountSimulator({ setCopyState: mockFn1 }); - expect(dragon.getSimulators().size).toBe(1); - fireEvent.keyDown(document, { ctrlKey: true }); - expect(mockFn1).toHaveBeenCalled(); - }); - - it('from', () => { - const dragStartMockFn = jest.fn(); - const dragMockFn = jest.fn(); - const dragEndMockFn = jest.fn(); - - const offDragStart = dragon.onDragstart(dragStartMockFn); - const offDrag = dragon.onDrag(dragMockFn); - const offDragEnd = dragon.onDragend(dragEndMockFn); - const mockBoostFn = jest - .fn((e) => { - return { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }; - }) - .mockImplementationOnce(() => null); - - const offFrom = dragon.from(document, mockBoostFn); - - // 无用 mouseDown,无效的按钮 - fireEvent.mouseDown(document, { button: 2 }); - expect(dragStartMockFn).not.toHaveBeenCalled(); - - // 无用 mouseDown,无效的 dragObject - fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - expect(dragStartMockFn).not.toHaveBeenCalled(); - - fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - expect(dragStartMockFn).not.toHaveBeenCalled(); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockFn).toHaveBeenCalledTimes(1); - expect(dragMockFn).toHaveBeenCalledTimes(1); - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockFn).toHaveBeenCalledTimes(2); - expect(dragon.dragging).toBeTruthy(); - - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - - expect(dragEndMockFn).toHaveBeenCalledTimes(1); - - offDragStart(); - offDrag(); - offDragEnd(); - dragMockFn.mockClear(); - - fireEvent.mouseMove(document, { clientX: 100, clientY: 100 }); - expect(dragMockFn).not.toHaveBeenCalled(); - - offFrom(); - fireEvent.mouseMove(document, { clientX: 100, clientY: 100 }); - expect(dragMockFn).not.toHaveBeenCalled(); - }); - - it('addSensor / removeSensor', () => { - const sensor = { - locate: () => {}, - sensorAvailable: true, - isEnter: () => true, - fixEvent: () => {}, - deactiveSensor: () => {}, - }; - const sensor2 = {}; - dragon.addSensor(sensor); - expect(dragon.sensors.length).toBe(1); - expect(dragon.activeSensor).toBeUndefined(); - dragon.boost( - { - type: IPublicEnumDragObjectType.NodeData, - data: [{ componentName: 'Button' }], - }, - new MouseEvent('mousedown', { clientX: 100, clientY: 100 }), - ); - - fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - expect(dragon.activeSensor).toBe(sensor); - // remove a non-existing sensor - dragon.removeSensor(sensor2); - expect(dragon.sensors.length).toBe(1); - dragon.removeSensor(sensor); - expect(dragon.sensors.length).toBe(0); - }); - - it('has sensor', () => { - const mockFn1 = jest.fn(); - const mockDoc = document.createElement('iframe').contentWindow?.document; - dragon.addSensor({ - fixEvent: () => {}, - locate: () => {}, - contentDocument: mockDoc, - }); - project.mountSimulator({ - setCopyState: mockFn1, - setNativeSelection: () => {}, - clearState: () => {}, - setDraggingState: () => {}, - }); - - const mockBoostFn = jest - .fn((e) => { - return { - type: IPublicEnumDragObjectType.Node, - nodes: [doc.getNode('node_k1ow3cbn')], - }; - }) - .mockImplementationOnce(() => null); - - const offFrom = dragon.from(document, mockBoostFn); - - // TODO: 想办法 mock 一个 iframe.currentDocument - fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - }); -}); - -describe('导出的其他函数', () => { - it('isDragNodeObject', () => { - expect(isDragNodeObject({ type: IPublicEnumDragObjectType.Node, nodes: [] })).toBeTruthy(); - }); - it('isDragNodeDataObject', () => { - expect(isDragNodeDataObject({ type: IPublicEnumDragObjectType.NodeData, data: [] })).toBeTruthy(); - }); - it('isDragAnyObject', () => { - expect(isDragAnyObject()).toBeFalsy(); - expect(isDragAnyObject({ type: IPublicEnumDragObjectType.Node, nodes: [] })).toBeFalsy(); - expect(isDragAnyObject({ type: IPublicEnumDragObjectType.NodeData, data: [] })).toBeFalsy(); - expect(isDragAnyObject({ type: 'others', data: [] })).toBeTruthy(); - }); - it('isLocateEvent', () => { - expect(isLocateEvent({ type: 'LocateEvent' })).toBeTruthy(); - }); - it('isShaken', () => { - expect( - isShaken( - { clientX: 1, clientY: 1, target: {} }, - { clientX: 1, clientY: 1, target: { other: 1 } }, - ), - ).toBeTruthy(); - expect(isShaken({ shaken: true })).toBeTruthy(); - expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 2 })).toBeFalsy(); - expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 3, clientY: 5 })).toBeTruthy(); - }); - it('setShaken', () => { - const e = {}; - setShaken(e); - expect(isShaken(e)).toBeTruthy(); - }); - - it('isInvalidPoint', () => { - expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 1 })).toBeTruthy(); - expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 1, clientY: 6 })).toBeTruthy(); - expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 6 })).toBeTruthy(); - expect(isInvalidPoint({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy(); - }); - - it('isSameAs', () => { - expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 1, clientY: 1 })).toBeTruthy(); - expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy(); - }); -}); diff --git a/packages/designer/tests/designer/location.test.ts b/packages/designer/tests/designer/location.test.ts deleted file mode 100644 index 205fc24b0..000000000 --- a/packages/designer/tests/designer/location.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { - DropLocation, - isLocationData, - isLocationChildrenDetail, - isRowContainer, - isChildInline, - getRectTarget, - isVerticalContainer, - isVertical, - getWindow, -} from '../../src/designer/location'; -import { getMockElement } from '../utils'; - -describe('DropLocation 测试', () => { - it('constructor', () => { - const mockTarget = { document }; - const mockDetail = {}; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - const loc = new DropLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(loc.getContainer()).toBe(mockTarget); - expect(loc.document).toBe(document); - expect(loc.target).toBe(mockTarget); - expect(loc.detail).toBe(mockDetail); - expect(loc.source).toBe(mockSource); - expect(loc.event).toBe(mockEvent); - - const mockEvent2 = { type: 'LocateEvent', data: [] }; - const loc2 = loc.clone(mockEvent2); - expect(loc2.target).toBe(mockTarget); - expect(loc2.detail).toBe(mockDetail); - expect(loc2.source).toBe(mockSource); - expect(loc2.event).toBe(mockEvent2); - }); - - it('constructor, detail: undefined', () => { - const mockTarget = { document }; - const mockDetail = undefined; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - const loc = new DropLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(loc.getInsertion()).toBeNull(); - }); - - it('constructor, detail.type: Children, detail.index <= 0', () => { - const mockTarget = { document }; - const mockDetail = { type: 'Children', index: -1 }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - const loc = new DropLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(loc.getInsertion()).toBeNull(); - }); - - it('constructor, detail.type: Children, detail.index > 0', () => { - const mockTarget = { - document, - children: { - get(x) { - return x; - }, - }, - }; - const mockDetail = { type: 'Children', index: 1 }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - const loc = new DropLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(loc.getInsertion()).toBe(0); - }); - - it('constructor, detail.type: Prop', () => { - const mockTarget = { - document, - children: { - get(x) { - return x; - }, - }, - }; - const mockDetail = { type: 'Prop', index: 1, near: { node: { x: 1 } } }; - const mockSource = {}; - const mockEvent = { type: 'LocateEvent', nodes: [] }; - const loc = new DropLocation({ - target: mockTarget, - detail: mockDetail, - source: mockSource, - event: mockEvent, - }); - - expect(loc.getInsertion()).toEqual({ x: 1 }); - }); -}); - -it('isLocationData', () => { - expect(isLocationData({ target: {}, detail: {} })).toBeTruthy(); -}); - -it('isLocationChildrenDetail', () => { - expect(isLocationChildrenDetail({ type: 'Children' })).toBeTruthy(); -}); - -it('isRowContainer', () => { - expect(isRowContainer({ nodeType: Node.TEXT_NODE })).toBeTruthy(); - window.getComputedStyle = jest - .fn(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'flex' : ''; - }, - }; - }) - .mockImplementationOnce(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'flex' : 'column'; - }, - }; - }) - .mockImplementationOnce(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'grid' : 'column'; - }, - }; - }); - expect(isRowContainer(getMockElement('div'))).toBeFalsy(); - expect(isRowContainer(getMockElement('div'))).toBeTruthy(); - expect(isRowContainer(getMockElement('div'))).toBeTruthy(); -}); - -it('isChildInline', () => { - window.getComputedStyle = jest - .fn(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'inline' : 'float'; - }, - }; - }); - - expect(isChildInline({ nodeType: Node.TEXT_NODE })).toBeTruthy(); - expect(isChildInline(getMockElement('div'))).toBeTruthy(); -}); - -it('getRectTarget', () => { - expect(getRectTarget()).toBeNull(); - expect(getRectTarget({ computed: false })).toBeNull(); - expect(getRectTarget({ elements: [{}] })).toEqual({}); -}); - -it('isVerticalContainer', () => { - window.getComputedStyle = jest - .fn(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'flex' : 'row'; - }, - }; - }); - expect(isVerticalContainer()).toBeFalsy(); - expect(isVerticalContainer({ elements: [getMockElement('div')] })).toBeTruthy(); -}); - -it('isVertical', () => { - expect(isVertical({ elements: [] })).toBeFalsy(); - expect(isVertical({ elements: [getMockElement('div')] })).toBeFalsy(); - const e1 = getMockElement('div'); - const e2 = getMockElement('div'); - e2.appendChild(e1); - expect(isVertical({ elements: [e1] })).toBeTruthy(); - window.getComputedStyle = jest - .fn(() => { - return { - getPropertyValue: (pName) => { - return pName === 'display' ? 'inline' : 'float'; - }, - }; - }); - expect(isVertical({ elements: [getMockElement('div')] })).toBeTruthy(); -}); - -it('getWindow', () => { - const mockElem = getMockElement('div'); - expect(getWindow(mockElem)).toBe(window); - expect(getWindow(document)).toBe(window); -}); diff --git a/packages/designer/tests/designer/scroller.test.ts b/packages/designer/tests/designer/scroller.test.ts deleted file mode 100644 index ff03608b0..000000000 --- a/packages/designer/tests/designer/scroller.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import '../fixtures/window'; -import { Editor, globalContext } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { DocumentModel } from '../../src/document/document-model'; -import { ScrollTarget, Scroller } from '../../src/designer/scroller'; -import { Designer } from '../../src/designer/designer'; -import { - Dragon, -} from '../../src/designer/dragon'; -import formSchema from '../fixtures/schema/form'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - -describe('Scroller 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - let dragon: Dragon; - - beforeAll(() => { - editor = new Editor(); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - }); - - beforeEach(() => { - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = project.createDocument(formSchema); - dragon = new Dragon(designer); - }); - - afterEach(() => { - project.unload(); - project.mountSimulator(undefined); - designer.purge(); - designer = null; - project = null; - dragon = null; - }); - - function getMockWindow() { - let scrollX = 0; - let scrollY = 0; - const mockWindow = { - scrollTo(x, y) { - if (typeof x === 'number') { - scrollX = x; - scrollY = y; - } else { - scrollX = x.left; - scrollY = x.top; - } - }, - get scrollX() { return scrollX; }, - get scrollY() { return scrollY; }, - scrollHeight: 1000, - scrollWidth: 500, - document: {}, - nodeType: Node.ELEMENT_NODE, - }; - return mockWindow; - } - - describe('ScrollTarget 测试', () => { - it('constructor', () => { - const win = getMockWindow(); - const target = new ScrollTarget(win); - expect(target.scrollWidth).toBe(500); - expect(target.scrollHeight).toBe(1000); - target.scrollToXY(50, 50); - expect(target.left).toBe(50); - expect(target.top).toBe(50); - - target.scrollTo({ left: 100, top: 100 }); - expect(target.left).toBe(100); - expect(target.top).toBe(100); - }); - }); - - function mockRAF() { - let rafCount = 0; - window.requestAnimationFrame = (fn) => { - if (rafCount++ < 2) { - fn(); - } else { - window.requestAnimationFrame = () => {}; - } - }; - } - describe('Scroller 测试', () => { - it('scrollTarget: ScrollTarget', () => { - const win = getMockWindow(); - const scrollTarget = new ScrollTarget(win); - const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } }); - mockRAF(); - scroller.scrollTo({ left: 50, top: 50 }); - - mockRAF(); - scroller.scrolling({ globalX: 100, globalY: 100 }); - }); - - it('scrollTarget: ScrollTarget, same left / top', () => { - const win = getMockWindow(); - const scrollTarget = new ScrollTarget(win); - const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } }); - mockRAF(); - scrollTarget.scrollTo({ left: 50, top: 50 }); - scroller.scrollTo({ left: 50, top: 50 }); - - mockRAF(); - scroller.scrolling({ globalX: 100, globalY: 100 }); - }); - - it('scrollTarget: Element', () => { - const win = getMockWindow(); - // const scrollTarget = new ScrollTarget(win); - const scroller = new Scroller({ scrollTarget: win, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } }); - mockRAF(); - scroller.scrollTo({ left: 50, top: 50 }); - - mockRAF(); - scroller.scrolling({ globalX: 100, globalY: 100 }); - }); - - it('scrollTarget: null', () => { - const win = getMockWindow(); - // const scrollTarget = new ScrollTarget(win); - const scroller = new Scroller({ scrollTarget: null, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } }); - mockRAF(); - scroller.scrollTo({ left: 50, top: 50 }); - - mockRAF(); - scroller.scrolling({ globalX: 100, globalY: 100 }); - }); - }); -}); diff --git a/packages/designer/tests/designer/setting/__snapshots__/setting-field.test.ts.snap b/packages/designer/tests/designer/setting/__snapshots__/setting-field.test.ts.snap deleted file mode 100644 index 240c679a7..000000000 --- a/packages/designer/tests/designer/setting/__snapshots__/setting-field.test.ts.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`setting-field 测试 纯粹的 UnitTest 常规方法 1`] = ` -Object { - "extraProps": Object { - "defaultValue": "NORMAL", - "display": "inline", - }, - "name": "behavior", - "setter": Object { - "componentName": "MixedSetter", - "props": Object { - "setters": Array [ - Object { - "_owner": null, - "key": null, - "props": Object { - "cancelable": false, - "loose": false, - "options": Array [ - Object { - "title": "普通", - "value": "NORMAL", - }, - Object { - "title": "隐藏", - "value": "HIDDEN", - }, - ], - }, - "ref": null, - }, - "VariableSetter", - ], - }, - }, - "title": "默认状态", - "type": "field", -} -`; - -exports[`setting-field 测试 纯粹的 UnitTest 常规方法 2`] = ` -Object { - "extraProps": Object { - "defaultValue": "NORMAL", - "display": "inline", - }, - "name": "behavior", - "setter": Object { - "componentName": "MixedSetter", - "props": Object { - "setters": Array [ - Object { - "_owner": null, - "key": null, - "props": Object { - "cancelable": false, - "loose": false, - "options": Array [ - Object { - "title": "普通", - "value": "NORMAL", - }, - Object { - "title": "隐藏", - "value": "HIDDEN", - }, - ], - }, - "ref": null, - }, - "VariableSetter", - ], - }, - }, - "title": "默认状态", - "type": "field", -} -`; diff --git a/packages/designer/tests/designer/setting/setting-field.test.ts b/packages/designer/tests/designer/setting/setting-field.test.ts deleted file mode 100644 index 53ed2829d..000000000 --- a/packages/designer/tests/designer/setting/setting-field.test.ts +++ /dev/null @@ -1,281 +0,0 @@ -// @ts-nocheck -import '../../fixtures/window'; -import { - Editor, - Setters as InnerSetters, -} from '@alilc/lowcode-editor-core'; -import { - Setters, -} from '@alilc/lowcode-shell'; -import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry'; -import { SettingField } from '../../../src/designer/setting/setting-field'; -import { Node } from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import settingSchema from '../../fixtures/schema/setting'; -import buttonMeta from '../../fixtures/component-metadata/button'; -import { DocumentModel } from 'designer/src/document'; -import { delayObxTick } from '../../utils'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -const editor = new Editor(); - -describe('setting-field 测试', () => { - let designer: Designer; - let doc: DocumentModel; - let setters: Setters; - beforeEach(() => { - setters = new InnerSetters(); - editor.set('setters', setters); - designer = new Designer({ editor, shellModelFactory }); - designer.createComponentMeta(buttonMeta); - doc = designer.project.open(settingSchema); - }); - afterEach(() => { - designer._componentMetasMap.clear(); - designer = null; - doc.purge(); - doc = null; - }); - - describe('纯粹的 UnitTest', () => { - let mockNode: Node; - let mockTopEntry: SettingTopEntry; - beforeEach(() => { - mockNode = new Node(designer.currentDocument, { - componentName: 'Button', - props: { - // a: 'str', - // b: 222, - // obj: { - // x: 1, - // }, - // jse: { - // type: 'JSExpression', - // value: 'state.a', - // mock: 111, - // } - }, - }); - // mockTopEntry = new SettingTopEntry(editor, [mockNode]); - }); - afterEach(() => { - mockNode = null; - mockTopEntry = null; - }); - - it('常规方法', () => { - // 普通 field - const settingEntry = mockNode.settingEntry; - const field = settingEntry.get('behavior'); - expect(field.title).toBe('默认状态'); - expect(field.expanded).toBeTruthy(); - field.setExpanded(false); - expect(field.expanded).toBeFalsy(); - expect(field.config).toMatchSnapshot(); - expect(field.getConfig()).toMatchSnapshot(); - expect(field.getConfig('extraProps')).toEqual({ - display: 'inline', - defaultValue: 'NORMAL', - }); - expect(field.items).toHaveLength(0); - expect(field.getItems()).toHaveLength(0); - expect(field.getItems(x => x)).toHaveLength(0); - - expect(field.setter.componentName).toBe('MixedSetter'); - field.purge(); - expect(field.items).toHaveLength(0); - - const subField = field.createField({ - name: 'sub', - title: 'sub', - }); - subField.setValue({ - type: 'JSExpression', - value: 'state.a', - mock: 'haha', - }); - subField.setHotValue('heihei'); - expect(subField.getHotValue('heihei')); - expect(subField.getValue().mock).toBe('heihei'); - - // 不存在的 field - const nonExistingField = mockNode.settingEntry.get('non-exsiting'); - expect(nonExistingField.setter).toBeNull(); - - // group 类型的 field - const groupField = settingEntry.get('groupkgzzeo41'); - expect(groupField.items).toEqual([]); - - // 有子节点的 field - const objField = settingEntry.get('obj'); - expect(objField.items).toHaveLength(3); - expect(objField.getItems()).toHaveLength(3); - expect(objField.getItems(x => x.name === 'a')).toHaveLength(1); - objField.purge(); - expect(objField.items).toHaveLength(0); - const objAField = settingEntry.get('obj.a'); - expect(objAField.setter).toBe('StringSetter'); - }); - - it('setValue / getValue / setHotValue / getHotValue', () => { - // 获取已有的 prop - const settingEntry = mockNode.settingEntry as SettingTopEntry; - const field = settingEntry.get('behavior'); - - // 会读取 extraProps.defaultValue - expect(field.getHotValue()).toBe('NORMAL'); - - field.setValue('HIDDEN'); - expect(field.getValue()).toBe('HIDDEN'); - expect(field.getHotValue()).toBe('HIDDEN'); - - field.setHotValue('DISABLED'); - expect(field.getHotValue()).toBe('DISABLED'); - - field.setHotValue('NORMAL', { fromSetHotValue: true }); - expect(field.getHotValue()).toBe('NORMAL'); - - field.setValue('HIDDEN', true); - expect(field.getHotValue()).toBe('HIDDEN'); - - // dirty fix list setter - field.setHotValue([{ __sid__: 1 }]); - - // 数组的 field - const arrField = settingEntry.get('arr'); - const subArrField = arrField.createField({ - name: 0, - title: 'sub', - }); - const subArrField02 = arrField.createField({ - name: 1, - title: 'sub', - }); - const subArrField03 = arrField.createField({ - name: '2', - title: 'sub', - }); - subArrField.setValue({name: '1'}); - expect(subArrField.path).toEqual(['arr', 0]); - expect(subArrField02.path).toEqual(['arr', 1]); - subArrField02.setValue({name: '2'}); - expect(subArrField.getValue()).toEqual({name: '1'}); - expect(arrField.getHotValue()).toEqual([{name: '1'}, {name: '2'}]); - subArrField.clearValue(); - expect(subArrField.getValue()).toBeUndefined(); - expect(arrField.getHotValue()).toEqual([undefined, {name: '2'}]); - subArrField03.setValue({name: '3'}); - expect(arrField.getHotValue()).toEqual([undefined, {name: '2'}, {name: '3'}]); - }); - - it('js expression setValue / setHotValue', () => { - const settingEntry = mockNode.settingEntry; - const field = settingEntry.get('behavior'); - - const subField = field.createField({ - name: 'sub', - title: 'sub', - }); - subField.setValue({ - type: 'JSExpression', - value: 'state.a', - mock: 'haha', - }); - - subField.setHotValue({ - type: 'JSExpression', - value: 'state.b', - }); - - expect(subField.getValue()).toEqual({ - type: 'JSExpression', - value: 'state.b', - mock: 'haha', - }); - - subField.setHotValue('mock02'); - - expect(subField.getValue()).toEqual({ - type: 'JSExpression', - value: 'state.b', - mock: 'mock02', - }); - }); - - it('onEffect', async () => { - const settingEntry = mockNode.settingEntry as SettingTopEntry; - const field = settingEntry.get('behavior'); - - const mockFn = jest.fn(); - - field.onEffect(mockFn); - - field.setValue('DISABLED'); - - await delayObxTick(); - - expect(mockFn).toHaveBeenCalled(); - }); - - it('autorun', async () => { - const settingEntry = mockNode.settingEntry as SettingTopEntry; - const arrField = settingEntry.get('columns'); - const subArrField = arrField.createField({ - name: 0, - title: 'sub', - }); - const objSubField = subArrField.createField({ - name: 'objSub', - title: 'objSub', - }); - const mockFnArrField = jest.fn(); - const mockFnSubArrField = jest.fn(); - const mockFnObjSubField = jest.fn(); - - arrField.setValue([{ objSub: "subMock0.Index.0" }]); - // 这里需要 setValue 两遍,触发 prop 的 purge 方法,使 purged 为 true,之后的 purge 方法不会正常执行,prop 才能正常缓存,autorun 才能正常执行 - // TODO: 该机制后续得研究一下,再确定是否要修改 - arrField.setValue([{ objSub: "subMock0.Index.0" }]); - - arrField.onEffect(() => { - mockFnArrField(arrField.getValue()); - }); - arrField.onEffect(() => { - mockFnSubArrField(subArrField.getValue()); - }); - arrField.onEffect(() => { - mockFnObjSubField(objSubField.getValue()); - }); - - await delayObxTick(); - - expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.0'); - expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.0" }); - expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.0" }]); - - arrField.setValue([{ objSub: "subMock0.Index.1" }]); - - await delayObxTick(); - - expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.1'); - expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.1" }); - expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.1" }]); - - subArrField.setValue({ objSub: "subMock0.Index.2" }); - - await delayObxTick(); - - expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.2'); - expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.2" }); - expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.2" }]); - - objSubField.setValue('subMock0.Index.3'); - - await delayObxTick(); - - expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.3'); - expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.3" }); - expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.3" }]); - }) - }); -}); diff --git a/packages/designer/tests/designer/setting/setting-prop-entry.test.ts b/packages/designer/tests/designer/setting/setting-prop-entry.test.ts deleted file mode 100644 index 3ece67af7..000000000 --- a/packages/designer/tests/designer/setting/setting-prop-entry.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import '../../fixtures/window'; -import { - Editor, - Setters as InnerSetters, -} from '@alilc/lowcode-editor-core'; -import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry'; -import { SettingPropEntry } from '../../../src/designer/setting/setting-prop-entry'; -import { Node } from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import settingSchema from '../../fixtures/schema/setting'; -import divMeta from '../../fixtures/component-metadata/div'; -import { DocumentModel } from 'designer/src/document'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -const editor = new Editor(); - -describe('setting-prop-entry 测试', () => { - let designer: Designer; - let doc: DocumentModel; - let setters: any; - beforeEach(() => { - setters = new InnerSetters(); - editor.set('setters', setters); - designer = new Designer({ editor, shellModelFactory }); - designer.createComponentMeta(divMeta); - doc = designer.project.open(settingSchema); - }); - afterEach(() => { - designer._componentMetasMap.clear(); - designer = null; - doc.purge(); - doc = null; - }); - - describe('纯粹的 UnitTest', () => { - let mockNode: Node; - let mockTopEntry: SettingTopEntry; - beforeEach(() => { - mockNode = new Node(designer.currentDocument, { - componentName: 'Button', - props: { - a: 'str', - b: 222, - obj: { - x: 1, - }, - jse: { - type: 'JSExpression', - value: 'state.a', - mock: 111, - } - }, - }); - mockTopEntry = new SettingTopEntry(editor, [mockNode]); - }); - afterEach(() => { - mockNode = null; - mockTopEntry = null; - }); - - it('常规方法', () => { - // type: group 类型 - const prop = new SettingPropEntry(mockTopEntry, 'xGroup', 'group'); - expect(prop.setKey('xxx')).toBeUndefined(); - expect(prop.remove()).toBeUndefined(); - - const prop2 = new SettingPropEntry(mockTopEntry, '#xGroup'); - expect(prop2.setKey('xxx')).toBeUndefined(); - expect(prop2.remove()).toBeUndefined(); - - expect(prop.getVariableValue()).toBe(''); - }); - - it('setValue / getValue / onValueChange', () => { - // 获取已有的 prop - const prop1 = mockTopEntry.getProp('a'); - prop1.extraProps = { - getValue: (prop, val) => `prefix ${val}`, - // prop 是 shell prop entry - setValue: (prop, val) => { prop.setValue(`modified ${val}`, { disableMutator: true }) }, - defaultValue: 'default', - }; - - expect(prop1.getDefaultValue()).toBe('default'); - expect(prop1.getValue()).toBe('prefix str'); - - // disableMutator: true - prop1.setValue('bbb', false, false, { disableMutator: true }); - expect(prop1.getValue()).toBe('prefix bbb'); - - // disableMutator: false - prop1.setValue('bbb'); - expect(prop1.getValue()).toBe('prefix modified bbb'); - - const mockFn3 = jest.fn(); - const prop2 = mockTopEntry.getProp('obj'); - const prop3 = prop2.get('x'); - const offFn = prop3.onValueChange(mockFn3); - expect(prop3.getValue()).toBe(1); - prop3.setValue(2); - expect(mockFn3).toHaveBeenCalled(); - - offFn(); - prop3.setValue(3); - mockFn3.mockClear(); - expect(mockFn3).toHaveBeenCalledTimes(0); - - const prop4 = mockTopEntry.getProp('b'); - prop4.extraProps = { - getValue: () => { throw 'error'; }, - }; - expect(prop4.getValue()).toBe(222); - }); - - it('clearValue', () => { - const prop1 = mockTopEntry.getProp('a'); - prop1.clearValue(); - expect(prop1.getValue()).toBeUndefined(); - - const mockFn = jest.fn(); - prop1.extraProps = { - setValue: mockFn, - }; - prop1.clearValue(); - expect(mockFn).toHaveBeenCalled(); - }); - - it('getVariableValue/ setUseVariable / isUseVariable / getMockOrValue', () => { - const prop1 = mockTopEntry.getProp('jse'); - - expect(prop1.isUseVariable()).toBeTruthy(); - expect(prop1.useVariable).toBeTruthy(); - - expect(prop1.getMockOrValue()).toEqual(111); - expect(prop1.getVariableValue()).toEqual('state.a'); - - prop1.setUseVariable(false); - expect(prop1.getValue()).toEqual(111); - prop1.setUseVariable(true); - expect(prop1.getValue()).toEqual({ - type: 'JSExpression', - value: '', - mock: 111, - }); - prop1.setUseVariable(true); - }); - }); - - describe('node 构造函数生成 settingEntry', () => { - it('常规方法测试', () => { - const divNode = doc?.getNode('div'); - - const { settingEntry } = divNode!; - const behaviorProp = settingEntry.getProp('behavior'); - expect(behaviorProp.getProps()).toBe(settingEntry); - expect(behaviorProp.props).toBe(settingEntry); - expect(behaviorProp.getName()).toBe('behavior'); - expect(behaviorProp.getKey()).toBe('behavior'); - expect(behaviorProp.isIgnore()).toBeFalsy(); - behaviorProp.setKey('behavior2'); - expect(behaviorProp.getKey()).toBe('behavior2'); - behaviorProp.setKey('behavior'); - - expect(behaviorProp.getNode()).toBe(divNode); - expect(behaviorProp.getId().startsWith('entry')).toBeTruthy(); - expect(behaviorProp.designer).toBe(designer); - expect(behaviorProp.isSingle).toBeTruthy(); - expect(behaviorProp.isMultiple).toBeFalsy(); - expect(behaviorProp.isGroup).toBeFalsy(); - expect(behaviorProp.isSameComponent).toBeTruthy(); - expect(typeof settingEntry.getValue).toBe('function'); - settingEntry.getValue(); - - behaviorProp.setExtraPropValue('extraPropA', 'heihei'); - expect(behaviorProp.getExtraPropValue('extraPropA', 'heihei')); - }); - - it('setValue / getValue', () => { - const divNode = doc?.getNode('div'); - - const { settingEntry } = divNode!; - const behaviorProp = settingEntry.getProp('behavior'); - expect(behaviorProp.getValue()).toBe('NORMAL'); - expect(behaviorProp.getMockOrValue()).toBe('NORMAL'); - - behaviorProp.setValue('LARGE'); - expect(behaviorProp.getValue()).toBe('LARGE'); - // behaviorProp.setPropValue('behavior', 'SMALL'); - // expect(behaviorProp.getValue()).toBe('SMALL'); - behaviorProp.setValue('NORMAL'); - expect(behaviorProp.getValue()).toBe('NORMAL'); - behaviorProp.clearValue(); - behaviorProp.clearPropValue(); - expect(behaviorProp.getValue()).toBeUndefined(); - - behaviorProp.setValue('LARGE'); - expect(behaviorProp.getValue()).toBe('LARGE'); - behaviorProp.remove(); - expect(divNode?.getProp('behavior').getValue()).toBeUndefined(); - }); - - it.skip('type: group 场景测试', () => {}); - - it('JSExpression 类型的 prop', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - - const { settingEntry } = divNode!; - const customClassNameProp = settingEntry.getProp('customClassName'); - expect(customClassNameProp.isUseVariable()).toBeTruthy(); - expect(customClassNameProp.useVariable).toBeTruthy(); - - expect(customClassNameProp.getValue()).toEqual({ - type: 'JSExpression', - value: 'getFromSomewhere()', - }); - expect(customClassNameProp.getMockOrValue()).toBeUndefined(); - expect(customClassNameProp.getVariableValue()).toBe('getFromSomewhere()'); - customClassNameProp.setVariableValue('xxx'); - expect(customClassNameProp.getVariableValue()).toBe('xxx'); - - const customClassName2Prop = settingEntry.getProp('customClassName2'); - expect(customClassName2Prop.getMockOrValue()).toEqual({ - hi: 'mock', - }); - }); - }); -}); diff --git a/packages/designer/tests/designer/setting/setting-top-entry.test.ts b/packages/designer/tests/designer/setting/setting-top-entry.test.ts deleted file mode 100644 index 23a42c2af..000000000 --- a/packages/designer/tests/designer/setting/setting-top-entry.test.ts +++ /dev/null @@ -1,196 +0,0 @@ -import '../../fixtures/window'; -import { Editor, Setters } from '@alilc/lowcode-editor-core'; -import { Node } from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import settingSchema from '../../fixtures/schema/setting'; -import divMeta from '../../fixtures/component-metadata/div'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -const editor = new Editor(); - -describe('setting-top-entry 测试', () => { - let designer: Designer; - beforeEach(() => { - editor.set('setters', new Setters()) - designer = new Designer({ editor, shellModelFactory }); - }); - afterEach(() => { - designer._componentMetasMap.clear(); - designer = null; - }); - - describe('node 构造函数生成 settingEntry', () => { - it('常规方法测试', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - - const { settingEntry } = divNode!; - expect(settingEntry.getPropValue('behavior')).toBe('NORMAL'); - expect(settingEntry.getProp('behavior').getValue()).toBe('NORMAL'); - settingEntry.setPropValue('behavior', 'LARGE'); - expect(settingEntry.getPropValue('behavior')).toBe('LARGE'); - expect(settingEntry.get('behavior').getValue()).toBe('LARGE'); - settingEntry.getProp('behavior').setValue('SMALL'); - expect(settingEntry.getPropValue('behavior')).toBe('SMALL'); - settingEntry.clearPropValue('behavior'); - expect(settingEntry.getPropValue('behavior')).toBeUndefined(); - - expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o'); - settingEntry.setPropValue('fieldId', 'div_k1ow3h1o_new'); - expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o_new'); - - expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha'); - settingEntry.setExtraPropValue('extraPropA', 'haha2'); - expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha2'); - - settingEntry.mergeProps({ - newPropA: 'haha', - }); - expect(settingEntry.getPropValue('newPropA')).toBe('haha'); - settingEntry.setProps({ - newPropB: 'haha', - }); - expect(settingEntry.getPropValue('newPropB')).toBe('haha'); - settingEntry.setValue({ - newPropC: 'haha', - }); - expect(settingEntry.getPropValue('newPropC')).toBe('haha'); - - expect(settingEntry.getPage()).toBe(currentDocument); - expect(settingEntry.getNode()).toBe(divNode); - expect(settingEntry.node).toBe(divNode); - expect(settingEntry.getId()).toBe('div'); - expect(settingEntry.first).toBe(divNode); - expect(settingEntry.designer).toBe(designer); - expect(settingEntry.isSingle).toBeTruthy(); - expect(settingEntry.isMultiple).toBeFalsy(); - expect(settingEntry.isSameComponent).toBeTruthy(); - - expect(typeof settingEntry.getValue).toBe('function'); - settingEntry.getValue(); - }); - - it('onMetadataChange', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div') as Node; - - const { settingEntry } = divNode!; - const mockFn = jest.fn(); - settingEntry.componentMeta.onMetadataChange(mockFn); - settingEntry.componentMeta.refreshMetadata(); - expect(mockFn).toHaveBeenCalled(); - }); - - it.skip('setupItems - customView', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div') as Node; - - const { settingEntry } = divNode; - // 模拟将第一个配置变成 react funcional component - settingEntry.componentMeta.getMetadata().combined[0].items[0] = props => props.xx; - settingEntry.setupItems(); - }); - - it('清理方法测试', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - - const { settingEntry } = divNode!; - expect(settingEntry.items).toHaveLength(3); - settingEntry.purge(); - expect(settingEntry.items).toHaveLength(0); - }); - - it('vision 兼容测试', () => { - designer.createComponentMeta(divMeta); - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - - // console.log(divNode?.getPropValue('behavior')); - const { settingEntry } = divNode!; - - expect(typeof settingEntry.getChildren).toBe('function'); - expect(typeof settingEntry.getDOMNode).toBe('function'); - expect(typeof settingEntry.getStatus).toBe('function'); - expect(typeof settingEntry.setStatus).toBe('function'); - settingEntry.getStatus(); - settingEntry.setStatus(); - settingEntry.getChildren(); - settingEntry.getDOMNode(); - }); - - it('没有 node', () => { - const create1 = designer.createSettingEntry.bind(designer); - const create2 = designer.createSettingEntry.bind(designer, []); - expect(create1).toThrowError('nodes should not be empty'); - expect(create2).toThrowError('nodes should not be empty'); - }); - }); - - describe('designer.createSettingEntry 生成 settingEntry(多 node 场景)', () => { - it('相同类型的 node', () => { - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - const divNode2 = currentDocument?.getNode('div2'); - const settingEntry = designer.createSettingEntry([divNode, divNode2]); - - expect(settingEntry.isMultiple).toBeTruthy(); - expect(settingEntry.isSameComponent).toBeTruthy(); - expect(settingEntry.isSingle).toBeFalsy(); - - expect(settingEntry.getPropValue('behavior')).toBe('NORMAL'); - expect(settingEntry.getProp('behavior').getValue()).toBe('NORMAL'); - settingEntry.setPropValue('behavior', 'LARGE'); - expect(settingEntry.getPropValue('behavior')).toBe('LARGE'); - expect(settingEntry.get('behavior').getValue()).toBe('LARGE'); - // 多个 node 都被成功设值 - expect(divNode?.getPropValue('behavior')).toBe('LARGE'); - expect(divNode2?.getPropValue('behavior')).toBe('LARGE'); - - settingEntry.getProp('behavior').setValue('SMALL'); - expect(settingEntry.getPropValue('behavior')).toBe('SMALL'); - // 多个 node 都被成功设值 - expect(divNode?.getPropValue('behavior')).toBe('SMALL'); - expect(divNode2?.getPropValue('behavior')).toBe('SMALL'); - - settingEntry.clearPropValue('behavior'); - expect(settingEntry.getPropValue('behavior')).toBeUndefined(); - // 多个 node 都被成功设值 - expect(divNode?.getPropValue('behavior')).toBeUndefined(); - expect(divNode2?.getPropValue('behavior')).toBeUndefined(); - - expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o'); - settingEntry.setPropValue('fieldId', 'div_k1ow3h1o_new'); - expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o_new'); - - expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha'); - settingEntry.setExtraPropValue('extraPropA', 'haha2'); - expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha2'); - }); - - it('不同类型的 node', () => { - designer.project.open(settingSchema); - const { currentDocument } = designer.project; - const divNode = currentDocument?.getNode('div'); - const testNode = currentDocument?.getNode('test'); - const settingEntry = designer.createSettingEntry([divNode, testNode]); - - expect(settingEntry.isMultiple).toBeTruthy(); - expect(settingEntry.isSameComponent).toBeFalsy(); - expect(settingEntry.isSingle).toBeFalsy(); - - // 不同类型的 node 场景下,理论上从页面上已没有修改属性的方法调用,所以此处不再断言各设值方法 - // 思考:假如以后面向其他场景,比如用户用 API 强行调用,是否需要做健壮性保护? - }); - }); -}); diff --git a/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap b/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap deleted file mode 100644 index 906313303..000000000 --- a/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap +++ /dev/null @@ -1,1215 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`document-model 测试 各种方法测试 1`] = ` -Object { - "componentsMap": Array [ - Object { - "componentName": "PageHeader", - "devMode": "lowCode", - }, - Object { - "componentName": "RootHeader", - "devMode": "lowCode", - }, - Object { - "componentName": "TextField", - "devMode": "lowCode", - }, - Object { - "componentName": "Column", - "devMode": "lowCode", - }, - Object { - "componentName": "SelectField", - "devMode": "lowCode", - }, - Object { - "componentName": "ColumnsLayout", - "devMode": "lowCode", - }, - Object { - "componentName": "CardContent", - "devMode": "lowCode", - }, - Object { - "componentName": "Card", - "devMode": "lowCode", - }, - Object { - "componentName": "Button", - "devMode": "lowCode", - }, - Object { - "componentName": "Div", - "devMode": "lowCode", - }, - Object { - "componentName": "Form", - "devMode": "lowCode", - }, - Object { - "componentName": "RootContent", - "devMode": "lowCode", - }, - Object { - "componentName": "RootFooter", - "devMode": "lowCode", - }, - Object { - "componentName": "Page", - "devMode": "lowCode", - }, - ], - "componentsTree": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "componentName": "PageHeader", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbd", - "isLocked": false, - "props": Object { - "__slot__action": false, - "__slot__content": false, - "__slot__crumb": false, - "__slot__extraContent": false, - "__slot__logo": false, - "__slot__tab": false, - "__style__": Object {}, - "action": "", - "content": "", - "crumb": "", - "extraContent": "", - "fieldId": "pageHeader_k1ow3h1i", - "logo": "", - "subTitle": false, - "tab": "", - "title": Object { - "value": Array [ - Object { - "componentName": "Text", - "condition": true, - "id": "node_k1ow3cbf", - "props": Object { - "__style__": Object {}, - "behavior": "NORMAL", - "content": Object { - "en-US": "Title", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "个人信息", - }, - "fieldId": "text_k1ow3h1j", - "maxLine": 0, - "showTitle": false, - }, - }, - ], - }, - }, - "title": "", - }, - ], - "componentName": "RootHeader", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cba", - "isLocked": false, - "props": Object {}, - "title": "", - }, - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbz", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h1w", - "fieldName": "name", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "姓名", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [ - Object { - "type": "required", - }, - ], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc1", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h1y", - "fieldName": "englishName", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "英文名", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc3", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h20", - "fieldName": "jobTitle", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "职位", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - ], - "componentName": "Column", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbx", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "colSpan": "", - "fieldId": "column_k1p1bnjm", - }, - "title": "", - }, - Object { - "children": Array [ - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc2", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h1z", - "fieldName": "nickName", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "花名", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - Object { - "componentName": "SelectField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc0", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "autoWidth": true, - "behavior": "NORMAL", - "dataSource": Array [ - Object { - "__sid__": "serial_k1owc4t1", - "defaultChecked": false, - "sid": "opt_k1owc4t2", - "text": Object { - "__sid__": "param_k1owc4tb", - "en-US": "Option 1", - "type": "i18n", - "zh-CN": "男", - }, - "value": "M", - }, - Object { - "__sid__": "serial_k1owc4t2", - "defaultChecked": false, - "sid": "opt_k1owc4t3", - "text": Object { - "__sid__": "param_k1owc4tf", - "en-US": "Option 2", - "type": "i18n", - "zh-CN": "女", - }, - "value": "F", - }, - ], - "fieldId": "select_k1ow3h1x", - "fieldName": "gender", - "filterLocal": true, - "hasArrow": true, - "hasBorder": true, - "hasClear": false, - "hasSelectAll": false, - "label": Object { - "en-US": "SelectField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "性别", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "mode": "single", - "notFoundContent": Object { - "type": "i18n", - "use": "zh-CN", - }, - "placeholder": Object { - "en-US": "please select", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请选择", - }, - "searchDelay": 300, - "showSearch": false, - "size": "medium", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "validation": Array [ - Object { - "type": "required", - }, - ], - "value": "", - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - ], - "componentName": "Column", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cby", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "colSpan": "", - "fieldId": "column_k1p1bnjn", - }, - "title": "", - }, - ], - "componentName": "ColumnsLayout", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbw", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "columnGap": "20", - "fieldId": "columns_k1ow3h1v", - "layout": "6:6", - "rowGap": 0, - }, - "title": "", - }, - ], - "componentName": "CardContent", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbk", - "isLocked": false, - "props": Object {}, - "title": "", - }, - ], - "componentName": "Card", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbj", - "isLocked": false, - "props": Object { - "__slot__extra": false, - "__slot__subTitle": false, - "__slot__title": false, - "__style__": ":root { - margin-bottom: 12px; -}", - "className": "card_kgaqfbm5", - "contentHeight": "", - "dividerNoInset": false, - "extra": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "fieldId": "card_k1ow3h1l", - "showHeadDivider": true, - "showTitleBullet": true, - "subTitle": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "title": Object { - "en-US": "Title", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "基本信息", - }, - }, - "title": "", - }, - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc4", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h21", - "fieldName": "department", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "所属部门", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc8", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h23", - "fieldName": "leader", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "主管", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - ], - "componentName": "Column", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc6", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "colSpan": "", - "fieldId": "column_k1p1bnjo", - }, - "title": "", - }, - Object { - "children": Array [ - Object { - "componentName": "TextField", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc9", - "isLocked": false, - "props": Object { - "__category__": "form", - "__style__": Object {}, - "__useMediator": "value", - "addonAfter": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "addonBefore": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "autoFocus": false, - "autoHeight": false, - "behavior": "NORMAL", - "cutString": false, - "fieldId": "textField_k1ow3h24", - "fieldName": "hrg", - "hasClear": false, - "hasLimitHint": false, - "htmlType": "input", - "label": Object { - "en-US": "TextField", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "HRG", - }, - "labelAlign": "top", - "labelColOffset": 0, - "labelColSpan": 4, - "labelTextAlign": "right", - "labelTipsIcon": "", - "labelTipsText": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "labelTipsTypes": "none", - "placeholder": Object { - "en-US": "please input", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "请输入", - }, - "rows": 4, - "size": "medium", - "state": "", - "tips": Object { - "en-US": "", - "type": "i18n", - "zh-CN": "", - }, - "trim": false, - "validation": Array [], - "value": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "wrapperColOffset": 0, - "wrapperColSpan": 0, - }, - "title": "", - }, - ], - "componentName": "Column", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc7", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "colSpan": "", - "fieldId": "column_k1p1bnjp", - }, - "title": "", - }, - ], - "componentName": "ColumnsLayout", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cc5", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "columnGap": "20", - "fieldId": "columns_k1ow3h22", - "layout": "6:6", - "rowGap": 0, - }, - "title": "", - }, - ], - "componentName": "CardContent", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbm", - "isLocked": false, - "props": Object {}, - "title": "", - }, - ], - "componentName": "Card", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbl", - "isLocked": false, - "props": Object { - "__slot__extra": false, - "__slot__subTitle": false, - "__slot__title": false, - "__style__": ":root { - margin-bottom: 12px; -}", - "className": "card_kgaqfbm6", - "contentHeight": "", - "dividerNoInset": false, - "extra": Object { - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "fieldId": "card_k1ow3h1m", - "showHeadDivider": true, - "showTitleBullet": true, - "subTitle": Object { - "en-US": "", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "", - }, - "title": Object { - "en-US": "Title", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "部门信息", - }, - }, - "title": "", - }, - Object { - "children": Array [ - Object { - "componentName": "Button", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbn", - "isLocked": false, - "props": Object { - "__style__": ":root { - margin-right: 16px; - width: 80px -}", - "baseIcon": "", - "behavior": "NORMAL", - "className": "button_kgaqfbm7", - "content": Object { - "en-US": "Button", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "提交", - }, - "fieldId": "button_k1ow3h1n", - "loading": false, - "onClick": Object { - "events": Array [ - Object { - "id": "submit", - "name": "submit", - "params": Object {}, - "type": "actionRef", - "uuid": "1570966253282_0", - }, - ], - "rawType": "events", - "type": "JSExpression", - "value": "this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])", - }, - "otherIcon": "", - "size": "medium", - "triggerEventsWhenLoading": false, - "type": "primary", - }, - "title": "", - }, - Object { - "componentName": "Button", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbp", - "isLocked": false, - "props": Object { - "__style__": ":root { - width: 80px; -}", - "baseIcon": "", - "behavior": "NORMAL", - "className": "button_kgaqfbm8", - "content": Object { - "en-US": "Button", - "type": "i18n", - "use": "zh-CN", - "zh-CN": "取消", - }, - "fieldId": "button_k1ow3h1p", - "greeting": Object { - "value": Array [ - Object { - "componentName": "Text", - "props": Object {}, - }, - ], - }, - "loading": false, - "otherIcon": "", - "size": "medium", - "triggerEventsWhenLoading": false, - "type": "normal", - }, - "title": "", - }, - ], - "componentName": "Div", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbo", - "isLocked": false, - "props": Object { - "__style__": ":root { - display: flex; - align-items: flex-start; - justify-content: center; - background: #fff; - padding: 20px 0; -}", - "behavior": "NORMAL", - "className": "div_kgaqfbm9", - "customClassName": "", - "events": Object {}, - "fieldId": "div_k1ow3h1o", - "useFieldIdAsDomId": false, - }, - "title": "", - }, - ], - "componentName": "Form", - "condition": true, - "conditionGroup": "", - "extraPropA": "extraPropA", - "hidden": false, - "id": "form", - "isLocked": false, - "props": Object { - "__style__": Object {}, - "autoUnmount": true, - "autoValidate": true, - "behavior": "NORMAL", - "dataSource": Object { - "type": "variable", - "variable": "state.formData", - }, - "fieldId": "form", - "fieldOptions": Object {}, - "labelAlign": "top", - "obj": Object { - "a": 1, - "b": false, - "c": "string", - }, - "scrollToFirstError": true, - "size": "medium", - "slotA": "", - }, - "title": "", - }, - ], - "componentName": "RootContent", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbb", - "isLocked": false, - "props": Object { - "contentBgColor": "transparent", - "contentMargin": "20", - "contentPadding": "0", - }, - "title": "", - }, - Object { - "componentName": "RootFooter", - "condition": true, - "conditionGroup": "", - "hidden": false, - "id": "node_k1ow3cbc", - "isLocked": false, - "props": Object {}, - "title": "", - }, - ], - "componentName": "Page", - "condition": true, - "conditionGroup": "", - "css": "body{background-color:#f2f3f5}.card_kgaqfbm5 { - margin-bottom: 12px; -}.card_kgaqfbm6 { - margin-bottom: 12px; -}.button_kgaqfbm7 { - margin-right: 16px; - width: 80px -}.button_kgaqfbm8 { - width: 80px; -}.div_kgaqfbm9 { - display: flex; - align-items: flex-start; - justify-content: center; - background: #fff; - padding: 20px 0; -}", - "dataSource": Object { - "globalConfig": Object { - "fit": Object { - "compiled": "", - "error": Object {}, - "source": "", - "type": "js", - }, - }, - "list": Array [], - "offline": Array [], - "online": Array [], - "sync": true, - }, - "hidden": false, - "i18n": Object { - "en-US": Object { - "i18n-jwg27yo3": "China", - "i18n-jwg27yo4": "Hello", - }, - "zh-CN": Object { - "i18n-jwg27yo3": "中国", - "i18n-jwg27yo4": "你好", - }, - }, - "id": "page", - "isLocked": false, - "lifeCycles": Object { - "constructor": Object { - "compiled": "function constructor() { -var module = { exports: {} }; -var _this = this; -this.__initMethods__(module.exports, module); -Object.keys(module.exports).forEach(function(item) { - if(typeof module.exports[item] === 'function'){ - _this[item] = module.exports[item]; - } -}); - -}", - "source": "function constructor() { -var module = { exports: {} }; -var _this = this; -this.__initMethods__(module.exports, module); -Object.keys(module.exports).forEach(function(item) { - if(typeof module.exports[item] === 'function'){ - _this[item] = module.exports[item]; - } -}); - -}", - "type": "js", - }, - }, - "methods": Object { - "__initMethods__": Object { - "compiled": "function (exports, module) { /*set actions code here*/ }", - "source": "function (exports, module) { /*set actions code here*/ }", - "type": "js", - }, - }, - "props": Object { - "className": "page_kgaqfbm4", - "containerStyle": Object {}, - "extensions": Object { - "启用页头": true, - }, - "pageStyle": Object { - "backgroundColor": "#f2f3f5", - }, - "templateVersion": "1.0.0", - }, - "title": "hey, i' a page!", - }, - ], - "utils": undefined, -} -`; - -exports[`document-model 测试 各种方法测试 2`] = `null`; diff --git a/packages/designer/tests/document/document-model/document-model.test.ts b/packages/designer/tests/document/document-model/document-model.test.ts deleted file mode 100644 index b47200cba..000000000 --- a/packages/designer/tests/document/document-model/document-model.test.ts +++ /dev/null @@ -1,313 +0,0 @@ -import '../../fixtures/window'; -import { DocumentModel, isDocumentModel, isPageSchema } from '../../../src/document/document-model'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Project } from '../../../src/project/project'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import divMeta from '../../fixtures/component-metadata/div'; -import formMeta from '../../fixtures/component-metadata/form'; -import otherMeta from '../../fixtures/component-metadata/other'; -import pageMeta from '../../fixtures/component-metadata/page'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -describe('document-model 测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - - beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - }); - - it('empty schema', () => { - const doc = new DocumentModel(project); - expect(doc.rootNode?.id).toBe('root'); - expect(doc.currentRoot).toBe(doc.rootNode); - expect(doc.root).toBe(doc.rootNode); - expect(doc.modalNode).toBeUndefined(); - expect(doc.isBlank()).toBeTruthy(); - expect(doc.schema).toEqual({ - componentName: 'Page', - condition: true, - conditionGroup: '', - hidden: false, - isLocked: false, - loop: undefined, - title: '', - id: 'root', - fileName: '', - props: {}, - }); - }); - - it('各种方法测试', () => { - const doc = new DocumentModel(project, formSchema); - const mockNode = { id: 1 }; - doc.addWillPurge(mockNode); - expect(doc.willPurgeSpace).toHaveLength(1); - doc.removeWillPurge(mockNode); - expect(doc.willPurgeSpace).toHaveLength(0); - - expect(doc.toData()).toMatchSnapshot(); - - // 测试插入已存在的 id,id 将会被重置 - const formParentNode = doc.getNode('form').parent; - doc.insertNode(formParentNode, { id: 'form', componentName: 'Form' }); - expect(formParentNode.children.get(formParentNode.children.size - 1).id).not.toBe('form'); - - doc.internalRemoveAndPurgeNode({ id: 'mockId' }); - - // internalSetDropLocation - doc.dropLocation = { a: 1 }; - expect(doc.dropLocation).toEqual({ a: 1 }); - - // wrapWith - // none-selected - doc.wrapWith({ componentName: 'Wrap' }); - doc.selection.select('form'); - doc.wrapWith({ componentName: 'Wrap' }); - expect(doc.getNode('form').parent.componentName).toBe('Wrap'); - expect(doc.wrapWith({ componentName: 'Leaf' })).toBeNull(); - - // fileName - expect(doc.fileName).toBeTruthy(); - doc.fileName = 'fileName'; - expect(doc.fileName).toBe('fileName'); - - expect(doc.getNodeSchema(doc.getNode('form'))).toMatchSnapshot(); - - // TODO: - // expect(doc.simulatorProps).toMatchSnapshot(); - - const mockSimulator = { - isSimulator: true, - getComponent() { - return 'haha'; - }, - setSuspense() {}, - }; - doc.project.mountSimulator(mockSimulator); - expect(doc.simulator).toEqual(mockSimulator); - expect(doc.getComponent('Div')).toBe('haha'); - - expect(doc.opened).toBeFalsy(); - expect(doc.isModified).toBeTruthy(); - expect(doc.suspensed).toBeTruthy(); - - doc.open(); - expect(doc.opened).toBeTruthy(); - expect(doc.actived).toBeTruthy(); - expect(doc.isModified).toBeTruthy(); - expect(doc.suspensed).toBeFalsy(); - - doc.suspense(); - doc.activate(); - doc.close(); - doc.remove(); - - const offReady = doc.onReady(() => {}); - offReady(); - - expect(doc.history).toBe(doc.getHistory()); - }); - - it('focusNode - using drillDown', () => { - const doc = new DocumentModel(project, formSchema); - expect(doc.focusNode.id).toBe('page'); - - doc.drillDown(doc.getNode('node_k1ow3cbb')); - expect(doc.focusNode.id).toBe('node_k1ow3cbb'); - }); - - it('focusNode - using drillDown & import', () => { - const doc = new DocumentModel(project, formSchema); - expect(doc.focusNode.id).toBe('page'); - - doc.drillDown(doc.getNode('node_k1ow3cbb')); - doc.import(formSchema); - expect(doc.focusNode.id).toBe('node_k1ow3cbb'); - }); - - it('focusNode - using focusNodeSelector', () => { - const doc = new DocumentModel(project, formSchema); - editor.set('focusNodeSelector', (rootNode) => { - return rootNode.children.get(1); - }); - expect(doc.focusNode.id).toBe('node_k1ow3cbb'); - }); - - it('getNodeCount', () => { - const doc = new DocumentModel(project); - // using default schema, only one node - expect(doc.getNodeCount()).toBe(1); - }); - - it('getNodeSchema', () => { - const doc = new DocumentModel(project, formSchema); - expect(doc.getNodeSchema('page').id).toBe('page'); - }); - - it('export - with __isTopFixed__', () => { - formSchema.children[1].props.__isTopFixed__ = true; - const doc = new DocumentModel(project, formSchema); - - const schema = doc.export(); - expect(schema.children).toHaveLength(3); - expect(schema.children[0].componentName).toBe('RootContent'); - expect(schema.children[1].componentName).toBe('RootHeader'); - expect(schema.children[2].componentName).toBe('RootFooter'); - }); - - describe('createNode', () => { - it('same id && componentName', () => { - const doc = new DocumentModel(project, formSchema); - const node = doc.createNode({ - componentName: 'RootFooter', - id: 'node_k1ow3cbc', - props: {}, - condition: true, - }); - expect(node.parent).toBeNull(); - }); - - it('same id && different componentName', () => { - const doc = new DocumentModel(project, formSchema); - const originalNode = doc.getNode('node_k1ow3cbc'); - const node = doc.createNode({ - componentName: 'RootFooter2', - id: 'node_k1ow3cbc', - props: {}, - condition: true, - }); - // expect(originalNode.parent).toBeNull(); - expect(node.id).not.toBe('node_k1ow3cbc'); - }); - }); - - it('setSuspense', () => { - const doc = new DocumentModel(project, formSchema); - expect(doc.opened).toBeFalsy(); - doc.setSuspense(false); - }); - - it('registerAddon / getAddonData / exportAddonData', () => { - const doc = new DocumentModel(project); - expect(doc.getAddonData('a')).toBeUndefined(); - - doc.registerAddon('a', () => 'addon a'); - doc.registerAddon('a', () => 'modified addon a'); - doc.registerAddon('b', () => 'addon b'); - doc.registerAddon('c', () => null); - - ['id', 'layout', 'params'].forEach((name) => { - expect(() => doc.registerAddon(name, () => {})).toThrow(); - }); - - expect(doc.getAddonData('a')).toBe('modified addon a'); - expect(doc.getAddonData('b')).toBe('addon b'); - - expect(doc.exportAddonData()).toEqual({ - a: 'modified addon a', - b: 'addon b', - }); - }); - - it('checkNesting / checkDropTarget / checkNestingUp / checkNestingDown', () => { - designer.createComponentMeta(pageMeta); - designer.createComponentMeta(formMeta); - designer.createComponentMeta(otherMeta); - const doc = new DocumentModel(project, formSchema); - - expect( - doc.checkDropTarget(doc.getNode('page'), { type: 'node', nodes: [doc.getNode('form')] }), - ).toBeTruthy(); - expect( - doc.checkDropTarget(doc.getNode('page'), { - type: 'nodedata', - data: { componentName: 'Form' }, - }), - ).toBeTruthy(); - - expect( - doc.checkNesting(doc.getNode('page'), { type: 'node', nodes: [doc.getNode('form')] }), - ).toBeTruthy(); - expect( - doc.checkNesting(doc.getNode('page'), { - type: 'nodedata', - data: { componentName: 'Form' }, - }), - ).toBeTruthy(); - expect( - doc.checkNesting(doc.getNode('page'), doc.getNode('form')) - ).toBeTruthy(); - expect( - doc.checkNesting(doc.getNode('page'), null) - ).toBeTruthy(); - expect( - doc.checkNesting(doc.getNode('page'), { - type: 'nodedata', - data: { componentName: 'Other' }, - }) - ).toBeFalsy(); - - expect( - doc.checkNestingUp(doc.getNode('page'), { componentName: 'Other' }) - ).toBeFalsy(); - - expect( - doc.checkNestingDown(doc.getNode('page'), { componentName: 'Other' }) - ).toBeTruthy(); - - expect(doc.checkNestingUp(doc.getNode('page'), null)).toBeTruthy(); - }); - - it('getComponentsMap', () => { - designer.createComponentMeta(divMeta); - designer.createComponentMeta(otherMeta); - const doc = new DocumentModel(project, formSchema); - const comps = doc.getComponentsMap(['Other']); - expect(comps.find(comp => comp.componentName === 'Div')).toEqual( - { componentName: 'Div', package: '@ali/vc-div' } - ); - expect(comps.find(comp => comp.componentName === 'Other')).toEqual( - { componentName: 'Other', package: '@ali/vc-other' } - ); - expect(comps.find(comp => comp.componentName === 'Page')).toEqual( - { componentName: 'Page', devMode: 'lowCode' } - ); - - const comps2 = doc.getComponentsMap(['Div']); - }); - - it('acceptRootNodeVisitor / getRootNodeVisitor', () => { - designer.createComponentMeta(divMeta); - designer.createComponentMeta(otherMeta); - const doc = new DocumentModel(project, formSchema); - const ret = doc.acceptRootNodeVisitor('getPageId', (root) => { - return 'page'; - }); - expect(ret).toBe('page'); - expect(doc.getRootNodeVisitor('getPageId')).toBe('page'); - - // expect(doc.getComponentsMap(['Other'])).toEqual([ - // { componentName: 'Div', package: '@ali/vc-div' }, - // { componentName: 'Other', package: '@ali/vc-other' }, - // ]); - }); - - it('deprecated methods', () => { - const doc = new DocumentModel(project, formSchema); - doc.refresh(); - doc.onRefresh(); - }); -}); - -it('isDocumentModel', () => { - expect(isDocumentModel({ rootNode: {} })).toBeTruthy(); -}); - -it('isPageSchema', () => { - expect(isPageSchema({ componentName: 'Page' })).toBeTruthy(); -}); diff --git a/packages/designer/tests/document/history/__snapshots__/history.test.ts.snap b/packages/designer/tests/document/history/__snapshots__/history.test.ts.snap deleted file mode 100644 index 1249a4165..000000000 --- a/packages/designer/tests/document/history/__snapshots__/history.test.ts.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`History data function & records 1`] = `"{\\"data\\":1,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`; - -exports[`History data function & records 2`] = `"{\\"data\\":3,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`; - -exports[`History data function & records 3`] = `"{\\"data\\":5,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`; - -exports[`History data function & records 4`] = `"{\\"data\\":7,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`; diff --git a/packages/designer/tests/document/history/history.test.ts b/packages/designer/tests/document/history/history.test.ts deleted file mode 100644 index 63af8ecbf..000000000 --- a/packages/designer/tests/document/history/history.test.ts +++ /dev/null @@ -1,343 +0,0 @@ -import '../../fixtures/window'; -import { mobx, makeAutoObservable, globalContext, Editor } from '@alilc/lowcode-editor-core'; -import { History } from '../../../src/document/history'; -import { delay } from '../../utils/misc'; -import { Workspace } from '@alilc/lowcode-workspace'; - -class Node { - data: number; - children: Node[] = []; - - constructor(data: number) { - makeAutoObservable(this); - this.data = data; - } - - addNode(node: Node) { - this.children.push(node); - } - - toObject() { - return { - data: this.data, - children: this.children.map((c) => c.toObject()), - }; - } -} - -let tree: Node = null; -beforeEach(() => { - tree = new Node(1); - tree.addNode(new Node(2)); -}); - -afterEach(() => { - tree = null; -}); - -describe('History', () => { - beforeAll(() => { - const editor = new Editor(); - globalContext.register(editor, Editor); - globalContext.register(editor, 'editor'); - globalContext.register(new Workspace(), 'workspace'); - }); - - it('data function & records', async () => { - const mockRedoFn = jest.fn(); - const mockDataFn = jest.fn(); - const history = new History(() => { - const data = tree.toObject(); - mockDataFn(data); - return data; - }, mockRedoFn); - - expect(mockDataFn).toHaveBeenCalledTimes(1); - expect(mockDataFn).toHaveBeenCalledWith({ data: 1, children: [{ data: 2, children: [] }] }); - expect(history.hotData).toMatchSnapshot(); - // @ts-ignore - expect(history.session.cursor).toBe(0); - // @ts-ignore - expect(history.records).toHaveLength(1); - - tree.data = 3; - expect(mockDataFn).toHaveBeenCalledTimes(2); - expect(mockDataFn).toHaveBeenCalledWith({ data: 3, children: [{ data: 2, children: [] }] }); - expect(history.hotData).toMatchSnapshot(); - // @ts-ignore - expect(history.session.cursor).toBe(0); - // @ts-ignore - expect(history.records).toHaveLength(1); - - // modify data after timeGap - await delay(1200); - tree.data = 5; - expect(mockDataFn).toHaveBeenCalledTimes(3); - expect(mockDataFn).toHaveBeenCalledWith({ data: 5, children: [{ data: 2, children: [] }] }); - expect(history.hotData).toMatchSnapshot(); - // @ts-ignore - expect(history.session.cursor).toBe(1); - // @ts-ignore - expect(history.records).toHaveLength(2); - - history.setSerialization({ - serialize(data: Node): string { - return JSON.stringify(data); - }, - unserialize(data: string) { - return JSON.parse(data); - }, - }); - - // modify data after timeGap - await delay(1200); - tree.data = 7; - expect(mockDataFn).toHaveBeenCalledTimes(4); - expect(mockDataFn).toHaveBeenCalledWith({ data: 7, children: [{ data: 2, children: [] }] }); - expect(history.hotData).toMatchSnapshot(); - }); - - it('isSavePoint & savePoint', async () => { - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - () => {}, - ); - - expect(history.isSavePoint()).toBeFalsy(); - expect(history.isModified()).toBeFalsy(); - - await delay(1200); - tree.data = 3; - expect(history.isSavePoint()).toBeTruthy(); - - history.savePoint(); - expect(history.isSavePoint()).toBeFalsy(); - }); - - it('go & forward & back & onCursor', async () => { - const mockRedoFn = jest.fn(); - const mockCursorFn = jest.fn(); - const mockStateFn = jest.fn(); - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - mockRedoFn(data); - }, - ); - - // undoable ❌ & redoable ❌ & modified ❌ - expect(history.getState()).toBe(0); - - await delay(1200); - tree.data = 3; - - await delay(1200); - tree.data = 5; - - await delay(1200); - tree.data = 7; - - const dataCursor0 = { data: 1, children: [{ data: 2, children: [] }] }; - const dataCursor1 = { data: 3, children: [{ data: 2, children: [] }] }; - const dataCursor2 = { data: 5, children: [{ data: 2, children: [] }] }; - const dataCursor3 = { data: 7, children: [{ data: 2, children: [] }] }; - - // redoable ❌ - expect(history.getState()).toBe(7 - 2); - - const off1 = history.onCursor(mockCursorFn); - const off2 = history.onStateChange(mockStateFn); - - // @ts-ignore - expect(history.records).toHaveLength(4); - // @ts-ignore - expect(history.session.cursor).toBe(3); - - // step 1 - history.back(); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 1, - JSON.stringify(dataCursor2), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(1, 7); - expect(mockRedoFn).toHaveBeenNthCalledWith(1, dataCursor2); - - // step 2 - history.back(); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 2, - JSON.stringify(dataCursor1), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(2, 7); - expect(mockRedoFn).toHaveBeenNthCalledWith(2, dataCursor1); - - // step 3 - history.back(); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 3, - JSON.stringify(dataCursor0), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(3, 7 - 4 - 1); - expect(mockRedoFn).toHaveBeenNthCalledWith(3, dataCursor0); - - // step 4 - history.forward(); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 4, - JSON.stringify(dataCursor1), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(4, 7); - expect(mockRedoFn).toHaveBeenNthCalledWith(4, dataCursor1); - - // step 5 - history.forward(); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 5, - JSON.stringify(dataCursor2), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(5, 7); - expect(mockRedoFn).toHaveBeenNthCalledWith(5, dataCursor2); - - // step 6 - history.go(3); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 6, - JSON.stringify(dataCursor3), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(6, 7 - 2); - expect(mockRedoFn).toHaveBeenNthCalledWith(6, dataCursor3); - - // step 7 - history.go(0); - expect(mockCursorFn).toHaveBeenNthCalledWith( - 7, - JSON.stringify(dataCursor0), - ); - expect(mockStateFn).toHaveBeenNthCalledWith(7, 7 - 4 - 1); - expect(mockRedoFn).toHaveBeenNthCalledWith(7, dataCursor0); - - off1(); - off2(); - mockStateFn.mockClear(); - mockCursorFn.mockClear(); - history.go(1); - expect(mockStateFn).not.toHaveBeenCalled(); - expect(mockCursorFn).not.toHaveBeenCalled(); - }); - - it('go() - edge case of cursor', async () => { - const mockRedoFn = jest.fn(); - const mockCursorFn = jest.fn(); - const mockStateFn = jest.fn(); - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - mockRedoFn(data); - }, - ); - - await delay(1200); - tree.data = 3; - - await delay(1200); - tree.data = 5; - - history.go(-1); - // @ts-ignore - expect(history.session.cursor).toBe(0); - - history.go(3); - // @ts-ignore - expect(history.session.cursor).toBe(2); - }); - - it('destroy()', async () => { - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - mockRedoFn(data); - }, - ); - - history.destroy(); - // @ts-ignore - expect(history.records).toHaveLength(0); - }); - - it('sleep & wakeup', async () => { - const mockRedoFn = jest.fn(); - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - mockRedoFn(data); - }, - ); - - // @ts-ignore - history.sleep(); - - await delay(1200); - tree.data = 3; - // no record has been pushed into records because of history is asleep. - // @ts-ignore - expect(history.records).toHaveLength(1); - - // @ts-ignore - history.wakeup(); - tree.data = 4; - // @ts-ignore - expect(history.records).toHaveLength(2); - }); -}); - -describe('History - errors', () => { - beforeAll(() => { - globalContext.replace(Editor, null); - }); - - it('no editor', () => { - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - }, - ); - - history.back(); - history.forward(); - }); - - it('no session', () => { - const history = new History( - () => { - const data = tree.toObject(); - return data; - }, - (data) => { - }, - ); - - // @ts-ignore - history.session = undefined; - history.back(); - history.forward(); - history.savePoint(); - }); -}); \ No newline at end of file diff --git a/packages/designer/tests/document/history/session.test.ts b/packages/designer/tests/document/history/session.test.ts deleted file mode 100644 index 3e9e8c628..000000000 --- a/packages/designer/tests/document/history/session.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import '../../fixtures/window'; -import { Session } from '../../../src/document/history'; -import { delay } from '../../utils/misc'; - -describe('Session', () => { - it('constructor', () => { - const session = new Session(1, { a: 1 }); - expect(session.cursor).toBe(1); - expect(session.data).toEqual({ a: 1 }); - // @ts-ignore - expect(session.timeGap).toBe(1000); - expect(session.isActive()).toBeTruthy(); - }); - - it('log()', () => { - const session = new Session(1, { a: 1 }); - - session.log({ a: 2 }); - session.log({ a: 3 }); - expect(session.data).toEqual({ a: 3 }); - }); - - it('end()', () => { - const session = new Session(1, { a: 1 }); - - session.end(); - expect(session.isActive()).toBeFalsy(); - session.log({ a: 2 }); - // log is not possible if current session is inactive - expect(session.data).toEqual({ a: 1 }); - }); - - - it('timeGap', async () => { - const session = new Session(1, { a: 1 }); - - expect(session.isActive()).toBeTruthy(); - await delay(1200); - expect(session.isActive()).toBeFalsy(); - session.log({ a: 2 }); - // log is not possible if current session is inactive - expect(session.data).toEqual({ a: 1 }); - }); - - it('custom timeGap', async () => { - const session = new Session(1, { a: 1 }, 2000); - - expect(session.isActive()).toBeTruthy(); - await delay(1200); - expect(session.isActive()).toBeTruthy(); - await delay(1000); - expect(session.isActive()).toBeFalsy(); - session.log({ a: 2 }); - // log is not possible if current session is inactive - expect(session.data).toEqual({ a: 1 }); - }); -}); \ No newline at end of file diff --git a/packages/designer/tests/document/node/modal-nodes-manager.test.ts b/packages/designer/tests/document/node/modal-nodes-manager.test.ts deleted file mode 100644 index 3e5dcdb79..000000000 --- a/packages/designer/tests/document/node/modal-nodes-manager.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import '../../fixtures/window'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Project } from '../../../src/project/project'; -import { DocumentModel } from '../../../src/document/document-model'; -import { Node } from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form-with-modal'; -import dlgMetadata from '../../fixtures/component-metadata/dialog'; -import { getModalNodes } from '../../../src/document/node/modal-nodes-manager'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -let editor: Editor; -let designer: Designer; -let project: Project; -let doc: DocumentModel; - -beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - designer.createComponentMeta(dlgMetadata); - project = designer.project; - doc = new DocumentModel(project, formSchema); -}); - -afterEach(() => { - project.unload(); - designer.purge(); - editor = null; - designer = null; - project = null; -}); - -describe('ModalNodesManager 方法测试', () => { - it('getModalNodes / getVisibleModalNode', () => { - const mgr = doc.modalNodesManager; - const nodes = mgr.getModalNodes(); - expect(nodes).toHaveLength(1); - expect(nodes[0].componentName).toBe('Dialog'); - expect(mgr.getVisibleModalNode()).toBeFalsy(); - }); - - it('setVisible / setInvisible / onVisibleChange', () => { - const mgr = doc.modalNodesManager; - const nodes = mgr.getModalNodes(); - - const mockFn = jest.fn(); - const off = mgr.onVisibleChange(mockFn); - - mgr.setVisible(nodes[0]); - expect(mockFn).toHaveBeenCalledTimes(2); - - mgr.setInvisible(nodes[0]); - expect(mockFn).toHaveBeenCalledTimes(3); - - off(); - }); - - it('addNode / removeNode', () => { - const mgr = doc.modalNodesManager!; - const nodes = mgr.getModalNodes(); - - const nodesMockFn = jest.fn(); - const visibleMockFn = jest.fn(); - const off = mgr.onModalNodesChange(nodesMockFn); - const offVisible = mgr.onVisibleChange(visibleMockFn); - - const newNode = new Node(doc, { componentName: 'Dialog' }); - mgr.addNode(newNode); - expect(visibleMockFn).toHaveBeenCalledTimes(2); - expect(nodesMockFn).toHaveBeenCalledTimes(1); - - mgr.setVisible(newNode); - mgr.removeNode(newNode); - - expect(visibleMockFn).toHaveBeenCalledTimes(6); - expect(nodesMockFn).toHaveBeenCalledTimes(2); - - off(); - offVisible(); - visibleMockFn.mockClear(); - nodesMockFn.mockClear(); - - mgr.addNode(newNode); - expect(visibleMockFn).not.toHaveBeenCalled(); - expect(nodesMockFn).not.toHaveBeenCalled(); - - const newNode2 = new Node(doc, { componentName: 'Dialog' }); - mgr.addNode(newNode2); - mgr.setInvisible(newNode2); - mgr.removeNode(newNode2); - - const newNode3 = new Node(doc, { componentName: 'Dialog' }); - mgr.removeNode(newNode3); - - const newNode4 = new Node(doc, { componentName: 'Non-Modal' }); - mgr.removeNode(newNode4); - - const newNode5 = doc.createNode({ componentName: 'Non-Modal' }); - newNode5.remove(); // trigger node destroy - }); -}); - -describe('其他方法', () => { - it('getModalNodes - null', () => { - expect(getModalNodes()).toEqual([]); - }); - - it('getModalNodes - no children', () => { - const node = doc.createNode({ componentName: 'Leaf', children: 'haha' }); - expect(getModalNodes(node)).toEqual([]); - }); -}); \ No newline at end of file diff --git a/packages/designer/tests/document/node/node-children.test.ts b/packages/designer/tests/document/node/node-children.test.ts deleted file mode 100644 index 1aa7e3ccb..000000000 --- a/packages/designer/tests/document/node/node-children.test.ts +++ /dev/null @@ -1,250 +0,0 @@ -import '../../fixtures/window'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Project } from '../../../src/project/project'; -import { DocumentModel } from '../../../src/document/document-model'; -import { - Node, -} from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import divMetadata from '../../fixtures/component-metadata/div'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; - -describe('NodeChildren 方法测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - - beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = new DocumentModel(project, formSchema); - }); - - afterEach(() => { - project.unload(); - designer.purge(); - editor = null; - designer = null; - project = null; - }); - - it('isEmpty / notEmpty', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const secondBtn = doc.getNode('node_k1ow3cbp')!; - const { children } = firstBtn.parent!; - - expect(children.isEmpty()).toBeFalsy(); - expect(children.notEmpty()).toBeTruthy(); - expect(firstBtn.children.notEmpty()).toBeFalsy(); - expect(firstBtn.children.isEmpty()).toBeTruthy(); - }); - - it('export', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - expect(children.export().length).toBe(2); - }); - - it('export - Leaf', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.parent!.insertAfter({ componentName: 'Leaf', children: 'haha' }); - const { children } = firstBtn.parent!; - - expect(children.export().length).toBe(3); - expect(children.export()[2]).toBe('haha'); - }); - - it('import', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - children.import(children.export()); - - expect(children.export().length).toBe(2); - }); - - it('delete', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const leafNode = doc.createNode({ componentName: 'Leaf', children: 'haha' }); - firstBtn.parent!.insertAfter(leafNode); - const { children } = firstBtn.parent!; - - children.delete(leafNode); - expect(children.export().length).toBe(2); - }); - - it('delete - 插入已有的节点', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.parent!.insertBefore(firstBtn, firstBtn); - const { children } = firstBtn.parent!; - - expect(children.export().length).toBe(2); - }); - - it('purge / for of', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - children.purge(); - - for (const child of children) { - expect(child.isPurged).toBeTruthy(); - } - - // purge when children is purged - children.purge(); - }); - - it('splice', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - children.splice(0, 1); - - expect(children.size).toBe(1); - expect(children.length).toBe(1); - - children.splice(0, 0, doc.createNode({ componentName: 'Button' })); - expect(children.size).toBe(2); - expect(children.length).toBe(2); - }); - - it('map', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const newMap = children.map((item) => item); - - newMap?.forEach((item) => { - expect(item.componentName).toBe('Button'); - }); - }); - - it('forEach', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - children.forEach((item) => { - expect(item.componentName).toBe('Button'); - }); - }); - - it('some', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - children.some((item) => { - return expect(item.componentName).toBe('Button'); - }); - }); - - it('every', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - children.every((item) => { - return expect(item.componentName).toBe('Button'); - }); - }); - - it('filter', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - children - .filter((item) => item.componentName === 'Button') - .forEach((item) => { - expect(item.componentName).toBe('Button'); - }); - }); - - it('find', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const found = children.find((item) => item.componentName === 'Button'); - - expect(found?.componentName).toBe('Button'); - }); - - it('concat', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const ret = children.concat([doc.createNode({ componentName: 'Button' })]); - - expect(ret.length).toBe(3); - }); - - it('reduce', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - let ret = 0; - ret = children.reduce((count, node) => { - count = count + 1; - return count; - }, 0); - - expect(ret).toBe(2); - }); - - it('mergeChildren', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const changeMockFn = jest.fn(); - const offChange = children.onChange(changeMockFn); - const rmMockFn = jest.fn((item) => { - if (item.index === 1) return true; - return false; - }); - const addMockFn = jest.fn((children) => { - return [{ componentName: 'Button' }, { componentName: 'Button' }]; - }); - const sortMockFn = jest.fn((a, b) => { - return a > b ? 1 : -1; - }); - children.mergeChildren(rmMockFn, addMockFn, sortMockFn); - - expect(children.size).toBe(3); - expect(changeMockFn).toHaveBeenCalled(); - offChange(); - - // no remover && adder && sorter - children.mergeChildren(); - }); - - it('insert / onInsert', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const mockFn = jest.fn(); - const off = children.onInsert(mockFn); - - children.insert(new Node(doc, { componentName: 'Button' })); - expect(mockFn).toHaveBeenCalledTimes(1); - off(); - children.insert(new Node(doc, { componentName: 'Button' })); - expect(mockFn).toHaveBeenCalledTimes(1); - }); - - it('reportModified', () => { - const divMeta = designer.createComponentMeta(divMetadata); - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const { children } = firstBtn.parent!; - - const modifiedMockFn = jest.fn(); - divMeta.getMetadata = () => { - return { configure: { advanced: { callbacks: { onSubtreeModified: modifiedMockFn } } } }; - }; - - children.reportModified(null); - children.reportModified(doc.rootNode); - - children.reportModified(firstBtn, firstBtn.parent); - expect(modifiedMockFn).toHaveBeenCalled(); - }); -}); diff --git a/packages/designer/tests/document/node/node.add.test.ts b/packages/designer/tests/document/node/node.add.test.ts deleted file mode 100644 index 272d62d6c..000000000 --- a/packages/designer/tests/document/node/node.add.test.ts +++ /dev/null @@ -1,588 +0,0 @@ -import { set, cloneDeep } from 'lodash-es'; -import '../../fixtures/window'; -import { Project, IProject } from '../../../src/project/project'; -import { Node, INode } from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - get advanced() { - return {}; - }, - }; - }, - transformProps(props) { - return props; - }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -describe('schema 生成节点模型测试', () => { - describe('block ❌ | component ❌ | slot ❌', () => { - let project: IProject; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - }); - afterEach(() => { - project.unload(); - }); - it('基本的节点模型初始化,模型导出', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap?.size).toBe(expectedNodeCnt); - ids.forEach((id) => { - expect(nodesMap?.get(id)?.componentName).toBe( - getNodeFromSchemaById(formSchema, id).componentName, - ); - }); - - const pageNode = currentDocument?.getNode('page'); - expect(pageNode?.getComponentName()).toBe('Page'); - expect(pageNode?.getIcon()).toBeUndefined(); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - nodesMap.forEach((node) => { - // 触发 getter - node.settingEntry; - }); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); - }); - - it('基本的节点模型初始化,节点深度', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const getNode = currentDocument?.getNode.bind(currentDocument); - - const pageNode = getNode?.('page'); - const rootHeaderNode = getNode?.('node_k1ow3cba'); - const rootContentNode = getNode?.('node_k1ow3cbb'); - const rootFooterNode = getNode?.('node_k1ow3cbc'); - const formNode = getNode?.('form'); - const cardNode = getNode?.('node_k1ow3cbj'); - const cardContentNode = getNode?.('node_k1ow3cbk'); - const columnsLayoutNode = getNode?.('node_k1ow3cbw'); - const columnNode = getNode?.('node_k1ow3cbx'); - const textFieldNode = getNode?.('node_k1ow3cbz'); - - expect(pageNode?.zLevel).toBe(0); - expect(rootHeaderNode?.zLevel).toBe(1); - expect(rootContentNode?.zLevel).toBe(1); - expect(rootFooterNode?.zLevel).toBe(1); - expect(formNode?.zLevel).toBe(2); - expect(cardNode?.zLevel).toBe(3); - expect(cardContentNode?.zLevel).toBe(4); - expect(columnsLayoutNode?.zLevel).toBe(5); - expect(columnNode?.zLevel).toBe(6); - expect(textFieldNode?.zLevel).toBe(7); - - expect(textFieldNode?.getZLevelTop(7)).toEqual(textFieldNode); - expect(textFieldNode?.getZLevelTop(6)).toEqual(columnNode); - expect(textFieldNode?.getZLevelTop(5)).toEqual(columnsLayoutNode); - expect(textFieldNode?.getZLevelTop(4)).toEqual(cardContentNode); - expect(textFieldNode?.getZLevelTop(3)).toEqual(cardNode); - expect(textFieldNode?.getZLevelTop(2)).toEqual(formNode); - expect(textFieldNode?.getZLevelTop(1)).toEqual(rootContentNode); - expect(textFieldNode?.getZLevelTop(0)).toEqual(pageNode); - - // 异常情况 - expect(textFieldNode?.getZLevelTop(8)).toBeNull(); - expect(textFieldNode?.getZLevelTop(-1)).toBeNull(); - }); - - it('基本的节点模型初始化,节点父子、兄弟相关方法', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const getNode = currentDocument.getNode.bind(currentDocument); - - const pageNode = getNode('page'); - const rootHeaderNode = getNode('node_k1ow3cba'); - const rootContentNode = getNode('node_k1ow3cbb'); - const rootFooterNode = getNode('node_k1ow3cbc'); - const formNode = getNode('form'); - const cardNode = getNode('node_k1ow3cbj'); - const cardContentNode = getNode('node_k1ow3cbk'); - const columnsLayoutNode = getNode('node_k1ow3cbw'); - const columnNode = getNode('node_k1ow3cbx'); - const textFieldNode = getNode('node_k1ow3cbz'); - - expect(pageNode?.index).toBe(-1); - expect(pageNode?.children?.toString()).toBe('[object Array]'); - expect(pageNode?.children?.get(1)).toBe(rootContentNode); - expect(pageNode?.getChildren()?.get(1)).toBe(rootContentNode); - expect(pageNode?.getNode()).toBe(pageNode); - - expect(rootFooterNode?.index).toBe(2); - - expect(textFieldNode?.getParent()).toBe(columnNode); - expect(columnNode?.getParent()).toBe(columnsLayoutNode); - expect(columnsLayoutNode?.getParent()).toBe(cardContentNode); - expect(cardContentNode?.getParent()).toBe(cardNode); - expect(cardNode?.getParent()).toBe(formNode); - expect(formNode?.getParent()).toBe(rootContentNode); - expect(rootContentNode?.getParent()).toBe(pageNode); - expect(rootContentNode?.prevSibling).toBe(rootHeaderNode); - expect(rootContentNode?.nextSibling).toBe(rootFooterNode); - - expect(pageNode?.isRoot()).toBe(true); - expect(pageNode?.contains(textFieldNode)).toBe(true); - expect(textFieldNode?.getRoot()).toBe(pageNode); - expect(columnNode?.getRoot()).toBe(pageNode); - expect(columnsLayoutNode?.getRoot()).toBe(pageNode); - expect(cardContentNode?.getRoot()).toBe(pageNode); - expect(cardNode?.getRoot()).toBe(pageNode); - expect(formNode?.getRoot()).toBe(pageNode); - expect(rootContentNode?.getRoot()).toBe(pageNode); - }); - - it('基本的节点模型初始化,节点新建、删除等事件', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const getNode = currentDocument?.getNode.bind(currentDocument); - const createNode = currentDocument?.createNode.bind(currentDocument); - - const pageNode = getNode?.('page'); - const nodeCreateHandler = jest.fn(); - const offCreate = currentDocument?.onNodeCreate(nodeCreateHandler); - - const node = createNode?.({ - componentName: 'TextInput', - props: { - propA: 'haha', - }, - }); - pageNode && node && currentDocument?.insertNode(pageNode, node); - - expect(nodeCreateHandler).toHaveBeenCalledTimes(1); - expect(nodeCreateHandler.mock.calls[0][0]).toBe(node); - expect(nodeCreateHandler.mock.calls[0][0].componentName).toBe('TextInput'); - expect(nodeCreateHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha'); - - const nodeDestroyHandler = jest.fn(); - const offDestroy = currentDocument?.onNodeDestroy(nodeDestroyHandler); - node?.remove(); - expect(nodeDestroyHandler).toHaveBeenCalledTimes(1); - expect(nodeDestroyHandler.mock.calls[0][0]).toBe(node); - expect(nodeDestroyHandler.mock.calls[0][0].componentName).toBe('TextInput'); - expect(nodeDestroyHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha'); - - offCreate(); - offDestroy(); - }); - - it.skip('基本的节点模型初始化,节点插入等方法', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const getNode = currentDocument.getNode.bind(currentDocument); - - const formNode = getNode('form'); - const node1 = currentDocument.createNode({ - componentName: 'TextInput', - props: { - propA: 'haha', - }, - }); - const node2 = currentDocument.createNode({ - componentName: 'TextInput', - props: { - propA: 'heihei', - }, - }); - const node3 = currentDocument.createNode({ - componentName: 'TextInput', - props: { - propA: 'heihei2', - }, - }); - const node4 = currentDocument.createNode({ - componentName: 'TextInput', - props: { - propA: 'heihei3', - }, - }); - - formNode?.insertBefore(node2); - // formNode?.insertBefore(node1, node2); - // formNode?.insertAfter(node3); - // formNode?.insertAfter(node4, node3); - - expect(formNode?.children?.get(0)).toBe(node1); - expect(formNode?.children?.get(1)).toBe(node2); - // expect(formNode?.children?.get(5)).toBe(node3); - // expect(formNode?.children?.get(6)).toBe(node4); - }); - - it('基本的节点模型初始化,节点其他方法', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const getNode = currentDocument.getNode.bind(currentDocument); - - const pageNode = getNode('page'); - expect(pageNode?.isPage()).toBe(true); - expect(pageNode?.isComponent()).toBe(false); - expect(pageNode?.isSlot()).toBe(false); - expect(pageNode?.title).toBe("hey, i' a page!"); - }); - - describe('节点新增(insertNode)', () => { - let project: Project; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - }); - it('场景一:插入 NodeSchema,不指定 index', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const formNode = nodesMap.get('form') as Node; - const formNode2 = currentDocument?.getNode('form'); - expect(formNode).toEqual(formNode2); - currentDocument?.insertNode(formNode, { - componentName: 'TextInput', - id: 'nodeschema-id1', - props: { - propA: 'haha', - propB: 3, - }, - }); - expect(nodesMap.size).toBe(ids.length + 1); - expect(formNode.children?.length).toBe(4); - const insertedNode = formNode.children.get(formNode.children.length - 1); - expect(insertedNode.componentName).toBe('TextInput'); - expect(insertedNode.propsData).toEqual({ - propA: 'haha', - propB: 3, - }); - // TODO: 把 checkId 的 commit pick 过来 - // expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput'); - }); - - it('场景一:插入 NodeSchema,指定 index: 0', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form'); - formNode && - currentDocument?.insertNode( - formNode, - { - componentName: 'TextInput', - id: 'nodeschema-id1', - props: { - propA: 'haha', - propB: 3, - }, - }, - 0, - ); - expect(nodesMap?.size).toBe(ids.length + 1); - expect(formNode?.children?.length).toBe(4); - const insertedNode = formNode?.children?.get(0); - expect(insertedNode?.componentName).toBe('TextInput'); - expect(insertedNode?.propsData).toEqual({ - propA: 'haha', - propB: 3, - }); - // TODO: 把 checkId 的 commit pick 过来 - // expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput'); - }); - - it('场景一:插入 NodeSchema,指定 index: 1', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form'); - formNode && - currentDocument?.insertNode( - formNode, - { - componentName: 'TextInput', - id: 'nodeschema-id1', - props: { - propA: 'haha', - propB: 3, - }, - }, - 1, - ); - expect(nodesMap?.size).toBe(ids.length + 1); - expect(formNode?.children?.length).toBe(4); - const insertedNode = formNode?.children?.get(1); - expect(insertedNode?.componentName).toBe('TextInput'); - expect(insertedNode?.propsData).toEqual({ - propA: 'haha', - propB: 3, - }); - // TODO: 把 checkId 的 commit pick 过来 - // expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput'); - }); - - it('场景一:插入 NodeSchema,有 children', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form') as INode; - currentDocument?.insertNode(formNode, { - componentName: 'ParentNode', - props: { - propA: 'haha', - propB: 3, - }, - children: [ - { - componentName: 'SubNode', - props: { - propA: 'haha', - propB: 3, - }, - }, - { - componentName: 'SubNode2', - props: { - propA: 'haha', - propB: 3, - }, - }, - ], - }); - expect(nodesMap?.size).toBe(ids.length + 3); - expect(formNode.children?.length).toBe(4); - expect(formNode.children?.get(3)?.componentName).toBe('ParentNode'); - expect(formNode.children?.get(3)?.children?.get(0)?.componentName).toBe('SubNode'); - expect(formNode.children?.get(3)?.children?.get(1)?.componentName).toBe('SubNode2'); - }); - - it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form'); - formNode && - currentDocument?.insertNode(formNode, { - componentName: 'TextInput', - id: 'nodeschema-id1', - props: { - propA: 'haha', - propB: 3, - }, - }); - expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput'); - expect(nodesMap?.size).toBe(ids.length + 1); - }); - - it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复,但关闭了 id 检测器', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form'); - formNode && - currentDocument?.insertNode(formNode, { - componentName: 'TextInput', - id: 'nodeschema-id1', - props: { - propA: 'haha', - propB: 3, - }, - }); - expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput'); - expect(nodesMap?.size).toBe(ids.length + 1); - }); - - it('场景二:插入 Node 实例', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form'); - const inputNode = currentDocument?.createNode({ - componentName: 'TextInput', - id: 'nodeschema-id2', - props: { - propA: 'haha', - propB: 3, - }, - }); - formNode && currentDocument?.insertNode(formNode, inputNode); - expect(formNode?.children?.get(3)?.componentName).toBe('TextInput'); - expect(nodesMap?.size).toBe(ids.length + 1); - }); - - it('场景三:插入 JSExpression', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form') as Node; - currentDocument?.insertNode(formNode, { - type: 'JSExpression', - value: 'just a expression', - }); - expect(nodesMap?.size).toBe(ids.length + 1); - expect(formNode.children?.get(3)?.componentName).toBe('Leaf'); - // expect(formNode.children?.get(3)?.children).toEqual({ - // type: 'JSExpression', - // value: 'just a expression' - // }); - }); - it('场景四:插入 string', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form') as Node; - currentDocument?.insertNode(formNode, 'just a string'); - expect(nodesMap?.size).toBe(ids.length + 1); - expect(formNode.children?.get(3)?.componentName).toBe('Leaf'); - // expect(formNode.children?.get(3)?.children).toBe('just a string'); - }); - }); - - describe('节点新增(insertNodes)', () => { - let project: Project; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - }); - it('场景一:插入 NodeSchema,指定 index', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form') as Node; - const formNode2 = currentDocument?.getNode('form'); - expect(formNode).toEqual(formNode2); - currentDocument?.insertNodes( - formNode, - [ - { - componentName: 'TextInput', - props: { - propA: 'haha2', - propB: 3, - }, - }, - { - componentName: 'TextInput2', - props: { - propA: 'haha', - propB: 3, - }, - }, - ], - 1, - ); - expect(nodesMap?.size).toBe(ids.length + 2); - expect(formNode.children?.length).toBe(5); - const insertedNode1 = formNode.children?.get(1); - const insertedNode2 = formNode.children?.get(2); - expect(insertedNode1?.componentName).toBe('TextInput'); - expect(insertedNode1?.propsData).toEqual({ - propA: 'haha2', - propB: 3, - }); - expect(insertedNode2?.componentName).toBe('TextInput2'); - expect(insertedNode2?.propsData).toEqual({ - propA: 'haha', - propB: 3, - }); - }); - - it.only('场景二:插入 Node 实例,指定 index', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const formNode = nodesMap?.get('form') as INode; - const formNode2 = currentDocument?.getNode('form'); - expect(formNode).toEqual(formNode2); - const createdNode1 = currentDocument?.createNode({ - componentName: 'TextInput', - props: { - propA: 'haha2', - propB: 3, - }, - }); - const createdNode2 = currentDocument?.createNode({ - componentName: 'TextInput2', - props: { - propA: 'haha', - propB: 3, - }, - }); - currentDocument?.insertNodes(formNode, [createdNode1, createdNode2], 1); - expect(nodesMap?.size).toBe(ids.length + 2); - expect(formNode.children?.length).toBe(5); - const insertedNode1 = formNode.children?.get(1); - const insertedNode2 = formNode.children?.get(2); - expect(insertedNode1?.componentName).toBe('TextInput'); - expect(insertedNode1?.propsData).toEqual({ - propA: 'haha2', - propB: 3, - }); - expect(insertedNode2?.componentName).toBe('TextInput2'); - expect(insertedNode2?.propsData).toEqual({ - propA: 'haha', - propB: 3, - }); - }); - }); - }); - - describe('block ❌ | component ❌ | slot ✅', () => { - it('基本的 slot 创建', () => { - const formSchemaWithSlot = set( - cloneDeep(formSchema), - 'children[0].children[0].props.title.type', - 'JSSlot', - ); - const project = new Project(designer, { - componentsTree: [formSchemaWithSlot], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const nodesMap = currentDocument?.nodesMap; - const ids = getIdsFromSchema(formSchema); - // 目前每个 slot 会新增(1 + children.length)个节点 - const expectedNodeCnt = ids.length + 2; - expect(nodesMap?.size).toBe(expectedNodeCnt); - // PageHeader - expect(nodesMap?.get('node_k1ow3cbd')?.slots).toHaveLength(1); - }); - }); -}); diff --git a/packages/designer/tests/document/node/node.dragdrop.test.ts b/packages/designer/tests/document/node/node.dragdrop.test.ts deleted file mode 100644 index 3fe909124..000000000 --- a/packages/designer/tests/document/node/node.dragdrop.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import '../../fixtures/window'; -import { Project } from '../../../src/project/project'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - }; - }, - transformProps(props) { return props; }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -describe.skip('节点拖拽测试', () => { - describe('block ❌ | component ❌ | slot ❌', () => { - it('修改普通属性,string | number', () => { - const project = new Project(designer, { - componentsTree: [ - formSchema, - ], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap.size).toBe(expectedNodeCnt); - ids.forEach(id => { - expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName); - }); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); - }); - }); -}); diff --git a/packages/designer/tests/document/node/node.modify.test.ts b/packages/designer/tests/document/node/node.modify.test.ts deleted file mode 100644 index f7bd7dd5e..000000000 --- a/packages/designer/tests/document/node/node.modify.test.ts +++ /dev/null @@ -1,456 +0,0 @@ -import '../../fixtures/window'; -import { Project } from '../../../src/project/project'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - get advanced() { - return {}; - }, - }; - }, - transformProps(props) { return props; }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -describe('schema 生成节点模型测试', () => { - describe('block ❌ | component ❌ | slot ❌', () => { - let project: Project; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [ - formSchema, - ], - }); - project.open(); - }); - it('读取普通属性,string | number | object', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - /* - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - }, - id: 'form', - condition: true, - */ - const sizeProp = formNode?.getProp('size'); - const sizeProp2 = formNode?.getProps().getProp('size'); - expect(sizeProp).toBe(sizeProp2); - expect(sizeProp?.getAsString()).toBe('medium'); - expect(sizeProp?.getValue()).toBe('medium'); - - const autoValidateProp = formNode?.getProp('autoValidate'); - expect(autoValidateProp?.getValue()).toBe(true); - - const objProp = formNode?.getProp('obj'); - expect(objProp?.getValue()).toEqual({ - a: 1, - b: false, - c: 'string', - }); - const objAProp = formNode?.getProp('obj.a'); - const objBProp = formNode?.getProp('obj.b'); - const objCProp = formNode?.getProp('obj.c'); - expect(objAProp?.getValue()).toBe(1); - expect(objBProp?.getValue()).toBe(false); - expect(objCProp?.getValue()).toBe('string'); - - const idProp = formNode?.getExtraProp('extraPropA'); - expect(idProp?.getValue()).toBe('extraPropA'); - }); - - it('修改普通属性,string | number | object,使用 Node 实例接口', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - /* - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - }, - id: 'form', - condition: true, - */ - formNode?.setPropValue('size', 'large'); - const sizeProp = formNode?.getProp('size'); - expect(sizeProp?.getAsString()).toBe('large'); - expect(sizeProp?.getValue()).toBe('large'); - - formNode?.setPropValue('autoValidate', false); - const autoValidateProp = formNode?.getProp('autoValidate'); - expect(autoValidateProp?.getValue()).toBe(false); - - formNode?.setPropValue('obj', { - a: 2, - b: true, - c: 'another string', - }); - const objProp = formNode?.getProp('obj'); - expect(objProp?.getValue()).toEqual({ - a: 2, - b: true, - c: 'another string', - }); - formNode?.setPropValue('obj.a', 3); - formNode?.setPropValue('obj.b', false); - formNode?.setPropValue('obj.c', 'string'); - const objAProp = formNode?.getProp('obj.a'); - const objBProp = formNode?.getProp('obj.b'); - const objCProp = formNode?.getProp('obj.c'); - expect(objAProp?.getValue()).toBe(3); - expect(objBProp?.getValue()).toBe(false); - expect(objCProp?.getValue()).toBe('string'); - expect(objProp?.getValue()).toEqual({ - a: 3, - b: false, - c: 'string', - }); - }); - - it('修改普通属性,string | number | object,使用 Props 实例接口', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - /* - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - }, - id: 'form', - condition: true, - */ - const props = formNode?.getProps(); - props?.setPropValue('size', 'large'); - const sizeProp = formNode?.getProp('size'); - expect(sizeProp?.getAsString()).toBe('large'); - expect(sizeProp?.getValue()).toBe('large'); - - props?.setPropValue('autoValidate', false); - const autoValidateProp = formNode?.getProp('autoValidate'); - expect(autoValidateProp?.getValue()).toBe(false); - - props?.setPropValue('obj', { - a: 2, - b: true, - c: 'another string', - }); - const objProp = formNode?.getProp('obj'); - expect(objProp?.getValue()).toEqual({ - a: 2, - b: true, - c: 'another string', - }); - props?.setPropValue('obj.a', 3); - props?.setPropValue('obj.b', false); - props?.setPropValue('obj.c', 'string'); - const objAProp = formNode?.getProp('obj.a'); - const objBProp = formNode?.getProp('obj.b'); - const objCProp = formNode?.getProp('obj.c'); - expect(objAProp?.getValue()).toBe(3); - expect(objBProp?.getValue()).toBe(false); - expect(objCProp?.getValue()).toBe('string'); - expect(objProp?.getValue()).toEqual({ - a: 3, - b: false, - c: 'string', - }); - }); - - it('修改普通属性,string | number | object,使用 Prop 实例接口', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - /* - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - }, - id: 'form', - condition: true, - */ - const sizeProp = formNode?.getProp('size'); - sizeProp?.setValue('large'); - expect(sizeProp?.getAsString()).toBe('large'); - expect(sizeProp?.getValue()).toBe('large'); - - const autoValidateProp = formNode?.getProp('autoValidate'); - autoValidateProp?.setValue(false); - expect(autoValidateProp?.getValue()).toBe(false); - - - const objProp = formNode?.getProp('obj'); - objProp?.setValue({ - a: 2, - b: true, - c: 'another string', - }); - expect(objProp?.getValue()).toEqual({ - a: 2, - b: true, - c: 'another string', - }); - const objAProp = formNode?.getProp('obj.a'); - const objBProp = formNode?.getProp('obj.b'); - const objCProp = formNode?.getProp('obj.c'); - objAProp?.setValue(3); - objBProp?.setValue(false); - objCProp?.setValue('string'); - expect(objAProp?.getValue()).toBe(3); - expect(objBProp?.getValue()).toBe(false); - expect(objCProp?.getValue()).toBe('string'); - expect(objProp?.getValue()).toEqual({ - a: 3, - b: false, - c: 'string', - }); - }); - }); - - describe('block ❌ | component ❌ | slot ✅', () => { - let project: Project; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [ - formSchema, - ], - }); - project.open(); - }); - it('修改 slot 属性,初始存在 slot 属性名,正常生成节点模型', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - - formNode?.setPropValue('slotA', { - type: 'JSSlot', - value: [{ - componentName: 'TextInput1', - props: { - txt: 'haha', - num: 1, - bool: true, - }, - }, { - componentName: 'TextInput2', - props: { - txt: 'heihei', - num: 2, - bool: false, - }, - }], - }); - - expect(nodesMap.size).toBe(ids.length + 3); - expect(formNode?.slots).toHaveLength(1); - expect(formNode?.slots[0].children).toHaveLength(2); - const firstChildNode = formNode?.slots[0].children?.get(0); - const secondChildNode = formNode?.slots[0].children?.get(1); - expect(firstChildNode?.componentName).toBe('TextInput1'); - expect(firstChildNode?.getPropValue('txt')).toBe('haha'); - expect(firstChildNode?.getPropValue('num')).toBe(1); - expect(firstChildNode?.getPropValue('bool')).toBe(true); - expect(secondChildNode?.componentName).toBe('TextInput2'); - expect(secondChildNode?.getPropValue('txt')).toBe('heihei'); - expect(secondChildNode?.getPropValue('num')).toBe(2); - expect(secondChildNode?.getPropValue('bool')).toBe(false); - }); - - it('修改 slot 属性,初始存在 slot 属性名,关闭 slot', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - - formNode?.setPropValue('slotA', { - type: 'JSSlot', - value: [{ - componentName: 'TextInput1', - props: { - txt: 'haha', - num: 1, - bool: true, - }, - }, { - componentName: 'TextInput2', - props: { - txt: 'heihei', - num: 2, - bool: false, - }, - }], - }); - - expect(nodesMap.size).toBe(ids.length + 3); - expect(formNode?.slots).toHaveLength(1); - - formNode?.setPropValue('slotA', ''); - - expect(nodesMap.size).toBe(ids.length); - expect(formNode?.slots).toHaveLength(0); - }); - - it('修改 slot 属性,初始存在 slot 属性名,同名覆盖 slot', () => { - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - const formNode = currentDocument?.getNode('form'); - - formNode?.setPropValue('slotA', { - type: 'JSSlot', - name: 'slotA', - value: [{ - componentName: 'TextInput1', - props: { - txt: 'haha', - num: 1, - bool: true, - }, - }, { - componentName: 'TextInput2', - props: { - txt: 'heihei', - num: 2, - bool: false, - }, - }], - }); - - expect(nodesMap.size).toBe(ids.length + 3); - expect(formNode?.slots).toHaveLength(1); - expect(formNode?.slots[0].children).toHaveLength(2); - - let firstChildNode = formNode?.slots[0].children?.get(0); - expect(firstChildNode?.componentName).toBe('TextInput1'); - expect(firstChildNode?.getPropValue('txt')).toBe('haha'); - expect(firstChildNode?.getPropValue('num')).toBe(1); - expect(firstChildNode?.getPropValue('bool')).toBe(true); - - formNode?.setPropValue('slotA', { - type: 'JSSlot', - name: 'slotA', - value: [{ - componentName: 'TextInput3', - props: { - txt: 'xixi', - num: 3, - bool: false, - }, - }], - }); - - expect(nodesMap.size).toBe(ids.length + 2); - expect(formNode?.slots).toHaveLength(1); - expect(formNode?.slots[0].children).toHaveLength(1); - firstChildNode = formNode?.slots[0].children?.get(0); - expect(firstChildNode?.componentName).toBe('TextInput3'); - expect(firstChildNode?.getPropValue('txt')).toBe('xixi'); - expect(firstChildNode?.getPropValue('num')).toBe(3); - expect(firstChildNode?.getPropValue('bool')).toBe(false); - }); - }); -}); diff --git a/packages/designer/tests/document/node/node.remove.test.ts b/packages/designer/tests/document/node/node.remove.test.ts deleted file mode 100644 index e5556c20a..000000000 --- a/packages/designer/tests/document/node/node.remove.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { set, cloneDeep } from 'lodash-es'; -import '../../fixtures/window'; -import { Project } from '../../../src/project/project'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import { getIdsFromSchema } from '../../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - get advanced() { - return {}; - }, - }; - }, - transformProps(props) { - return props; - }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -describe('节点模型删除测试', () => { - it('删除叶子节点', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const originalNodeCnt = ids.length; - expect(nodesMap.size).toBe(originalNodeCnt); - - currentDocument?.removeNode('node_k1ow3cbn'); - // Button#1 - expect(nodesMap.size).toBe(originalNodeCnt - 1); - - currentDocument?.removeNode(nodesMap.get('node_k1ow3cbp')); - // Button#2 - expect(nodesMap.size).toBe(originalNodeCnt - 2); - - currentDocument?.removeNode('unexisting_node'); - expect(nodesMap.size).toBe(originalNodeCnt - 2); - }); - - it('删除叶子节点,带有 slot', () => { - const formSchemaWithSlot = set( - cloneDeep(formSchema), - 'children[1].children[0].children[2].children[1].props.greeting.type', - 'JSSlot', - ); - const project = new Project(designer, { - componentsTree: [formSchemaWithSlot], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const originalNodeCnt = ids.length + 2; - expect(nodesMap.size).toBe(originalNodeCnt); - - currentDocument?.removeNode('node_k1ow3cbp'); - // Button + Slot + Text - expect(nodesMap.size).toBe(originalNodeCnt - 3); - }); - - it('删除分支节点', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const originalNodeCnt = ids.length; - expect(nodesMap.size).toBe(originalNodeCnt); - - currentDocument?.removeNode('node_k1ow3cbo'); - // Div + 2 * Button - expect(nodesMap.size).toBe(originalNodeCnt - 3); - }); - - it('删除分支节点,带有 slot', () => { - const formSchemaWithSlot = set( - cloneDeep(formSchema), - 'children[1].children[0].children[2].children[1].props.greeting.type', - 'JSSlot', - ); - const project = new Project(designer, { - componentsTree: [formSchemaWithSlot], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const originalNodeCnt = ids.length + 2; - expect(nodesMap.size).toBe(originalNodeCnt); - - currentDocument?.removeNode('node_k1ow3cbo'); - // Div + 2 * Button + Slot + Text - expect(nodesMap.size).toBe(originalNodeCnt - 5); - }); -}); diff --git a/packages/designer/tests/document/node/node.test.ts b/packages/designer/tests/document/node/node.test.ts deleted file mode 100644 index 2695d6c83..000000000 --- a/packages/designer/tests/document/node/node.test.ts +++ /dev/null @@ -1,664 +0,0 @@ -// @ts-nocheck -import '../../fixtures/window'; -import { set } from '../../utils'; -import { - Editor, - globalContext, - Setters as InnerSetters, -} from '@alilc/lowcode-editor-core'; -import { Project } from '../../../src/project/project'; -import { Workspace as InnerWorkspace } from '@alilc/lowcode-workspace'; -import { DocumentModel } from '../../../src/document/document-model'; -import { - isRootNode, - Node, - comparePosition, - contains, - PositionNO, -} from '../../../src/document/node/node'; -import { Designer } from '../../../src/designer/designer'; -import formSchema from '../../fixtures/schema/form'; -import divMetadata from '../../fixtures/component-metadata/div'; -import dialogMetadata from '../../fixtures/component-metadata/dialog'; -import btnMetadata from '../../fixtures/component-metadata/button'; -import formMetadata from '../../fixtures/component-metadata/form'; -import pageMetadata from '../../fixtures/component-metadata/page'; -import rootHeaderMetadata from '../../fixtures/component-metadata/root-header'; -import rootContentMetadata from '../../fixtures/component-metadata/root-content'; -import rootFooterMetadata from '../../fixtures/component-metadata/root-footer'; -import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; -import { isNode } from '@alilc/lowcode-utils'; -import { Setters } from '@alilc/lowcode-shell'; - -describe('Node 方法测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - - beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = new DocumentModel(project, formSchema); - editor.set('setters', new Setters(new InnerSetters())); - !globalContext.has(Editor) && globalContext.register(editor, Editor); - !globalContext.has('workspace') && globalContext.register(new InnerWorkspace(), 'workspace'); - }); - - afterEach(() => { - project.unload(); - designer.purge(); - editor = null; - designer = null; - project = null; - }); - - // Case 1: When children is null - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const result = node.initialChildren(null); - // 预期结果是一个空数组 - expect(result).toEqual([]); - }); - - // Case 2: When children is undefined - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const result = node.initialChildren(undefined); - // 预期结果是一个空数组 - expect(result).toEqual([]); - }); - - // Case 3: When children is array - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const childrenArray = [{ id: 1, name: 'Child 1' }, { id: 2, name: 'Child 2' }]; - const result = node.initialChildren(childrenArray); - // 预期结果是一个数组 - expect(result).toEqual(childrenArray); - }); - - // Case 4: When children is not null and not an array - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const childObject = { id: 1, name: 'Child 1' }; - const result = node.initialChildren(childObject); - // 预期结果是一个数组 - expect(result).toEqual([childObject]); - }); - - // Case 5: When children 0 - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const childObject = 0; - const result = node.initialChildren(childObject); - // 预期结果是一个数组 - expect(result).toEqual([0]); - }); - - // Case 6: When children false - test('initialChildren returns result of initialChildren function when children is null ', () => { - const node = new Node(doc, { componentName: 'Button', props: { a: 1 } }); - const childObject = false; - const result = node.initialChildren(childObject); - // 预期结果是一个数组 - expect(result).toEqual([false]); - }); - - - it('condition group', () => { }); - - it('getExtraProp / setExtraProp', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - expect(firstBtn.getExtraProp('non-existing', false)).toBeNull(); - - firstBtn.setExtraProp('xxx', '1111'); - expect(firstBtn.getExtraProp('xxx', false).getValue()).toBe('1111'); - }); - - it('import(leaf)', () => { - const form = doc.getNode('node_k1ow3cbo'); - form.insert({ componentName: 'Leaf', children: '111' }); - - const leaf = form.getChildren().get(2); - expect(leaf.getPropValue('children')).toBe('111'); - - leaf.import({ componentName: 'Leaf', children: '222' }); - expect(leaf.getPropValue('children')).toBe('222'); - - leaf.import({ componentName: 'Leaf', children: { type: 'JSExpression', value: 'state.x' } }); - expect(leaf.getPropValue('children')).toEqual({ type: 'JSExpression', value: 'state.x' }); - }); - - it('hasCondition', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.getExtraProp('condition')?.setValue(undefined); - expect(firstBtn.hasCondition()).toBeFalsy(); - - firstBtn.getExtraProp('condition')?.setValue(null); - expect(firstBtn.hasCondition()).toBeFalsy(); - - firstBtn.getExtraProp('condition')?.setValue(true); - expect(firstBtn.hasCondition()).toBeFalsy(); - - firstBtn.getExtraProp('condition')?.setValue(''); - expect(firstBtn.hasCondition()).toBeFalsy(); - - firstBtn.getExtraProp('condition')?.setValue(1); - expect(firstBtn.hasCondition()).toBeTruthy(); - - firstBtn.getExtraProp('condition')?.setValue(false); - expect(firstBtn.hasCondition()).toBeTruthy(); - }); - - it('hasLoop', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - expect(firstBtn.hasLoop()).toBeFalsy(); - - // 这里必须用 add,因为 hasLoop 实现的跳过了 stash - firstBtn.props.add([1, 2], '___loop___'); - expect(firstBtn.hasLoop()).toBeTruthy(); - - firstBtn.getExtraProp('loop')?.setValue({ type: 'JSExpression', value: 'state.a' }); - expect(firstBtn.hasLoop()).toBeTruthy(); - - firstBtn.getExtraProp('loop')?.setValue(1); - expect(firstBtn.hasLoop()).toBeFalsy(); - }); - - describe('getSuitablePlace', () => { - it('root,子节点中有容器节点', () => { - designer.createComponentMeta(pageMetadata); - designer.createComponentMeta(rootHeaderMetadata); - designer.createComponentMeta(rootContentMetadata); - designer.createComponentMeta(rootFooterMetadata); - - const rootHeaderMeta = designer.getComponentMeta('RootHeader'); - set(rootHeaderMeta, 'prototype.options.canDropIn', true); - - let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toEqual({ - container: doc.getNode('node_k1ow3cba'), - ref: 1, - }); - - set(rootHeaderMeta, 'prototype.options.canDropIn', () => true); - o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toEqual({ - container: doc.getNode('node_k1ow3cba'), - ref: 1, - }); - }); - - it('root,直接子节点中无容器节点,自身支持放入子节点', () => { - designer.createComponentMeta(pageMetadata); - - let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - - const pageMeta = designer.getComponentMeta('Page'); - set(pageMeta, 'prototype.options.canDropIn', () => true); - - expect(o).toEqual({ - container: doc.rootNode, - ref: 1, - }); - - set(pageMeta, 'prototype.options.canDropIn', undefined); - o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toEqual({ - container: doc.rootNode, - ref: 1, - }); - - set(pageMeta, 'prototype.options.canDropIn', true); - o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toEqual({ - container: doc.rootNode, - ref: 1, - }); - }); - - it('root,子节点中无容器节点,自己也不支持放入子节点', () => { - designer.createComponentMeta(pageMetadata); - - let pageMeta = designer.getComponentMeta('Page'); - - pageMeta = set(pageMeta, 'prototype.options.canDropIn', () => false); - let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toBeNull(); - - set(pageMeta, 'prototype.options.canDropIn', false); - o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1); - expect(o).toBeNull(); - }); - - it('放入模态节点', () => { - designer.createComponentMeta(pageMetadata); - designer.createComponentMeta(dialogMetadata); - - const dialog = doc.createNode({ componentName: 'Dialog' }); - - const o = doc.rootNode?.getSuitablePlace(dialog, 1); - expect(o.container).toBe(doc.rootNode); - expect(o.ref).toBe(1); - }); - - it('包含 focusNode', () => { - const o = doc.rootNode?.getSuitablePlace(doc.rootNode); - expect(o.container).toBe(doc.rootNode); - }); - - it.skip('非 root 节点,不能放入子节点', () => { - designer.createComponentMeta(formMetadata); - designer.createComponentMeta(pageMetadata); - - // form 子节点以及自身都不能放入子节点 - const formMeta = designer.getComponentMeta('Form'); - set(formMeta, 'prototype.options.canDropIn', false); - - const pageMeta = designer.getComponentMeta('Page'); - set(pageMeta, 'prototype.options.canDropIn', () => true); - - const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), { index: 1 }); - expect(o).toEqual({ - container: doc.rootNode, - ref: { index: 1 }, - }); - }); - - it('非 root 节点,能放入子节点', () => { - designer.createComponentMeta(formMetadata); - designer.createComponentMeta(pageMetadata); - - // form 子节点以及自身都不能放入子节点 - const formMeta = designer.getComponentMeta('Form'); - set(formMeta, 'prototype.options.canDropIn', true); - - const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), 1); - expect(o).toEqual({ - container: doc.getNode('form'), - ref: 1, - }); - }); - - it('null', () => { - expect( - doc.rootNode?.getSuitablePlace.call({ - contains: () => false, - isContainer: () => false, - isRoot: () => false, - }), - ).toBeNull(); - }); - }); - - it('removeChild / replaceWith / replaceChild', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const form = doc.getNode('node_k1ow3cbo'); - - // 不符合条件的节点直接返回 - expect(firstBtn.replaceChild(form, { componentName: 'Button', props: { x: 1 } })).toBe(form); - - firstBtn.select(); - firstBtn.parent?.replaceChild(firstBtn, { componentName: 'Button', props: { x: 1 } }); - - expect(firstBtn.parent?.getChildren()?.size).toBe(2); - expect(firstBtn.parent?.getChildren()?.get(0)?.getPropValue('x')).toBe(1); - - const secondBtn = doc.getNode('node_k1ow3cbp')!; - secondBtn.replaceWith({ componentName: 'Button', props: { y: 1 } }); - expect(firstBtn.parent?.getChildren()?.size).toBe(2); - expect(firstBtn.parent?.getChildren()?.get(1)?.getPropValue('y')).toBe(1); - }); - - it('schema', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const schema = firstBtn.schema; - schema.props.size = 'large'; - firstBtn.schema = schema; - - expect(firstBtn.getPropValue('size')).toBe('large'); - }); - - describe('插入相关方法', () => { - it('insertBefore / onChildrenChange', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const secondBtn = doc.getNode('node_k1ow3cbp')!; - const btnParent = firstBtn.parent!; - const mockFn = jest.fn(); - const off = btnParent.onChildrenChange(mockFn); - - // Node 实例 - btnParent.insertBefore(new Node(doc, { componentName: 'Button', props: { a: 1 } }), firstBtn); - expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ a: 1 }); - expect(mockFn).toHaveBeenCalledTimes(1); - - // TODO: 暂时不支持,后面补上 - // // NodeSchema - // btnParent.insertBefore({ componentName: 'Button', props: { b: 1 } }, firstBtn); - // expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ b: 1 }); - // expect(mockFn).toHaveBeenCalledTimes(2); - - // // getComponentName - // btnParent.insertBefore({ getComponentName: () => 'Button', props: { c: 1 } }, firstBtn); - // expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ c: 1 }); - // expect(mockFn).toHaveBeenCalledTimes(3); - }); - - it('insertAfter / onChildrenChange', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const secondBtn = doc.getNode('node_k1ow3cbp')!; - const btnParent = firstBtn.parent!; - const mockFn = jest.fn(); - const off = btnParent.onChildrenChange(mockFn); - - // Node 实例 - btnParent.insertAfter(new Node(doc, { componentName: 'Button', props: { a: 1 } }), firstBtn); - expect(btnParent.children.get(1)?.getProps().export().props).toEqual({ a: 1 }); - expect(mockFn).toHaveBeenCalledTimes(1); - - // NodeSchema - btnParent.insertAfter({ componentName: 'Button', props: { b: 1 } }, firstBtn); - expect(btnParent.children.get(1)?.getProps().export().props).toEqual({ b: 1 }); - expect(mockFn).toHaveBeenCalledTimes(2); - - // getComponentName - btnParent.insertAfter({ getComponentName: () => 'Button' }, firstBtn); - expect(btnParent.children.get(1)?.getProps().export().props).toEqual({}); - expect(mockFn).toHaveBeenCalledTimes(3); - }); - }); - - it('setVisible / getVisible / onVisibleChange', () => { - const mockFn = jest.fn(); - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const off = firstBtn.onVisibleChange(mockFn); - firstBtn.setVisible(true); - expect(firstBtn.getVisible()).toBeTruthy(); - expect(mockFn).toHaveBeenCalledTimes(1); - expect(mockFn).toHaveBeenCalledWith(true); - - firstBtn.setVisible(false); - - expect(firstBtn.getVisible()).toBeFalsy(); - expect(mockFn).toHaveBeenCalledTimes(2); - expect(mockFn).toHaveBeenCalledWith(false); - - off(); - mockFn.mockClear(); - firstBtn.setVisible(true); - expect(mockFn).not.toHaveBeenCalled(); - }); - - it('RGL / getRGL', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.isRGLContainer = true; - expect(firstBtn.isRGLContainer).toBeTruthy(); - - const rgl = firstBtn.getRGL(); - expect(rgl.isContainerNode).toBeFalsy(); - expect(rgl.isEmptyNode).toBeTruthy(); - expect(rgl.isRGLContainerNode).toBeTruthy(); - expect(rgl.isRGLNode).toBeFalsy(); - expect(rgl.isRGL).toBeTruthy(); - }); - - it('onPropChange', () => { - const mockFn = jest.fn(); - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const off = firstBtn.onPropChange(mockFn); - - firstBtn.setPropValue('x', 1); - expect(mockFn).toHaveBeenCalledTimes(1); - firstBtn.setPropValue('x', 2); - expect(mockFn).toHaveBeenCalledTimes(2); - - off(); - mockFn.mockClear(); - firstBtn.setPropValue('x', 3); - expect(mockFn).not.toHaveBeenCalled(); - }); - - it('addSlot / unlinkSlot / removeSlot', () => { }); - - it('setProps', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const secondBtn = doc.getNode('node_k1ow3cbp')!; - - firstBtn.setProps(secondBtn.getProps()); - expect(firstBtn.getProps()).toBe(secondBtn.getProps()); - }); - - it('advanced initials / autoruns', async () => { - designer.createComponentMeta(pageMetadata); - - const pageMeta = designer.getComponentMeta('Page'); - const autorunMockFn = jest.fn(); - set(pageMeta, '_transformedMetadata.configure.advanced.autoruns', [ - { name: 'a', autorun: autorunMockFn }, - ]); - const initialChildrenMockFn = jest.fn(); - set(pageMeta, '_transformedMetadata.configure.advanced.initialChildren', initialChildrenMockFn); - doc.createNode({ componentName: 'Page', props: { a: 1 } }); - - expect(autorunMockFn).toHaveBeenCalled(); - expect(initialChildrenMockFn).toHaveBeenCalled(); - - set(pageMeta, '_transformedMetadata.configure.advanced.initialChildren', {}); - doc.createNode({ componentName: 'Page', props: { a: 1 } }); - expect(autorunMockFn).toHaveBeenCalledTimes(2); - }); - - it('isValidComponent', () => { - designer.createComponentMeta(divMetadata); - expect(doc.getNode('node_k1ow3cbo')?.isValidComponent()).toBeTruthy(); - expect(doc.getNode('form')?.isValidComponent()).toBeFalsy(); - }); - - it('title', () => { - designer.createComponentMeta(btnMetadata); - const btn = doc.getNode('node_k1ow3cbn'); - // 从 componentMeta 中获取到 title 值 - expect(btn.title).toEqual({ type: 'i18n', 'zh-CN': '按钮', 'en-US': 'Button' }); - // 从 extraProp 中获取值 - btn.setExtraProp('title', 'hello button'); - expect(btn.title).toBe('hello button'); - - // btn.props.deleteKey('___title___'); - // 从 componentMeta descriptor 指向的 key 获取 title - // btn.setPropValue('xTitle', 'title from descriptor') - // expect(btn.title).toBe('title from descriptor'); - }); - - it('isEmpty / getIndex / getIcon', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - // expect(firstBtn.children).toBeNull(); - expect(firstBtn.isEmpty()).toBeTruthy(); - expect(firstBtn.index).toBe(0); - expect(firstBtn.getIndex()).toBe(0); - expect(typeof firstBtn.getIcon()).toBe('function'); - expect(doc.getNode('page')!.index).toBe(-1); - }); - - it('schema / toData / export', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - expect(firstBtn.toData().componentName).toBe('Button'); - }); - - it('internalSetParent / internalSetWillPurge', () => { - const firstChild = doc.rootNode?.getChildren()?.get(0); - firstChild?.internalSetParent(doc.rootNode); - - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.internalSetWillPurge(); - // expect(firstBtn.parent).(); - - expect(firstBtn.hasSlots()).toBeFalsy(); - }); - - it('prevSibling / nextSibling', () => { - // no parent - const page = doc.getNode('page'); - expect(page?.nextSibling).toBeNull(); - expect(page?.prevSibling).toBeNull(); - - // normal - const firstBtn = doc.getNode('node_k1ow3cbn'); - const secondBtn = doc.getNode('node_k1ow3cbp'); - expect(firstBtn?.nextSibling).toBe(secondBtn); - expect(secondBtn?.prevSibling).toBe(firstBtn); - expect(secondBtn?.nextSibling).toBeNull(); - - // index < 0 - firstBtn?.parent?.removeChild(firstBtn); - expect(firstBtn?.nextSibling).toBeNull(); - expect(firstBtn?.prevSibling).toBeNull(); - }); - - it('toString', () => { - expect(doc.rootNode.toString()).toBe('page'); - }); - - it('lock', () => { - const form = doc.getNode('node_k1ow3cbo'); - expect(form.isLocked).toBeFalsy(); - form.lock(true); - expect(form.isLocked).toBeTruthy(); - form.lock(false); - expect(form.isLocked).toBeFalsy(); - form.lock(); - expect(form.isLocked).toBeTruthy(); - }); - - it('didDropIn / didDropOut', () => { - const form = doc.getNode('node_k1ow3cbo'); - designer.createComponentMeta(divMetadata); - designer.createComponentMeta(formMetadata); - const callbacks = form.componentMeta.advanced.callbacks; - const fn1 = callbacks.onNodeAdd = jest.fn(); - const fn2 = callbacks.onNodeRemove = jest.fn(); - const textField = doc.getNode('node_k1ow3cc9'); - form.didDropIn(textField); - expect(fn1).toHaveBeenCalledWith(textField.internalToShellNode(), form.internalToShellNode()); - - form.didDropOut(textField); - expect(fn2).toHaveBeenCalledWith(textField.internalToShellNode(), form.internalToShellNode()); - }); - - it('hover', () => { - const firstBtn = doc.getNode('node_k1ow3cbn')!; - firstBtn.hover(true); - expect(doc.designer.detecting.current).toBe(firstBtn); - firstBtn.hover(false); - expect(doc.designer.detecting.current).toBeNull(); - firstBtn.hover(); - expect(doc.designer.detecting.current).toBe(firstBtn); - }); - - it('getRect', () => { - const root = doc.rootNode!; - const firstBtn = doc.getNode('node_k1ow3cbn')!; - expect(root.getRect()).toBeNull(); - expect(firstBtn.getRect()).toBeNull(); - - doc.project.mountSimulator({ - computeRect: () => ({ x: 2, y: 2 }), - viewport: { - contentBounds: { x: 1, y: 1 }, - }, - }); - - expect(root.getRect()).toEqual({ x: 1, y: 1 }); - expect(firstBtn.getRect()).toEqual({ x: 2, y: 2 }); - }); - - it('isRootNode / isRoot / isNode', () => { - expect(isRootNode(doc.rootNode)).toBeTruthy(); - expect(isNode(doc.rootNode)).toBeTruthy(); - }); - - it('contains / comparePosition', () => { - const page = doc.getNode('page')!; - const content = doc.getNode('node_k1ow3cbb')!; - const firstBtn = doc.getNode('node_k1ow3cbn')!; - const secondBtn = doc.getNode('node_k1ow3cbp')!; - const firstCard = doc.getNode('node_k1ow3cbj')!; - expect(contains(firstBtn, firstBtn)).toBeTruthy(); - expect(contains(firstBtn, secondBtn)).toBeFalsy(); - expect(contains(firstBtn, page)).toBeFalsy(); - expect(contains(firstBtn, content)).toBeFalsy(); - expect(contains(firstCard, firstBtn)).toBeFalsy(); - - expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter); - expect(firstBtn.comparePosition(firstBtn)).toBe(PositionNO.TheSame); - expect(comparePosition(firstBtn, firstBtn)).toBe(PositionNO.TheSame); - expect(comparePosition(firstBtn, firstBtn.parent)).toBe(PositionNO.ContainedBy); - expect(comparePosition(firstBtn.parent, firstBtn)).toBe(PositionNO.Contains); - expect(comparePosition(firstCard, firstBtn)).toBe(PositionNO.BeforeOrAfter); - expect(comparePosition(firstBtn, firstCard)).toBe(PositionNO.BeforeOrAfter); - }); - - it('getZLevelTop', () => { }); - it('propsData', () => { - expect(new Node(doc, { componentName: 'Leaf' }).propsData).toBeNull(); - expect(new Node(doc, { componentName: 'Fragment' }).propsData).toBeNull(); - }); - - describe('deprecated methods', () => { - it('setStatus / getStatus', () => { - const root = doc.rootNode!; - root.setStatus('xxx', true); - - root.setStatus('locking', true); - root.setStatus('pseudo', true); - root.setStatus('inPlaceEditing', true); - - expect(root.getStatus('locking')).toBeTruthy(); - expect(root.getStatus('pseudo')).toBeTruthy(); - expect(root.getStatus('inPlaceEditing')).toBeTruthy(); - expect(root.getStatus()).toEqual({ - locking: true, - pseudo: true, - inPlaceEditing: true, - }); - }); - - it('getPage', () => { - expect(doc.rootNode?.getPage()).toBe(doc); - }); - - it('getDOMNode', () => { - const root = doc.rootNode!; - const firstBtn = doc.getNode('node_k1ow3cbn')!; - - doc.project.mountSimulator({ - findDOMNodes: () => [{ x: 1, y: 1 }], - getComponentInstances: (node) => { - if (node.componentName === 'Page') { - return []; - } - return [{}]; - }, - }); - - expect(root.getDOMNode()).toBeUndefined(); - expect(firstBtn.getDOMNode()).toEqual({ x: 1, y: 1 }); - }); - - it('registerAddon / getAddonData', () => { - const page = doc.getNode('page')!; - page.registerAddon('a', () => 'prop a'); - expect(page.getAddonData('a')).toBe('prop a'); - expect(page.getAddonData('b')).toBeUndefined(); - - expect(page.export().a).toBe('prop a'); - }); - - it('getPrototype / setPrototype', () => { - const page = doc.getNode('page')!; - page.setPrototype({ a: 1 }); - expect(page.getPrototype()).toEqual({ a: 1 }); - }); - }); -}); diff --git a/packages/designer/tests/document/node/props/__snapshots__/value-to-source.test.ts.snap b/packages/designer/tests/document/node/props/__snapshots__/value-to-source.test.ts.snap deleted file mode 100644 index f148298d2..000000000 --- a/packages/designer/tests/document/node/props/__snapshots__/value-to-source.test.ts.snap +++ /dev/null @@ -1,37 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`valueToSource 1`] = `"1"`; - -exports[`valueToSource 2`] = `"true"`; - -exports[`valueToSource 3`] = `"[]"`; - -exports[`valueToSource 4`] = ` -"[{ - \\"a\\": 1 -}]" -`; - -exports[`valueToSource 5`] = ` -"{ - \\"a\\": 1 -}" -`; - -exports[`valueToSource 6`] = `"null"`; - -exports[`valueToSource 7`] = `"() => {}"`; - -exports[`valueToSource 8`] = `"new Map()"`; - -exports[`valueToSource 9`] = `"new Set()"`; - -exports[`valueToSource 10`] = `"/haha/"`; - -exports[`valueToSource 11`] = `"\\"hahah\\""`; - -exports[`valueToSource 12`] = `"Symbol(\\"haha\\")"`; - -exports[`valueToSource 13`] = `"undefined"`; - -exports[`valueToSource 14`] = `"new Date(\\"2020-12-11T10:03:18.520Z\\")"`; diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts deleted file mode 100644 index ff4147a34..000000000 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ /dev/null @@ -1,697 +0,0 @@ -import '../../../fixtures/window'; -import { Editor, engineConfig } from '@alilc/lowcode-editor-core'; -import { Designer } from '../../../../src/designer/designer'; -import { DocumentModel } from '../../../../src/document/document-model'; -import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop'; -import { GlobalEvent, IPublicEnumTransformStage } from '@alilc/lowcode-types'; -import { shellModelFactory } from '../../../../../engine/src/modules/shell-model-factory'; - -const slotNodeImportMockFn = jest.fn(); -const slotNodeRemoveMockFn = jest.fn(); -const mockOwner = { - componentName: 'Div', - addSlot() {}, - document: { - createNode(schema) { - return { - ...schema, - addSlot() {}, - internalSetSlotFor() {}, - import: slotNodeImportMockFn, - export() { - return schema; - }, - remove: slotNodeRemoveMockFn, - }; - }, - designer: { - editor: { - eventBus: { - emit: jest.fn(), - }, - }, - }, - }, - isInited: true, - emitPropChange: jest.fn(), - delete() {}, -}; - -const mockPropsInst = { - owner: mockOwner, - delete() {}, -}; - -mockPropsInst.props = mockPropsInst; - -describe('Prop 类测试', () => { - describe('基础类型', () => { - let boolProp: Prop; - let strProp: Prop; - let numProp: Prop; - let nullProp: Prop; - let expProp: Prop; - let slotProp: Prop; - beforeEach(() => { - boolProp = new Prop(mockPropsInst, true, 'boolProp'); - strProp = new Prop(mockPropsInst, 'haha', 'strProp'); - numProp = new Prop(mockPropsInst, 1, 'numProp'); - nullProp = new Prop(mockPropsInst, null, 'nullProp'); - expProp = new Prop(mockPropsInst, { type: 'JSExpression', value: 'state.haha' }, 'expProp'); - slotProp = new Prop( - mockPropsInst, - { - type: 'JSSlot', - title: '测试 slot', - name: 'testSlot', - params: { a: 1 }, - value: [{ componentName: 'Button' }], - }, - 'slotProp', - ); - slotNodeImportMockFn.mockClear(); - slotNodeRemoveMockFn.mockClear(); - }); - afterEach(() => { - boolProp.purge(); - strProp.purge(); - numProp.purge(); - nullProp.purge(); - expProp.purge(); - slotProp.purge(); - }); - - it('consturctor / getProps / getNode', () => { - expect(boolProp.parent).toBe(mockPropsInst); - expect(boolProp.getProps()).toBe(mockPropsInst); - expect(boolProp.getNode()).toBe(mockOwner); - }); - - it('misc', () => { - expect(boolProp.get('x', false)).toBeNull(); - expect(boolProp.maps).toBeNull(); - expect(boolProp.add()).toBeNull(); - - strProp.unset(); - strProp.add(2, true); - strProp.set(0); - - expect(numProp.set()).toBeNull(); - expect(numProp.has()).toBeFalsy(); - expect(numProp.path).toEqual(['numProp']); - }); - - it('getValue / getAsString / setValue', () => { - expect(strProp.getValue()).toBe('haha'); - strProp.setValue('heihei'); - strProp.setValue('heihei'); - expect(strProp.getValue()).toBe('heihei'); - expect(strProp.getAsString()).toBe('heihei'); - - strProp.unset(); - expect(strProp.getValue()).toBeUndefined(); - }); - - it('code', () => { - expect(expProp.code).toBe('state.haha'); - expect(boolProp.code).toBe('true'); - expect(strProp.code).toBe('"haha"'); - - expProp.code = 'state.heihei'; - expect(expProp.code).toBe('state.heihei'); - expect(expProp.getValue()).toEqual({ - type: 'JSExpression', - value: 'state.heihei', - }); - - boolProp.code = 'false'; - expect(boolProp.code).toBe('false'); - expect(boolProp.getValue()).toBe(false); - - strProp.code = '"heihei"'; - expect(strProp.code).toBe('"heihei"'); - expect(strProp.getValue()).toBe('heihei'); - - // TODO: 不确定为什么会有这个分支 - strProp.code = 'state.a'; - expect(strProp.code).toBe('state.a'); - expect(strProp.getValue()).toEqual({ - type: 'JSExpression', - value: 'state.a', - mock: 'heihei', - }); - }); - - it('export', () => { - expect(boolProp.export(IPublicEnumTransformStage.Save)).toBe(true); - expect(strProp.export(IPublicEnumTransformStage.Save)).toBe('haha'); - expect(numProp.export(IPublicEnumTransformStage.Save)).toBe(1); - expect(nullProp.export(IPublicEnumTransformStage.Save)).toBe(null); - expect(nullProp.export(IPublicEnumTransformStage.Serilize)).toBe(null); - expect(expProp.export(IPublicEnumTransformStage.Save)).toEqual({ - type: 'JSExpression', - value: 'state.haha', - }); - - strProp.unset(); - expect(strProp.getValue()).toBeUndefined(); - expect(strProp.isUnset()).toBeTruthy(); - expect(strProp.export(IPublicEnumTransformStage.Save)).toBeUndefined(); - - expect( - new Prop(mockPropsInst, false, '___condition___').export(IPublicEnumTransformStage.Render), - ).toBeTruthy(); - engineConfig.set('enableCondition', true); - expect( - new Prop(mockPropsInst, false, '___condition___').export(IPublicEnumTransformStage.Render), - ).toBeFalsy(); - expect(slotProp.export(IPublicEnumTransformStage.Render)).toEqual({ - type: 'JSSlot', - params: { a: 1 }, - value: { - componentName: 'Slot', - title: '测试 slot', - name: 'testSlot', - params: { a: 1 }, - children: [{ componentName: 'Button' }], - }, - }); - expect(slotProp.export(IPublicEnumTransformStage.Save)).toEqual({ - type: 'JSSlot', - params: { a: 1 }, - value: [{ componentName: 'Button' }], - title: '测试 slot', - name: 'testSlot', - }); - }); - - it('compare', () => { - const newProp = new Prop(mockPropsInst, 'haha'); - const newProp2 = new Prop(mockPropsInst, { a: 1 }); - expect(strProp.compare(newProp)).toBe(0); - expect(strProp.compare(expProp)).toBe(2); - - newProp.unset(); - expect(strProp.compare(newProp)).toBe(2); - strProp.unset(); - expect(strProp.compare(newProp)).toBe(0); - expect(strProp.compare(newProp2)).toBe(2); - }); - - it('isVirtual', () => { - expect(new Prop(mockPropsInst, 111, '!virtualProp')).toBeTruthy(); - }); - - it('purge', () => { - boolProp.purge(); - expect(boolProp.purged).toBeTruthy(); - boolProp.purge(); - }); - - it('slot', () => { - // 更新 slot - slotProp.setValue({ - type: 'JSSlot', - value: [ - { - componentName: 'Form', - }, - ], - }); - expect(slotNodeImportMockFn).toBeCalled(); - - // 节点类型转换 - slotProp.setValue(true); - expect(slotNodeRemoveMockFn).toBeCalled(); - }); - - it('迭代器 / map / forEach', () => { - const mockFn = jest.fn(); - for (const item of strProp) { - mockFn(); - } - expect(mockFn).not.toHaveBeenCalled(); - mockFn.mockClear(); - - strProp.forEach((item) => { - mockFn(); - }); - expect(mockFn).not.toHaveBeenCalled(); - mockFn.mockClear(); - - strProp.map((item) => { - return mockFn(); - }); - expect(mockFn).not.toHaveBeenCalled(); - mockFn.mockClear(); - }); - }); - - describe('复杂类型', () => { - describe('items(map 类型)', () => { - let prop: Prop; - beforeEach(() => { - prop = new Prop(mockPropsInst, { - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - emptyArr: [], - emptyObj: {}, - z: { - z1: 1, - z2: 'str', - }, - }); - }); - afterEach(() => { - prop.purge(); - }); - - it('items / get', async () => { - expect(prop.size).toBe(7); - - expect(prop.get('a').getValue()).toBe(1); - expect(prop.get('b').getValue()).toBe('str'); - expect(prop.get('c').getValue()).toBe(true); - expect(prop.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' }); - expect(prop.get('z').getValue()).toEqual({ - z1: 1, - z2: 'str', - }); - - expect(prop.getPropValue('a')).toBe(1); - prop.setPropValue('a', 2); - expect(prop.getPropValue('a')).toBe(2); - prop.clearPropValue('a'); - expect(prop.get('a')?.isUnset()).toBeTruthy(); - - expect(prop.get('z.z1')?.getValue()).toBe(1); - expect(prop.get('z.z2')?.getValue()).toBe('str'); - - const newlyCreatedProp = prop.get('l', true); - const newlyCreatedNestedProp = prop.get('m.m1', true); - newlyCreatedProp.setValue('newlyCreatedProp'); - newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp'); - - expect(prop.get('l').getValue()).toBe('newlyCreatedProp'); - expect(prop.get('m.m1').getValue()).toBe('newlyCreatedNestedProp'); - - const newlyCreatedNestedProp2 = prop.get('m.m2', true); - // .m2 的值为 undefined,导出时将会被移除 - expect(prop.get('m').getValue()).toEqual({ m1: 'newlyCreatedNestedProp' }); - - // 对于空值的 list / map 类型,_items 应该为 null - expect(prop.get('emptyArr')._items).toBeNull(); - expect(prop.get('emptyObj')._items).toBeNull(); - }); - - it('export', () => { - expect(prop.export()).toEqual({ - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - emptyArr: [], - emptyObj: {}, - z: { - z1: 1, - z2: 'str', - }, - }); - }); - - it('compare', () => { - const prop1 = new Prop(mockPropsInst, { a: 1 }); - const prop2 = new Prop(mockPropsInst, { b: 1 }); - expect(prop1.compare(prop2)).toBe(1); - }); - - it('has / add / delete / deleteKey / remove', () => { - expect(prop.has('a')).toBeTruthy(); - expect(prop.has('b')).toBeTruthy(); - expect(prop.has('c')).toBeTruthy(); - expect(prop.has('d')).toBeTruthy(); - expect(prop.has('z')).toBeTruthy(); - expect(prop.has('y')).toBeFalsy(); - - // 触发一下内部 maps 构造 - prop.items; - expect(prop.has('z')).toBeTruthy(); - - expect(prop.add(1)).toBeNull(); - - prop.deleteKey('c'); - expect(prop.get('c', false)).toBeNull(); - prop.delete(prop.get('b')); - expect(prop.get('b', false)).toBeNull(); - - prop.get('d')?.remove(); - expect(prop.get('d', false)).toBeNull(); - }); - - it('set', () => { - prop.set('e', 1); - expect(prop.get('e', false)?.getValue()).toBe(1); - prop.set('a', 5); - expect(prop.get('a', false)?.getValue()).toBe(5); - }); - - it('迭代器 / map / forEach', () => { - const mockFn = jest.fn(); - for (const item of prop) { - mockFn(); - } - expect(mockFn).toHaveBeenCalledTimes(7); - mockFn.mockClear(); - - prop.forEach((item) => { - mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(7); - mockFn.mockClear(); - - prop.map((item) => { - return mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(7); - mockFn.mockClear(); - }); - - it('dispose', () => { - prop.items; - prop.dispose(); - - expect(prop._items).toBeNull(); - }); - }); - - describe('items(list 类型)', () => { - let prop: Prop; - beforeEach(() => { - prop = new Prop(mockPropsInst, [1, true, 'haha']); - }); - afterEach(() => { - prop.purge(); - }); - - it('items / get', () => { - expect(prop.size).toBe(3); - - expect(prop.get(0).getValue()).toBe(1); - expect(prop.get(1).getValue()).toBe(true); - expect(prop.get(2).getValue()).toBe('haha'); - - expect(prop.getAsString()).toBe(''); - - prop.unset(); - prop.set(0, true); - expect(prop.set('x', 'invalid')).toBeNull(); - expect(prop.get(0).getValue()).toBeTruthy(); - - // map / list 级联测试 - prop.get('loopArgs.0', true).setValue('newItem');; - expect(prop.get('loopArgs.0').getValue()).toBe('newItem'); - }); - - it('export', () => { - expect(prop.export()).toEqual([1, true, 'haha']); - // 触发构造 - prop.items; - expect(prop.export()).toEqual([1, true, 'haha']); - }); - - it('compare', () => { - const prop1 = new Prop(mockPropsInst, [1]); - const prop2 = new Prop(mockPropsInst, [2]); - const prop3 = new Prop(mockPropsInst, [1, 2]); - expect(prop1.compare(prop2)).toBe(1); - expect(prop1.compare(prop3)).toBe(2); - }); - - it('set', () => { - prop.set(0, 1); - expect(prop.get(0, false)?.getValue()).toBe(1); - // illegal - // expect(prop.set(5, 1)).toBeNull(); - }); - - it('should return undefined when all items are undefined', () => { - prop = new Prop(mockPropsInst, [undefined, undefined], '___loopArgs___'); - expect(prop.getValue()).toEqual([undefined, undefined]); - }); - - it('迭代器 / map / forEach', () => { - const listProp = new Prop(mockPropsInst, [1, 2]); - const mockFn = jest.fn(); - for (const item of listProp) { - mockFn(); - } - expect(mockFn).toHaveBeenCalledTimes(2); - mockFn.mockClear(); - - listProp.forEach((item) => { - mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(2); - mockFn.mockClear(); - - listProp.map((item) => { - return mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(2); - mockFn.mockClear(); - }); - }); - }); - - describe('slotNode / setAsSlot', () => { - const editor = new Editor(); - const designer = new Designer({ editor, shellModelFactory }); - const doc = new DocumentModel(designer.project, { - componentName: 'Page', - children: [ - { - id: 'div', - componentName: 'Div', - }, - ], - }); - const div = doc.getNode('div'); - - const slotProp = new Prop(div?.getProps(), { - type: 'JSSlot', - value: [ - { - componentName: 'Button', - }, - ], - }); - - expect(slotProp.slotNode?.componentName).toBe('Slot'); - - // TODO: id 总是变,不好断言 - expect(slotProp.code.includes('Button')).toBeTruthy(); - - slotProp.export(); - - expect(slotProp.export().value[0].componentName).toBe('Button'); - expect(slotProp.export(IPublicEnumTransformStage.Serilize).value[0].componentName).toBe('Button'); - - slotProp.purge(); - expect(slotProp.purged).toBeTruthy(); - slotProp.dispose(); - }); - - describe('slotNode-value / setAsSlot', () => { - const editor = new Editor(); - const designer = new Designer({ editor, shellModelFactory }); - const doc = new DocumentModel(designer.project, { - componentName: 'Page', - children: [ - { - id: 'div', - componentName: 'Div', - }, - ], - }); - const div = doc.getNode('div'); - - const slotProp = new Prop(div?.getProps(), { - type: 'JSSlot', - value: { - componentName: 'Slot', - id: 'node_oclei5rv2e2', - props: { - slotName: "content", - slotTitle: "主内容" - }, - children: [ - { - componentName: 'Button', - } - ] - }, - }); - - expect(slotProp.slotNode?.componentName).toBe('Slot'); - - expect(slotProp.slotNode?.title).toBe('主内容'); - expect(slotProp.slotNode?.getExtraProp('name')?.getValue()).toBe('content'); - expect(slotProp.slotNode?.export()?.id).toBe('node_oclei5rv2e2'); - - slotProp.export(); - - // Save - expect(slotProp.export()?.value[0].componentName).toBe('Button'); - expect(slotProp.export()?.title).toBe('主内容'); - expect(slotProp.export()?.name).toBe('content'); - - // Render - expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.children[0].componentName).toBe('Button'); - expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.componentName).toBe('Slot'); - - slotProp.purge(); - expect(slotProp.purged).toBeTruthy(); - slotProp.dispose(); - }); -}); - -describe('其他导出函数', () => { - it('isProp', () => { - expect(isProp({ isProp: true })).toBeTruthy(); - }); - - it('isValidArrayIndex', () => { - expect(isValidArrayIndex('1')).toBeTruthy(); - expect(isValidArrayIndex('1', 2)).toBeTruthy(); - expect(isValidArrayIndex('2', 1)).toBeFalsy(); - }); -}); - -describe('setValue with event', () => { - let propInstance; - let mockEmitChange; - let mockEventBusEmit; - let mockEmitPropChange; - - beforeEach(() => { - // Initialize the instance of your class - propInstance = new Prop(mockPropsInst, true, 'stringProp');; - - // Mock necessary methods and properties - mockEmitChange = jest.spyOn(propInstance, 'emitChange'); - propInstance.owner = { - document: { - designer: { - editor: { - eventBus: { - emit: jest.fn(), - }, - }, - }, - }, - emitPropChange: jest.fn(), - delete() {}, - }; - mockEventBusEmit = jest.spyOn(propInstance.owner.document.designer.editor.eventBus, 'emit'); - mockEmitPropChange = jest.spyOn(propInstance.owner, 'emitPropChange'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should correctly handle string values and emit changes', () => { - const oldValue = propInstance._value; - const newValue = 'new string value'; - - propInstance.setValue(newValue); - - const expectedPartialPropsInfo = expect.objectContaining({ - key: propInstance.key, - newValue, // You can specifically test only certain keys - oldValue, - }); - - expect(propInstance.getValue()).toBe(newValue); - expect(propInstance.type).toBe('literal'); - expect(mockEmitChange).toHaveBeenCalledWith({ oldValue }); - expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo); - expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo); - }); - - it('should handle object values and set type to map', () => { - const oldValue = propInstance._value; - const newValue = 234; - - const expectedPartialPropsInfo = expect.objectContaining({ - key: propInstance.key, - newValue, // You can specifically test only certain keys - oldValue, - }); - - propInstance.setValue(newValue); - - expect(propInstance.getValue()).toEqual(newValue); - expect(propInstance.type).toBe('literal'); - expect(mockEmitChange).toHaveBeenCalledWith({ oldValue }); - expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo); - expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo); - }); - - it('should has event when unset call', () => { - const oldValue = propInstance._value; - - propInstance.unset(); - - const expectedPartialPropsInfo = expect.objectContaining({ - key: propInstance.key, - newValue: undefined, // You can specifically test only certain keys - oldValue, - }); - - expect(propInstance.getValue()).toEqual(undefined); - expect(propInstance.type).toBe('unset'); - expect(mockEmitChange).toHaveBeenCalledWith({ - oldValue, - newValue: undefined, - }); - expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo); - expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo); - - propInstance.unset(); - expect(mockEmitChange).toHaveBeenCalledTimes(1); - }); - - // remove - it('should has event when remove call', () => { - const oldValue = propInstance._value; - - propInstance.remove(); - - const expectedPartialPropsInfo = expect.objectContaining({ - key: propInstance.key, - newValue: undefined, // You can specifically test only certain keys - oldValue, - }); - - expect(propInstance.getValue()).toEqual(undefined); - // expect(propInstance.type).toBe('unset'); - expect(mockEmitChange).toHaveBeenCalledWith({ - oldValue, - newValue: undefined, - }); - expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo); - expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo); - - propInstance.remove(); - expect(mockEmitChange).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/designer/tests/document/node/props/props.test.ts b/packages/designer/tests/document/node/props/props.test.ts deleted file mode 100644 index 0b15a00c0..000000000 --- a/packages/designer/tests/document/node/props/props.test.ts +++ /dev/null @@ -1,313 +0,0 @@ -// @ts-nocheck -import '../../../fixtures/window'; -import { set, delayObxTick } from '../../../utils'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { - Props, - getConvertedExtraKey, - getOriginalExtraKey, - Prop, - isProp, - isValidArrayIndex, -} from '../../../../src/document/node/props/props'; -import { Designer } from '../../../../src/designer/designer'; -import { Project } from '../../../../src/project/project'; -import { DocumentModel } from '../../../../src/document/document-model'; - -import { TransformStage } from '@alilc/lowcode-types'; - -const mockOwner = { componentName: 'Page' }; - -describe('Props 类测试', () => { - let props: Props; - beforeEach(() => { - props = new Props( - mockOwner, - { - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - z: { - z1: 1, - z2: 'str', - }, - }, - { condition: true }, - ); - }); - afterEach(() => { - props.purge(); - }); - - it('getNode', () => { - expect(props.getNode()).toBe(mockOwner); - }); - - it('items / get', async () => { - expect(props.size).toBe(6); - - expect(props.get('a').getValue()).toBe(1); - expect(props.get('b').getValue()).toBe('str'); - expect(props.get('c').getValue()).toBe(true); - expect(props.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' }); - expect(props.get('z').getValue()).toEqual({ - z1: 1, - z2: 'str', - }); - - expect(props.getPropValue('a')).toBe(1); - props.setPropValue('a', 2); - expect(props.getPropValue('a')).toBe(2); - // props.clearPropValue('a'); - // expect(props.get('a')?.isUnset()).toBeTruthy(); - - expect(props.get('z.z1')?.getValue()).toBe(1); - expect(props.get('z.z2')?.getValue()).toBe('str'); - - const notCreatedProp = props.get('i'); - expect(notCreatedProp).toBeNull(); - const newlyCreatedProp = props.get('l', true); - const newlyCreatedNestedProp = props.get('m.m1', true); - newlyCreatedProp.setValue('newlyCreatedProp'); - newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp'); - - expect(props.get('l').getValue()).toBe('newlyCreatedProp'); - expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp'); - - // map / list 级联测试 - props.get('loopArgs.0', true).setValue('newItem'); - expect(props.get('loopArgs.0').getValue()).toBe('newItem'); - }); - - it('export', () => { - expect(props.export()).toEqual({ - props: { - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - z: { - z1: 1, - z2: 'str', - }, - }, - extras: { - condition: true, - }, - }); - - expect(props.toData()).toEqual({ - a: 1, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - z: { - z1: 1, - z2: 'str', - }, - }); - - props.get('a')?.unset(); - expect(props.toData()).toEqual({ - a: undefined, - b: 'str', - c: true, - d: { - type: 'JSExpression', - value: 'state.a', - }, - z: { - z1: 1, - z2: 'str', - }, - }); - }); - - it('export - remove undefined items', () => { - props.import( - { - a: 1, - }, - { loop: false }, - ); - props.setPropValue('x', undefined); - expect(props.export()).toEqual({ - props: { - a: 1, - }, - extras: { - loop: false, - }, - }); - - props.setPropValue('x', 2); - expect(props.export()).toEqual({ - props: { - a: 1, - x: 2, - }, - extras: { - loop: false, - }, - }); - - props.setPropValue('y.z', undefined); - expect(props.export()).toEqual({ - props: { - a: 1, - x: 2, - }, - extras: { - loop: false, - }, - }); - - props.setPropValue('y.z', 2); - expect(props.export()).toEqual({ - props: { - a: 1, - x: 2, - y: { z: 2 }, - }, - extras: { - loop: false, - }, - }); - }); - - it('import', () => { - props.import( - { - x: 1, - y: true, - }, - { loop: false }, - ); - expect(props.export()).toEqual({ - props: { - x: 1, - y: true, - }, - extras: { - loop: false, - }, - }); - - props.import(); - }); - - it('merge', async () => { - props.merge({ x: 1 }); - - await delayObxTick(); - - expect(props.get('x')?.getValue()).toBe(1); - }); - - it('has / add / delete / deleteKey / remove', () => { - expect(props.has('a')).toBeTruthy(); - expect(props.has('b')).toBeTruthy(); - expect(props.has('c')).toBeTruthy(); - expect(props.has('d')).toBeTruthy(); - expect(props.has('z')).toBeTruthy(); - expect(props.has('y')).toBeFalsy(); - - props.add(1, 'newAdded'); - expect(props.has('newAdded')).toBeTruthy(); - - props.deleteKey('c'); - expect(props.get('c', false)).toBeNull(); - props.delete(props.get('b')); - expect(props.get('b', false)).toBeNull(); - - props.get('d')?.remove(); - expect(props.get('d', false)).toBeNull(); - }); - - it('迭代器 / map / forEach', () => { - const mockFn = jest.fn(); - for (const item of props) { - mockFn(); - } - expect(mockFn).toHaveBeenCalledTimes(6); - mockFn.mockClear(); - - props.forEach((item) => { - mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(6); - mockFn.mockClear(); - - props.map((item) => { - return mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(6); - mockFn.mockClear(); - - props.filter((item) => { - return mockFn(); - }); - expect(mockFn).toHaveBeenCalledTimes(6); - mockFn.mockClear(); - }); - - it('purge', () => { - props.purge(); - - expect(props.purged).toBeTruthy(); - }); - - it('empty items', () => { - expect(new Props(mockOwner).export()).toEqual({}); - }); - - describe('list 类型', () => { - let props: Props; - beforeEach(() => { - props = new Props(mockOwner, [1, true, 'haha'], { condition: true }); - }); - it('constructor', () => { - props.purge(); - }); - - it('export', () => { - expect(props.export().extras).toEqual({ - condition: true, - }); - }); - - it('import', () => { - props.import([1], { loop: true }); - expect(props.export().extras).toEqual({ - loop: true, - }); - - props.items[0]?.unset(); - props.export(); - }); - }); -}); - -describe('其他函数', () => { - it('getConvertedExtraKey', () => { - expect(getConvertedExtraKey()).toBe(''); - expect(getConvertedExtraKey('a')).toBe('___a___'); - expect(getConvertedExtraKey('a.b')).toBe('___a___.b'); - expect(getConvertedExtraKey('a.0')).toBe('___a___.0'); - }); - - it('getOriginalExtraKey', () => { - expect(getOriginalExtraKey('___a___')).toBe('a'); - expect(getOriginalExtraKey('___a___.b')).toBe('a.b'); - }); -}); diff --git a/packages/designer/tests/document/node/props/value-to-source.test.ts b/packages/designer/tests/document/node/props/value-to-source.test.ts deleted file mode 100644 index 43ed19b2a..000000000 --- a/packages/designer/tests/document/node/props/value-to-source.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -// @ts-nocheck -import '../../../fixtures/silent-console'; -import { getSource, valueToSource } from '../../../../src/document/node/props/value-to-source'; - -it('valueToSource', () => { - expect(valueToSource(1)).toMatchSnapshot(); - expect(valueToSource(true)).toMatchSnapshot(); - expect(valueToSource([])).toMatchSnapshot(); - expect(valueToSource([{ a: 1 }])).toMatchSnapshot(); - expect(valueToSource({ a: 1 })).toMatchSnapshot(); - expect(valueToSource(null)).toMatchSnapshot(); - expect(valueToSource(() => {})).toMatchSnapshot(); - expect(valueToSource(new Map())).toMatchSnapshot(); - expect(valueToSource(new Set())).toMatchSnapshot(); - expect(valueToSource(/haha/)).toMatchSnapshot(); - expect(valueToSource('hahah')).toMatchSnapshot(); - expect(valueToSource(Symbol('haha'))).toMatchSnapshot(); - expect(valueToSource()).toMatchSnapshot(); - expect(valueToSource(new Date(1607680998520))).toMatchSnapshot(); -}); - -it('getSource', () => { - expect(getSource({ __source: { a: 1 } })).toEqual({ a: 1 }); - expect(getSource()).toBe(''); - const value = { abc: 1 }; - getSource(value); - expect(value).toHaveProperty('__source'); - expect(getSource(1)).toBe('1'); -}); diff --git a/packages/designer/tests/document/selection.test.ts b/packages/designer/tests/document/selection.test.ts deleted file mode 100644 index aa8bfb04b..000000000 --- a/packages/designer/tests/document/selection.test.ts +++ /dev/null @@ -1,263 +0,0 @@ -import '../fixtures/window'; -import { Project } from '../../src/project/project'; -import { Designer } from '../../src/designer/designer'; -import formSchema from '../fixtures/schema/form'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - get advanced() { - return {}; - }, - }; - }, - transformProps(props) { - return props; - }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); -}); - -describe('选择区测试', () => { - it('常规方法', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - - selection.select('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selection.selected).toEqual(['form']); - selectionChangeHandler.mockClear(); - - selection.select('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(0); - expect(selection.selected).toEqual(['form']); - - selection.select('node_k1ow3cbj'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj']); - expect(selection.selected).toEqual(['node_k1ow3cbj']); - selectionChangeHandler.mockClear(); - - selection.selectAll(['node_k1ow3cbj', 'form']); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj', 'form']); - expect(selection.selected).toEqual(['node_k1ow3cbj', 'form']); - selectionChangeHandler.mockClear(); - - selection.remove('node_k1ow3cbj_fake'); - selection.remove('node_k1ow3cbj'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']); - expect(selection.selected).toEqual(['form']); - selectionChangeHandler.mockClear(); - - selection.clear(); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual([]); - expect(selection.selected).toEqual([]); - selectionChangeHandler.mockClear(); - - // 无选中时调用 clear,不再触发事件 - selection.clear(); - expect(selectionChangeHandler).toHaveBeenCalledTimes(0); - expect(selection.selected).toEqual([]); - selectionChangeHandler.mockClear(); - }); - - it('add 方法', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - - selection.add('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']); - expect(selection.selected).toEqual(['form']); - selectionChangeHandler.mockClear(); - - // 再加一次相同的节点,不触发事件 - selection.add('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(0); - expect(selection.selected).toEqual(['form']); - selectionChangeHandler.mockClear(); - - selection.add('form2'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'form2']); - expect(selection.selected).toEqual(['form', 'form2']); - selectionChangeHandler.mockClear(); - }); - - it('selectAll 包含不存在的 id', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - - selection.selectAll(['form', 'node_k1ow3cbj', 'form2']); - - expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']); - }); - - it('dispose 方法 - 选中的节点没有被删除的', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - - selection.selectAll(['form', 'node_k1ow3cbj']); - - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - selection.dispose(); - - expect(selectionChangeHandler).not.toHaveBeenCalled(); - }); - - it('containsNode 方法', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - - selection.select('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']); - expect(selection.selected).toEqual(['form']); - expect(selection.has('form')).toBe(true); - expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true); - expect(selection.containsNode(currentDocument?.getNode('node_k1ow3cbj'))).toBe(true); - expect(selection.containsNode(currentDocument?.getNode('page'))).toBe(false); - expect(selection.getNodes()).toEqual([currentDocument?.getNode('form')]); - selectionChangeHandler.mockClear(); - - selection.add('node_k1ow3cbj'); - expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']); - expect(selection.getTopNodes()).toEqual([currentDocument?.getNode('form')]); - expect(selection.getTopNodes(true)).toEqual([currentDocument?.getNode('form')]); - }); - - it('containsNode 方法 - excludeRoot: true', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - - selection.select('page'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['page']); - expect(selection.selected).toEqual(['page']); - expect(selection.has('page')).toBe(true); - expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true); - expect(selection.containsNode(currentDocument?.getNode('form'), true)).toBe(false); - selectionChangeHandler.mockClear(); - }); - - it('containsNode 方法 - excludeRoot: true', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap, selection } = currentDocument!; - const selectionChangeHandler = jest.fn(); - const dispose = selection.onSelectionChange(selectionChangeHandler); - - selection.select('form'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']); - selectionChangeHandler.mockClear(); - - // dispose 后,selected 会被赋值,但是变更事件不会被触发 - dispose(); - selection.select('page'); - expect(selectionChangeHandler).toHaveBeenCalledTimes(0); - expect(selection.selected).toEqual(['page']); - selectionChangeHandler.mockClear(); - }); - - it('getNodes', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - const { currentDocument } = project; - const { selection } = currentDocument!; - - selection.selectAll(['form', 'node_k1ow3cbj', 'form2']); - - // form2 is not a valid node - expect(selection.getNodes()).toHaveLength(2); - }); - - it('getTopNodes - BeforeOrAfter', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - const { currentDocument } = project; - const { selection } = currentDocument!; - - selection.selectAll(['node_k1ow3cbj', 'node_k1ow3cbo']); - - expect(selection.getTopNodes()).toHaveLength(2); - }); - it('getTopNodes', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - const { currentDocument } = project; - const { selection } = currentDocument!; - - selection.selectAll(['node_k1ow3cbj', 'node_k1ow3cbo', 'form', 'node_k1ow3cbl', 'form2']); - - // form2 is not a valid node, and node_k1ow3cbj is a child node of form - expect(selection.getTopNodes()).toHaveLength(1); - }); -}); diff --git a/packages/designer/tests/fixtures/component-metadata/abcgroup.ts b/packages/designer/tests/fixtures/component-metadata/abcgroup.ts deleted file mode 100644 index 6b9265ef5..000000000 --- a/packages/designer/tests/fixtures/component-metadata/abcgroup.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Abc.Group', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: { label: '容器' }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - }, - supports: {}, - advanced: { - isTopFixed: true, - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/abcitem.ts b/packages/designer/tests/fixtures/component-metadata/abcitem.ts deleted file mode 100644 index bbccf7119..000000000 --- a/packages/designer/tests/fixtures/component-metadata/abcitem.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Abc.Item', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: { label: '容器' }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - }, - supports: {}, - advanced: { - isTopFixed: true, - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/abcnode.ts b/packages/designer/tests/fixtures/component-metadata/abcnode.ts deleted file mode 100644 index b991042a3..000000000 --- a/packages/designer/tests/fixtures/component-metadata/abcnode.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Abc.Node', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: { label: '容器' }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - }, - supports: {}, - advanced: { - isTopFixed: true, - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/abcoption.ts b/packages/designer/tests/fixtures/component-metadata/abcoption.ts deleted file mode 100644 index 7d9d15a72..000000000 --- a/packages/designer/tests/fixtures/component-metadata/abcoption.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Abc.Option', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: { label: '容器' }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - }, - supports: {}, - advanced: { - isTopFixed: true, - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/button.ts b/packages/designer/tests/fixtures/component-metadata/button.ts deleted file mode 100644 index 55186a10c..000000000 --- a/packages/designer/tests/fixtures/component-metadata/button.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Button', - npm: { - package: '@ali/vc-button', - componentName: 'Button', - }, - title: '按钮', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'non-exsiting', - }, - { - type: 'field', - name: 'obj', - items: [ - { - name: 'a', - title: 'a', - setter: () => 'StringSetter', - }, - { - name: 'b', - title: 'b', - setter: 'NumberSetter', - }, - { - name: 'c', - title: 'c', - setter: { - componentName: 'ColorSetter' - }, - }, - ], - }, - () => 'haha', // IPublicTypeCustomView - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - descriptor: 'xTitle' - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/dialog.ts b/packages/designer/tests/fixtures/component-metadata/dialog.ts deleted file mode 100644 index 5445d92a6..000000000 --- a/packages/designer/tests/fixtures/component-metadata/dialog.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Dialog', - npm: { - package: '@ali/vc-dialog', - componentName: 'Dialog', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - isModal: true, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div.ts b/packages/designer/tests/fixtures/component-metadata/div.ts deleted file mode 100644 index 96a5f76ba..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - advanced: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div10.ts b/packages/designer/tests/fixtures/component-metadata/div10.ts deleted file mode 100644 index 9b7c1c487..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div10.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - component: { - nestingRule: { - parentWhitelist: (parent, my) => { - if (parent.componentName === 'Form' && my.componentName === 'Div') return true; - return false; - }, - childWhitelist: (child, my) => { - if (child.componentName === 'Image' && my.componentName === 'Div') return true; - return false; - }, - }, - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div2.ts b/packages/designer/tests/fixtures/component-metadata/div2.ts deleted file mode 100644 index c9c1be306..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div2.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: { label: '容器' }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - }, - supports: {}, - advanced: { - isTopFixed: true, - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div3.ts b/packages/designer/tests/fixtures/component-metadata/div3.ts deleted file mode 100644 index ced3947f4..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div3.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - } - }, - supports: {}, - advanced: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div4.ts b/packages/designer/tests/fixtures/component-metadata/div4.ts deleted file mode 100644 index cbe826fdc..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div4.ts +++ /dev/null @@ -1,272 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - experimental: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div5.ts b/packages/designer/tests/fixtures/component-metadata/div5.ts deleted file mode 100644 index 963d7dd86..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div5.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - disableBehaviors: '*', - }, - supports: {}, - advanced: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div6.ts b/packages/designer/tests/fixtures/component-metadata/div6.ts deleted file mode 100644 index de80a9364..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div6.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - disableBehaviors: '*', - }, - supports: {}, - }, - experimental: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div7.ts b/packages/designer/tests/fixtures/component-metadata/div7.ts deleted file mode 100644 index b970aa3a7..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div7.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - supports: {}, - advanced: { - callbacks: { - onNodeAdd: (dragment, self) => { console.log(dragment); }, - onNodeRemove: (dragment, self) => { console.log(dragment); } - }, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div8.ts b/packages/designer/tests/fixtures/component-metadata/div8.ts deleted file mode 100644 index ae04ad287..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div8.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - npm: { - package: '@ali/vc-div', - componentName: 'Div', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/div9.ts b/packages/designer/tests/fixtures/component-metadata/div9.ts deleted file mode 100644 index 2d3640b3b..000000000 --- a/packages/designer/tests/fixtures/component-metadata/div9.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Div', - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/form.ts b/packages/designer/tests/fixtures/component-metadata/form.ts deleted file mode 100644 index faa6e0608..000000000 --- a/packages/designer/tests/fixtures/component-metadata/form.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Form', - npm: { - package: '@ali/vc-form', - }, - title: '表单', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - parentWhitelist: 'Div,Page', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/other.ts b/packages/designer/tests/fixtures/component-metadata/other.ts deleted file mode 100644 index adc8659b8..000000000 --- a/packages/designer/tests/fixtures/component-metadata/other.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Other', - npm: { - package: '@ali/vc-other', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - parentWhitelist: 'Div', - childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/page.ts b/packages/designer/tests/fixtures/component-metadata/page.ts deleted file mode 100644 index 417037861..000000000 --- a/packages/designer/tests/fixtures/component-metadata/page.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Page', - npm: { - package: '@ali/vc-page', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/page2.ts b/packages/designer/tests/fixtures/component-metadata/page2.ts deleted file mode 100644 index 417037861..000000000 --- a/packages/designer/tests/fixtures/component-metadata/page2.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'Page', - npm: { - package: '@ali/vc-page', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/root-content.ts b/packages/designer/tests/fixtures/component-metadata/root-content.ts deleted file mode 100644 index 5546e8b8a..000000000 --- a/packages/designer/tests/fixtures/component-metadata/root-content.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'RootContent', - npm: { - package: '@ali/vc-page', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/root-footer.ts b/packages/designer/tests/fixtures/component-metadata/root-footer.ts deleted file mode 100644 index cd3291fb0..000000000 --- a/packages/designer/tests/fixtures/component-metadata/root-footer.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'RootFooter', - npm: { - package: '@ali/vc-page', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/component-metadata/root-header.ts b/packages/designer/tests/fixtures/component-metadata/root-header.ts deleted file mode 100644 index b2d3dd4ed..000000000 --- a/packages/designer/tests/fixtures/component-metadata/root-header.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types"; -export default { - componentName: 'RootHeader', - npm: { - package: '@ali/vc-page', - }, - title: '容器', - docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs', - devMode: 'proCode', - tags: ['布局'], - configure: { - props: [ - { - type: 'field', - name: 'behavior', - title: '默认状态', - extraProps: { - display: 'inline', - defaultValue: 'NORMAL', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - options: [ - { - title: '普通', - value: 'NORMAL', - }, - { - title: '隐藏', - value: 'HIDDEN', - }, - ], - loose: false, - cancelable: false, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: '__style__', - title: { - label: '样式设置', - tip: '点击 ? 查看样式设置器用法指南', - docUrl: 'https://lark.alipay.com/legao/help/design-tool-style', - }, - extraProps: { - display: 'accordion', - defaultValue: {}, - }, - setter: { - key: null, - ref: null, - props: { - advanced: true, - }, - _owner: null, - }, - }, - { - type: 'group', - name: 'groupkgzzeo41', - title: '高级', - extraProps: { - display: 'accordion', - }, - items: [ - { - type: 'field', - name: 'fieldId', - title: { - label: '唯一标识', - }, - extraProps: { - display: 'block', - }, - setter: { - key: null, - ref: null, - props: { - placeholder: '请输入唯一标识', - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'useFieldIdAsDomId', - title: { - label: '将唯一标识用作 DOM ID', - }, - extraProps: { - display: 'block', - defaultValue: false, - }, - setter: { - key: null, - ref: null, - props: {}, - _owner: null, - }, - }, - { - type: 'field', - name: 'customClassName', - title: '自定义样式类', - extraProps: { - display: 'block', - defaultValue: '', - }, - setter: { - componentName: 'MixedSetter', - props: { - setters: [ - { - key: null, - ref: null, - props: { - placeholder: null, - multiline: false, - rows: 10, - required: false, - pattern: null, - maxLength: null, - }, - _owner: null, - }, - 'VariableSetter', - ], - }, - }, - }, - { - type: 'field', - name: 'events', - title: { - label: '动作设置', - tip: '点击 ? 查看如何设置组件的事件响应动作', - docUrl: 'https://lark.alipay.com/legao/legao/events-call', - }, - extraProps: { - display: 'accordion', - defaultValue: { - ignored: true, - }, - }, - setter: { - key: null, - ref: null, - props: { - events: [ - { - name: 'onClick', - title: '当点击时', - initialValue: - "/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}", - }, - { - name: 'onMouseEnter', - title: '当鼠标进入时', - initialValue: - "/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}", - }, - { - name: 'onMouseLeave', - title: '当鼠标离开时', - initialValue: - "/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}", - }, - ], - }, - _owner: null, - }, - }, - { - type: 'field', - name: 'onClick', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseEnter', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - { - type: 'field', - name: 'onMouseLeave', - extraProps: { - defaultValue: { - ignored: true, - }, - }, - setter: 'I18nSetter', - }, - ], - }, - ], - component: { - isContainer: true, - nestingRule: { - // parentWhitelist: 'Div', - // childWhitelist: 'Div', - }, - }, - supports: {}, - }, - experimental: { - callbacks: {}, - initials: [ - { - name: 'behavior', - }, - { - name: '__style__', - }, - { - name: 'fieldId', - }, - { - name: 'useFieldIdAsDomId', - }, - { - name: 'customClassName', - }, - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - filters: [ - { - name: 'events', - }, - { - name: 'onClick', - }, - { - name: 'onMouseEnter', - }, - { - name: 'onMouseLeave', - }, - ], - autoruns: [], - }, -} as IPublicTypeComponentMetadata; diff --git a/packages/designer/tests/fixtures/disable-raf.ts b/packages/designer/tests/fixtures/disable-raf.ts deleted file mode 100644 index c19c3c097..000000000 --- a/packages/designer/tests/fixtures/disable-raf.ts +++ /dev/null @@ -1,3 +0,0 @@ -Object.defineProperty(window, 'requestAnimationFrame', { - value: null, -}); diff --git a/packages/designer/tests/fixtures/schema/form-with-modal.ts b/packages/designer/tests/fixtures/schema/form-with-modal.ts deleted file mode 100644 index 4c0fb9064..000000000 --- a/packages/designer/tests/fixtures/schema/form-with-modal.ts +++ /dev/null @@ -1,1021 +0,0 @@ -export default { - componentName: 'Page', - id: 'page', - title: "hey, i' a page!", - props: { - extensions: { - 启用页头: true, - }, - pageStyle: { - backgroundColor: '#f2f3f5', - }, - containerStyle: {}, - className: 'page_kgaqfbm4', - templateVersion: '1.0.0', - }, - lifeCycles: { - constructor: { - type: 'js', - compiled: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - source: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - }, - }, - condition: true, - css: - 'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - methods: { - __initMethods__: { - type: 'js', - source: 'function (exports, module) { /*set actions code here*/ }', - compiled: 'function (exports, module) { /*set actions code here*/ }', - }, - }, - dataSource: { - offline: [], - globalConfig: { - fit: { - compiled: '', - source: '', - type: 'js', - error: {}, - }, - }, - online: [], - sync: true, - list: [], - }, - children: [ - { - componentName: 'Dialog', - id: 'modal', - props: { - title: { - type: 'i18n', - use: 'zh-CN', - 'en-US': 'Dialog Title', - 'zh-CN': 'Dialog标题', - }, - visible: false, - hasMask: true, - closeable: 'esc', - autoFocus: true, - footer: true, - footerAlign: 'right', - footerActions: 'cancel,ok', - confirmText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': 'Confirm', - 'zh-CN': '确定', - }, - cancelText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': 'Cancel', - 'zh-CN': '取消', - }, - confirmStyle: 'primary', - confirmState: '确定', - __style__: {}, - fieldId: 'dialog_kijq2hni', - popupOutDialog: true, - }, - }, - { - componentName: 'RootHeader', - id: 'node_k1ow3cba', - props: {}, - condition: true, - children: [ - { - componentName: 'PageHeader', - id: 'node_k1ow3cbd', - props: { - extraContent: '', - __slot__extraContent: false, - __slot__action: false, - title: { - // type: 'JSSlot', - value: [ - { - componentName: 'Text', - id: 'node_k1ow3cbf', - props: { - showTitle: false, - behavior: 'NORMAL', - content: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '个人信息', - type: 'i18n', - }, - __style__: {}, - fieldId: 'text_k1ow3h1j', - maxLine: 0, - }, - condition: true, - }, - ], - }, - content: '', - __slot__logo: false, - __slot__crumb: false, - crumb: '', - tab: '', - logo: '', - action: '', - __slot__tab: false, - __style__: {}, - __slot__content: false, - fieldId: 'pageHeader_k1ow3h1i', - subTitle: false, - }, - condition: true, - }, - ], - }, - { - componentName: 'RootContent', - id: 'node_k1ow3cbb', - props: { - contentBgColor: 'transparent', - contentPadding: '0', - contentMargin: '20', - }, - condition: true, - children: [ - { - componentName: 'Form', - id: 'form', - extraPropA: 'extraPropA', - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - slotA: '', - }, - condition: true, - children: [ - { - componentName: 'Card', - id: 'node_k1ow3cbj', - props: { - __slot__title: false, - subTitle: { - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - __slot__subTitle: false, - extra: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - className: 'card_kgaqfbm5', - title: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '基本信息', - type: 'i18n', - }, - __slot__extra: false, - showHeadDivider: true, - __style__: ':root {\n margin-bottom: 12px;\n}', - showTitleBullet: true, - contentHeight: '', - fieldId: 'card_k1ow3h1l', - dividerNoInset: false, - }, - condition: true, - children: [ - { - componentName: 'CardContent', - id: 'node_k1ow3cbk', - props: {}, - condition: true, - children: [ - { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cbw', - props: { - layout: '6:6', - columnGap: '20', - rowGap: 0, - __style__: {}, - fieldId: 'columns_k1ow3h1v', - }, - condition: true, - children: [ - { - componentName: 'Column', - id: 'node_k1ow3cbx', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjm', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cbz', - props: { - fieldName: 'name', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [ - { - type: 'required', - }, - ], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1w', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '姓名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'TextField', - id: 'node_k1ow3cc1', - props: { - fieldName: 'englishName', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1y', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '英文名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'TextField', - id: 'node_k1ow3cc3', - props: { - fieldName: 'jobTitle', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h20', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '职位', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - { - componentName: 'Column', - id: 'node_k1ow3cby', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjn', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc2', - props: { - fieldName: 'nickName', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1z', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '花名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'SelectField', - id: 'node_k1ow3cc0', - props: { - fieldName: 'gender', - hasClear: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - mode: 'single', - showSearch: false, - autoWidth: true, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please select', - 'zh-CN': '请选择', - type: 'i18n', - }, - hasBorder: true, - behavior: 'NORMAL', - value: '', - validation: [ - { - type: 'required', - }, - ], - __style__: {}, - fieldId: 'select_k1ow3h1x', - notFoundContent: { - use: 'zh-CN', - type: 'i18n', - }, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'SelectField', - 'zh-CN': '性别', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - wrapperColOffset: 0, - hasSelectAll: false, - hasArrow: true, - size: 'medium', - labelAlign: 'top', - filterLocal: true, - dataSource: [ - { - defaultChecked: false, - text: { - 'en-US': 'Option 1', - 'zh-CN': '男', - type: 'i18n', - __sid__: 'param_k1owc4tb', - }, - __sid__: 'serial_k1owc4t1', - value: 'M', - sid: 'opt_k1owc4t2', - }, - { - defaultChecked: false, - text: { - 'en-US': 'Option 2', - 'zh-CN': '女', - type: 'i18n', - __sid__: 'param_k1owc4tf', - }, - __sid__: 'serial_k1owc4t2', - value: 'F', - sid: 'opt_k1owc4t3', - }, - ], - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - searchDelay: 300, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Card', - id: 'node_k1ow3cbl', - props: { - __slot__title: false, - subTitle: { - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - __slot__subTitle: false, - extra: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - className: 'card_kgaqfbm6', - title: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '部门信息', - type: 'i18n', - }, - __slot__extra: false, - showHeadDivider: true, - __style__: ':root {\n margin-bottom: 12px;\n}', - showTitleBullet: true, - contentHeight: '', - fieldId: 'card_k1ow3h1m', - dividerNoInset: false, - }, - condition: true, - children: [ - { - componentName: 'CardContent', - id: 'node_k1ow3cbm', - props: {}, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc4', - props: { - fieldName: 'department', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h21', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '所属部门', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cc5', - props: { - layout: '6:6', - columnGap: '20', - rowGap: 0, - __style__: {}, - fieldId: 'columns_k1ow3h22', - }, - condition: true, - children: [ - { - componentName: 'Column', - id: 'node_k1ow3cc6', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjo', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc8', - props: { - fieldName: 'leader', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h23', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '主管', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - { - componentName: 'Column', - id: 'node_k1ow3cc7', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjp', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc9', - props: { - fieldName: 'hrg', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h24', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': 'HRG', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': null, - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Div', - id: 'node_k1ow3cbo', - props: { - className: 'div_kgaqfbm9', - behavior: 'NORMAL', - __style__: - ':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - events: {}, - fieldId: 'div_k1ow3h1o', - useFieldIdAsDomId: false, - customClassName: '', - }, - condition: true, - children: [ - { - componentName: 'Button', - id: 'node_k1ow3cbn', - props: { - triggerEventsWhenLoading: false, - onClick: { - rawType: 'events', - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])', - events: [ - { - name: 'submit', - id: 'submit', - params: {}, - type: 'actionRef', - uuid: '1570966253282_0', - }, - ], - }, - size: 'medium', - baseIcon: '', - otherIcon: '', - className: 'button_kgaqfbm7', - type: 'primary', - behavior: 'NORMAL', - loading: false, - content: { - use: 'zh-CN', - 'en-US': 'Button', - 'zh-CN': '提交', - type: 'i18n', - }, - __style__: ':root {\n margin-right: 16px;\n width: 80px\n}', - fieldId: 'button_k1ow3h1n', - }, - condition: true, - }, - { - componentName: 'Button', - id: 'node_k1ow3cbp', - props: { - triggerEventsWhenLoading: false, - size: 'medium', - baseIcon: '', - otherIcon: '', - className: 'button_kgaqfbm8', - type: 'normal', - behavior: 'NORMAL', - loading: false, - content: { - use: 'zh-CN', - 'en-US': 'Button', - 'zh-CN': '取消', - type: 'i18n', - }, - __style__: ':root {\n width: 80px;\n}', - fieldId: 'button_k1ow3h1p', - greeting: { - // type: 'JSSlot', - value: [ - { - componentName: 'Text', - props: {}, - }, - ], - }, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'RootFooter', - id: 'node_k1ow3cbc', - props: {}, - condition: true, - }, - ], -}; diff --git a/packages/designer/tests/fixtures/schema/form.ts b/packages/designer/tests/fixtures/schema/form.ts deleted file mode 100644 index e8479629f..000000000 --- a/packages/designer/tests/fixtures/schema/form.ts +++ /dev/null @@ -1,993 +0,0 @@ -export default { - componentName: 'Page', - id: 'page', - title: 'hey, i\' a page!', - props: { - extensions: { - 启用页头: true, - }, - pageStyle: { - backgroundColor: '#f2f3f5', - }, - containerStyle: {}, - className: 'page_kgaqfbm4', - templateVersion: '1.0.0', - }, - lifeCycles: { - constructor: { - type: 'js', - compiled: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - source: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - }, - }, - condition: true, - css: - 'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - methods: { - __initMethods__: { - type: 'js', - source: 'function (exports, module) { /*set actions code here*/ }', - compiled: 'function (exports, module) { /*set actions code here*/ }', - }, - }, - dataSource: { - offline: [], - globalConfig: { - fit: { - compiled: '', - source: '', - type: 'js', - error: {}, - }, - }, - online: [], - sync: true, - list: [], - }, - children: [ - { - componentName: 'RootHeader', - id: 'node_k1ow3cba', - props: {}, - condition: true, - children: [ - { - componentName: 'PageHeader', - id: 'node_k1ow3cbd', - props: { - extraContent: '', - __slot__extraContent: false, - __slot__action: false, - title: { - // type: 'JSSlot', - value: [ - { - componentName: 'Text', - id: 'node_k1ow3cbf', - props: { - showTitle: false, - behavior: 'NORMAL', - content: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '个人信息', - type: 'i18n', - }, - __style__: {}, - fieldId: 'text_k1ow3h1j', - maxLine: 0, - }, - condition: true, - }, - ], - }, - content: '', - __slot__logo: false, - __slot__crumb: false, - crumb: '', - tab: '', - logo: '', - action: '', - __slot__tab: false, - __style__: {}, - __slot__content: false, - fieldId: 'pageHeader_k1ow3h1i', - subTitle: false, - }, - condition: true, - }, - ], - }, - { - componentName: 'RootContent', - id: 'node_k1ow3cbb', - props: { - contentBgColor: 'transparent', - contentPadding: '0', - contentMargin: '20', - }, - condition: true, - children: [ - { - componentName: 'Form', - id: 'form', - extraPropA: 'extraPropA', - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - obj: { - a: 1, - b: false, - c: 'string', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - slotA: '', - }, - condition: true, - children: [ - { - componentName: 'Card', - id: 'node_k1ow3cbj', - props: { - __slot__title: false, - subTitle: { - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - __slot__subTitle: false, - extra: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - className: 'card_kgaqfbm5', - title: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '基本信息', - type: 'i18n', - }, - __slot__extra: false, - showHeadDivider: true, - __style__: ':root {\n margin-bottom: 12px;\n}', - showTitleBullet: true, - contentHeight: '', - fieldId: 'card_k1ow3h1l', - dividerNoInset: false, - }, - condition: true, - children: [ - { - componentName: 'CardContent', - id: 'node_k1ow3cbk', - props: {}, - condition: true, - children: [ - { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cbw', - props: { - layout: '6:6', - columnGap: '20', - rowGap: 0, - __style__: {}, - fieldId: 'columns_k1ow3h1v', - }, - condition: true, - children: [ - { - componentName: 'Column', - id: 'node_k1ow3cbx', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjm', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cbz', - props: { - fieldName: 'name', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [ - { - type: 'required', - }, - ], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1w', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '姓名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'TextField', - id: 'node_k1ow3cc1', - props: { - fieldName: 'englishName', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1y', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '英文名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'TextField', - id: 'node_k1ow3cc3', - props: { - fieldName: 'jobTitle', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h20', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '职位', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - { - componentName: 'Column', - id: 'node_k1ow3cby', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjn', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc2', - props: { - fieldName: 'nickName', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h1z', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '花名', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'SelectField', - id: 'node_k1ow3cc0', - props: { - fieldName: 'gender', - hasClear: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - mode: 'single', - showSearch: false, - autoWidth: true, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please select', - 'zh-CN': '请选择', - type: 'i18n', - }, - hasBorder: true, - behavior: 'NORMAL', - value: '', - validation: [ - { - type: 'required', - }, - ], - __style__: {}, - fieldId: 'select_k1ow3h1x', - notFoundContent: { - use: 'zh-CN', - type: 'i18n', - }, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'SelectField', - 'zh-CN': '性别', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - wrapperColOffset: 0, - hasSelectAll: false, - hasArrow: true, - size: 'medium', - labelAlign: 'top', - filterLocal: true, - dataSource: [ - { - defaultChecked: false, - text: { - 'en-US': 'Option 1', - 'zh-CN': '男', - type: 'i18n', - __sid__: 'param_k1owc4tb', - }, - __sid__: 'serial_k1owc4t1', - value: 'M', - sid: 'opt_k1owc4t2', - }, - { - defaultChecked: false, - text: { - 'en-US': 'Option 2', - 'zh-CN': '女', - type: 'i18n', - __sid__: 'param_k1owc4tf', - }, - __sid__: 'serial_k1owc4t2', - value: 'F', - sid: 'opt_k1owc4t3', - }, - ], - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - searchDelay: 300, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Card', - id: 'node_k1ow3cbl', - props: { - __slot__title: false, - subTitle: { - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - __slot__subTitle: false, - extra: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - className: 'card_kgaqfbm6', - title: { - use: 'zh-CN', - 'en-US': 'Title', - 'zh-CN': '部门信息', - type: 'i18n', - }, - __slot__extra: false, - showHeadDivider: true, - __style__: ':root {\n margin-bottom: 12px;\n}', - showTitleBullet: true, - contentHeight: '', - fieldId: 'card_k1ow3h1m', - dividerNoInset: false, - }, - condition: true, - children: [ - { - componentName: 'CardContent', - id: 'node_k1ow3cbm', - props: {}, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc4', - props: { - fieldName: 'department', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h21', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '所属部门', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cc5', - props: { - layout: '6:6', - columnGap: '20', - rowGap: 0, - __style__: {}, - fieldId: 'columns_k1ow3h22', - }, - condition: true, - children: [ - { - componentName: 'Column', - id: 'node_k1ow3cc6', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjo', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc8', - props: { - fieldName: 'leader', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h23', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': '主管', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - { - componentName: 'Column', - id: 'node_k1ow3cc7', - props: { - colSpan: '', - __style__: {}, - fieldId: 'column_k1p1bnjp', - }, - condition: true, - children: [ - { - componentName: 'TextField', - id: 'node_k1ow3cc9', - props: { - fieldName: 'hrg', - hasClear: false, - autoFocus: false, - tips: { - 'en-US': '', - 'zh-CN': '', - type: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh-CN', - 'en-US': 'please input', - 'zh-CN': '请输入', - type: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - addonBefore: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - validation: [], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1ow3h24', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh-CN', - 'en-US': 'TextField', - 'zh-CN': 'HRG', - type: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh-CN', - 'zh-CN': '', - type: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - __useMediator: 'value', - labelTipsTypes: 'none', - labelTipsIcon: '', - labelTipsText: { - type: 'i18n', - use: 'zh-CN', - 'en-US': '', - 'zh-CN': '', - }, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Div', - id: 'node_k1ow3cbo', - props: { - className: 'div_kgaqfbm9', - behavior: 'NORMAL', - __style__: - ':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - events: {}, - fieldId: 'div_k1ow3h1o', - useFieldIdAsDomId: false, - customClassName: '', - }, - condition: true, - children: [ - { - componentName: 'Button', - id: 'node_k1ow3cbn', - props: { - triggerEventsWhenLoading: false, - onClick: { - rawType: 'events', - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])', - events: [ - { - name: 'submit', - id: 'submit', - params: {}, - type: 'actionRef', - uuid: '1570966253282_0', - }, - ], - }, - size: 'medium', - baseIcon: '', - otherIcon: '', - className: 'button_kgaqfbm7', - type: 'primary', - behavior: 'NORMAL', - loading: false, - content: { - use: 'zh-CN', - 'en-US': 'Button', - 'zh-CN': '提交', - type: 'i18n', - }, - __style__: ':root {\n margin-right: 16px;\n width: 80px\n}', - fieldId: 'button_k1ow3h1n', - }, - condition: true, - }, - { - componentName: 'Button', - id: 'node_k1ow3cbp', - props: { - triggerEventsWhenLoading: false, - size: 'medium', - baseIcon: '', - otherIcon: '', - className: 'button_kgaqfbm8', - type: 'normal', - behavior: 'NORMAL', - loading: false, - content: { - use: 'zh-CN', - 'en-US': 'Button', - 'zh-CN': '取消', - type: 'i18n', - }, - __style__: ':root {\n width: 80px;\n}', - fieldId: 'button_k1ow3h1p', - greeting: { - // type: 'JSSlot', - value: [{ - componentName: 'Text', - props: {}, - }], - }, - }, - condition: true, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'RootFooter', - id: 'node_k1ow3cbc', - props: {}, - condition: true, - }, - ], - i18n: { - 'zh-CN': { - 'i18n-jwg27yo4': '你好', - 'i18n-jwg27yo3': '中国', - }, - 'en-US': { - 'i18n-jwg27yo4': 'Hello', - 'i18n-jwg27yo3': 'China', - }, - }, -}; diff --git a/packages/designer/tests/fixtures/schema/setting.ts b/packages/designer/tests/fixtures/schema/setting.ts deleted file mode 100644 index f325cdcea..000000000 --- a/packages/designer/tests/fixtures/schema/setting.ts +++ /dev/null @@ -1,90 +0,0 @@ -export default { - componentName: 'Page', - id: 'page', - title: 'hey, i\' a page!', - props: { - extensions: { - 启用页头: true, - }, - pageStyle: { - backgroundColor: '#f2f3f5', - }, - containerStyle: {}, - className: 'page_kgaqfbm4', - templateVersion: '1.0.0', - }, - lifeCycles: { - constructor: { - type: 'js', - compiled: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - source: - "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}", - }, - }, - condition: true, - css: - 'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - methods: { - __initMethods__: { - type: 'js', - source: 'function (exports, module) { /*set actions code here*/ }', - compiled: 'function (exports, module) { /*set actions code here*/ }', - }, - }, - children: [ - { - componentName: 'Div', - id: 'div', - props: { - className: 'div_kgaqfbm9', - behavior: 'NORMAL', - __style__: - ':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - events: {}, - fieldId: 'div_k1ow3h1o', - useFieldIdAsDomId: false, - customClassName: { - type: 'JSExpression', - value: 'getFromSomewhere()', - }, - customClassName2: { - type: 'JSExpression', - mock: { hi: 'mock' }, - value: 'getFromSomewhere()', - }, - }, - extraPropA: 'haha', - }, - { - componentName: 'Div', - id: 'div2', - props: { - className: 'div_kgaqfbm9', - behavior: 'NORMAL', - __style__: - ':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - events: {}, - fieldId: 'div_k1ow3h1o', - useFieldIdAsDomId: false, - customClassName: '', - }, - extraPropA: 'haha', - }, - { - componentName: 'Test', - id: 'test', - props: { - className: 'div_kgaqfbm9', - behavior: 'NORMAL', - __style__: - ':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}', - events: {}, - fieldId: 'div_k1ow3h1o', - useFieldIdAsDomId: false, - customClassName: '', - }, - extraPropA: 'haha', - }, - ], -}; diff --git a/packages/designer/tests/fixtures/silent-console.ts b/packages/designer/tests/fixtures/silent-console.ts deleted file mode 100644 index 54e1d071a..000000000 --- a/packages/designer/tests/fixtures/silent-console.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const mockConsoleError = jest.fn(); -export const mockConsoleWarn = jest.fn(); -// const mockConsoleInfo = jest.fn(); -console.error = mockConsoleError; -console.warn = mockConsoleWarn; - diff --git a/packages/designer/tests/fixtures/unhandled-rejection.ts b/packages/designer/tests/fixtures/unhandled-rejection.ts deleted file mode 100644 index b2c427548..000000000 --- a/packages/designer/tests/fixtures/unhandled-rejection.ts +++ /dev/null @@ -1,7 +0,0 @@ -if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) { - process.on('unhandledRejection', reason => { - throw reason; - }); - // Avoid memory leak by adding too many listeners - process.env.LISTENING_TO_UNHANDLED_REJECTION = true; -} diff --git a/packages/designer/tests/fixtures/window.ts b/packages/designer/tests/fixtures/window.ts deleted file mode 100644 index c57fcb686..000000000 --- a/packages/designer/tests/fixtures/window.ts +++ /dev/null @@ -1,28 +0,0 @@ -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation(query => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), // deprecated - removeListener: jest.fn(), // deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); - -Object.defineProperty(window, 'React', { - writable: true, - value: {}, -}); - -window.scrollTo = () => {}; -window.console.warn = () => {}; -const originalLog = window.console.log; -window.console.log = (...args) => { - // suppress boring warnings - if (args[0]?.includes && args[0].includes('@babel/plugin-proposal-private-property-in-object')) return; - originalLog.apply(window.console, args); -}; -window.React = window.React || {}; diff --git a/packages/designer/tests/main/meta/component-meta.test.ts b/packages/designer/tests/main/meta/component-meta.test.ts deleted file mode 100644 index d943f85af..000000000 --- a/packages/designer/tests/main/meta/component-meta.test.ts +++ /dev/null @@ -1,252 +0,0 @@ -import '../../fixtures/window'; -import { Designer } from '../../../src/designer/designer'; -import divMeta from '../../fixtures/component-metadata/div'; -import div2Meta from '../../fixtures/component-metadata/div2'; -import div3Meta from '../../fixtures/component-metadata/div3'; -import div4Meta from '../../fixtures/component-metadata/div4'; -import div5Meta from '../../fixtures/component-metadata/div5'; -import div6Meta from '../../fixtures/component-metadata/div6'; -import div7Meta from '../../fixtures/component-metadata/div7'; -import div8Meta from '../../fixtures/component-metadata/div8'; -import div9Meta from '../../fixtures/component-metadata/div9'; -import div10Meta from '../../fixtures/component-metadata/div10'; -import abcgroup from '../../fixtures/component-metadata/abcgroup'; -import abcitem from '../../fixtures/component-metadata/abcitem'; -import abcnode from '../../fixtures/component-metadata/abcnode'; -import abcoption from '../../fixtures/component-metadata/abcoption'; -import page2Meta from '../../fixtures/component-metadata/page2'; -import { - ComponentMeta, - isComponentMeta, - ensureAList, - buildFilter, -} from '../../../src/component-meta'; - - -jest.mock('../../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - const { ComponentActions } = require('../../../src/component-actions'); - return { - getGlobalComponentActions: () => [], - componentActions: new ComponentActions(), - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({} as any); -}); - -describe('组件元数据处理', () => { - it('构造函数', () => { - const meta = new ComponentMeta(designer, divMeta); - expect(meta.isContainer).toBeTruthy(); - expect(isComponentMeta(meta)).toBeTruthy(); - expect(meta.acceptable).toBeFalsy(); - expect(meta.isRootComponent()).toBeFalsy(); - expect(meta.isModal).toBeFalsy(); - expect(meta.rootSelector).toBeUndefined(); - expect(meta.liveTextEditing).toBeUndefined(); - expect(meta.descriptor).toBeUndefined(); - expect(typeof meta.icon).toBe('function'); - expect(meta.getMetadata().title).toBe('容器'); - expect(meta.title).toEqual({ type: 'i18n', 'en-US': 'Div', 'zh-CN': '容器' }); - expect(meta.isMinimalRenderUnit).toBeFalsy(); - expect(meta.isTopFixed).toBeFalsy(); - - meta.setNpm({ package: '@ali/vc-div', componentName: 'Div' }); - expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' }); - meta.npm = { package: '@ali/vc-div', componentName: 'Div' }; - expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' }); - - - const mockFn = jest.fn(); - const offFn = meta.onMetadataChange(mockFn); - meta.setMetadata(divMeta); - expect(mockFn).toHaveBeenCalledTimes(1); - offFn(); - meta.setMetadata(divMeta); - // 不会再触发函数 - expect(mockFn).toHaveBeenCalledTimes(1); - }); - - it('构造函数 - 兼容场景(title 是个普通对象)', () => { - const meta = new ComponentMeta(designer, div2Meta); - expect(meta.title).toEqual('容器'); - - expect(meta.isTopFixed).toBeTruthy(); - }); - - it('构造函数 - 兼容场景(title fallback 到 componentName)', () => { - const meta = new ComponentMeta(designer, div3Meta); - expect(meta.title).toEqual('Div'); - }); - - it('构造函数 - 兼容场景(configure 是个数组)', () => { - const meta = new ComponentMeta(designer, div4Meta); - expect(meta.configure).toEqual(div4Meta.configure); - }); - - it('构造函数 - 兼容场景(使用 experimental)', () => { - const meta = new ComponentMeta(designer, div6Meta); - expect(meta.getMetadata().configure.advanced.initials).toHaveLength(9); - }); - - it('构造函数 - 兼容场景(没有 configure.component)', () => { - const meta = new ComponentMeta(designer, div7Meta); - expect(meta.isContainer).toBeFalsy(); - expect(meta.isModal).toBeFalsy(); - }); - - it('构造函数 - 兼容场景(没有 configure)', () => { - const meta = new ComponentMeta(designer, div8Meta); - expect(meta.configure).toEqual([]); - }); - - it('构造函数 - 兼容场景(没有 npm)', () => { - const meta = new ComponentMeta(designer, div9Meta); - expect(meta.npm).toBeUndefined(); - - meta.setNpm({ package: '@ali/vc-div', componentName: 'Div' }); - expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' }); - }); - - it('availableActions', () => { - const meta = new ComponentMeta(designer, divMeta); - expect(meta.availableActions).toHaveLength(5); - expect(meta.availableActions[0].name).toBe('remove'); - expect(meta.availableActions[1].name).toBe('hide'); - expect(meta.availableActions[2].name).toBe('copy'); - - designer.componentActions.removeBuiltinComponentAction('remove'); - expect(meta.availableActions).toHaveLength(4); - expect(meta.availableActions[0].name).toBe('hide'); - expect(meta.availableActions[1].name).toBe('copy'); - - designer.componentActions.addBuiltinComponentAction({ - name: 'new', - content: { - action() {}, - }, - }); - expect(meta.availableActions).toHaveLength(5); - expect(meta.availableActions[0].name).toBe('hide'); - expect(meta.availableActions[1].name).toBe('copy'); - expect(meta.availableActions[4].name).toBe('new'); - }); - - it('availableActions - disableBehaviors: *', () => { - const meta = new ComponentMeta(designer, div5Meta); - expect(meta.availableActions).toHaveLength(0); - }); - - it('availableActions - rootCompoment', () => { - const meta = new ComponentMeta(designer, page2Meta); - // (hide + new) left - expect(meta.availableActions).toHaveLength(2); - }); - - describe('checkNesting', () => { - const mockNode = (componentName) => { - return { - internalToShellNode() { - return { - componentName, - }; - }, - isNode: true, - }; - }; - const mockNodeForm = mockNode('Form'); - const mockNodeImage = mockNode('Image'); - const mockNodeDiv = mockNode('Div'); - it('checkNestingUp', () => { - const meta1 = new ComponentMeta(designer, divMeta); - // 没有配置 parentWhitelist,判断默认为 true - expect(meta1.checkNestingUp(mockNodeDiv, mockNodeDiv)).toBeTruthy(); - - const meta2 = new ComponentMeta(designer, div10Meta); - expect(meta2.checkNestingUp(mockNodeDiv, mockNodeForm)).toBeTruthy(); - expect(meta2.checkNestingUp(mockNodeDiv, mockNodeDiv)).toBeFalsy(); - }); - - it('checkNestingDown', () => { - const meta1 = new ComponentMeta(designer, divMeta); - // 没有配置 childWhitelist,判断默认为 true - expect(meta1.checkNestingDown(mockNodeDiv, mockNodeDiv)).toBeTruthy(); - - const meta2 = new ComponentMeta(designer, div10Meta); - expect(meta2.checkNestingDown(mockNodeDiv, mockNodeForm)).toBeFalsy(); - expect(meta2.checkNestingDown(mockNodeDiv, mockNodeImage)).toBeTruthy(); - }); - }); -}); - -describe('组件元数据 transducers', () => { - it('legacyIssues', () => { - const legacyMeta: any = { - ...divMeta, - devMode: 'procode', - }; - const meta = new ComponentMeta(designer, legacyMeta); - const metadata = meta.getMetadata(); - expect(metadata.devMode).toBe('proCode'); - }); -}); - -describe('帮助函数', () => { - it('ensureAList', () => { - expect(ensureAList()).toBeNull(); - expect(ensureAList(1)).toBeNull(); - expect(ensureAList([])).toBeNull(); - expect(ensureAList('copy lock')).toEqual(['copy', 'lock']); - expect(ensureAList(['copy', 'lock'])).toEqual(['copy', 'lock']); - }); - - it('buildFilter', () => { - const mockFn = () => {}; - expect(buildFilter()).toBeNull(); - expect(buildFilter([])).toBeNull(); - expect(buildFilter(mockFn)).toBe(mockFn); - - const mockRE = /xxx/; - const filter = buildFilter(mockRE); - expect(filter({ componentName: 'xxx' })).toBeTruthy(); - expect(filter({ componentName: 'yyy' })).toBeFalsy(); - - expect(buildFilter('xxx yyy')({ componentName: 'xxx' })).toBeTruthy(); - expect(buildFilter('xxx yyy')({ componentName: 'zzz' })).toBeFalsy(); - }); - - it('registerMetadataTransducer', () => { - expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(2); - // 插入到 legacy-issues 和 component-defaults 的中间 - designer.componentActions.registerMetadataTransducer((metadata) => metadata, 3, 'noop'); - expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(3); - - designer.componentActions.registerMetadataTransducer((metadata) => metadata); - expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(4); - }); - - it('modifyBuiltinComponentAction', () => { - designer.componentActions.modifyBuiltinComponentAction('copy', (action) => { - expect(action.name).toBe('copy'); - }); - }); -}); - -describe('transducers', () => { - it('componentDefaults', () => { - const meta1 = new ComponentMeta(designer, abcgroup); - const meta2 = new ComponentMeta(designer, abcitem); - const meta3 = new ComponentMeta(designer, abcnode); - const meta4 = new ComponentMeta(designer, abcoption); - expect(meta1.getMetadata().configure.component.nestingRule.childWhitelist).toEqual(['Abc']); - expect(meta2.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc']); - expect(meta3.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc', 'Abc.Node']); - expect(meta4.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc']); - }); -}); diff --git a/packages/designer/tests/main/simulator.test.ts b/packages/designer/tests/main/simulator.test.ts deleted file mode 100644 index 13ab97301..000000000 --- a/packages/designer/tests/main/simulator.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import '../fixtures/window'; -import { isSimulatorHost } from '../../src/simulator'; - -it('isSimulatorHost', () => { - expect(isSimulatorHost({ isSimulator: true })).toBeTruthy(); - expect(isSimulatorHost({ a: 1 })).toBeFalsy(); -}); diff --git a/packages/designer/tests/plugin/plugin-manager.test.ts b/packages/designer/tests/plugin/plugin-manager.test.ts deleted file mode 100644 index 73915203f..000000000 --- a/packages/designer/tests/plugin/plugin-manager.test.ts +++ /dev/null @@ -1,529 +0,0 @@ -import '../fixtures/window'; -import { Editor, engineConfig } from '@alilc/lowcode-editor-core'; -import { LowCodePluginManager } from '../../src/plugin/plugin-manager'; -import { IPublicModelPluginContext, IPublicApiPlugins } from '@alilc/lowcode-types'; -import { ILowCodePluginContextPrivate } from '../../src/plugin/plugin-types'; - -const editor = new Editor(); -let contextApiAssembler; - -describe('plugin 测试', () => { - let pluginManager: IPublicApiPlugins; - beforeEach(() => { - contextApiAssembler = { - assembleApis(context: ILowCodePluginContextPrivate){ - context.plugins = pluginManager as IPublicApiPlugins; - // mock set apis - } - }; - pluginManager = new LowCodePluginManager(contextApiAssembler).toProxy(); - }); - afterEach(() => { - pluginManager.dispose(); - }); - - it('注册插件,插件参数生成函数能被调用,且能拿到正确的 ctx ', () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - mockFn(ctx); - return { - init: jest.fn(), - }; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - - const [expectedCtx] = mockFn.mock.calls[0]; - expect(expectedCtx).toHaveProperty('project'); - expect(expectedCtx).toHaveProperty('setters'); - expect(expectedCtx).toHaveProperty('material'); - expect(expectedCtx).toHaveProperty('hotkey'); - expect(expectedCtx).toHaveProperty('plugins'); - expect(expectedCtx).toHaveProperty('skeleton'); - expect(expectedCtx).toHaveProperty('logger'); - expect(expectedCtx).toHaveProperty('config'); - expect(expectedCtx).toHaveProperty('event'); - expect(expectedCtx).toHaveProperty('preference'); - }); - - it('注册插件,调用插件 init 方法', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - exports() { - return { - x: 1, - y: 2, - }; - }, - }; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(); - expect(pluginManager.size).toBe(1); - expect(pluginManager.has('demo1')).toBeTruthy(); - expect(pluginManager.get('demo1')!.isInited()).toBeTruthy(); - expect(pluginManager.demo1).toBeTruthy(); - expect(pluginManager.demo1.x).toBe(1); - expect(pluginManager.demo1.y).toBe(2); - expect(pluginManager.demo1.z).toBeUndefined(); - expect(mockFn).toHaveBeenCalled(); - }); - - it('注册插件,调用 setDisabled 方法', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - }; - }; - creator2.pluginName = 'demo1'; - - pluginManager.register(creator2); - await pluginManager.init(); - expect(pluginManager.demo1).toBeTruthy(); - pluginManager.setDisabled('demo1', true); - expect(pluginManager.demo1).toBeUndefined(); - }); - - it('注册插件,调用 plugin.setDisabled 方法', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - }; - }; - creator2.pluginName = 'demo1'; - - pluginManager.register(creator2); - await pluginManager.init(); - expect(pluginManager.demo1).toBeTruthy(); - pluginManager.get('demo1').setDisabled(); - expect(pluginManager.demo1).toBeUndefined(); - }); - - it('删除插件,调用插件 destroy 方法', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - init: jest.fn(), - destroy: mockFn, - }; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - - await pluginManager.init(); - await pluginManager.delete('demo1'); - expect(mockFn).toHaveBeenCalled(); - await pluginManager.delete('non-existing'); - }); - - describe('dependencies 依赖', () => { - it('dependencies 依赖', async () => { - const mockFn = jest.fn(); - const creator21 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo1'), - }; - }; - creator21.pluginName = 'demo1'; - creator21.meta = { - dependencies: ['demo2'], - }; - pluginManager.register(creator21); - const creator22 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo2'), - }; - }; - creator22.pluginName = 'demo2'; - pluginManager.register(creator22); - - await pluginManager.init(); - expect(mockFn).toHaveBeenNthCalledWith(1, 'demo2'); - expect(mockFn).toHaveBeenNthCalledWith(2, 'demo1'); - }); - - it('dependencies 依赖 - string', async () => { - const mockFn = jest.fn(); - const creator21 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo1'), - }; - }; - creator21.pluginName = 'demo1'; - creator21.meta = { - dependencies: 'demo2', - }; - pluginManager.register(creator21); - const creator22 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo2'), - }; - }; - creator22.pluginName = 'demo2'; - pluginManager.register(creator22); - - await pluginManager.init(); - expect(mockFn).toHaveBeenNthCalledWith(1, 'demo2'); - expect(mockFn).toHaveBeenNthCalledWith(2, 'demo1'); - }); - - it('dependencies 依赖 - 兼容 dep', async () => { - const mockFn = jest.fn(); - const creator21 = (ctx: IPublicModelPluginContext) => { - return { - dep: ['demo4'], - init: () => mockFn('demo3'), - }; - }; - creator21.pluginName = 'demo3'; - pluginManager.register(creator21); - const creator22 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo4'), - }; - }; - creator22.pluginName = 'demo4'; - pluginManager.register(creator22); - - await pluginManager.init(); - expect(mockFn).toHaveBeenNthCalledWith(1, 'demo4'); - expect(mockFn).toHaveBeenNthCalledWith(2, 'demo3'); - }); - - it('dependencies 依赖 - 兼容 dep & string', async () => { - const mockFn = jest.fn(); - const creator21 = (ctx: IPublicModelPluginContext) => { - return { - dep: 'demo4', - init: () => mockFn('demo3'), - }; - }; - creator21.pluginName = 'demo3'; - pluginManager.register(creator21); - const creator22 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo4'), - }; - }; - creator22.pluginName = 'demo4'; - pluginManager.register(creator22); - - await pluginManager.init(); - expect(mockFn).toHaveBeenNthCalledWith(1, 'demo4'); - expect(mockFn).toHaveBeenNthCalledWith(2, 'demo3'); - }); - }); - - it('version 依赖', async () => { - const mockFn = jest.fn(); - const creator21 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo1'), - }; - }; - creator21.pluginName = 'demo1'; - creator21.meta = { - engines: { - lowcodeEngine: '^1.1.0', - }, - }; - engineConfig.set('ENGINE_VERSION', '1.0.1'); - - console.log('version: ', engineConfig.get('ENGINE_VERSION')); - // not match should skip - pluginManager.register(creator21).catch((e) => { - expect(e).toEqual( - new Error( - 'plugin demo1 skipped, engine check failed, current engine version is 1.0.1, meta.engines.lowcodeEngine is ^1.1.0', - ), - ); - }); - - expect(pluginManager.plugins.length).toBe(0); - - const creator22 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo2'), - }; - }; - creator22.pluginName = 'demo2'; - creator22.meta = { - engines: { - lowcodeEngine: '^1.0.1', - }, - }; - - engineConfig.set('ENGINE_VERSION', '1.0.3'); - pluginManager.register(creator22); - expect(pluginManager.plugins.length).toBe(1); - - const creator23 = (ctx: IPublicModelPluginContext) => { - return { - init: () => mockFn('demo3'), - }; - }; - creator23.pluginName = 'demo3'; - creator23.meta = { - engines: { - lowcodeEngine: '1.x', - }, - }; - engineConfig.set('ENGINE_VERSION', '1.1.1'); - pluginManager.register(creator23); - expect(pluginManager.plugins.length).toBe(2); - }); - - it('autoInit 功能', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - }; - }; - creator2.pluginName = 'demo1'; - await pluginManager.register(creator2, { autoInit: true }); - expect(mockFn).toHaveBeenCalled(); - }); - - it('插件不会重复 init,除非强制重新 init', async () => { - const mockFn = jest.fn(); - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - name: 'demo1', - init: mockFn, - }; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(); - expect(mockFn).toHaveBeenCalledTimes(1); - - pluginManager.get('demo1')!.init(); - expect(mockFn).toHaveBeenCalledTimes(1); - - pluginManager.get('demo1')!.init(true); - expect(mockFn).toHaveBeenCalledTimes(2); - }); - - it('默认情况不允许重复注册', async () => { - const mockFn = jest.fn(); - const mockPlugin = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - }; - }; - mockPlugin.pluginName = 'demoDuplicated'; - pluginManager.register(mockPlugin); - pluginManager.register(mockPlugin).catch((e) => { - expect(e).toEqual(new Error('Plugin with name demoDuplicated exists')); - }); - await pluginManager.init(); - }); - - it('插件增加 override 参数时可以重复注册', async () => { - const mockFn = jest.fn(); - const mockPlugin = (ctx: IPublicModelPluginContext) => { - return { - init: mockFn, - }; - }; - mockPlugin.pluginName = 'demoOverride'; - pluginManager.register(mockPlugin); - pluginManager.register(mockPlugin, { override: true }); - await pluginManager.init(); - }); - - it('插件增加 override 参数时可以重复注册, 被覆盖的如果已初始化,会被销毁', async () => { - const mockInitFn = jest.fn(); - const mockDestroyFn = jest.fn(); - const mockPlugin = (ctx: IPublicModelPluginContext) => { - return { - init: mockInitFn, - destroy: mockDestroyFn, - }; - }; - mockPlugin.pluginName = 'demoOverride'; - await pluginManager.register(mockPlugin, { autoInit: true }); - expect(mockInitFn).toHaveBeenCalledTimes(1); - await pluginManager.register(mockPlugin, { override: true }); - expect(mockDestroyFn).toHaveBeenCalledTimes(1); - await pluginManager.init(); - }); - - it('dispose 方法', async () => { - const creator2 = (ctx: IPublicModelPluginContext) => { - return {}; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(); - const plugin = pluginManager.get('demo1')!; - await plugin.dispose(); - - expect(pluginManager.has('demo1')).toBeFalsy(); - }); - - it('getAll 方法', async () => { - const creator2 = (ctx: IPublicModelPluginContext) => { - return {}; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(); - - expect(pluginManager.getAll()).toHaveLength(1); - }); - - it('getPluginPreference 方法 - null', async () => { - const creator2 = (ctx: IPublicModelPluginContext) => { - return {}; - }; - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(); - - expect(pluginManager.getPluginPreference()).toBeNull(); - }); - - it('getPluginPreference 方法', async () => { - const creator2 = (ctx: IPublicModelPluginContext) => { - return {}; - }; - const preference = new Map(); - preference.set('demo1', { a: 1, b: 2 }); - creator2.pluginName = 'demo1'; - pluginManager.register(creator2); - await pluginManager.init(preference); - - expect(pluginManager.getPluginPreference('demo1')).toEqual({ a: 1, b: 2 }); - }); - - it('注册插件,调用插件 init 方法并传入 preference,可以成功获取', async () => { - const mockFn = jest.fn(); - const mockFnForCtx = jest.fn(); - const mockFnForCtx2 = jest.fn(); - const mockPreference = new Map(); - mockPreference.set('demo1', { - key1: 'value for key1', - key2: false, - key3: 123, - key5: 'value for key5, but declared, should not work', - }); - - const creator2 = (ctx: IPublicModelPluginContext) => { - mockFnForCtx(ctx); - return { - init: jest.fn(), - }; - }; - creator2.pluginName = 'demo1'; - creator2.meta = { - preferenceDeclaration: { - title: 'demo1的的参数定义', - properties: [ - { - key: 'key1', - type: 'string', - description: 'this is description for key1', - }, - { - key: 'key2', - type: 'boolean', - description: 'this is description for key2', - }, - { - key: 'key3', - type: 'number', - description: 'this is description for key3', - }, - { - key: 'key4', - type: 'string', - description: 'this is description for key4', - }, - ], - }, - }; - const creator22 = (ctx: IPublicModelPluginContext) => { - mockFnForCtx2(ctx); - return { - init: jest.fn(), - }; - }; - creator22.pluginName = 'demo2'; - creator22.meta = { - preferenceDeclaration: { - title: 'demo1的的参数定义', - properties: [ - { - key: 'key1', - type: 'string', - description: 'this is description for key1', - }, - ], - }, - }; - pluginManager.register(creator2); - pluginManager.register(creator22); - expect(mockFnForCtx).toHaveBeenCalledTimes(1); - - await pluginManager.init(mockPreference); - // creator2 only get excuted once - expect(mockFnForCtx).toHaveBeenCalledTimes(1); - - const [expectedCtx, expectedOptions] = mockFnForCtx.mock.calls[0]; - expect(expectedCtx).toHaveProperty('preference'); - - // test normal case - expect(expectedCtx.preference.getPreferenceValue('key1', 'default')).toBe('value for key1'); - - // test default value logic - expect(expectedCtx.preference.getPreferenceValue('key4', 'default for key4')).toBe( - 'default for key4', - ); - - // test undeclared key - expect(expectedCtx.preference.getPreferenceValue('key5', 'default for key5')).toBeUndefined(); - - // no preference defined - const [expectedCtx2] = mockFnForCtx2.mock.calls[0]; - expect(expectedCtx2.preference.getPreferenceValue('key1')).toBeUndefined(); - }); - - it('注册插件,没有填写 pluginName,默认值为 anonymous', async () => { - const mockFn = jest.fn(); - - const creator2 = (ctx: IPublicModelPluginContext) => { - return { - name: 'xxx', - init: () => mockFn('anonymous'), - }; - }; - await pluginManager.register(creator2); - expect(pluginManager.get('anonymous')).toBeUndefined(); - }); - - it('自定义/扩展 plugin context', async () => { - const mockFn = jest.fn(); - const mockFn2 = jest.fn(); - - const creator2 = (ctx: IPublicModelPluginContext) => { - mockFn2(ctx); - return { - init: () => mockFn('anonymous'), - }; - }; - creator2.pluginName = 'yyy'; - editor.set('enhancePluginContextHook', (originalContext) => { - originalContext.newProp = 1; - }); - await pluginManager.register(creator2); - const [expectedCtx] = mockFn2.mock.calls[0]; - expect(expectedCtx).toHaveProperty('newProp'); - }); -}); diff --git a/packages/designer/tests/plugin/plugin-utils.test.ts b/packages/designer/tests/plugin/plugin-utils.test.ts deleted file mode 100644 index eb152a049..000000000 --- a/packages/designer/tests/plugin/plugin-utils.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import '../fixtures/window'; -import { isValidPreferenceKey, filterValidOptions } from '../../src/plugin/plugin-utils'; - -describe('plugin utils 测试', () => { - it('isValidPreferenceKey', () => { - expect(isValidPreferenceKey('x')).toBeFalsy(); - expect(isValidPreferenceKey('x', { properties: {} })).toBeFalsy(); - expect(isValidPreferenceKey('x', { properties: 1 })).toBeFalsy(); - expect(isValidPreferenceKey('x', { properties: 'str' })).toBeFalsy(); - expect(isValidPreferenceKey('x', { properties: [] })).toBeFalsy(); - expect( - isValidPreferenceKey('x', { - title: 'title', - properties: [ - { - key: 'y', - type: 'string', - description: 'x desc', - }, - ], - }), - ).toBeFalsy(); - expect( - isValidPreferenceKey('x', { - title: 'title', - properties: [ - { - key: 'x', - type: 'string', - description: 'x desc', - }, - ], - }), - ).toBeTruthy(); - }); - - it('filterValidOptions', () => { - const mockDeclaration = { - title: 'title', - properties: [ - { - key: 'x', - type: 'string', - description: 'x desc', - }, - { - key: 'y', - type: 'string', - description: 'y desc', - }, - { - key: 'z', - type: 'string', - description: 'z desc', - }, - ], - }; - - expect(filterValidOptions()).toBeUndefined(); - expect(filterValidOptions(1)).toBe(1); - expect(filterValidOptions({ - x: 1, - y: 2, - }, mockDeclaration)).toEqual({ - x: 1, - y: 2, - }); - expect(filterValidOptions({ - x: 1, - y: undefined, - }, mockDeclaration)).toEqual({ - x: 1, - }); - expect(filterValidOptions({ - x: 1, - z: null, - }, mockDeclaration)).toEqual({ - x: 1, - }); - expect(filterValidOptions({ - a: 1, - }, mockDeclaration)).toEqual({ - }); - }); -}); diff --git a/packages/designer/tests/plugin/sequencify.test.ts b/packages/designer/tests/plugin/sequencify.test.ts deleted file mode 100644 index 89140e279..000000000 --- a/packages/designer/tests/plugin/sequencify.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import sequencify, { sequence } from '../../src/plugin/sequencify'; - -describe('sequence', () => { - it('handles tasks with no dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] }, - task2: { name: 'Task 2', dep: [] } - }; - const results = []; - const missing = []; - const recursive = []; - sequence({ tasks, names: ['task1', 'task2'], results, missing, recursive, nest: [] }); - - expect(results).toEqual(['task1', 'task2']); - expect(missing).toEqual([]); - expect(recursive).toEqual([]); - }); - - it('correctly orders tasks based on dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] }, - task2: { name: 'Task 2', dep: ['task1'] } - }; - const results = []; - const missing = []; - const recursive = []; - sequence({ tasks, names: ['task2', 'task1'], results, missing, recursive, nest: [] }); - - expect(results).toEqual(['task1', 'task2']); - expect(missing).toEqual([]); - expect(recursive).toEqual([]); - }); - - it('identifies missing tasks', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] } - }; - const results = []; - const missing = []; - const recursive = []; - const nest = [] - sequence({ tasks, names: ['task2'], results, missing, recursive, nest }); - - expect(results).toEqual(['task2']); - expect(missing).toEqual(['task2']); - expect(recursive).toEqual([]); - expect(nest).toEqual([]); - }); - - it('detects recursive dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: ['task2'] }, - task2: { name: 'Task 2', dep: ['task1'] } - }; - const results = []; - const missing = []; - const recursive = []; - const nest = [] - sequence({ tasks, names: ['task1', 'task2'], results, missing, recursive, nest }); - - expect(results).toEqual(['task1', 'task2', 'task1']); - expect(missing).toEqual([]); - expect(recursive).toEqual([['task1', 'task2', 'task1']]); - expect(nest).toEqual([]); - }); -}); - -describe('sequence', () => { - - it('should return tasks in sequence without dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] }, - task2: { name: 'Task 2', dep: [] }, - task3: { name: 'Task 3', dep: [] } - }; - const names = ['task1', 'task2', 'task3']; - const expected = { - sequence: ['task1', 'task2', 'task3'], - missingTasks: [], - recursiveDependencies: [] - }; - expect(sequencify(tasks, names)).toEqual(expected); - }); - - it('should handle tasks with dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] }, - task2: { name: 'Task 2', dep: ['task1'] }, - task3: { name: 'Task 3', dep: ['task2'] } - }; - const names = ['task3', 'task2', 'task1']; - const expected = { - sequence: ['task1', 'task2', 'task3'], - missingTasks: [], - recursiveDependencies: [] - }; - expect(sequencify(tasks, names)).toEqual(expected); - }); - - it('should identify missing tasks', () => { - const tasks = { - task1: { name: 'Task 1', dep: [] }, - task2: { name: 'Task 2', dep: ['task3'] } // task3 is missing - }; - const names = ['task1', 'task2']; - const expected = { - sequence: [], - missingTasks: ['task2.task3'], - recursiveDependencies: [] - }; - expect(sequencify(tasks, names)).toEqual(expected); - }); - - it('should detect recursive dependencies', () => { - const tasks = { - task1: { name: 'Task 1', dep: ['task2'] }, - task2: { name: 'Task 2', dep: ['task1'] } // Recursive dependency - }; - const names = ['task1', 'task2']; - const expected = { - sequence: [], - missingTasks: [], - recursiveDependencies: [['task1', 'task2', 'task1']] - }; - expect(sequencify(tasks, names)).toEqual(expected); - }); - -}); \ No newline at end of file diff --git a/packages/designer/tests/project/project-methods.test.ts b/packages/designer/tests/project/project-methods.test.ts deleted file mode 100644 index c710b29f1..000000000 --- a/packages/designer/tests/project/project-methods.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import '../fixtures/window'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { DocumentModel } from '../../src/document/document-model'; -import { Designer } from '../../src/designer/designer'; -import formSchema from '../fixtures/schema/form'; -import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; - -describe.only('Project 方法测试', () => { - let editor: Editor; - let designer: Designer; - let project: Project; - let doc: DocumentModel; - - beforeEach(() => { - editor = new Editor(); - designer = new Designer({ editor, shellModelFactory }); - project = designer.project; - doc = new DocumentModel(project, formSchema); - }); - - afterEach(() => { - project.unload(); - designer.purge(); - editor = null; - designer = null; - project = null; - }); - - it('simulator', () => { - const mockSimulator = { isSimulator: true, a: 1 }; - project.mountSimulator(mockSimulator); - expect(project.simulator).toEqual(mockSimulator); - }); - - it('config / get / set', () => { - const mockConfig = { version: '1.0.0', componentsTree: [] }; - project.config = mockConfig; - expect(project.config).toEqual(mockConfig); - const mockConfig2 = { version: '2.0.0', componentsTree: [] }; - project.set('config', mockConfig2); - expect(project.get('config')).toEqual(mockConfig2); - - project.set('version', '2.0.0'); - expect(project.get('version')).toBe('2.0.0'); - }); - - it('load', () => { - project.load({ - componentsTree: [{ - componentName: 'Page', - fileName: 'f1', - }], - }, 'f1'); - expect(project.currentDocument?.fileName).toBe('f1'); - }); - - it.skip('setSchema', () => { - project.load({ - componentsTree: [{ - componentName: 'Page', - fileName: 'f1', - }], - }, true); - project.setSchema({ - componentsTree: [{ - componentName: 'Page', - props: { a: 1 }, - }], - }); - expect(project.currentDocument?.rootNode?.propsData).toEqual({ a: 1 }); - }); - - it('open / getDocument / checkExclusive', () => { - project.load({ - componentsTree: [{ - componentName: 'Page', - fileName: 'f1', - }], - }); - const doc1 = project.createDocument({ - componentName: 'Page', - fileName: 'f2', - }); - const doc2 = project.createDocument({ - componentName: 'Page', - fileName: 'f3', - }); - - project.open(); - - project.open('f2'); - expect(project.currentDocument).toBe(doc1); - project.open('f3'); - expect(project.currentDocument).toBe(doc2); - - project.open('f1'); - expect(project.currentDocument?.fileName).toBe('f1'); - - expect(project.open('not-existing')).toBeNull(); - - project.open(doc2); - expect(project.currentDocument).toBe(doc2); - - const doc3 = project.open({ - componentName: 'Page', - fileName: 'f4', - }); - expect(project.currentDocument).toBe(doc3); - expect(project.documents.length).toBe(4); - - expect(project.getDocument(project.currentDocument?.id)).toBe(doc3); - expect(project.getDocumentByFileName(project.currentDocument?.fileName)).toBe(doc3); - expect(project.getDocumentByFileName('unknown')).toBeNull(); - expect(project.checkExclusive(project.currentDocument)); - - expect(project.documents[0].opened).toBeTruthy(); - expect(project.documents[1].opened).toBeTruthy(); - expect(project.documents[2].opened).toBeTruthy(); - expect(project.documents[3].opened).toBeTruthy(); - expect(project.documents[0].suspensed).toBeTruthy(); - expect(project.documents[1].suspensed).toBeTruthy(); - expect(project.documents[2].suspensed).toBeTruthy(); - expect(project.documents[3].suspensed).toBeFalsy(); - - project.closeOthers(project.currentDocument); - expect(project.documents[0].opened).toBeFalsy(); - expect(project.documents[1].opened).toBeFalsy(); - expect(project.documents[2].opened).toBeFalsy(); - expect(project.documents[3].opened).toBeTruthy(); - expect(project.documents[0].suspensed).toBeTruthy(); - expect(project.documents[1].suspensed).toBeTruthy(); - expect(project.documents[2].suspensed).toBeTruthy(); - expect(project.documents[3].suspensed).toBeFalsy(); - }); - - it('removeDocument', () => { - const doc1 = project.createDocument({ - componentName: 'Page', - fileName: 'f1', - }); - project.removeDocument({}); - expect(project.documents.length).toBe(1); - }); - - it('simulatorProps', () => { - designer._simulatorProps = { a: 1 }; - expect(designer.simulatorProps.a).toBe(1); - designer._simulatorProps = () => ({ a: 1 }); - expect(designer.simulatorProps.a).toBe(1); - }); - - it('onCurrentDocumentChange', () => { - const mockFn = jest.fn(); - const off = project.onCurrentDocumentChange(mockFn); - - project.open({ - componentName: 'Page', - }); - - expect(mockFn).toHaveBeenCalled(); - - off(); - mockFn.mockClear(); - project.open({ - componentName: 'Page', - }); - expect(mockFn).not.toHaveBeenCalled(); - }); - - it('setRendererReady / onRendererReady', () => { - const mockFn = jest.fn(); - const off = project.onRendererReady(mockFn); - project.setRendererReady({ a: 1 }); - expect(mockFn).toHaveBeenCalledWith({ a: 1 }); - off(); - mockFn.mockClear(); - project.setRendererReady({ a: 1 }); - expect(mockFn).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/designer/tests/project/project.test.ts b/packages/designer/tests/project/project.test.ts deleted file mode 100644 index 7750238cb..000000000 --- a/packages/designer/tests/project/project.test.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { set, cloneDeep } from 'lodash-es'; -import '../fixtures/window'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { Project } from '../../src/project/project'; -import { Designer } from '../../src/designer/designer'; -import formSchema from '../fixtures/schema/form'; -import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; - -const mockCreateSettingEntry = jest.fn(); -jest.mock('../../src/designer/designer', () => { - return { - Designer: jest.fn().mockImplementation(() => { - return { - getComponentMeta() { - return { - getMetadata() { - return { configure: { advanced: null } }; - }, - get advanced() { - return {}; - }, - }; - }, - transformProps(props) { - return props; - }, - createSettingEntry: mockCreateSettingEntry, - postEvent() {}, - }; - }), - }; -}); - -let designer = null; -beforeAll(() => { - designer = new Designer({}); - designer.editor = new Editor(); -}); - -describe('schema 生成节点模型测试', () => { - describe('block ❌ | component ❌ | slot ❌', () => { - beforeEach(() => { - mockCreateSettingEntry.mockClear(); - }); - - it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap.size).toBe(expectedNodeCnt); - ids.forEach((id) => { - expect(nodesMap.get(id).componentName).toBe( - getNodeFromSchemaById(formSchema, id).componentName, - ); - }); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - nodesMap.forEach((node) => { - // 触发 getter - node.settingEntry; - }); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); - }); - - it('onSimulatorReady works', () => { - const project = new Project(designer, { - componentsTree: [formSchema], - }); - project.open(); - expect(project).toBeTruthy(); - const mockCallback = jest.fn(); - const removeListener = project.onSimulatorReady(mockCallback); - project.mountSimulator(undefined); - expect(mockCallback).toBeCalled(); - removeListener(); - }); - - it('open doc when doc is blank', () => { - const project = new Project(designer); - project.open(); - expect(project).toBeTruthy(); - const blankDoc = project.documents[0]; - expect(blankDoc).toBeTruthy(); - // 触发保存 - blankDoc.history.savePoint(); - expect(blankDoc.isModified()).toBeFalsy(); - expect(blankDoc.isBlank()).toBeTruthy(); - - //二次打开doc,会使用前面那个 - const openedDoc = project.open(); - expect(openedDoc).toBe(blankDoc); - }); - - it('load schema with autoOpen === true', () => { - const project = new Project(designer); - expect(project).toBeTruthy(); - // trigger autoOpen case - project.load( - { - componentsTree: [formSchema], - }, - true, - ); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap.size).toBe(expectedNodeCnt); - ids.forEach((id) => { - expect(nodesMap.get(id).componentName).toBe( - getNodeFromSchemaById(formSchema, id).componentName, - ); - }); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - nodesMap.forEach((node) => { - // 触发 getter - node.settingEntry; - }); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); - }); - it('load schema with autoOpen === true, and config contains layout.props.tabBar.item', () => { - const project = new Project(designer); - expect(project).toBeTruthy(); - // trigger autoOpen case - project.load( - { - componentsTree: [ - { - ...formSchema, - fileName: 'demoFile1', - }, - { - ...formSchema, - fileName: 'demoFile2', - }, - ], - config: { - layout: { - props: { - tabBar: { - items: [ - { - path: '/demoFile2', - }, - ], - }, - }, - }, - }, - }, - true, - ); - const { currentDocument } = project; - expect(currentDocument.fileName).toBe('demoFile2'); - }); - - it('load schema with autoOpen === true', () => { - const project = new Project(designer); - expect(project).toBeTruthy(); - // trigger autoOpen case - project.load( - { - componentsTree: [ - { - ...formSchema, - fileName: 'demoFile1', - }, - { - ...formSchema, - fileName: 'demoFile2', - }, - ], - }, - 'demoFile2', - ); - const { currentDocument } = project; - expect(currentDocument.fileName).toBe('demoFile2'); - }); - - it('setSchema works', () => { - const project = new Project(designer); - project.open(); - expect(project).toBeTruthy(); - project.setSchema({ - componentsTree: [ - { - ...formSchema, - fileName: 'demoFile1', - }, - ], - }); - const { currentDocument } = project; - expect(currentDocument.fileName).toBe('demoFile1'); - }); - - it('基本的节点模型初始化,模型导出,project.open 传入 schema', () => { - const project = new Project(designer); - project.open(formSchema); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const ids = getIdsFromSchema(formSchema); - const expectedNodeCnt = ids.length; - expect(nodesMap.size).toBe(expectedNodeCnt); - ids.forEach((id) => { - expect(nodesMap.get(id).componentName).toBe( - getNodeFromSchemaById(formSchema, id).componentName, - ); - }); - - const exportSchema = currentDocument?.export(1); - expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); - nodesMap.forEach((node) => { - // 触发 getter - node.settingEntry; - }); - expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); - }); - - it('project 卸载所有 document - unload()', () => { - const project = new Project(designer); - project.open(formSchema); - expect(project).toBeTruthy(); - const { currentDocument, documents } = project; - - expect(documents).toHaveLength(1); - expect(currentDocument).toBe(documents[0]); - - project.unload(); - - expect(documents).toHaveLength(0); - }); - - it('project 卸载指定 document - removeDocument()', () => { - const project = new Project(designer); - project.open(formSchema); - expect(project).toBeTruthy(); - const { currentDocument, documents } = project; - - expect(documents).toHaveLength(1); - expect(currentDocument).toBe(documents[0]); - - project.removeDocument(currentDocument); - - expect(documents).toHaveLength(0); - }); - - it('get unknown document', () => { - const project = new Project(designer); - project.open(formSchema); - expect(project).toBeTruthy(); - expect(project.getDocument('unknownId')).toBeNull(); - }); - - it('get set i18n works', () => { - const project = new Project(designer); - project.open(formSchema); - expect(project).toBeTruthy(); - - project.i18n = formSchema.i18n; - expect(project.i18n).toStrictEqual(formSchema.i18n); - project.i18n = null; - expect(project.i18n).toStrictEqual({}); - - project.set('i18n', formSchema.i18n); - expect(project.get('i18n')).toStrictEqual(formSchema.i18n); - project.set('i18n', null); - expect(project.get('i18n')).toStrictEqual({}); - }); - }); - - describe('block ❌ | component ❌ | slot ✅', () => { - it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { - const formSchemaWithSlot = set( - cloneDeep(formSchema), - 'children[0].children[0].props.title.type', - 'JSSlot', - ); - const project = new Project(designer, { - componentsTree: [formSchemaWithSlot], - }); - project.open(); - expect(project).toBeTruthy(); - const { currentDocument } = project; - const { nodesMap } = currentDocument!; - const ids = getIdsFromSchema(formSchema); - // 目前每个 slot 会新增(1 + children.length)个节点 - const expectedNodeCnt = ids.length + 2; - expect(nodesMap.size).toBe(expectedNodeCnt); - // PageHeader - expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1); - }); - }); - - describe.skip('多 document 测试', () => {}); -}); diff --git a/packages/designer/tests/utils-ut/invariant.test.ts b/packages/designer/tests/utils-ut/invariant.test.ts deleted file mode 100644 index 6b38e934f..000000000 --- a/packages/designer/tests/utils-ut/invariant.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -// @ts-nocheck -import { invariant } from '../../src/utils/invariant'; - -it('invariant', () => { - expect(() => invariant(true)).not.toThrow(); - expect(() => invariant(false, 'abc', 'xxx')).toThrow(/Invariant failed:/); - expect(() => invariant(false, 'abc')).toThrow(/Invariant failed:/); -}); \ No newline at end of file diff --git a/packages/designer/tests/utils-ut/misc.test.ts b/packages/designer/tests/utils-ut/misc.test.ts deleted file mode 100644 index 245d76e70..000000000 --- a/packages/designer/tests/utils-ut/misc.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -// @ts-nocheck -import { isElementNode, isDOMNodeVisible, normalizeTriggers, makeEventsHandler } from '../../src/utils/misc'; - -it('isElementNode', () => { - expect(isElementNode(document.createElement('div'))).toBeTruthy(); - expect(isElementNode(1)).toBeFalsy(); -}); - -/** - * const domNodeRect = domNode.getBoundingClientRect(); - const { width, height } = viewport.contentBounds; - const { left, right, top, bottom, width: nodeWidth, height: nodeHeight } = domNodeRect; - return ( - left >= -nodeWidth && - top >= -nodeHeight && - bottom <= height + nodeHeight && - right <= width + nodeWidth - ); - */ - -const genMockNode = ({ left, right, top, bottom, width, height }) => { - return { getBoundingClientRect: () => { - if (width === undefined || height === undefined) throw new Error('width and height is required.'); - const base = { width, height }; - let coordinate = {}; - if (left !== undefined) { - coordinate = top !== undefined ? { - left, - right: left + width, - top, - bottom: top + height, - } : { - left, - right: left + width, - bottom, - top: bottom - height, - } - } else if (right !== undefined) { - coordinate = top !== undefined ? { - left: right - width, - right, - top, - bottom: top + height, - } : { - left: right - width, - right, - bottom, - top: bottom - height, - } - } - return { ...base, ...coordinate }; - } }; -}; -const mockViewport = { - contentBounds: { - width: 300, - height: 300, - }, -}; -describe('isDOMNodeVisible', () => { - it('isDOMNodeVisible', () => { - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: 0, - top: 0, - }), - mockViewport, - ), - ).toBeTruthy(); - - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: -100, - top: 0, - }), - mockViewport, - ), - ).toBeTruthy(); - - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: 50, - top: 50, - }), - mockViewport, - ), - ).toBeTruthy(); - - // 左侧出界了 - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: -101, - top: 0, - }), - mockViewport, - ), - ).toBeFalsy(); - - // 右侧出界了 - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - right: 401, - top: 0, - }), - mockViewport, - ), - ).toBeFalsy(); - - // 上侧出界了 - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: 50, - top: -101, - }), - mockViewport, - ), - ).toBeFalsy(); - - // 下侧出界了 - expect( - isDOMNodeVisible( - genMockNode({ - width: 100, - height: 100, - left: 50, - bottom: 401, - }), - mockViewport, - ), - ).toBeFalsy(); - }); -}); - -it('normalizeTriggers', () => { - expect(normalizeTriggers(['n', 'w'])).toEqual(['N', 'W']); -}); - -it('makeEventsHandler', () => { - const sensor = { contentDocument: document }; - // no contentDocument - const sensor2 = {}; - const bind = makeEventsHandler({ view: { document } } as any, [sensor, sensor2]); - const fn = jest.fn(); - bind((doc) => fn(doc)); - expect(fn).toHaveBeenCalledTimes(1); -}); diff --git a/packages/designer/tests/utils-ut/slot.test.ts b/packages/designer/tests/utils-ut/slot.test.ts deleted file mode 100644 index 079db30a1..000000000 --- a/packages/designer/tests/utils-ut/slot.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -// @ts-nocheck -import { includeSlot, removeSlot } from '../../src/utils/slot'; - -const genGetExtraProp = (val: string) => () => { - return { - getAsString() { - return val; - }, - }; -}; - -const remove = () => {}; - -const mockNode = { - slots: [{ - getExtraProp: genGetExtraProp('haha'), - remove, - }, { - getExtraProp: genGetExtraProp('heihei'), - remove, - }] -}; - -// 没有 slots -const mockNode2 = {}; - -it('includeSlot', () => { - expect(includeSlot(mockNode, 'haha')).toBeTruthy(); - expect(includeSlot(mockNode, 'heihei')).toBeTruthy(); - expect(includeSlot(mockNode, 'xixi')).toBeFalsy(); - expect(includeSlot(mockNode2, 'xixi')).toBeFalsy(); -}); - -it('removeSlot', () => { - expect(removeSlot(mockNode, 'xixi')).toBeFalsy(); - expect(mockNode.slots).toHaveLength(2); - expect(removeSlot(mockNode, 'haha')).toBeTruthy(); - expect(mockNode.slots).toHaveLength(1); - expect(removeSlot(mockNode, 'heihei')).toBeTruthy(); - expect(mockNode.slots).toHaveLength(0); - - expect(removeSlot(mockNode2, 'xixi')).toBeFalsy(); -}); diff --git a/packages/designer/tests/utils/bom.ts b/packages/designer/tests/utils/bom.ts deleted file mode 100644 index 743b151c3..000000000 --- a/packages/designer/tests/utils/bom.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { getMockRenderer } from './renderer'; - -interface MockDocument extends Document { - // open(): any; - // write(): any; - // close(): any; - // addEventListener(): any; - // removeEventListener(): any; - triggerEventListener(): any; - // createElement(): any; - // appendChild(): any; - // removeChild(): any; -} - - -const eventsMap : Map> = new Map>(); -const mockRemoveAttribute = jest.fn(); -const mockAddEventListener = jest.fn((eventName: string, cb) => { - if (!eventsMap.has(eventName)) { - eventsMap.set(eventName, new Set([cb])); - return; - } - eventsMap.get(eventName)!.add(cb); -}); - -const mockRemoveEventListener = jest.fn((eventName: string, cb) => { - if (!eventsMap.has(eventName)) return; - if (!cb) { - eventsMap.delete(eventName); - return; - } - eventsMap.get(eventName)?.delete(cb); -}); - -const mockTriggerEventListener = jest.fn((eventName: string, data: any, context: object = {}) => { - if (!eventsMap.has(eventName)) return; - for (const cb of eventsMap.get(eventName)) { - cb.call(context, data); - } -}); - -const mockCreateElement = jest.fn((tagName) => { - return { - style: {}, - appendChild() {}, - addEventListener: mockAddEventListener, - removeEventListener: mockRemoveEventListener, - triggerEventListener: mockTriggerEventListener, - removeAttribute: mockRemoveAttribute, - }; -}); - -export function getMockDocument(): MockDocument { - return { - open() {}, - write() {}, - close() {}, - addEventListener: mockAddEventListener, - removeEventListener: mockRemoveEventListener, - triggerEventListener: mockTriggerEventListener, - createElement: mockCreateElement, - removeChild() {}, - body: { appendChild() {}, removeChild() {} }, - }; -} - -export function getMockWindow(doc?: MockDocument) { - return { - SimulatorRenderer: getMockRenderer(), - addEventListener: mockAddEventListener, - removeEventListener: mockRemoveEventListener, - triggerEventListener: mockTriggerEventListener, - document: doc || getMockDocument(), - }; -} - -export function clearEventsMap() { - eventsMap.clear(); -} - -export function getMockElement(tagName, options = {}) { - const elem = document.createElement(tagName); - let { - width = 0, - height = 0, - top = 0, - bottom = 0, - left = 0, - right = 0, - } = options; - elem.getBoundingClientRect = () => { - return { - width, - height, - top, - bottom, - left, - right, - }; - }; - elem.setWidth = (newWidth) => { - width = newWidth; - }; - elem.setHeight = (newHeight) => { - height = newHeight; - }; - // console.log(elem.ownerDocument); - // elem.ownerDocument = document; - // elem.ownerDocument.defaultView = window; - return elem; -} diff --git a/packages/designer/tests/utils/event.ts b/packages/designer/tests/utils/event.ts deleted file mode 100644 index 62f7c44b7..000000000 --- a/packages/designer/tests/utils/event.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function getMockEvent(target, options) { - return { - target, - preventDefault() {}, - stopPropagation() {}, - ...options, - }; -} diff --git a/packages/designer/tests/utils/index.ts b/packages/designer/tests/utils/index.ts deleted file mode 100644 index 8486f1078..000000000 --- a/packages/designer/tests/utils/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { getIdsFromSchema, getNodeFromSchemaById } from '@alilc/lowcode-test-mate/es/utils'; -export * from './bom'; -export * from './event'; -export * from './renderer'; -export * from './misc'; diff --git a/packages/designer/tests/utils/misc.ts b/packages/designer/tests/utils/misc.ts deleted file mode 100644 index 7be1fdbb4..000000000 --- a/packages/designer/tests/utils/misc.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { set as lodashSet } from 'lodash-es'; - -export function set(obj: any, path: any, val: any) { - if (typeof path === 'string' && path.startsWith('prototype')) { - const segs = path.split('.'); - let acc = obj; - segs.forEach((seg, idx) => { - if (idx !== segs.length - 1) { - acc[seg] = acc[seg] || {}; - acc = acc[seg]; - } else { - acc[seg] = val; - } - }); - } - return lodashSet(obj, path, val); -} - -export function delay(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -export function delayObxTick() { - return delay(100); -} diff --git a/packages/designer/tests/utils/renderer.ts b/packages/designer/tests/utils/renderer.ts deleted file mode 100644 index 9fbac91be..000000000 --- a/packages/designer/tests/utils/renderer.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function getMockRenderer() { - return { - isSimulatorRenderer: true, - run() { - // console.log('renderer run'); - }, - }; -} diff --git a/packages/engine-core/__tests__/configuration/configurationModel.spec.ts b/packages/engine-core/__tests__/configuration/configurationModel.spec.ts new file mode 100644 index 000000000..ce3cd20dc --- /dev/null +++ b/packages/engine-core/__tests__/configuration/configurationModel.spec.ts @@ -0,0 +1,59 @@ +import { ConfigurationModel } from '../../src'; +import { describe, it, expect } from 'vitest'; + +describe('ConfigurationModel', () => { + it('should create an empty model', () => { + const model = ConfigurationModel.createEmptyModel(); + expect(model.isEmpty()).toBe(true); + }); + + it('should add, set, and get values', () => { + const model = ConfigurationModel.createEmptyModel(); + const key = 'testKey'; + const value = 'testValue'; + + model.setValue(key, value); + expect(model.getValue(key)).toBe(value); + + const newValue = 'newValue'; + model.addValue(key, newValue); + expect(model.getValue(key)).toBe(newValue); + + model.removeValue(key); + expect(model.getValue(key)).toBeUndefined(); + }); + + it('should handle overrides', () => { + const model = ConfigurationModel.createEmptyModel(); + const key = '[env].testKey'; + const value = 'testValue'; + + model.setValue(key, value); + const overrides = model.overrides[0]; + expect(overrides.keys).toContain('testKey'); + expect(overrides.contents).toHaveProperty('testKey', value); + expect(overrides.identifiers).toContain('env'); + + model.removeValue(key); + expect(model.getValue(key)).toBeUndefined(); + expect(model.overrides.length).toBe(0); + }); + + it('should create overrides correctly', () => { + const model = ConfigurationModel.createEmptyModel(); + const baseKey = 'baseKey'; + const baseValue = 'baseValue'; + const overrideKey = '[environment].overrideKey'; + const overrideValue = 'overrideValue'; + + // Set base and override values + model.setValue(baseKey, baseValue); + model.setValue(overrideKey, overrideValue); + + // Override configuration model for a specific environment + const envModel = model.override('environment'); + expect(envModel.getValue('overrideKey')).toBe(overrideValue); + expect(envModel.getValue(baseKey)).toBeUndefined(); + expect(envModel.overrides).toEqual(model.overrides); + }); +}); diff --git a/packages/engine-core/package.json b/packages/engine-core/package.json index 97347bcdb..498cbbc6c 100644 --- a/packages/engine-core/package.json +++ b/packages/engine-core/package.json @@ -1,5 +1,6 @@ { - "name": "@ali/lowcode-engine-core", + "name": "@alilc/lowcode-engine-core", + "version": "2.0.0-beta.0", "description": "", "type": "module", "main": "dist/engine-core.js", diff --git a/packages/engine-core/src/command/command.ts b/packages/engine-core/src/command/command.ts new file mode 100644 index 000000000..be4e9a13c --- /dev/null +++ b/packages/engine-core/src/command/command.ts @@ -0,0 +1,34 @@ +import { type InstanceAccessor } from '@alilc/lowcode-shared'; + +export interface ICommandEvent { + commandId: string; + args: any[]; +} + +export interface ICommandHandler { + (accessor: InstanceAccessor, ...args: any[]): void; +} + +export interface ICommand { + id: string; + handler: ICommandHandler; + metadata?: ICommandMetadata | null; +} + +export interface ICommandMetadata { + /** + * A short summary of what the command does. This will be used in: + * - API commands + * - when showing keybindings that have no other UX + * - when searching for commands in the Command Palette + */ + readonly description: string; + readonly args?: ReadonlyArray<{ + readonly name: string; + readonly isOptional?: boolean; + readonly description?: string; + // readonly constraint?: TypeConstraint; + // readonly schema?: IJSONSchema; + }>; + readonly returns?: string; +} diff --git a/packages/engine-core/src/command/commandRegistry.ts b/packages/engine-core/src/command/commandRegistry.ts new file mode 100644 index 000000000..d9223d1e9 --- /dev/null +++ b/packages/engine-core/src/command/commandRegistry.ts @@ -0,0 +1,115 @@ +import { + type Event, + type EventDisposable, + type EventListener, + Emitter, +} from '@alilc/lowcode-shared'; +import { ICommand, ICommandHandler } from './command'; +import { Registry } from '../extension'; + +export type ICommandsMap = Map; + +export interface ICommandRegistry { + onDidRegisterCommand: Event; + + registerCommand(id: string, command: ICommandHandler): EventDisposable; + registerCommand(command: ICommand): EventDisposable; + + registerCommandAlias(oldId: string, newId: string): EventDisposable; + + getCommand(id: string): ICommand | undefined; + getCommands(): ICommandsMap; +} + +class CommandsRegistry implements ICommandRegistry { + private readonly _commands = new Map>(); + + private readonly _onDidRegisterCommand = new Emitter(); + + onDidRegisterCommand(fn: EventListener) { + return this._onDidRegisterCommand.on(fn); + } + + registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): EventDisposable { + if (!idOrCommand) { + throw new Error(`invalid command`); + } + + if (typeof idOrCommand === 'string') { + if (!handler) { + throw new Error(`invalid command`); + } + return this.registerCommand({ id: idOrCommand, handler }); + } + + // add argument validation if rich command metadata is provided + if (idOrCommand.metadata && Array.isArray(idOrCommand.metadata.args)) { + const constraints: Array = []; + for (const arg of idOrCommand.metadata.args) { + constraints.push(arg.constraint); + } + const actualHandler = idOrCommand.handler; + idOrCommand.handler = function (accessor, ...args: any[]) { + validateConstraints(args, constraints); + return actualHandler(accessor, ...args); + }; + } + + // find a place to store the command + const { id } = idOrCommand; + + let commands = this._commands.get(id); + if (!commands) { + commands = new LinkedList(); + this._commands.set(id, commands); + } + + const removeFn = commands.unshift(idOrCommand); + + const ret = toDisposable(() => { + removeFn(); + const command = this._commands.get(id); + if (command?.isEmpty()) { + this._commands.delete(id); + } + }); + + // tell the world about this command + this._onDidRegisterCommand.emit(id); + + return ret; + } + + registerCommandAlias(oldId: string, newId: string): IDisposable { + return this.registerCommand(oldId, (accessor, ...args) => + accessor.get(ICommandService).executeCommand(newId, ...args), + ); + } + + getCommand(id: string): ICommand | undefined { + const list = this._commands.get(id); + if (!list || list.isEmpty()) { + return undefined; + } + return Iterable.first(list); + } + + getCommands(): ICommandsMap { + const result = new Map(); + for (const key of this._commands.keys()) { + const command = this.getCommand(key); + if (command) { + result.set(key, command); + } + } + return result; + } +} + +const commandsRegistry = new CommandsRegistry(); + +export const Extension = { + command: 'base.contributions.command', +}; + +Registry.add(Extension.command, commandsRegistry); diff --git a/packages/engine-core/src/command/commandService.ts b/packages/engine-core/src/command/commandService.ts new file mode 100644 index 000000000..0be16247f --- /dev/null +++ b/packages/engine-core/src/command/commandService.ts @@ -0,0 +1,20 @@ +import { createDecorator, Provide } from '@alilc/lowcode-shared'; +import { Registry } from '../extension'; +import { ICommandRegistry, Extension } from './commandRegistry'; + +export interface ICommandService { + executeCommand(commandId: string, ...args: any[]): Promise; +} + +export const ICommandService = createDecorator('commandService'); + +@Provide(ICommandService) +export class CommandService implements ICommandService { + executeCommand(id: string, ...args: any[]): Promise { + const command = Registry.as(Extension.command).getCommand(id); + + if (!command) { + return Promise.reject(new Error(`command '${id}' not found`)); + } + } +} diff --git a/packages/engine-core/src/configuration/configuration.ts b/packages/engine-core/src/configuration/configuration.ts new file mode 100644 index 000000000..d8271c089 --- /dev/null +++ b/packages/engine-core/src/configuration/configuration.ts @@ -0,0 +1,81 @@ +import { type StringDictionary, Emitter, type EventListener } from '@alilc/lowcode-shared'; +import { ConfigurationModel } from './configurationModel'; +import { + type IConfigurationRegistry, + type IRegisteredConfigurationPropertySchema, + Extension, +} from './configurationRegistry'; +import { Registry } from '../extension'; + +export interface IConfigurationOverrides { + overrideIdentifier?: string | null; +} + +export interface IConfigurationUpdateOverrides { + overrideIdentifiers?: string[] | null; +} + +export class DefaultConfiguration { + private emitter = new Emitter<{ + defaults: ConfigurationModel; + properties: string[]; + }>(); + + private _configurationModel = ConfigurationModel.createEmptyModel(); + + get configurationModel(): ConfigurationModel { + return this._configurationModel; + } + + initialize(): ConfigurationModel { + this.resetConfigurationModel(); + Registry.as(Extension.Configuration).onDidUpdateConfiguration( + ({ properties }) => this.onDidUpdateConfiguration([...properties]), + ); + + return this.configurationModel; + } + + reload(): ConfigurationModel { + this.resetConfigurationModel(); + return this.configurationModel; + } + + onDidChangeConfiguration( + listener: EventListener<[{ defaults: ConfigurationModel; properties: string[] }]>, + ) { + return this.emitter.on(listener); + } + + private onDidUpdateConfiguration(properties: string[]): void { + this.updateConfigurationModel( + properties, + Registry.as(Extension.Configuration).getConfigurationProperties(), + ); + this.emitter.emit({ defaults: this.configurationModel, properties }); + } + + private resetConfigurationModel(): void { + this._configurationModel = ConfigurationModel.createEmptyModel(); + + const properties = Registry.as( + Extension.Configuration, + ).getConfigurationProperties(); + + this.updateConfigurationModel(Object.keys(properties), properties); + } + + private updateConfigurationModel( + properties: string[], + configurationProperties: StringDictionary, + ): void { + for (const key of properties) { + const propertySchema = configurationProperties[key]; + if (propertySchema) { + this.configurationModel.setValue(key, propertySchema.default); + } else { + this.configurationModel.removeValue(key); + } + } + } +} diff --git a/packages/engine-core/src/configuration/configurationModel.ts b/packages/engine-core/src/configuration/configurationModel.ts new file mode 100644 index 000000000..74821c9b5 --- /dev/null +++ b/packages/engine-core/src/configuration/configurationModel.ts @@ -0,0 +1,406 @@ +import { type StringDictionary } from '@alilc/lowcode-shared'; +import { get as lodasgGet, isEqual, uniq, cloneDeep, isObject } from 'lodash-es'; +import { OVERRIDE_PROPERTY_REGEX, overrideIdentifiersFromKey } from './configurationRegistry'; + +export type InspectValue = { + readonly value?: V; + readonly override?: V; + readonly overrides?: { readonly identifiers: string[]; readonly value: V }[]; + merged?: V; +}; + +export interface IConfigurationModel { + contents: any; + keys: string[]; + overrides: IOverrides[]; +} + +export interface IOverrides { + keys: string[]; + contents: any; + identifiers: string[]; +} + +/** + * 支持配置覆盖的 model 类 + * + * 举例来说: + * 假设有一个应用程序,它的行为在开发环境和生产环境下有细微差别。在开发环境中,可能需要额外的日志记录和调试工具,而生产环境则需要优化性能和安全性。 + * 使用配置覆盖,就可以为这两个环境创建一组基础配置,并通过以 [development] 或 [production] 为标识符的覆盖键来为两个环境提供不同的设置。 + * + * case: + * const model = ConfigurationModel.createEmptyModel(); + * const baseKey = 'baseKey'; + * const baseValue = 'baseValue'; + * const overrideKey = '[environment]'; + * const overrideValue = { baseKey: 'overrideValue' }; + * + * const envModel = model.override('environment'); + * model.getValue(baseKey) === 'baseValue' + * envModel.getValue(baseKey) === 'overrideValue' + */ +export class ConfigurationModel implements IConfigurationModel { + static createEmptyModel(): ConfigurationModel { + return new ConfigurationModel({}, [], [], undefined); + } + + private readonly overrideConfigurations = new Map(); + + constructor( + private readonly _contents: StringDictionary, + private readonly _keys: string[], + private readonly _overrides: IOverrides[], + public readonly raw?: ReadonlyArray | undefined, + ) {} + + get contents(): any { + return this._contents; + } + + get overrides(): IOverrides[] { + return this._overrides; + } + + get keys(): string[] { + return this._keys; + } + + private _rawConfiguration: ConfigurationModel | undefined; + get rawConfiguration(): ConfigurationModel { + if (!this._rawConfiguration) { + if (this.raw?.length) { + const rawConfigurationModels = this.raw; + this._rawConfiguration = rawConfigurationModels.reduce( + (previous, current) => (current === previous ? current : previous.merge(current)), + rawConfigurationModels[0], + ); + } else { + // raw is same as current + this._rawConfiguration = this; + } + } + return this._rawConfiguration; + } + + toJSON(): IConfigurationModel { + return { + contents: this.contents, + overrides: this.overrides, + keys: this.keys, + }; + } + + inspect(section?: string | undefined, overrideIdentifier?: string | null): InspectValue { + const _this = this; + return { + get value() { + return _this.rawConfiguration.getValue(section); + }, + get override() { + return overrideIdentifier + ? _this.rawConfiguration.getOverrideValue(section, overrideIdentifier) + : undefined; + }, + get merged() { + return overrideIdentifier + ? _this.rawConfiguration.override(overrideIdentifier).getValue(section) + : _this.rawConfiguration.getValue(section); + }, + get overrides() { + const overrides: { readonly identifiers: string[]; readonly value: V }[] = []; + for (const { contents, identifiers, keys } of _this.rawConfiguration.overrides) { + const value = new ConfigurationModel(contents, keys, [], undefined).getValue(section); + if (value !== undefined) { + overrides.push({ identifiers, value }); + } + } + return overrides.length ? overrides : undefined; + }, + }; + } + + merge(...others: ConfigurationModel[]): ConfigurationModel { + const contents = cloneDeep(this.contents); + const overrides = cloneDeep(this.overrides); + const keys = [...this.keys]; + const raws = this.raw?.length ? [...this.raw] : [this]; + + for (const other of others) { + raws.push(...(other.raw?.length ? other.raw : [other])); + if (other.isEmpty()) { + continue; + } + this.mergeContents(contents, other.contents); + + for (const otherOverride of other.overrides) { + const [override] = overrides.filter((o) => + isEqual(o.identifiers, otherOverride.identifiers), + ); + if (override) { + this.mergeContents(override.contents, otherOverride.contents); + override.keys.push(...otherOverride.keys); + override.keys = uniq(override.keys); + } else { + overrides.push(cloneDeep(otherOverride)); + } + } + for (const key of other.keys) { + if (keys.indexOf(key) === -1) { + keys.push(key); + } + } + } + + return new ConfigurationModel(contents, keys, overrides, raws); + } + + override(identifier: string): ConfigurationModel { + let overrideConfigurationModel = this.overrideConfigurations.get(identifier); + if (!overrideConfigurationModel) { + overrideConfigurationModel = this.createOverrideConfigurationModel(identifier); + this.overrideConfigurations.set(identifier, overrideConfigurationModel); + } + return overrideConfigurationModel; + } + + private createOverrideConfigurationModel(identifier: string): ConfigurationModel { + const overrideContents = this.getContentsForOverrideIdentifer(identifier); + + if ( + !overrideContents || + typeof overrideContents !== 'object' || + !Object.keys(overrideContents).length + ) { + // If there are no valid overrides, return self + return this; + } + + const contents: any = {}; + for (const key of uniq([...Object.keys(this.contents), ...Object.keys(overrideContents)])) { + let contentsForKey = this.contents[key]; + const overrideContentsForKey = overrideContents[key]; + + // If there are override contents for the key, clone and merge otherwise use base contents + if (overrideContentsForKey) { + // Clone and merge only if base contents and override contents are of type object otherwise just override + if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') { + contentsForKey = cloneDeep(contentsForKey); + this.mergeContents(contentsForKey, overrideContentsForKey); + } else { + contentsForKey = overrideContentsForKey; + } + } + + contents[key] = contentsForKey; + } + + return new ConfigurationModel(contents, this.keys, this.overrides); + } + + private getContentsForOverrideIdentifer(identifier: string): any { + let contentsForIdentifierOnly: StringDictionary | null = null; + let contents: StringDictionary | null = null; + const mergeContents = (contentsToMerge: any) => { + if (contentsToMerge) { + if (contents) { + this.mergeContents(contents, contentsToMerge); + } else { + contents = cloneDeep(contentsToMerge); + } + } + }; + for (const override of this.overrides) { + if (override.identifiers.length === 1 && override.identifiers[0] === identifier) { + contentsForIdentifierOnly = override.contents; + } else if (override.identifiers.includes(identifier)) { + mergeContents(override.contents); + } + } + // Merge contents of the identifier only at the end to take precedence. + mergeContents(contentsForIdentifierOnly); + return contents; + } + + private mergeContents(source: any, target: any): void { + for (const key of Object.keys(target)) { + if (key in source) { + if (isObject(source[key]) && isObject(target[key])) { + this.mergeContents(source[key], target[key]); + continue; + } + } + source[key] = cloneDeep(target[key]); + } + } + + isEmpty(): boolean { + return ( + this._keys.length === 0 && + Object.keys(this._contents).length === 0 && + this._overrides.length === 0 + ); + } + + getValue(section?: string | undefined): V { + return section ? lodasgGet(this.contents, section) : this.contents; + } + + // Update methods + + addValue(key: string, value: any): void { + this.updateValue(key, value, true); + } + + setValue(key: string, value: any): void { + this.updateValue(key, value, false); + } + + removeValue(key: string): void { + const index = this.keys.indexOf(key); + if (index !== -1) { + this.keys.splice(index, 1); + removeFromValueTree(this.contents, key); + } + + const isOverrideKey = OVERRIDE_PROPERTY_REGEX.test(key); + if (isOverrideKey) { + const identifiers = overrideIdentifiersFromKey(key); + const overrideIndex = this.overrides.findIndex((o) => isEqual(o.identifiers, identifiers)); + if (overrideIndex !== -1) { + const override = this.overrides[overrideIndex]; + removeFromValueTree(override.contents, key); + if (Object.keys(override.contents).length === 0) { + this.overrides.splice(overrideIndex, 1); + } else { + override.keys = Object.keys(override.contents); + } + } + } + } + + private updateValue(key: string, value: any, add: boolean): void { + addToValueTree(this.contents, key, value); + if (add || !this.keys.includes(key)) { + this.keys.push(key); + } + + if (OVERRIDE_PROPERTY_REGEX.test(key)) { + const identifiers = overrideIdentifiersFromKey(key); + const override = this.overrides.find((o) => isEqual(o.identifiers, identifiers)); + + if (override) { + addToValueTree(override.contents, key, value); + } else { + this.overrides.push({ + identifiers, + keys: Object.keys(this.contents[key]), + contents: toValuesTree(this.contents[key]), + }); + } + } + } + + getOverrideValue(section: string | undefined, overrideIdentifier: string): V | undefined { + const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier); + return overrideContents + ? section + ? lodasgGet(overrideContents, section) + : overrideContents + : undefined; + } + + getKeysForOverrideIdentifier(identifier: string): string[] { + const keys: string[] = []; + for (const override of this.overrides) { + if (override.identifiers.includes(identifier)) { + keys.push(...override.keys); + } + } + return uniq(keys); + } + + getAllOverrideIdentifiers(): string[] { + const result: string[] = []; + for (const override of this.overrides) { + result.push(...override.identifiers); + } + return uniq(result); + } +} + +function removeFromValueTree(valueTree: any, key: string): void { + const segments = key.split('.'); + doRemoveFromValueTree(valueTree, segments); +} + +function doRemoveFromValueTree(valueTree: any, segments: string[]): void { + const first = segments.shift()!; + if (segments.length === 0) { + // Reached last segment + delete valueTree[first]; + return; + } + + if (Object.keys(valueTree).includes(first)) { + const value = valueTree[first]; + if (typeof value === 'object' && !Array.isArray(value)) { + doRemoveFromValueTree(value, segments); + if (Object.keys(value).length === 0) { + delete valueTree[first]; + } + } + } +} + +function addToValueTree( + settingsTreeRoot: any, + key: string, + value: any, + conflictReporter: (message: string) => void = console.error, +): void { + const segments = key.split('.'); + const last = segments.pop()!; + + let curr = settingsTreeRoot; + for (let i = 0; i < segments.length; i++) { + const s = segments[i]; + let obj = curr[s]; + switch (typeof obj) { + case 'undefined': + obj = curr[s] = Object.create(null); + break; + case 'object': + if (obj === null) { + conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is null`); + return; + } + break; + default: + conflictReporter( + `Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`, + ); + return; + } + curr = obj; + } + + if (typeof curr === 'object' && curr !== null) { + try { + curr[last] = value; // workaround https://github.com/microsoft/vscode/issues/13606 + } catch (e) { + conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`); + } + } else { + conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`); + } +} + +function toValuesTree(properties: StringDictionary): any { + const root = Object.create(null); + + for (const key in properties) { + addToValueTree(root, key, properties[key]); + } + + return root; +} diff --git a/packages/engine-core/src/configuration/configurationRegistry.ts b/packages/engine-core/src/configuration/configurationRegistry.ts new file mode 100644 index 000000000..034ecc2d8 --- /dev/null +++ b/packages/engine-core/src/configuration/configurationRegistry.ts @@ -0,0 +1,301 @@ +import { + type Event, + Emitter, + type StringDictionary, + type JSONValueType, + jsonTypes, +} from '@alilc/lowcode-shared'; +import { uniq, isUndefined } from 'lodash-es'; +import { Registry } from '../extension/registry'; + +const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`; +const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g'); +export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`; +export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN); + +export function overrideIdentifiersFromKey(key: string): string[] { + const identifiers: string[] = []; + if (OVERRIDE_PROPERTY_REGEX.test(key)) { + let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key); + while (matches?.length) { + const identifier = matches[1].trim(); + if (identifier) { + identifiers.push(identifier); + } + matches = OVERRIDE_IDENTIFIER_REGEX.exec(key); + } + } + return uniq(identifiers); +} + +export interface IConfigurationRegistry { + /** + * Register a configuration to the registry. + */ + registerConfiguration(configuration: IConfigurationNode, validate?: boolean): void; + + /** + * Register multiple configurations to the registry. + */ + registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void; + + /** + * Deregister multiple configurations from the registry. + */ + deregisterConfigurations(configurations: IConfigurationNode[]): void; + + /** + * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values. + * Property or default value changes are not allowed. + */ + notifyConfigurationSchemaUpdated(): void; + /** + * Event that fires whenever a configuration has been + * registered. + */ + readonly onDidSchemaChange: Event; + /** + * Event that fires whenever a configuration has been + * registered. + */ + readonly onDidUpdateConfiguration: Event<{ + properties: ReadonlySet; + defaultsOverrides?: boolean; + }>; + + /** + * Returns all configuration nodes contributed to this registry. + */ + getConfigurations(): IConfigurationNode[]; + /** + * Returns all configurations settings of all configuration nodes contributed to this registry. + */ + getConfigurationProperties(): StringDictionary; + /** + * Returns all excluded configurations settings of all configuration nodes contributed to this registry. + */ + getExcludedConfigurationProperties(): StringDictionary; +} + +export interface IConfigurationNode { + id?: string; + order?: number; + type?: JSONValueType | JSONValueType[]; + title?: string; + description?: string; + properties?: StringDictionary; + allOf?: IConfigurationNode[]; + extensionInfo?: IExtensionInfo; +} + +export interface IConfigurationPropertySchema { + type?: JSONValueType; + default?: any; + tags?: string[]; + included?: boolean; + deprecated?: boolean; + deprecationMessage?: string; +} + +export interface IExtensionInfo { + id: string; + displayName?: string; +} + +export type ConfigurationDefaultValueSource = IExtensionInfo | Map; + +export interface IConfigurationDefaults { + overrides: StringDictionary; + source?: IExtensionInfo; +} + +export interface IRegisteredConfigurationPropertySchema extends IConfigurationPropertySchema { + source?: IExtensionInfo; // Source of the Property + defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value +} + +export class ConfigurationRegistry implements IConfigurationRegistry { + private configurationContributors: IConfigurationNode[]; + private configurationProperties: StringDictionary; + private excludedConfigurationProperties: StringDictionary; + + private schemaChangeEmitter = new Emitter(); + private updateConfigurationEmitter = new Emitter<{ + properties: ReadonlySet; + defaultsOverrides?: boolean; + }>(); + + constructor() { + this.configurationContributors = []; + this.configurationProperties = {}; + this.excludedConfigurationProperties = {}; + } + + registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void { + this.registerConfigurations([configuration], validate); + } + + registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void { + const properties = new Set(); + this.doRegisterConfigurations(configurations, validate, properties); + + this.schemaChangeEmitter.emit(); + this.updateConfigurationEmitter.emit({ properties }); + } + + private doRegisterConfigurations( + configurations: IConfigurationNode[], + validate: boolean, + bucket: Set, + ): void { + configurations.forEach((configuration) => { + this.validateAndRegisterProperties( + configuration, + validate, + configuration.extensionInfo, + bucket, + ); + + this.configurationContributors.push(configuration); + }); + } + + private validateAndRegisterProperties( + configuration: IConfigurationNode, + validate: boolean = true, + extensionInfo: IExtensionInfo | undefined, + bucket: Set, + ): void { + const properties = configuration.properties; + if (properties) { + for (const key in properties) { + const property: IRegisteredConfigurationPropertySchema = properties[key]; + + if (validate && this.validateProperty(key)) { + continue; + } + + property.source = extensionInfo; + + // update default value + this.updatePropertyDefaultValue(property); + + // Add to properties maps + // Property is included by default if 'included' is unspecified + if ( + Object.prototype.hasOwnProperty.call(properties[key], 'included') && + !properties[key].included + ) { + this.excludedConfigurationProperties[key] = properties[key]; + continue; + } + + this.configurationProperties[key] = properties[key]; + bucket.add(key); + } + + const subNodes = configuration.allOf; + if (subNodes) { + for (const node of subNodes) { + this.validateAndRegisterProperties(node, validate, extensionInfo, bucket); + } + } + } + } + + private validateProperty(property: string): string | null { + if (!property.trim()) { + return 'Cannot register an empty property'; + } + if (OVERRIDE_PROPERTY_REGEX.test(property)) { + return `Cannot register ${property}. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.`; + } + if (this.configurationProperties[property] !== undefined) { + return `Cannot register ${property}. This property is already registered.`; + } + return null; + } + + private updatePropertyDefaultValue(property: IRegisteredConfigurationPropertySchema): void { + let defaultValue = undefined; + let defaultSource = undefined; + + if (isUndefined(defaultValue)) { + defaultValue = property.default; + defaultSource = undefined; + } + if (isUndefined(defaultValue)) { + defaultValue = jsonTypes.getDefaultValue(property.type); + } + + property.default = defaultValue; + property.defaultValueSource = defaultSource; + } + + deregisterConfigurations(configurations: IConfigurationNode[]): void { + const properties = new Set(); + this.doDeregisterConfigurations(configurations, properties); + + this.schemaChangeEmitter.emit(); + this.updateConfigurationEmitter.emit({ properties }); + } + + private doDeregisterConfigurations( + configurations: IConfigurationNode[], + bucket: Set, + ): void { + const deregisterConfiguration = (configuration: IConfigurationNode) => { + if (configuration.properties) { + for (const key in configuration.properties) { + bucket.add(key); + delete this.configurationProperties[key]; + } + } + configuration.allOf?.forEach((node) => deregisterConfiguration(node)); + }; + + for (const configuration of configurations) { + deregisterConfiguration(configuration); + + const index = this.configurationContributors.indexOf(configuration); + if (index !== -1) { + this.configurationContributors.splice(index, 1); + } + } + } + + notifyConfigurationSchemaUpdated(): void { + this.schemaChangeEmitter.emit(); + } + + getConfigurationProperties(): StringDictionary { + return this.configurationProperties; + } + + getConfigurations(): IConfigurationNode[] { + return this.configurationContributors; + } + + getExcludedConfigurationProperties(): StringDictionary { + return this.excludedConfigurationProperties; + } + + onDidUpdateConfiguration( + fn: (change: { + properties: ReadonlySet; + defaultsOverrides?: boolean | undefined; + }) => void, + ) { + return this.updateConfigurationEmitter.on(fn); + } + + onDidSchemaChange(fn: () => void) { + return this.schemaChangeEmitter.on(fn); + } +} + +export const Extension = { + Configuration: 'base.contributions.configuration', +}; + +Registry.add(Extension.Configuration, new ConfigurationRegistry()); diff --git a/packages/engine-core/src/configuration/configurationService.ts b/packages/engine-core/src/configuration/configurationService.ts new file mode 100644 index 000000000..466312a09 --- /dev/null +++ b/packages/engine-core/src/configuration/configurationService.ts @@ -0,0 +1,63 @@ +import { createDecorator, Provide, type Event } from '@alilc/lowcode-shared'; +import { IConfigurationOverrides, IConfigurationUpdateOverrides } from './configuration'; + +export interface IConfigurationChangeEvent { + readonly affectedKeys: ReadonlySet; + readonly change: IConfigurationChange; + + affectsConfiguration(configuration: string, overrides?: string[]): boolean; +} + +export interface IConfigurationChange { + keys: string[]; + overrides: [string, string[]][]; +} + +export interface IConfigurationService { + /** + * Fetches the value of the section for the given overrides. + * Value can be of native type or an object keyed off the section name. + * + * @param section - Section of the configuration. Can be `null` or `undefined`. + * @param overrides - Overrides that has to be applied while fetching + * + */ + getValue(): T; + getValue(section: string): T; + getValue(overrides: IConfigurationOverrides): T; + getValue(section: string, overrides: IConfigurationOverrides): T; + + /** + * Update a configuration value. + * + * Use `overrides` to update the configuration for a resource or for override identifiers or both. + * + * Passing a resource through overrides will update the configuration in the workspace folder containing that resource. + * + * *Note 1:* Updating configuration to a default value will remove the configuration from the requested target. If not target is passed, it will be removed from all writeable targets. + * + * *Note 2:* Use `undefined` value to remove the configuration from the given target. If not target is passed, it will be removed from all writeable targets. + * + * @param key setting to be updated + * @param value The new value + */ + updateValue(key: string, value: any): Promise; + updateValue( + key: string, + value: any, + overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, + ): Promise; + + inspect(key: string, overrides?: IConfigurationOverrides): Readonly; + + reloadConfiguration(): Promise; + + keys(): string[]; + + onDidChangeConfiguration: Event; +} + +export const IConfigurationService = createDecorator('configurationService'); + +@Provide(IConfigurationService) +export class ConfigurationService implements IConfigurationService {} diff --git a/packages/engine-core/src/configuration/index.ts b/packages/engine-core/src/configuration/index.ts new file mode 100644 index 000000000..7efdece07 --- /dev/null +++ b/packages/engine-core/src/configuration/index.ts @@ -0,0 +1,3 @@ +export * from './configurationModel'; +export * from './configurationRegistry'; +export * from './configuration'; diff --git a/packages/engine-core/src/extension/index.ts b/packages/engine-core/src/extension/index.ts new file mode 100644 index 000000000..585b667a0 --- /dev/null +++ b/packages/engine-core/src/extension/index.ts @@ -0,0 +1 @@ +export * from './registry'; diff --git a/packages/engine-core/src/extension/registry.ts b/packages/engine-core/src/extension/registry.ts new file mode 100644 index 000000000..153cfdba8 --- /dev/null +++ b/packages/engine-core/src/extension/registry.ts @@ -0,0 +1,39 @@ +export interface IRegistry { + /** + * Adds the extension functions and properties defined by data to the + * platform. The provided id must be unique. + * @param id a unique identifier + * @param data a contribution + */ + add(id: string, data: any): void; + + /** + * Returns true iff there is an extension with the provided id. + * @param id an extension identifier + */ + knows(id: string): boolean; + + /** + * Returns the extension functions and properties defined by the specified key or null. + * @param id an extension identifier + */ + as(id: string): T; +} + +class RegistryImpl implements IRegistry { + private readonly data = new Map(); + + public add(id: string, data: any): void { + this.data.set(id, data); + } + + public knows(id: string): boolean { + return this.data.has(id); + } + + public as(id: string): any { + return this.data.get(id) || null; + } +} + +export const Registry: IRegistry = new RegistryImpl(); diff --git a/packages/engine-core/src/index.ts b/packages/engine-core/src/index.ts index e69de29bb..3400c6181 100644 --- a/packages/engine-core/src/index.ts +++ b/packages/engine-core/src/index.ts @@ -0,0 +1,2 @@ +export * from './configuration'; +export * from './extension'; diff --git a/packages/engine-core/src/lifeCycle/lifeCycleService.ts b/packages/engine-core/src/lifeCycle/lifeCycleService.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/engine/src/common/plugin/context.ts b/packages/engine-core/src/plugin/context.ts similarity index 93% rename from packages/engine/src/common/plugin/context.ts rename to packages/engine-core/src/plugin/context.ts index f1da5af4c..8309560e1 100644 --- a/packages/engine/src/common/plugin/context.ts +++ b/packages/engine-core/src/plugin/context.ts @@ -1,4 +1,4 @@ -import { createEventBus, EventBus } from '@alilc/lowcode-shared'; +import { EventEmitter } from '@alilc/lowcode-shared'; import type { PluginMeta, PluginPreferenceValue, PluginDeclaration } from './types'; import { PluginManager } from './manager'; @@ -21,7 +21,7 @@ export class PluginContext> { public pluginName: string; - public pluginEvent: EventBus; + public pluginEvent: EventEmitter; public preference: PluginPreferenceMananger; @@ -30,7 +30,7 @@ export class PluginContext> { pluginManager: PluginManager, ) { this.pluginName = options.pluginName; - this.pluginEvent = createEventBus(this.pluginName); + this.pluginEvent = new EventEmitter(this.pluginName); this.#pluginManager = pluginManager; if (options.meta) this.#meta = options.meta; diff --git a/packages/engine/src/common/plugin/index.ts b/packages/engine-core/src/plugin/index.ts similarity index 100% rename from packages/engine/src/common/plugin/index.ts rename to packages/engine-core/src/plugin/index.ts diff --git a/packages/engine/src/common/plugin/manager.ts b/packages/engine-core/src/plugin/manager.ts similarity index 100% rename from packages/engine/src/common/plugin/manager.ts rename to packages/engine-core/src/plugin/manager.ts diff --git a/packages/engine/src/common/plugin/runtime.ts b/packages/engine-core/src/plugin/runtime.ts similarity index 100% rename from packages/engine/src/common/plugin/runtime.ts rename to packages/engine-core/src/plugin/runtime.ts diff --git a/packages/engine/src/common/plugin/types.ts b/packages/engine-core/src/plugin/types.ts similarity index 100% rename from packages/engine/src/common/plugin/types.ts rename to packages/engine-core/src/plugin/types.ts diff --git a/packages/engine/src/common/plugin/utils.ts b/packages/engine-core/src/plugin/utils.ts similarity index 100% rename from packages/engine/src/common/plugin/utils.ts rename to packages/engine-core/src/plugin/utils.ts diff --git a/packages/engine-core/src/resource.ts b/packages/engine-core/src/resource.ts new file mode 100644 index 000000000..eebb79a04 --- /dev/null +++ b/packages/engine-core/src/resource.ts @@ -0,0 +1,5 @@ +import { Assets } from '@alilc/lowcode-shared'; + +export interface IResourceManagementService { + setAssets(assets: Assets): void; +} diff --git a/packages/engine-core/src/workbench/layout/layoutService.ts b/packages/engine-core/src/workbench/layout/layoutService.ts new file mode 100644 index 000000000..4bac019e7 --- /dev/null +++ b/packages/engine-core/src/workbench/layout/layoutService.ts @@ -0,0 +1,6 @@ +export interface ILayoutService { + /** + * Main container of the application. + */ + mainContainer: HTMLElement; +} diff --git a/packages/engine-core/src/workbench/window/windowService.ts b/packages/engine-core/src/workbench/window/windowService.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/engine-core/src/workspace/workspace.ts b/packages/engine-core/src/workspace/workspace.ts new file mode 100644 index 000000000..13811bc3b --- /dev/null +++ b/packages/engine-core/src/workspace/workspace.ts @@ -0,0 +1,12 @@ +import { createDecorator, Provide } from '@alilc/lowcode-shared'; + +export interface IWorkspaceService { + mount(container: HTMLElement): void; +} + +export const IWorkspaceService = createDecorator('workspaceService'); + +@Provide(IWorkspaceService) +export class WorkspaceService implements IWorkspaceService { + mount(container: HTMLElement): void {} +} diff --git a/packages/engine/package.json b/packages/engine/package.json index 7f70d3f18..340a613a6 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -29,6 +29,7 @@ }, "license": "MIT", "dependencies": { + "@alilc/lowcode-engine-core": "workspace:*", "@alilc/lowcode-shared": "workspace:*", "lodash-es": "^4.17.21", "react": "^18.2.0", diff --git a/packages/engine/src/common/command.ts b/packages/engine/src/common/command.ts deleted file mode 100644 index 25e852756..000000000 --- a/packages/engine/src/common/command.ts +++ /dev/null @@ -1,50 +0,0 @@ -export interface Command { - /** - * 命令名称 - * 命名规则:commandName - * 使用规则:commandScope:commandName (commandScope 在插件 meta 中定义,用于区分不同插件的命令) - */ - name: string; - - /** - * 命令描述 - */ - description?: string; - - /** - * 命令处理函数 - */ - handler: (...args: any[]) => void | Promise; -} - -export class Commands { - /** - * 注册一个新命令及其处理函数 - */ - registerCommand(command: Command): void; - - /** - * 注销一个已存在的命令 - */ - unregisterCommand(name: string): void; - - /** - * 通过名称和给定参数执行一个命令,会校验参数是否符合命令定义 - */ - executeCommand(name: string, args?: IPublicTypeCommandHandlerArgs): void; - - /** - * 批量执行命令,执行完所有命令后再进行一次重绘,历史记录中只会记录一次 - */ - batchExecuteCommand(commands: { name: string; args?: IPublicTypeCommandHandlerArgs }[]): void; - - /** - * 列出所有已注册的命令 - */ - listCommands(): IPublicTypeListCommand[]; - - /** - * 注册错误处理回调函数 - */ - onCommandError(callback: (name: string, error: Error) => void): void; -} diff --git a/packages/engine/src/main.ts b/packages/engine/src/main.ts index 54c392d6f..c5bc51437 100644 --- a/packages/engine/src/main.ts +++ b/packages/engine/src/main.ts @@ -1,4 +1,5 @@ import { InstantiationService } from '@alilc/lowcode-shared'; +import { IConfigurationService, IWorkspaceService } from '@alilc/lowcode-engine-core'; export class MainEngineApplication { instantiationService = new InstantiationService(); @@ -7,5 +8,12 @@ export class MainEngineApplication { this.instantiationService.bootstrapModules(); } - startup(container: HTMLElement): void {} + startup(container: HTMLElement): void { + const configurationService = this.instantiationService.get(IConfigurationService); + const workspaceService = this.instantiationService.get(IWorkspaceService); + + configurationService.initialize(); + + workspaceService.mount(container); + } } diff --git a/packages/engine/src/services/command/commandService.ts b/packages/engine/src/services/command/commandService.ts deleted file mode 100644 index 14a7f1557..000000000 --- a/packages/engine/src/services/command/commandService.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createDecorator, Provide } from '@alilc/lowcode-shared'; - -export interface ICommandService {} - -export const ICommandService = createDecorator('commandService'); - -@Provide(ICommandService) -export class CommandService implements ICommandService {} diff --git a/packages/engine/src/services/configuration/configuration.ts b/packages/engine/src/services/configuration/configuration.ts deleted file mode 100644 index 06d1abe48..000000000 --- a/packages/engine/src/services/configuration/configuration.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { get as lodashGet, isPlainObject, cloneDeep } from 'lodash-es'; -import { type PlainObject } from '@alilc/lowcode-shared'; -import { invariant } from '@alilc/lowcode-shared'; - -export class Configuration { - private config: Config; - - private setterValidator: ((key: K, value: Config[K]) => boolean | string) | undefined; - - private waits = new Map< - K, - { - once?: boolean; - resolve: (data: any) => void; - }[] - >(); - - constructor(config: Config, setterValidator?: (key: K, value: Config[K]) => boolean | string) { - invariant(config, 'config must exist', 'Configuration'); - - this.config = cloneDeep(config); - - if (setterValidator) { - invariant( - typeof setterValidator === 'function', - 'setterValidator must be a function', - 'Configuration', - ); - this.setterValidator = setterValidator; - } - } - - /** - * 判断指定 key 是否有值 - * @param key - */ - has(key: K): boolean { - return this.config[key] !== undefined; - } - /** - * 获取指定 key 的值 - * @param key - * @param defaultValue - */ - get(key: K, defaultValue?: T): T | undefined { - return lodashGet(this.config, key, defaultValue); - } - /** - * 设置指定 key 的值 - * @param key - * @param value - */ - set(key: K, value: any) { - if (this.setterValidator) { - const valid = this.setterValidator(key, value); - - invariant( - valid === false || typeof valid === 'string', - `failed to config ${key.toString()}, only predefined options can be set under strict mode, predefined options: ${valid ? valid : ''}`, - 'Configuration', - ); - } - - this.config[key] = value; - this.notifyGot(key); - } - /** - * 批量设值,set 的对象版本 - * @param config - */ - setConfig(config: Partial) { - if (isPlainObject(config)) { - Object.keys(config).forEach((key) => { - this.set(key as K, config[key]); - }); - } - } - /** - * 获取指定 key 的值,若此时还未赋值,则等待,若已有值,则直接返回值 - * 注:此函数返回 Promise 实例,只会执行(fullfill)一次 - * @param key - * @returns - */ - onceGot(key: K) { - const val = this.get(key); - if (val !== undefined) { - return Promise.resolve(val); - } - return new Promise((resolve) => { - this.setWait(key, resolve, true); - }); - } - /** - * 获取指定 key 的值,函数回调模式,若多次被赋值,回调会被多次调用 - * @param key - * @param fn - * @returns - */ - onGot(key: K, fn: (data: Config[K]) => void): () => void { - const val = this.config[key]; - if (val !== undefined) { - fn(val); - } - this.setWait(key, fn); - return () => { - this.delWait(key, fn); - }; - } - - private notifyGot(key: K): void { - let waits = this.waits.get(key); - if (!waits) { - return; - } - waits = waits.slice().reverse(); - let i = waits.length; - while (i--) { - waits[i].resolve(this.get(key)); - if (waits[i].once) { - waits.splice(i, 1); - } - } - if (waits.length > 0) { - this.waits.set(key, waits); - } else { - this.waits.delete(key); - } - } - - private setWait(key: K, resolve: (data: any) => void, once?: boolean) { - const waits = this.waits.get(key); - if (waits) { - waits.push({ resolve, once }); - } else { - this.waits.set(key, [{ resolve, once }]); - } - } - - private delWait(key: K, fn: any) { - const waits = this.waits.get(key); - if (!waits) { - return; - } - let i = waits.length; - while (i--) { - if (waits[i].resolve === fn) { - waits.splice(i, 1); - } - } - if (waits.length < 1) { - this.waits.delete(key); - } - } -} diff --git a/packages/engine/src/services/configuration/configurationService.ts b/packages/engine/src/services/configuration/configurationService.ts deleted file mode 100644 index d41400e76..000000000 --- a/packages/engine/src/services/configuration/configurationService.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createDecorator, Provide } from '@alilc/lowcode-shared'; - -export interface IConfigurationService {} - -export const IConfigurationService = createDecorator('configurationService'); - -@Provide(IConfigurationService) -export class ConfigurationService implements IConfigurationService {} diff --git a/packages/engine/src/themeService.ts b/packages/engine/src/themeService.ts new file mode 100644 index 000000000..5f2d47d6e --- /dev/null +++ b/packages/engine/src/themeService.ts @@ -0,0 +1,28 @@ +import { type Event, type EventListener, createDecorator, Provide } from '@alilc/lowcode-shared'; + +export interface ITheme { + type: string; + + value: string; +} + +export interface IThemeService { + getTheme(): ITheme; + + onDidColorThemeChange: Event<[ITheme]>; +} + +export const IThemeService = createDecorator('themeService'); + +@Provide(IThemeService) +export class ThemeService implements IThemeService { + private activeTheme: ITheme; + + getTheme(): ITheme { + return this.activeTheme; + } + + onDidColorThemeChange(listener: EventListener<[ITheme]>) { + return () => {}; + } +} diff --git a/packages/plugin-command/package.json b/packages/plugin-command/package.json index adc092037..13fe0f254 100644 --- a/packages/plugin-command/package.json +++ b/packages/plugin-command/package.json @@ -34,13 +34,10 @@ "url": "https://github.com/alibaba/lowcode-engine/issues" }, "dependencies": { - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, "peerDependencies": { - "@alilc/lowcode-utils": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json index e6a8fcca3..20e102f2d 100644 --- a/packages/plugin-designer/package.json +++ b/packages/plugin-designer/package.json @@ -35,8 +35,7 @@ "author": "xiayang.xy", "dependencies": { "@alilc/lowcode-designer": "workspace:*", - "@alilc/lowcode-core": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", + "@alilc/lowcode-engine-core": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -48,8 +47,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "@alilc/lowcode-designer": "workspace:*", - "@alilc/lowcode-core": "workspace:*", - "@alilc/lowcode-utils": "workspace:*" + "@alilc/lowcode-engine-core": "workspace:*" }, "publishConfig": { "access": "public", diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index d7493b9c4..1a18869f8 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -30,9 +30,7 @@ }, "dependencies": { "@alifd/next": "^1.27.8", - "@alilc/lowcode-core": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", + "@alilc/lowcode-engine-core": "workspace:*", "classnames": "^2.5.1", "events": "^3.3.0", "react": "^18.2.0", @@ -45,9 +43,7 @@ }, "peerDependencies": { "@alifd/next": "^1.27.8", - "@alilc/lowcode-core": "workspace:*", - "@alilc/lowcode-types": "workspace:*", - "@alilc/lowcode-utils": "workspace:*", + "@alilc/lowcode-engine-core": "workspace:*", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/react-renderer/src/runtime/createComponent.tsx b/packages/react-renderer/src/runtime/createComponent.tsx index e95e6a949..03e300615 100644 --- a/packages/react-renderer/src/runtime/createComponent.tsx +++ b/packages/react-renderer/src/runtime/createComponent.tsx @@ -1,4 +1,8 @@ -import { invariant, isLowCodeComponentPackage, type Spec } from '@alilc/lowcode-shared'; +import { + invariant, + isLowCodeComponentPackage, + type ComponentTreeRoot, +} from '@alilc/lowcode-shared'; import { forwardRef, useRef, useEffect } from 'react'; import { isValidElementType } from 'react-is'; import { useRendererContext } from '../api/context'; @@ -64,7 +68,7 @@ export function getComponentByName( } export function createComponent( - schema: string | Spec.ComponentTreeRoot, + schema: string | ComponentTreeRoot, componentOptions: ComponentOptions = {}, ) { const { displayName = '__LowCodeComponent__', modelOptions } = componentOptions; diff --git a/packages/react-renderer/src/runtime/elements.tsx b/packages/react-renderer/src/runtime/elements.tsx index 702be02e8..48a798d8f 100644 --- a/packages/react-renderer/src/runtime/elements.tsx +++ b/packages/react-renderer/src/runtime/elements.tsx @@ -5,12 +5,12 @@ import { mapValue, } from '@alilc/lowcode-renderer-core'; import { - type PlainObject, - isJSExpression, - isJSI18nNode, - isJSFunction, - isJSSlot, - type Spec, + type StringDictionary, + specTypes, + type JSExpression, + type JSFunction, + type JSSlot, + type JSI18n, } from '@alilc/lowcode-shared'; import { type ComponentType, type ReactInstance, useMemo, createElement } from 'react'; import { useRendererContext } from '../api/context'; @@ -40,11 +40,11 @@ export function createElementByWidget( return rawNode; } - if (isJSExpression(rawNode)) { + if (specTypes.isJSExpression(rawNode)) { return ; } - if (isJSI18nNode(rawNode)) { + if (specTypes.isJSI18nNode(rawNode)) { return ; } @@ -55,7 +55,7 @@ export function createElementByWidget( // loop 为数组且为空的情况下 不渲染 if (Array.isArray(loop) && loop.length === 0) return null; - if (isJSExpression(loop)) { + if (specTypes.isJSExpression(loop)) { return ( isJSFunction(node) || isJSSlot(node), - (node: Spec.JSSlot | Spec.JSFunction) => { - if (isJSSlot(node)) { - const slot = node as Spec.JSSlot; + (node) => specTypes.isJSFunction(node) || specTypes.isJSSlot(node), + (node: JSSlot | JSFunction) => { + if (specTypes.isJSSlot(node)) { + const slot = node as JSSlot; if (slot.value) { const widgets = widget.model.buildWidgets( @@ -114,7 +114,7 @@ export function WidgetComponent(props: WidgetRendererProps) { return (...args: any[]) => { const params = slot.params!.reduce((prev, cur, idx) => { return (prev[cur] = args[idx]); - }, {} as PlainObject); + }, {} as StringDictionary); return widgets.map((n) => createElementByWidget( @@ -128,7 +128,7 @@ export function WidgetComponent(props: WidgetRendererProps) { return widgets.map((n) => createElementByWidget(n, codeRuntime, options)); } } - } else if (isJSFunction(node)) { + } else if (specTypes.isJSFunction(node)) { return widget.model.codeRuntime.resolve(node); } @@ -182,7 +182,7 @@ export function WidgetComponent(props: WidgetRendererProps) { ); } -function Text(props: { expr: Spec.JSExpression; codeRuntime: ICodeRuntime }) { +function Text(props: { expr: JSExpression; codeRuntime: ICodeRuntime }) { const text: string = useReactiveStore({ target: props.expr, getter: (obj) => { @@ -195,7 +195,7 @@ function Text(props: { expr: Spec.JSExpression; codeRuntime: ICodeRuntime }) { Text.displayName = 'Text'; -function I18nText(props: { i18n: Spec.JSI18n; codeRuntime: ICodeRuntime }) { +function I18nText(props: { i18n: JSI18n; codeRuntime: ICodeRuntime }) { const text: string = useReactiveStore({ target: props.i18n, getter: (obj) => { @@ -215,7 +215,7 @@ function LoopWidgetRenderer({ options, ...otherProps }: { - loop: Spec.JSExpression; + loop: JSExpression; widget: ReactWidget; codeRuntime: ICodeRuntime; options: ComponentOptions; diff --git a/packages/react-renderer/src/runtime/hooks/useReactiveStore.tsx b/packages/react-renderer/src/runtime/hooks/useReactiveStore.tsx index 0176694b3..0bb8e9b23 100644 --- a/packages/react-renderer/src/runtime/hooks/useReactiveStore.tsx +++ b/packages/react-renderer/src/runtime/hooks/useReactiveStore.tsx @@ -1,8 +1,8 @@ import { mapValue } from '@alilc/lowcode-renderer-core'; import { type AnyFunction, - type PlainObject, - isJSExpression, + type StringDictionary, + specTypes, computed, watch, invariant, @@ -12,23 +12,23 @@ import { produce } from 'immer'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; interface ReactiveOptions { - target: PlainObject; + target: StringDictionary; getter?: (obj: any) => any; valueGetter?: (expr: any) => any; filter?: (obj: any) => boolean; } -export interface ReactiveStore { +export interface ReactiveStore { value: Snapshot | null; onStateChange: AnyFunction | null; subscribe: (onStoreChange: () => void) => () => void; getSnapshot: () => Snapshot | null; } -function createReactiveStore( +function createReactiveStore( options: ReactiveOptions, ): ReactiveStore { - const { target, getter, filter = isJSExpression, valueGetter } = options; + const { target, getter, filter = specTypes.isJSExpression, valueGetter } = options; invariant( getter || valueGetter, diff --git a/packages/react-renderer/src/runtime/reactiveState.ts b/packages/react-renderer/src/runtime/reactiveState.ts index b833c5160..0ec8e5318 100644 --- a/packages/react-renderer/src/runtime/reactiveState.ts +++ b/packages/react-renderer/src/runtime/reactiveState.ts @@ -1,7 +1,7 @@ -import { signal, type PlainObject, type Spec } from '@alilc/lowcode-shared'; +import { signal, type StringDictionary, type InstanceStateApi } from '@alilc/lowcode-shared'; import { isPlainObject } from 'lodash-es'; -export function reactiveStateFactory(initState: PlainObject): Spec.InstanceStateApi { +export function reactiveStateFactory(initState: StringDictionary): InstanceStateApi { const proxyState = signal(initState); return { @@ -13,8 +13,8 @@ export function reactiveStateFactory(initState: PlainObject): Spec.InstanceState throw Error('newState mush be a object'); } - Object.keys(newState as PlainObject).forEach((key) => { - proxyState.value[key] = (newState as PlainObject)[key]; + Object.keys(newState as StringDictionary).forEach((key) => { + proxyState.value[key] = (newState as StringDictionary)[key]; }); }, }; diff --git a/packages/react-simulator-renderer/src.bak/README.md b/packages/react-simulator-renderer/src.bak/README.md deleted file mode 100644 index d218758d5..000000000 --- a/packages/react-simulator-renderer/src.bak/README.md +++ /dev/null @@ -1 +0,0 @@ -沙箱环境 diff --git a/packages/react-simulator-renderer/src.bak/builtin-components/builtin-components.ts b/packages/react-simulator-renderer/src.bak/builtin-components/builtin-components.ts deleted file mode 100644 index 01cdb57db..000000000 --- a/packages/react-simulator-renderer/src.bak/builtin-components/builtin-components.ts +++ /dev/null @@ -1,413 +0,0 @@ -import { ReactElement, createElement, ReactType } from 'react'; -import classNames from 'classnames'; - -const supportedEvents = [ - // MouseEvents - { - name: 'onClick', - description: '点击时', - }, - { - name: 'onDoubleClick', - description: '双击时', - }, - { - name: 'onMouseDown', - description: '鼠标按下', - }, - { - name: 'onMouseEnter', - description: '鼠标进入', - }, - { - name: 'onMouseMove', - description: '鼠标移动', - }, - { - name: 'onMouseOut', - description: '鼠标移出', - }, - { - name: 'onMouseOver', - description: '鼠标悬停', - }, - { - name: 'onMouseUp', - description: '鼠标松开', - }, - // Focus Events - { - name: 'onFocus', - description: '获得焦点', - snippet: '', - }, - { - name: 'onBlur', - description: '失去焦点', - snippet: '', - }, - // Form Events - { - name: 'onChange', - description: '值改变时', - snippet: '', - }, - { - name: 'onSelect', - description: '选择', - }, - { - name: 'onInput', - description: '输入', - snippet: '', - }, - { - name: 'onReset', - description: '重置', - snippet: '', - }, - { - name: 'onSubmit', - description: '提交', - snippet: '', - }, - // Clipboard Events - { - name: 'onCopy', - description: '复制', - snippet: '', - }, - { - name: 'onCut', - description: '剪切', - snippet: '', - }, - { - name: 'onPaste', - description: '粘贴', - snippet: '', - }, - - // Keyboard Events - { - name: 'onKeyDown', - description: '键盘按下', - snippet: '', - }, - { - name: 'onKeyPress', - description: '键盘按下并释放', - snippet: '', - }, - { - name: 'onKeyUp', - description: '键盘松开', - snippet: '', - }, - // Touch Events - { - name: 'onTouchCancel', - description: '触摸退出', - snippet: '', - }, - { - name: 'onTouchEnd', - description: '触摸结束', - snippet: '', - }, - { - name: 'onTouchMove', - description: '触摸移动', - snippet: '', - }, - { - name: 'onTouchStart', - description: '触摸开始', - snippet: '', - }, - // UI Events - { - name: 'onScroll', - description: '滚动', - snippet: '', - }, - { - name: 'onLoad', - description: '加载完毕', - snippet: '', - }, - { - name: 'onWheel', - description: '滚轮事件', - snippet: '', - }, - // Animation Events - { - name: 'onAnimationStart', - description: '动画开始', - }, - { - name: 'onAnimationEnd', - description: '动画结束', - }, -]; - -// eslint-disable-next-line func-call-spacing -const builtinComponents = new Map ReactElement>(); -function getBlockElement(tag: string): (props: any) => ReactElement { - if (builtinComponents.has(tag)) { - return builtinComponents.get(tag)!; - } - const mock = ({ className, children, ...rest }: any = {}) => { - const props = { - ...rest, - className: classNames('lc-block-container', className), - }; - return createElement(tag, props, children); - }; - - mock.metadata = { - componentName: tag, - // selfControlled: true, - configure: { - props: [], - events: { - supportedEvents, - }, - styles: { - supportClassName: true, - supportInlineStyle: true, - }, - component: { - ...metasMap[tag], - }, - }, - }; - - builtinComponents.set(tag, mock); - return mock; -} - -const HTMLBlock = [ - 'div', - 'p', - 'article', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'aside', - 'blockquote', - 'footer', - 'form', - 'header', - 'table', - 'tbody', - 'section', - 'ul', - 'li', -]; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const HTMLInlineBlock = ['a', 'b', 'span', 'em']; -export function getIntrinsicMock(tag: string): ReactType { - if (HTMLBlock.indexOf(tag) > -1) { - return getBlockElement(tag); - } - - return tag as any; -} - -const metasMap: any = { - div: { - isContainer: true, - nesting: { - ancestorBlacklist: 'p', - }, - }, - ul: { - isContainer: true, - nesting: { - childWhitelist: 'li', - }, - }, - p: { - isContainer: true, - nesting: { - ancestorBlacklist: 'button,p', - }, - }, - li: { - isContainer: true, - nesting: { - parentWhitelist: 'ui,ol', - }, - }, - span: { - isContainer: true, - selfControlled: true, - }, - a: { - isContainer: true, - nesting: { - ancestorBlacklist: 'a', - }, - }, - b: { - isContainer: true, - }, - strong: { - isContainer: true, - }, - em: { - isContainer: true, - }, - i: { - isContainer: true, - }, - form: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'form,button', - }, - }, - table: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - caption: { - isContainer: true, - selfControlled: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - select: { - isContainer: true, - selfControlled: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - button: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - input: { - isContainer: false, - nestingRule: { - ancestorBlacklist: 'button,h1,h2,h3,h4,h5,h6', - }, - }, - textarea: { - isContainer: false, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - image: { - isContainer: false, - }, - canvas: { - isContainer: false, - }, - br: { - isContainer: false, - }, - h1: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - h2: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - h3: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - h4: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - h5: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - h6: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - article: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - aside: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - footer: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - header: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - blockquote: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - address: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - section: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'p,h1,h2,h3,h4,h5,h6,button', - }, - }, - summary: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, - nav: { - isContainer: true, - nestingRule: { - ancestorBlacklist: 'button', - }, - }, -}; diff --git a/packages/react-simulator-renderer/src.bak/builtin-components/leaf.tsx b/packages/react-simulator-renderer/src.bak/builtin-components/leaf.tsx deleted file mode 100644 index 69590fb71..000000000 --- a/packages/react-simulator-renderer/src.bak/builtin-components/leaf.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Component } from 'react'; - -class Leaf extends Component { - static displayName = 'Leaf'; - - static componentMetadata = { - componentName: 'Leaf', - configure: { - props: [{ - name: 'children', - setter: 'StringSetter', - }], - // events/className/style/general/directives - supports: false, - }, - }; - - render() { - const { children } = this.props; - return children; - } -} - -export default Leaf; diff --git a/packages/react-simulator-renderer/src.bak/builtin-components/slot.tsx b/packages/react-simulator-renderer/src.bak/builtin-components/slot.tsx deleted file mode 100644 index 2dd44b978..000000000 --- a/packages/react-simulator-renderer/src.bak/builtin-components/slot.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Component } from 'react'; - -class Slot extends Component { - static displayName = 'Slot'; - - static componentMetadata = { - componentName: 'Slot', - configure: { - props: [ - { - name: '___title', - title: { - type: 'i18n', - 'en-US': 'Slot Title', - 'zh-CN': '插槽标题', - }, - setter: 'StringSetter', - defaultValue: '插槽容器', - }, - { - name: '___params', - title: { - type: 'i18n', - 'en-US': 'Slot Params', - 'zh-CN': '插槽入参', - }, - setter: { - componentName: 'ArraySetter', - props: { - itemSetter: { - componentName: 'StringSetter', - props: { - placeholder: { - type: 'i18n', - 'zh-CN': '参数名称', - 'en-US': 'Argument Name', - }, - }, - }, - }, - }, - }, - ], - component: { - isContainer: true, - }, - // events/className/style/general/directives - supports: false, - }, - }; - - render() { - const { children } = this.props; - return <>{children}; - } -} - -export default Slot; diff --git a/packages/react-simulator-renderer/src.bak/host.ts b/packages/react-simulator-renderer/src.bak/host.ts deleted file mode 100644 index c5cf2e3e1..000000000 --- a/packages/react-simulator-renderer/src.bak/host.ts +++ /dev/null @@ -1,4 +0,0 @@ -// NOTE: 仅做类型标注,切勿做其它用途 -import { BuiltinSimulatorHost } from '@alilc/lowcode-designer'; - -export const host: BuiltinSimulatorHost = (window as any).LCSimulatorHost; diff --git a/packages/react-simulator-renderer/src.bak/index.ts b/packages/react-simulator-renderer/src.bak/index.ts deleted file mode 100644 index 5f2ecae78..000000000 --- a/packages/react-simulator-renderer/src.bak/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { runInAction } from 'mobx'; -import renderer from './renderer'; - -if (typeof window !== 'undefined') { - (window as any).SimulatorRenderer = renderer; -} - -window.addEventListener('beforeunload', () => { - runInAction(() => { - (window as any).LCSimulatorHost = null; - renderer.dispose?.(); - (window as any).SimulatorRenderer = null; - (window as any).ReactDOM.unmountComponentAtNode(document.getElementById('app')); - }); -}); - -export default renderer; diff --git a/packages/react-simulator-renderer/src.bak/locale/en-US.json b/packages/react-simulator-renderer/src.bak/locale/en-US.json deleted file mode 100644 index ac864c0a2..000000000 --- a/packages/react-simulator-renderer/src.bak/locale/en-US.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Drag and drop components or templates here": "Drag and drop components or templates here", - "Locked elements and child elements cannot be edited": "Locked elements and child elements cannot be edited" -} \ No newline at end of file diff --git a/packages/react-simulator-renderer/src.bak/locale/index.ts b/packages/react-simulator-renderer/src.bak/locale/index.ts deleted file mode 100644 index 5f4ef0150..000000000 --- a/packages/react-simulator-renderer/src.bak/locale/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createElement } from 'react'; -import enUS from './en-US.json'; -import zhCN from './zh-CN.json'; - -const instance: Record> = { - 'zh-CN': zhCN as Record, - 'en-US': enUS as Record, -}; - -export function createIntl(locale: string = 'zh-CN') { - const intl = (id: string) => { - return instance[locale]?.[id] || id; - }; - - const intlNode = (id: string) => createElement('span', instance[locale]?.[id] || id); - - return { - intl, - intlNode, - }; -} diff --git a/packages/react-simulator-renderer/src.bak/locale/zh-CN.json b/packages/react-simulator-renderer/src.bak/locale/zh-CN.json deleted file mode 100644 index 74bb821dd..000000000 --- a/packages/react-simulator-renderer/src.bak/locale/zh-CN.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Drag and drop components or templates here": "拖拽组件或模板到这里", - "Locked elements and child elements cannot be edited": "锁定元素及子元素无法编辑" -} \ No newline at end of file diff --git a/packages/react-simulator-renderer/src.bak/renderer-view.tsx b/packages/react-simulator-renderer/src.bak/renderer-view.tsx deleted file mode 100644 index aa1683cd2..000000000 --- a/packages/react-simulator-renderer/src.bak/renderer-view.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import { ReactInstance, Fragment, Component, createElement } from 'react'; -import { Router, Route, Switch } from 'react-router'; -import cn from 'classnames'; -import { Node } from '@alilc/lowcode-designer'; -import LowCodeRenderer from '@alilc/lowcode-react-renderer'; -import { observer } from 'mobx-react'; -import { getClosestNode, isFromVC, isReactComponent } from '@alilc/lowcode-utils'; -import { GlobalEvent } from '@alilc/lowcode-types'; -import { SimulatorRendererContainer, DocumentInstance } from './renderer'; -import { host } from './host'; -import { isRendererDetached } from './utils/misc'; -import './renderer.less'; -import { createIntl } from './locale'; - -// patch cloneElement avoid lost keyProps -const originCloneElement = window.React.cloneElement; -(window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}, ...rest: any[]) => { - if (child.ref && props.ref) { - const dRef = props.ref; - const cRef = child.ref; - props.ref = (x: any) => { - if (cRef) { - if (typeof cRef === 'function') { - cRef(x); - } else { - try { - cRef.current = x; - } catch (e) { - console.error(e); - } - } - } - if (dRef) { - if (typeof dRef === 'function') { - dRef(x); - } else { - try { - dRef.current = x; - } catch (e) { - console.error(e); - } - } - } - }; - } - return originCloneElement(child, props, ...rest); -}; - -export default class SimulatorRendererView extends Component<{ rendererContainer: SimulatorRendererContainer }> { - render() { - const { rendererContainer } = this.props; - return ( - - - - - - ); - } -} - -@observer -export class Routes extends Component<{ rendererContainer: SimulatorRendererContainer }> { - render() { - const { rendererContainer } = this.props; - return ( - - {rendererContainer.documentInstances.map((instance) => { - return ( - } - /> - ); - })} - - ); - } -} -function ucfirst(s: string) { - return s.charAt(0).toUpperCase() + s.substring(1); -} -function getDeviceView(view: any, device: string, mode: string) { - if (!view || typeof view === 'string') { - return view; - } - - // compatible vision Mobile | Preview - device = ucfirst(device); - if (device === 'Mobile' && view.hasOwnProperty(device)) { - view = view[device]; - } - mode = ucfirst(mode); - if (mode === 'Preview' && view.hasOwnProperty(mode)) { - view = view[mode]; - } - return view; -} - -@observer -class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }> { - render() { - const { rendererContainer, children } = this.props; - const { layout } = rendererContainer; - if (layout) { - const { Component, props, componentName } = layout; - if (Component) { - return {children}; - } - if (componentName && rendererContainer.getComponent(componentName)) { - return createElement( - rendererContainer.getComponent(componentName), - { - ...props, - rendererContainer, - key: 'layout', - }, - [children], - ); - } - } - - return {children}; - } -} - -@observer -class Renderer extends Component<{ - rendererContainer: SimulatorRendererContainer; - documentInstance: DocumentInstance; -}> { - startTime: number | null = null; - schemaChangedSymbol = false; - - componentDidUpdate() { - this.recordTime(); - } - - recordTime() { - if (this.startTime) { - const time = Date.now() - this.startTime; - const nodeCount = host.designer.currentDocument?.getNodeCount?.(); - host.designer.editor?.eventBus.emit(GlobalEvent.Node.Rerender, { - componentName: 'Renderer', - type: 'All', - time, - nodeCount, - }); - } - } - - componentDidMount() { - this.recordTime(); - } - - getSchemaChangedSymbol = () => { - return this.schemaChangedSymbol; - }; - - setSchemaChangedSymbol = (symbol: boolean) => { - this.schemaChangedSymbol = symbol; - }; - - render() { - const { documentInstance, rendererContainer: renderer } = this.props; - const { container, document } = documentInstance; - const { designMode, device, locale } = container; - const messages = container.context?.utils?.i18n?.messages || {}; - this.startTime = Date.now(); - this.schemaChangedSymbol = false; - - if (!container.autoRender || isRendererDetached()) { - return null; - } - - const { intl } = createIntl(locale); - - return ( - documentInstance.getNode(id) as Node} - rendererName="PageRenderer" - thisRequiredInJSE={host.thisRequiredInJSE} - notFoundComponent={host.notFoundComponent} - faultComponent={host.faultComponent} - faultComponentMap={host.faultComponentMap} - customCreateElement={(Component: any, props: any, children: any) => { - const { __id, ...viewProps } = props; - viewProps.componentId = __id; - const leaf = documentInstance.getNode(__id) as Node; - if (isFromVC(leaf?.componentMeta)) { - viewProps._leaf = leaf.internalToShellNode(); - } - viewProps._componentName = leaf?.componentName; - // 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动 - if ( - !viewProps.dataSource && - leaf?.isContainer() && - (children == null || (Array.isArray(children) && !children.length)) && - (!viewProps.style || Object.keys(viewProps.style).length === 0) - ) { - let defaultPlaceholder = intl('Drag and drop components or templates here'); - const lockedNode = getClosestNode(leaf, (node) => { - return node?.getExtraProp('isLocked')?.getValue() === true; - }); - if (lockedNode) { - defaultPlaceholder = intl('Locked elements and child elements cannot be edited'); - } - children = ( -
- {viewProps.placeholder || defaultPlaceholder} -
- ); - } - if (viewProps._componentName === 'a') { - delete viewProps.href; - } - // FIXME: 渲染仍有问题 - if (viewProps._componentName === 'Menu') { - Object.assign(viewProps, { - _componentName: 'Menu', - className: '_css_pesudo_menu_kbrzyh0f', - context: { VE: (window as any).VisualEngine }, - direction: undefined, - events: { ignored: true }, - fieldId: 'menu_kbrzyh0f', - footer: '', - header: '', - mode: 'inline', - onItemClick: { ignored: true }, - onSelect: { ignored: true }, - popupAlign: 'follow', - selectMode: false, - triggerType: 'click', - }); - } - - if (!isReactComponent(Component)) { - console.error(`${viewProps._componentName} is not a react component!`); - return null; - } - - return createElement( - getDeviceView(Component, device, designMode), - viewProps, - leaf?.isContainer() ? (children == null ? [] : Array.isArray(children) ? children : [children]) : children, - ); - }} - __host={host} - __container={container} - onCompGetRef={(schema: any, ref: ReactInstance | null) => { - documentInstance.mountInstance(schema.id, ref); - }} - enableStrictNotFoundMode={host.enableStrictNotFoundMode} - /> - ); - } -} diff --git a/packages/react-simulator-renderer/src.bak/renderer.less b/packages/react-simulator-renderer/src.bak/renderer.less deleted file mode 100644 index 8f0119353..000000000 --- a/packages/react-simulator-renderer/src.bak/renderer.less +++ /dev/null @@ -1,95 +0,0 @@ -body, html { - display: block; - background: white; - padding: 0; - margin: 0; -} - -html.engine-design-mode { - padding-bottom: 0; -} - -html.engine-cursor-move, html.engine-cursor-move * { - cursor: grabbing !important; -} - -html.engine-cursor-copy, html.engine-cursor-copy * { - cursor: copy !important; -} - -html.engine-cursor-ew-resize, html.engine-cursor-ew-resize * { - cursor: ew-resize !important; -} - -::-webkit-scrollbar { - width: 5px; - height: 5px; -} - -::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.3); - border-radius: 5px; -} - -.lc-container { - &:empty { - background: #f2f3f5; - color: #a7b1bd; - outline: 1px dashed rgba(31, 56, 88, 0.2); - outline-offset: -1px !important; - height: 66px; - max-height: 100%; - min-width: 140px; - text-align: center; - overflow: hidden; - display: flex; - align-items: center; - &:before { - content: '\62D6\62FD\7EC4\4EF6\6216\6A21\677F\5230\8FD9\91CC'; - font-size: 14px; - z-index: 1; - width: 100%; - white-space: nowrap; - } - } -} - -.lc-container-placeholder { - min-height: 60px; - height: 100%; - width: 100%; - background-color: rgb(240, 240, 240); - border: 1px dotted; - color: rgb(167, 177, 189); - display: flex; - align-items: center; - justify-content: center; - font-size: 14px; - - &.lc-container-locked { - background: #eccfcf; - } -} - -body.engine-document { - &:after, &:before { - content: ""; - display: table; - } - &:after { - clear: both; - } -} - -.engine-live-editing { - cursor: text; - outline: none; - box-shadow: 0 0 0 2px rgb(102, 188, 92); - user-select: text; -} - -#app { - height: 100vh; -} - - diff --git a/packages/react-simulator-renderer/src.bak/renderer.ts b/packages/react-simulator-renderer/src.bak/renderer.ts deleted file mode 100644 index 08e078caa..000000000 --- a/packages/react-simulator-renderer/src.bak/renderer.ts +++ /dev/null @@ -1,637 +0,0 @@ -import React, { createElement, ReactInstance } from 'react'; -import { render as reactRender } from 'react-dom'; -import { host } from './host'; -import SimulatorRendererView from './renderer-view'; -import { computed, observable as observable, untracked, makeObservable, configure } from 'mobx'; -import { getClientRects } from './utils/get-client-rects'; -import { reactFindDOMNodes, getReactInternalFiber } from './utils/react-find-dom-nodes'; -import { - Asset, - isElement, - cursor, - setNativeSelection, - buildComponents, - getSubComponent, - compatibleLegaoSchema, - isPlainObject, - AssetLoader, - getProjectUtils, -} from '@alilc/lowcode-utils'; -import { IPublicTypeComponentSchema, IPublicEnumTransformStage, IPublicTypeNodeInstance, IPublicTypeProjectSchema } from '@alilc/lowcode-types'; -// just use types -import { BuiltinSimulatorRenderer, Component, IDocumentModel, INode } from '@alilc/lowcode-designer'; -import LowCodeRenderer from '@alilc/lowcode-react-renderer'; -import { createMemoryHistory, MemoryHistory } from 'history'; -import Slot from './builtin-components/slot'; -import Leaf from './builtin-components/leaf'; -import { withQueryParams, parseQuery } from './utils/url'; -import { merge } from 'lodash'; - -const loader = new AssetLoader(); -configure({ enforceActions: 'never' }); - -export class DocumentInstance { - instancesMap = new Map(); - - get schema(): any { - return this.document.export(IPublicEnumTransformStage.Render); - } - - private disposeFunctions: Array<() => void> = []; - - @observable.ref private _components: any = {}; - - @computed get components(): object { - // 根据 device 选择不同组件,进行响应式 - // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl - return this._components; - } - - // context from: utils、constants、history、location、match - @observable.ref private _appContext = {}; - - @computed get context(): any { - return this._appContext; - } - - @observable.ref private _designMode = 'design'; - - @computed get designMode(): any { - return this._designMode; - } - - @observable.ref private _requestHandlersMap = null; - - @computed get requestHandlersMap(): any { - return this._requestHandlersMap; - } - - @observable.ref private _device = 'default'; - - @computed get device() { - return this._device; - } - - @observable.ref private _componentsMap = {}; - - @computed get componentsMap(): any { - return this._componentsMap; - } - - @computed get suspended(): any { - return false; - } - - @computed get scope(): any { - return null; - } - - get path(): string { - return `/${this.document.fileName}`; - } - - get id() { - return this.document.id; - } - - constructor(readonly container: SimulatorRendererContainer, readonly document: IDocumentModel) { - makeObservable(this); - } - - private unmountInstance(id: string, instance: ReactInstance) { - const instances = this.instancesMap.get(id); - if (instances) { - const i = instances.indexOf(instance); - if (i > -1) { - instances.splice(i, 1); - host.setInstance(this.document.id, id, instances); - } - } - } - - mountInstance(id: string, instance: ReactInstance | null) { - const docId = this.document.id; - const { instancesMap } = this; - if (instance == null) { - let instances = this.instancesMap.get(id); - if (instances) { - instances = instances.filter(checkInstanceMounted); - if (instances.length > 0) { - instancesMap.set(id, instances); - host.setInstance(this.document.id, id, instances); - } else { - instancesMap.delete(id); - host.setInstance(this.document.id, id, null); - } - } - return; - } - const unmountInstance = this.unmountInstance.bind(this); - const origId = (instance as any)[SYMBOL_VNID]; - if (origId && origId !== id) { - // 另外一个节点的 instance 在此被复用了,需要从原来地方卸载 - unmountInstance(origId, instance); - } - if (isElement(instance)) { - cacheReactKey(instance); - } else if (origId !== id) { - // 涵盖 origId == null || origId !== id 的情况 - let origUnmount: any = instance.componentWillUnmount; - if (origUnmount && origUnmount.origUnmount) { - origUnmount = origUnmount.origUnmount; - } - // hack! delete instance from map - const newUnmount = function (this: any) { - unmountInstance(id, instance); - origUnmount && origUnmount.call(this); - }; - (newUnmount as any).origUnmount = origUnmount; - instance.componentWillUnmount = newUnmount; - } - - (instance as any)[SYMBOL_VNID] = id; - (instance as any)[SYMBOL_VDID] = docId; - let instances = this.instancesMap.get(id); - if (instances) { - const l = instances.length; - instances = instances.filter(checkInstanceMounted); - let updated = instances.length !== l; - if (!instances.includes(instance)) { - instances.push(instance); - updated = true; - } - if (!updated) { - return; - } - } else { - instances = [instance]; - } - instancesMap.set(id, instances); - host.setInstance(this.document.id, id, instances); - } - - mountContext() { - } - - getNode(id: string): INode | null { - return this.document.getNode(id); - } - - dispose() { - this.disposeFunctions.forEach(fn => fn()); - this.instancesMap = new Map(); - } -} - -export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { - readonly isSimulatorRenderer = true; - private disposeFunctions: Array<() => void> = []; - readonly history: MemoryHistory; - - @observable.ref private _documentInstances: DocumentInstance[] = []; - private _requestHandlersMap: any; - get documentInstances() { - return this._documentInstances; - } - - @observable private _layout: any = null; - - @computed get layout(): any { - // TODO: parse layout Component - return this._layout; - } - - set layout(value: any) { - this._layout = value; - } - - private _libraryMap: { [key: string]: string } = {}; - - private _components: Record | null = {}; - - get components(): Record { - // 根据 device 选择不同组件,进行响应式 - // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl - return this._components || {}; - } - // context from: utils、constants、history、location、match - @observable.ref private _appContext: any = {}; - @computed get context(): any { - return this._appContext; - } - @observable.ref private _designMode: string = 'design'; - @computed get designMode(): any { - return this._designMode; - } - @observable.ref private _device: string = 'default'; - @computed get device() { - return this._device; - } - @observable.ref private _locale: string | undefined = undefined; - @computed get locale() { - return this._locale; - } - @observable.ref private _componentsMap = {}; - @computed get componentsMap(): any { - return this._componentsMap; - } - - /** - * 是否为画布自动渲染 - */ - autoRender = true; - - /** - * 画布是否自动监听事件来重绘节点 - */ - autoRepaintNode = true; - - private _running = false; - - constructor() { - makeObservable(this); - this.autoRender = host.autoRender; - - this.disposeFunctions.push(host.connect(this, () => { - // sync layout config - this._layout = host.project.get('config').layout; - - // todo: split with others, not all should recompute - if (this._libraryMap !== host.libraryMap - || this._componentsMap !== host.designer.componentsMap) { - this._libraryMap = host.libraryMap || {}; - this._componentsMap = host.designer.componentsMap; - this.buildComponents(); - } - - // sync designMode - this._designMode = host.designMode; - - this._locale = host.locale; - - // sync requestHandlersMap - this._requestHandlersMap = host.requestHandlersMap; - - // sync device - this._device = host.device; - })); - const documentInstanceMap = new Map(); - let initialEntry = '/'; - let firstRun = true; - this.disposeFunctions.push(host.autorun(() => { - this._documentInstances = host.project.documents.map((doc) => { - let inst = documentInstanceMap.get(doc.id); - if (!inst) { - inst = new DocumentInstance(this, doc); - documentInstanceMap.set(doc.id, inst); - } - return inst; - }); - const path = host.project.currentDocument - ? documentInstanceMap.get(host.project.currentDocument.id)!.path - : '/'; - if (firstRun) { - initialEntry = path; - firstRun = false; - } else if (this.history.location.pathname !== path) { - this.history.replace(path); - } - })); - const history = createMemoryHistory({ - initialEntries: [initialEntry], - }); - this.history = history; - history.listen((location) => { - const docId = location.pathname.slice(1); - docId && host.project.open(docId); - }); - host.componentsConsumer.consume(async (componentsAsset) => { - if (componentsAsset) { - await this.load(componentsAsset); - this.buildComponents(); - } - }); - this._appContext = { - utils: { - router: { - push(path: string, params?: object) { - history.push(withQueryParams(path, params)); - }, - replace(path: string, params?: object) { - history.replace(withQueryParams(path, params)); - }, - }, - legaoBuiltins: { - getUrlParams() { - const { search } = history.location; - return parseQuery(search); - }, - }, - i18n: { - setLocale: (loc: string) => { - this._appContext.utils.i18n.currentLocale = loc; - this._locale = loc; - }, - currentLocale: this.locale, - messages: {}, - }, - ...getProjectUtils(this._libraryMap, host.get('utilsMetadata')), - }, - constants: {}, - requestHandlersMap: this._requestHandlersMap, - }; - - host.injectionConsumer.consume((data) => { - // TODO: sync utils, i18n, contants,... config - const newCtx = { - ...this._appContext, - }; - merge(newCtx, data.appHelper || {}); - this._appContext = newCtx; - }); - - host.i18nConsumer.consume((data) => { - const newCtx = { - ...this._appContext, - }; - newCtx.utils.i18n.messages = data || {}; - this._appContext = newCtx; - }); - } - - private buildComponents() { - this._components = buildComponents( - this._libraryMap, - this._componentsMap, - this.createComponent.bind(this), - ); - this._components = { - ...builtinComponents, - ...this._components, - }; - } - - /** - * 加载资源 - */ - load(asset: Asset): Promise { - return loader.load(asset); - } - - async loadAsyncLibrary(asyncLibraryMap: Record) { - await loader.loadAsyncLibrary(asyncLibraryMap); - this.buildComponents(); - } - - getComponent(componentName: string) { - const paths = componentName.split('.'); - const subs: string[] = []; - - while (true) { - const component = this._components?.[componentName]; - if (component) { - return getSubComponent(component, subs); - } - - const sub = paths.pop(); - if (!sub) { - return null; - } - subs.unshift(sub); - componentName = paths.join('.'); - } - } - - getClosestNodeInstance(from: ReactInstance, nodeId?: string): IPublicTypeNodeInstance | null { - return getClosestNodeInstance(from, nodeId); - } - - findDOMNodes(instance: ReactInstance): Array | null { - return reactFindDOMNodes(instance); - } - - getClientRects(element: Element | Text) { - return getClientRects(element); - } - - setNativeSelection(enableFlag: boolean) { - setNativeSelection(enableFlag); - } - - setDraggingState(state: boolean) { - cursor.setDragging(state); - } - - setCopyState(state: boolean) { - cursor.setCopy(state); - } - - clearState() { - cursor.release(); - } - - createComponent(schema: IPublicTypeProjectSchema): Component | null { - const _schema: IPublicTypeProjectSchema = { - ...schema, - componentsTree: schema.componentsTree.map(compatibleLegaoSchema), - }; - - const componentsTreeSchema = _schema.componentsTree[0]; - - if (componentsTreeSchema.componentName === 'Component' && componentsTreeSchema.css) { - const doc = window.document; - const s = doc.createElement('style'); - s.setAttribute('type', 'text/css'); - s.setAttribute('id', `Component-${componentsTreeSchema.id || ''}`); - s.appendChild(doc.createTextNode(componentsTreeSchema.css || '')); - doc.getElementsByTagName('head')[0].appendChild(s); - } - - const renderer = this; - - class LowCodeComp extends React.Component { - render() { - const extraProps = getLowCodeComponentProps(this.props); - return createElement(LowCodeRenderer, { - ...extraProps, // 防止覆盖下面内置属性 - // 使用 _schema 为了使低代码组件在页面设计中使用变量,同 react 组件使用效果一致 - schema: componentsTreeSchema, - components: renderer.components, - designMode: '', - locale: renderer.locale, - messages: _schema.i18n || {}, - device: renderer.device, - appHelper: renderer.context, - rendererName: 'LowCodeRenderer', - thisRequiredInJSE: host.thisRequiredInJSE, - faultComponent: host.faultComponent, - faultComponentMap: host.faultComponentMap, - customCreateElement: (Comp: any, props: any, children: any) => { - const componentMeta = host.currentDocument?.getComponentMeta(Comp.displayName); - if (componentMeta?.isModal) { - return null; - } - - const { __id, __designMode, ...viewProps } = props; - // mock _leaf,减少性能开销 - const _leaf = { - isEmpty: () => false, - isMock: true, - }; - viewProps._leaf = _leaf; - return createElement(Comp, viewProps, children); - }, - }); - } - } - - return LowCodeComp; - } - - run() { - if (this._running) { - return; - } - this._running = true; - const containerId = 'app'; - let container = document.getElementById(containerId); - if (!container) { - container = document.createElement('div'); - document.body.appendChild(container); - container.id = containerId; - } - - // ==== compatible vision - document.documentElement.classList.add('engine-page'); - document.body.classList.add('engine-document'); // important! Stylesheet.invoke depends - - reactRender(createElement(SimulatorRendererView, { rendererContainer: this }), container); - host.project.setRendererReady(this); - } - - /** - * 刷新渲染器 - */ - rerender() { - this.autoRender = true; - // TODO: 不太优雅 - this._appContext = { ...this._appContext }; - } - - stopAutoRepaintNode() { - this.autoRepaintNode = false; - } - - enableAutoRepaintNode() { - this.autoRepaintNode = true; - } - - dispose() { - this.disposeFunctions.forEach((fn) => fn()); - this.documentInstances.forEach((docInst) => docInst.dispose()); - untracked(() => { - this._componentsMap = {}; - this._components = null; - this._appContext = null; - }); - } -} - -// Slot/Leaf and Fragment|FunctionComponent polyfill(ref) - -const builtinComponents = { - Slot, - Leaf, -}; - -let REACT_KEY = ''; -function cacheReactKey(el: Element): Element { - if (REACT_KEY !== '') { - return el; - } - // react17 采用 __reactFiber 开头 - REACT_KEY = Object.keys(el).find( - (key) => key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$'), - ) || ''; - if (!REACT_KEY && (el as HTMLElement).parentElement) { - return cacheReactKey((el as HTMLElement).parentElement!); - } - return el; -} - -const SYMBOL_VNID = Symbol('_LCNodeId'); -const SYMBOL_VDID = Symbol('_LCDocId'); - -function getClosestNodeInstance( - from: ReactInstance, - specId?: string, - ): IPublicTypeNodeInstance | null { - let el: any = from; - if (el) { - if (isElement(el)) { - el = cacheReactKey(el); - } else { - return getNodeInstance(getReactInternalFiber(el), specId); - } - } - while (el) { - if (SYMBOL_VNID in el) { - const nodeId = el[SYMBOL_VNID]; - const docId = el[SYMBOL_VDID]; - if (!specId || specId === nodeId) { - return { - docId, - nodeId, - instance: el, - }; - } - } - // get fiberNode from element - if (el[REACT_KEY]) { - return getNodeInstance(el[REACT_KEY], specId); - } - el = el.parentElement; - } - return null; -} - -function getNodeInstance(fiberNode: any, specId?: string): IPublicTypeNodeInstance | null { - const instance = fiberNode?.stateNode; - if (instance && SYMBOL_VNID in instance) { - const nodeId = instance[SYMBOL_VNID]; - const docId = instance[SYMBOL_VDID]; - if (!specId || specId === nodeId) { - return { - docId, - nodeId, - instance, - }; - } - } - if (!instance && !fiberNode?.return) return null; - return getNodeInstance(fiberNode?.return); -} - -function checkInstanceMounted(instance: any): boolean { - if (isElement(instance)) { - return instance.parentElement != null && window.document.contains(instance); - } - return true; -} - -function getLowCodeComponentProps(props: any) { - if (!props || !isPlainObject(props)) { - return props; - } - const newProps: any = {}; - Object.keys(props).forEach((k) => { - if (['children', 'componentId', '__designMode', '_componentName', '_leaf'].includes(k)) { - return; - } - newProps[k] = props[k]; - }); - newProps['componentName'] = props['_componentName']; - return newProps; -} - -export default new SimulatorRendererContainer(); diff --git a/packages/react-simulator-renderer/src.bak/utils/get-client-rects.ts b/packages/react-simulator-renderer/src.bak/utils/get-client-rects.ts deleted file mode 100644 index dd13aba81..000000000 --- a/packages/react-simulator-renderer/src.bak/utils/get-client-rects.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { isElement } from '@alilc/lowcode-utils'; - -// a range for test TextNode clientRect -const cycleRange = document.createRange(); - -export function getClientRects(node: Element | Text) { - if (isElement(node)) { - return [node.getBoundingClientRect()]; - } - - cycleRange.selectNode(node); - return Array.from(cycleRange.getClientRects()); -} diff --git a/packages/react-simulator-renderer/src.bak/utils/is-dom-node.ts b/packages/react-simulator-renderer/src.bak/utils/is-dom-node.ts deleted file mode 100644 index 1d84eaa02..000000000 --- a/packages/react-simulator-renderer/src.bak/utils/is-dom-node.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function isDOMNode(node: any): node is Element | Text { - return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE); -} diff --git a/packages/react-simulator-renderer/src.bak/utils/misc.ts b/packages/react-simulator-renderer/src.bak/utils/misc.ts deleted file mode 100644 index 95ca6bfbd..000000000 --- a/packages/react-simulator-renderer/src.bak/utils/misc.ts +++ /dev/null @@ -1,38 +0,0 @@ -// interface UtilsMetadata { -// name: string; -// npm: { -// package: string; -// version?: string; -// exportName: string; -// subName?: string; -// destructuring?: boolean; -// main?: string; -// }; -// } - -// invalid code - -// interface LibrayMap { -// [key: string]: string; -// } - -// export function getProjectUtils(librayMap: LibrayMap, utilsMetadata: UtilsMetadata[]) { - -// const projectUtils: { [packageName: string]: any } = {}; -// if (utilsMetadata) { -// utilsMetadata.forEach(meta => { -// if (librayMap[meta?.npm.package]) { -// const lib = window[librayMap[meta?.npm.package] as any]; -// } -// }); -// } -// } - -/** - * judges if current simulator renderer deteched or not - * @returns detached or not - */ -export function isRendererDetached() { - // if current iframe detached from host document, the `window.parent` will be undefined. - return !window.parent; -} \ No newline at end of file diff --git a/packages/react-simulator-renderer/src.bak/utils/react-find-dom-nodes.ts b/packages/react-simulator-renderer/src.bak/utils/react-find-dom-nodes.ts deleted file mode 100644 index d7af90346..000000000 --- a/packages/react-simulator-renderer/src.bak/utils/react-find-dom-nodes.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ReactInstance } from 'react'; -import { findDOMNode } from 'react-dom'; -import { isElement } from '@alilc/lowcode-utils'; -import { isDOMNode } from './is-dom-node'; - -export const getReactInternalFiber = (el: any) => { - return el._reactInternals || el._reactInternalFiber; -}; - -function elementsFromFiber(fiber: any, elements: Array) { - if (fiber) { - if (fiber.stateNode && isDOMNode(fiber.stateNode)) { - elements.push(fiber.stateNode); - } else if (fiber.child) { - // deep fiberNode.child - elementsFromFiber(fiber.child, elements); - } - - if (fiber.sibling) { - elementsFromFiber(fiber.sibling, elements); - } - } -} - -export function reactFindDOMNodes(elem: ReactInstance | null): Array | null { - if (!elem) { - return null; - } - if (isElement(elem)) { - return [elem]; - } - const elements: Array = []; - const fiberNode = getReactInternalFiber(elem); - elementsFromFiber(fiberNode?.child, elements); - if (elements.length > 0) return elements; - try { - return [findDOMNode(elem)]; - } catch (e) { - return null; - } -} diff --git a/packages/react-simulator-renderer/src.bak/utils/url.ts b/packages/react-simulator-renderer/src.bak/utils/url.ts deleted file mode 100644 index d720323b3..000000000 --- a/packages/react-simulator-renderer/src.bak/utils/url.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Parse queryString - * @param {String} str '?q=query&b=test' - * @return {Object} - */ -export function parseQuery(str: string): object { - const ret: any = {}; - - if (typeof str !== 'string') { - return ret; - } - - const s = str.trim().replace(/^(\?|#|&)/, ''); - - if (!s) { - return ret; - } - - s.split('&').forEach((param) => { - const parts = param.replace(/\+/g, ' ').split('='); - let key = parts.shift()!; - let val: any = parts.length > 0 ? parts.join('=') : undefined; - - key = decodeURIComponent(key); - - val = val === undefined ? null : decodeURIComponent(val); - - if (ret[key] === undefined) { - ret[key] = val; - } else if (Array.isArray(ret[key])) { - ret[key].push(val); - } else { - ret[key] = [ret[key], val]; - } - }); - - return ret; -} - -/** - * Stringify object to query parammeters - * @param {Object} obj - * @return {String} - */ -export function stringifyQuery(obj: any): string { - const param: string[] = []; - Object.keys(obj).forEach((key) => { - let value = obj[key]; - if (value && typeof value === 'object') { - value = JSON.stringify(value); - } - param.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); - }); - return param.join('&'); -} - -export function uriEncode(uri: string) { - return encodeURIComponent(uri); -} - -export function uriDecode(uri: string) { - return decodeURIComponent(uri); -} - -export function withQueryParams(url: string, params?: object) { - const queryStr = params ? stringifyQuery(params) : ''; - if (queryStr === '') { - return url; - } - const urlSplit = url.split('#'); - const hash = urlSplit[1] ? `#${urlSplit[1]}` : ''; - const urlWithoutHash = urlSplit[0]; - return `${urlWithoutHash}${~urlWithoutHash.indexOf('?') ? '&' : '?'}${queryStr}${hash}`; -} diff --git a/packages/renderer-core/src/services/code-runtime/codeRuntime.ts b/packages/renderer-core/src/services/code-runtime/codeRuntime.ts index 0782d6b57..2584e81f7 100644 --- a/packages/renderer-core/src/services/code-runtime/codeRuntime.ts +++ b/packages/renderer-core/src/services/code-runtime/codeRuntime.ts @@ -1,43 +1,44 @@ import { - type PlainObject, - type Spec, + type StringDictionary, + type JSNode, type EventDisposable, - isJSExpression, - isJSFunction, + type JSExpression, + type JSFunction, + specTypes, } from '@alilc/lowcode-shared'; import { type ICodeScope, CodeScope } from './codeScope'; import { isNode } from '../../../../shared/src/utils/node'; import { mapValue } from '../../utils/value'; import { evaluate } from './evaluate'; -export interface CodeRuntimeOptions { +export interface CodeRuntimeOptions { initScopeValue?: Partial; parentScope?: ICodeScope; evalCodeFunction?: EvalCodeFunction; } -export interface ICodeRuntime { +export interface ICodeRuntime { getScope(): ICodeScope; run(code: string, scope?: ICodeScope): R | undefined; - resolve(value: PlainObject): any; + resolve(value: StringDictionary): any; onResolve(handler: NodeResolverHandler): EventDisposable; - createChild( + createChild( options: Omit, 'parentScope'>, ): ICodeRuntime; } -export type NodeResolverHandler = (node: Spec.JSNode) => Spec.JSNode | false | undefined; +export type NodeResolverHandler = (node: JSNode) => JSNode | false | undefined; let onResolveHandlers: NodeResolverHandler[] = []; export type EvalCodeFunction = (code: string, scope: any) => any; -export class CodeRuntime implements ICodeRuntime { +export class CodeRuntime implements ICodeRuntime { private codeScope: ICodeScope; private evalCodeFunction: EvalCodeFunction = evaluate; @@ -70,13 +71,13 @@ export class CodeRuntime implements ICodeRu } } - resolve(data: PlainObject): any { + resolve(data: StringDictionary): any { if (onResolveHandlers.length > 0) { - data = mapValue(data, isNode, (node: Spec.JSNode) => { - let newNode: Spec.JSNode | false | undefined = node; + data = mapValue(data, isNode, (node: JSNode) => { + let newNode: JSNode | false | undefined = node; for (const handler of onResolveHandlers) { - newNode = handler(newNode as Spec.JSNode); + newNode = handler(newNode as JSNode); if (newNode === false || typeof newNode === 'undefined') { break; } @@ -89,15 +90,15 @@ export class CodeRuntime implements ICodeRu return mapValue( data, (data) => { - return isJSExpression(data) || isJSFunction(data); + return specTypes.isJSExpression(data) || specTypes.isJSFunction(data); }, - (node: Spec.JSExpression | Spec.JSFunction) => { + (node: JSExpression | JSFunction) => { return this.resolveExprOrFunction(node); }, ); } - private resolveExprOrFunction(node: Spec.JSExpression | Spec.JSFunction) { + private resolveExprOrFunction(node: JSExpression | JSFunction) { const v = this.run(node.value) as any; if (typeof v === 'undefined' && node.mock) { @@ -116,7 +117,7 @@ export class CodeRuntime implements ICodeRu }; } - createChild( + createChild( options?: Omit, 'parentScope'>, ): ICodeRuntime { return new CodeRuntime({ diff --git a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts index 321542e11..134b70e9b 100644 --- a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts +++ b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts @@ -1,4 +1,4 @@ -import { createDecorator, invariant, Provide, type PlainObject } from '@alilc/lowcode-shared'; +import { createDecorator, invariant, Provide, type StringDictionary } from '@alilc/lowcode-shared'; import { type ICodeRuntime, type CodeRuntimeOptions, CodeRuntime } from './codeRuntime'; export interface ICodeRuntimeService { @@ -6,8 +6,8 @@ export interface ICodeRuntimeService { initialize(options: CodeRuntimeOptions): void; - createCodeRuntime( - options: CodeRuntimeOptions, + createCodeRuntime( + options?: CodeRuntimeOptions, ): ICodeRuntime; } @@ -21,7 +21,7 @@ export class CodeRuntimeService implements ICodeRuntimeService { this.rootRuntime = new CodeRuntime(options); } - createCodeRuntime( + createCodeRuntime( options: CodeRuntimeOptions = {}, ): ICodeRuntime { invariant(this.rootRuntime, `please initialize codeRuntimeService on renderer starting!`); diff --git a/packages/renderer-core/src/services/code-runtime/codeScope.ts b/packages/renderer-core/src/services/code-runtime/codeScope.ts index 914aa5f47..c56e4da01 100644 --- a/packages/renderer-core/src/services/code-runtime/codeScope.ts +++ b/packages/renderer-core/src/services/code-runtime/codeScope.ts @@ -1,4 +1,4 @@ -import { type PlainObject } from '@alilc/lowcode-shared'; +import { type StringDictionary } from '@alilc/lowcode-shared'; import { trustedGlobals } from './globals-es2015'; /* @@ -9,23 +9,23 @@ const unscopables = trustedGlobals.reduce((acc, key) => ({ ...acc, [key]: true } __proto__: null, }); -export interface ICodeScope { +export interface ICodeScope { readonly value: T; set(name: keyof T, value: any): void; setValue(value: Partial, replace?: boolean): void; - createChild(initValue: Partial): ICodeScope; + createChild(initValue: Partial): ICodeScope; } /** * 双链表实现父域值的获取 */ -interface IScopeNode { - parent?: IScopeNode; +interface IScopeNode { + parent?: IScopeNode; current: Partial; } -export class CodeScope implements ICodeScope { +export class CodeScope implements ICodeScope { __node: IScopeNode; private proxyValue: T; @@ -54,7 +54,7 @@ export class CodeScope implements ICodeScop } } - createChild(initValue: Partial): ICodeScope { + createChild(initValue: Partial): ICodeScope { const childScope = new CodeScope(initValue); childScope.__node.parent = this.__node; @@ -75,7 +75,7 @@ export class CodeScope implements ICodeScop private findValue(prop: PropertyKey) { if (prop === Symbol.unscopables) return unscopables; - let node: IScopeNode | undefined = this.__node; + let node: IScopeNode | undefined = this.__node; while (node) { if (Object.hasOwnProperty.call(node.current, prop)) { return node.current[prop as string]; @@ -87,7 +87,7 @@ export class CodeScope implements ICodeScop private hasProperty(prop: PropertyKey): boolean { if (prop in unscopables) return true; - let node: IScopeNode | undefined = this.__node; + let node: IScopeNode | undefined = this.__node; while (node) { if (prop in node.current) { return true; diff --git a/packages/renderer-core/src/services/extension/boosts.ts b/packages/renderer-core/src/services/extension/boosts.ts index 92bd1392d..085551c96 100644 --- a/packages/renderer-core/src/services/extension/boosts.ts +++ b/packages/renderer-core/src/services/extension/boosts.ts @@ -1,4 +1,4 @@ -import { createDecorator, Provide, type PlainObject } from '@alilc/lowcode-shared'; +import { createDecorator, Provide, type StringDictionary } from '@alilc/lowcode-shared'; import { isObject } from 'lodash-es'; import { ICodeRuntime, ICodeRuntimeService } from '../code-runtime'; import { IRuntimeUtilService } from '../runtimeUtilService'; @@ -23,7 +23,7 @@ export interface IBoostsApi { */ export interface IBoostsService { extend(name: string, value: any, force?: boolean): void; - extend(value: PlainObject, force?: boolean): void; + extend(value: StringDictionary, force?: boolean): void; toExpose(): IBoosts; } @@ -34,7 +34,7 @@ export const IBoostsService = createDecorator('boostsService'); export class BoostsService implements IBoostsService { private builtInApis: IBoostsApi; - private extendsValue: PlainObject = {}; + private extendsValue: StringDictionary = {}; private _expose: any; @@ -56,8 +56,8 @@ export class BoostsService implements IBoostsService { } extend(name: string, value: any, force?: boolean | undefined): void; - extend(value: PlainObject, force?: boolean | undefined): void; - extend(name: string | PlainObject, value?: any, force?: boolean | undefined): void { + extend(value: StringDictionary, force?: boolean | undefined): void; + extend(name: string | StringDictionary, value?: any, force?: boolean | undefined): void { if (typeof name === 'string') { if (force) { this.extendsValue[name] = value; diff --git a/packages/renderer-core/src/services/extension/plugin.ts b/packages/renderer-core/src/services/extension/plugin.ts index effe95a78..c8ffb5c1a 100644 --- a/packages/renderer-core/src/services/extension/plugin.ts +++ b/packages/renderer-core/src/services/extension/plugin.ts @@ -1,4 +1,4 @@ -import { type EventEmitter, type IStore, type PlainObject } from '@alilc/lowcode-shared'; +import { type EventEmitter, type IStore, type StringDictionary } from '@alilc/lowcode-shared'; import { type IBoosts } from './boosts'; import { ILifeCycleService } from '../lifeCycleService'; import { type ISchemaService } from '../schema'; @@ -6,7 +6,7 @@ import { type IPackageManagementService } from '../package'; export interface PluginContext { eventEmitter: EventEmitter; - globalState: IStore; + globalState: IStore; boosts: IBoosts; schema: Pick; packageManager: IPackageManagementService; diff --git a/packages/renderer-core/src/services/model/componentTreeModel.ts b/packages/renderer-core/src/services/model/componentTreeModel.ts index 555c29054..7eacc2706 100644 --- a/packages/renderer-core/src/services/model/componentTreeModel.ts +++ b/packages/renderer-core/src/services/model/componentTreeModel.ts @@ -1,16 +1,24 @@ import { - type Spec, - type PlainObject, - isComponentNode, + type ComponentNode, + type ComponentNodeProps, + type StringDictionary, + type ComponentLifeCycle, + type NodeType, + type InstanceApi, + type InstanceStateApi, + type ComponentDataSource, + type InstanceDataSourceApi, + type ComponentTree, + specTypes, invariant, uniqueId, } from '@alilc/lowcode-shared'; import { type ICodeRuntime } from '../code-runtime'; import { IWidget, Widget } from '../widget'; -export interface NormalizedComponentNode extends Spec.ComponentNode { +export interface NormalizedComponentNode extends ComponentNode { loopArgs: [string, string]; - props: Spec.ComponentNodeProps; + props: ComponentNodeProps; } /** @@ -30,7 +38,7 @@ export interface IComponentTreeModel { /** * 调用生命周期方法 */ - triggerLifeCycle(lifeCycleName: Spec.ComponentLifeCycle, ...args: any[]): void; + triggerLifeCycle(lifeCycleName: ComponentLifeCycle, ...args: any[]): void; /** * 设置 ref 对应的组件实例, 提供给 scope.$() 方式使用 */ @@ -43,18 +51,18 @@ export interface IComponentTreeModel { /** * 根据 compoonentsTree.children 构建 widget 渲染对象 */ - buildWidgets(nodes: Spec.NodeType[]): IWidget[]; + buildWidgets(nodes: NodeType[]): IWidget[]; } -export type ModelStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi; +export type ModelStateCreator = (initalState: StringDictionary) => InstanceStateApi; export type ModelDataSourceCreator = ( - dataSourceSchema: Spec.ComponentDataSource, - codeRuntime: ICodeRuntime, -) => Spec.InstanceDataSourceApi; + dataSourceSchema: ComponentDataSource, + codeRuntime: ICodeRuntime, +) => InstanceDataSourceApi; export interface ComponentTreeModelOptions { id?: string; - metadata?: PlainObject; + metadata?: StringDictionary; stateCreator: ModelStateCreator; dataSourceCreator?: ModelDataSourceCreator; @@ -69,11 +77,11 @@ export class ComponentTreeModel public widgets: IWidget[] = []; - public metadata: PlainObject = {}; + public metadata: StringDictionary = {}; constructor( - public componentsTree: Spec.ComponentTree, - public codeRuntime: ICodeRuntime, + public componentsTree: ComponentTree, + public codeRuntime: ICodeRuntime, options: ComponentTreeModelOptions, ) { invariant(componentsTree, 'componentsTree must to provide', 'ComponentTreeModel'); @@ -101,7 +109,7 @@ export class ComponentTreeModel const stateApi = stateCreator(initalState); codeScope.setValue(stateApi); - let dataSourceApi: Spec.InstanceDataSourceApi | undefined; + let dataSourceApi: InstanceDataSourceApi | undefined; if (dataSource && dataSourceCreator) { const dataSourceProps = this.codeRuntime.resolve(dataSource); dataSourceApi = dataSourceCreator(dataSourceProps, this.codeRuntime); @@ -135,7 +143,7 @@ export class ComponentTreeModel return this.componentsTree.css; } - triggerLifeCycle(lifeCycleName: Spec.ComponentLifeCycle, ...args: any[]) { + triggerLifeCycle(lifeCycleName: ComponentLifeCycle, ...args: any[]) { // keys 用来判断 lifeCycleName 存在于 schema 对象上,不获取原型链上的对象 if ( !this.componentsTree.lifeCycles || @@ -173,9 +181,9 @@ export class ComponentTreeModel } } - buildWidgets(nodes: Spec.NodeType[]): IWidget[] { + buildWidgets(nodes: NodeType[]): IWidget[] { return nodes.map((node) => { - if (isComponentNode(node)) { + if (specTypes.isComponentNode(node)) { const normalized = normalizeComponentNode(node); const widget = new Widget(normalized, this); @@ -191,7 +199,7 @@ export class ComponentTreeModel } } -export function normalizeComponentNode(node: Spec.ComponentNode): NormalizedComponentNode { +export function normalizeComponentNode(node: ComponentNode): NormalizedComponentNode { const [loopArgsOne, loopArgsTwo] = node.loopArgs ?? []; const { children, ...props } = node.props ?? {}; diff --git a/packages/renderer-core/src/services/model/componentTreeModelService.ts b/packages/renderer-core/src/services/model/componentTreeModelService.ts index c5097e337..486efd4a4 100644 --- a/packages/renderer-core/src/services/model/componentTreeModelService.ts +++ b/packages/renderer-core/src/services/model/componentTreeModelService.ts @@ -2,8 +2,8 @@ import { createDecorator, Provide, invariant, - type Spec, - type PlainObject, + type ComponentTree, + type StringDictionary, } from '@alilc/lowcode-shared'; import { ICodeRuntimeService } from '../code-runtime'; import { @@ -14,12 +14,12 @@ import { import { ISchemaService } from '../schema'; export interface CreateComponentTreeModelOptions extends ComponentTreeModelOptions { - codeScopeValue?: PlainObject; + codeScopeValue?: StringDictionary; } export interface IComponentTreeModelService { create( - componentsTree: Spec.ComponentTree, + componentsTree: ComponentTree, options?: CreateComponentTreeModelOptions, ): IComponentTreeModel; @@ -41,11 +41,12 @@ export class ComponentTreeModelService implements IComponentTreeModelService { ) {} create( - componentsTree: Spec.ComponentTree, + componentsTree: ComponentTree, options: CreateComponentTreeModelOptions, ): IComponentTreeModel { return new ComponentTreeModel( componentsTree, + // @ts-expect-error: preset scope value this.codeRuntimeService.createCodeRuntime({ initScopeValue: options?.codeScopeValue, }), @@ -64,6 +65,7 @@ export class ComponentTreeModelService implements IComponentTreeModelService { return new ComponentTreeModel( componentsTree, + // @ts-expect-error: preset scope value this.codeRuntimeService.createCodeRuntime({ initScopeValue: options?.codeScopeValue, }), diff --git a/packages/renderer-core/src/services/package/loader.ts b/packages/renderer-core/src/services/package/loader.ts index 2188cafe6..379b13ee8 100644 --- a/packages/renderer-core/src/services/package/loader.ts +++ b/packages/renderer-core/src/services/package/loader.ts @@ -1,10 +1,10 @@ -import { type Spec } from '@alilc/lowcode-shared'; +import { type Package } from '@alilc/lowcode-shared'; import { type IPackageManagementService } from './managementService'; export interface PackageLoader { name?: string; - load(this: IPackageManagementService, info: Spec.Package): Promise; - active(info: Spec.Package): boolean; + load(this: IPackageManagementService, info: Package): Promise; + active(info: Package): boolean; } export function definePackageLoader(loader: PackageLoader) { diff --git a/packages/renderer-core/src/services/package/managementService.ts b/packages/renderer-core/src/services/package/managementService.ts index 5ab95bbed..99838e10d 100644 --- a/packages/renderer-core/src/services/package/managementService.ts +++ b/packages/renderer-core/src/services/package/managementService.ts @@ -1,11 +1,11 @@ import { - type Spec, + type Package, type LowCodeComponent, type ProCodeComponent, + type ComponentMap, createDecorator, Provide, - isLowCodeComponentPackage, - isProCodeComponentPackage, + specTypes, } from '@alilc/lowcode-shared'; import { get as lodashGet } from 'lodash-es'; import { PackageLoader } from './loader'; @@ -25,20 +25,18 @@ export interface IPackageManagementService { * 新增资产包 * @param packages */ - loadPackages(packages: Spec.Package[]): Promise; + loadPackages(packages: Package[]): Promise; /** 通过包名获取资产包信息 */ - getPackageInfo(packageName: string): Spec.Package | undefined; + getPackageInfo(packageName: string): Package | undefined; getLibraryByPackageName(packageName: string): any; setLibraryByPackageName(packageName: string, library: any): void; - getLibraryByComponentMap( - componentMap: Spec.ComponentMap, - ): { key: string; value: any } | undefined; + getLibraryByComponentMap(componentMap: ComponentMap): { key: string; value: any } | undefined; /** 解析组件映射 */ - resolveComponentMaps(componentMaps: Spec.ComponentMap[]): void; + resolveComponentMaps(componentMaps: ComponentMap[]): void; /** 通过组件名获取对应的组件 */ getComponent(componentName: string): C | LowCodeComponent | undefined; @@ -74,15 +72,15 @@ export class PackageManagementService implements IPackageManagementService { }); } - async loadPackages(packages: Spec.Package[]) { + async loadPackages(packages: Package[]) { for (const item of packages) { // low code component not need load - if (isLowCodeComponentPackage(item)) { + if (specTypes.isLowCodeComponentPackage(item)) { this.lowCodeComponentPackages.set(item.id, item); continue; } - if (!isProCodeComponentPackage(item)) continue; + if (!specTypes.isProCodeComponentPackage(item)) continue; const normalized = this.normalizePackage(item); await this.loadPackageByNormalized(normalized); @@ -105,12 +103,10 @@ export class PackageManagementService implements IPackageManagementService { this.packageStore.set(packageName, library); } - getLibraryByComponentMap( - componentMap: Spec.ComponentMap, - ): { key: string; value: any } | undefined { + getLibraryByComponentMap(componentMap: ComponentMap): { key: string; value: any } | undefined { if (!componentMap.componentName && !componentMap.exportName) return; - if (this.packageStore.has(componentMap.package)) { + if (componentMap.package && this.packageStore.has(componentMap.package)) { const library = this.packageStore.get(componentMap.package!); // export { exportName } from xxx exportName === global.libraryName.exportName // export exportName from xxx exportName === global.libraryName.default || global.libraryName @@ -138,7 +134,7 @@ export class PackageManagementService implements IPackageManagementService { } } - resolveComponentMaps(componentMaps: Spec.ComponentMap[]) { + resolveComponentMaps(componentMaps: ComponentMap[]) { for (const map of componentMaps) { if (map.devMode === 'lowCode') { const packageInfo = this.lowCodeComponentPackages.get((map as LowCodeComponent).id); diff --git a/packages/renderer-core/src/services/runtimeIntlService.ts b/packages/renderer-core/src/services/runtimeIntlService.ts index d8e1029c6..086177dee 100644 --- a/packages/renderer-core/src/services/runtimeIntlService.ts +++ b/packages/renderer-core/src/services/runtimeIntlService.ts @@ -2,7 +2,7 @@ import { createDecorator, Provide, Intl, - type Spec, + type IntlApi, type Locale, type Translations, platformLocale, @@ -84,7 +84,7 @@ export class RuntimeIntlService implements IRuntimeIntlService { } private injectScope(): void { - const exposed: Spec.IntlApi = { + const exposed: IntlApi = { i18n: (key, params) => { return this.t({ key, params }); }, diff --git a/packages/renderer-core/src/services/runtimeUtilService.ts b/packages/renderer-core/src/services/runtimeUtilService.ts index b59b6f4f3..3a4d3e62b 100644 --- a/packages/renderer-core/src/services/runtimeUtilService.ts +++ b/packages/renderer-core/src/services/runtimeUtilService.ts @@ -1,9 +1,9 @@ import { type AnyFunction, - type Spec, + type UtilDescription, createDecorator, Provide, - type PlainObject, + type StringDictionary, } from '@alilc/lowcode-shared'; import { isPlainObject } from 'lodash-es'; import { IPackageManagementService } from './package'; @@ -12,8 +12,8 @@ import { ISchemaService } from './schema'; import { ILifeCycleService, LifecyclePhase } from './lifeCycleService'; export interface IRuntimeUtilService { - add(utilItem: Spec.Util, force?: boolean): void; - add(name: string, target: AnyFunction | PlainObject, force?: boolean): void; + add(utilItem: UtilDescription, force?: boolean): void; + add(name: string, target: AnyFunction | StringDictionary, force?: boolean): void; remove(name: string): void; } @@ -41,17 +41,21 @@ export class RuntimeUtilService implements IRuntimeUtilService { }); } - add(utilItem: Spec.Util, force?: boolean): void; - add(name: string, fn: AnyFunction | PlainObject, force?: boolean): void; - add(util: Spec.Util | string, fn?: AnyFunction | PlainObject | boolean, force?: boolean): void { + add(utilItem: UtilDescription, force?: boolean): void; + add(name: string, fn: AnyFunction | StringDictionary, force?: boolean): void; + add( + util: UtilDescription | string, + fn?: AnyFunction | StringDictionary | boolean, + force?: boolean, + ): void { let name: string; - let utilObj: AnyFunction | PlainObject | Spec.Util; + let utilObj: AnyFunction | StringDictionary | UtilDescription; if (typeof util === 'string') { if (!fn) return; name = util; - utilObj = fn as AnyFunction | PlainObject; + utilObj = fn as AnyFunction | StringDictionary; } else { if (!util) return; @@ -65,20 +69,20 @@ export class RuntimeUtilService implements IRuntimeUtilService { private addUtilByName( name: string, - fn: AnyFunction | PlainObject | Spec.Util, + fn: AnyFunction | StringDictionary | UtilDescription, force?: boolean, ): void { if (this.utilsMap.has(name) && !force) return; if (isPlainObject(fn)) { - if ((fn as Spec.Util).type === 'function' || (fn as Spec.Util).type === 'npm') { - const utilFn = this.parseUtil(fn as Spec.Util); + if ((fn as UtilDescription).type === 'function' || (fn as UtilDescription).type === 'npm') { + const utilFn = this.parseUtil(fn as UtilDescription); if (utilFn) { this.addUtilByName(utilFn.key, utilFn.value, force); } - } else if ((fn as PlainObject).destructuring) { + } else if ((fn as StringDictionary).destructuring) { for (const key of Object.keys(fn)) { - this.addUtilByName(key, (fn as PlainObject)[key], force); + this.addUtilByName(key, (fn as StringDictionary)[key], force); } } else { this.utilsMap.set(name, fn); @@ -92,7 +96,7 @@ export class RuntimeUtilService implements IRuntimeUtilService { this.utilsMap.delete(name); } - private parseUtil(utilItem: Spec.Util) { + private parseUtil(utilItem: UtilDescription) { if (utilItem.type === 'function') { const { content } = utilItem; return { diff --git a/packages/renderer-core/src/services/schema/schemaService.ts b/packages/renderer-core/src/services/schema/schemaService.ts index 4a59dd1a1..0e4bb0bf6 100644 --- a/packages/renderer-core/src/services/schema/schemaService.ts +++ b/packages/renderer-core/src/services/schema/schemaService.ts @@ -1,5 +1,5 @@ import { - type Spec, + type Project, createDecorator, Provide, type IStore, @@ -12,12 +12,12 @@ import { schemaValidation } from './validation'; import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService'; import { ICodeRuntimeService } from '../code-runtime'; -export interface NormalizedSchema extends Spec.Project {} +export interface NormalizedSchema extends Project {} export type NormalizedSchemaKey = keyof NormalizedSchema; export interface ISchemaService { - initialize(schema: Spec.Project): void; + initialize(schema: Project): void; get(key: K): NormalizedSchema[K]; @@ -47,7 +47,7 @@ export class SchemaService implements ISchemaService { @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, ) { this.onChange('constants', (value = {}) => { - this.codeRuntimeService.getScope().set('constants', value); + this.codeRuntimeService.rootRuntime.getScope().set('constants', value); }); this.lifeCycleService.when(LifecyclePhase.Destroying, () => { diff --git a/packages/renderer-core/src/services/schema/validation.ts b/packages/renderer-core/src/services/schema/validation.ts index fa119ebcb..705e1a535 100644 --- a/packages/renderer-core/src/services/schema/validation.ts +++ b/packages/renderer-core/src/services/schema/validation.ts @@ -1,4 +1,4 @@ -import { type Spec } from '@alilc/lowcode-shared'; +import { type Project } from '@alilc/lowcode-shared'; interface ValidationRule { valid: (value: T) => boolean; @@ -6,7 +6,7 @@ interface ValidationRule { } type ValidOptionRecord = { - [K in keyof Spec.Project]: ValidationRule; + [K in keyof Project]: ValidationRule; }; const SCHEMA_KEYS = [ @@ -38,7 +38,7 @@ const SCHEMA_VALIDATIONS_OPTIONS: Partial = { }, }; -export function schemaValidation(key: K, value: Spec.Project[K]) { +export function schemaValidation(key: K, value: Project[K]) { if (!SCHEMA_KEYS.includes(key)) { return `schema 的字段名必须是${JSON.stringify(SCHEMA_KEYS)}中的一个`; } diff --git a/packages/renderer-core/src/services/widget/widget.ts b/packages/renderer-core/src/services/widget/widget.ts index 42fe619f3..85a8091b1 100644 --- a/packages/renderer-core/src/services/widget/widget.ts +++ b/packages/renderer-core/src/services/widget/widget.ts @@ -1,10 +1,10 @@ -import { type Spec, uniqueId } from '@alilc/lowcode-shared'; +import { type NodeType, uniqueId, type ComponentNode } from '@alilc/lowcode-shared'; import { IComponentTreeModel } from '../model'; export interface IWidget { readonly key: string; - readonly rawNode: Spec.NodeType; + readonly rawNode: NodeType; model: IComponentTreeModel; @@ -14,17 +14,17 @@ export interface IWidget { export class Widget implements IWidget { - public rawNode: Spec.NodeType; + public rawNode: NodeType; public key: string; public children?: IWidget[] | undefined; constructor( - node: Spec.NodeType, + node: NodeType, public model: IComponentTreeModel, ) { this.rawNode = node; - this.key = (node as Spec.ComponentNode)?.id ?? uniqueId(); + this.key = (node as ComponentNode)?.id ?? uniqueId(); } } diff --git a/packages/renderer-core/src/types.ts b/packages/renderer-core/src/types.ts index b31505fa5..151638e68 100644 --- a/packages/renderer-core/src/types.ts +++ b/packages/renderer-core/src/types.ts @@ -1,4 +1,4 @@ -import { type Spec } from '@alilc/lowcode-shared'; +import { type Project, type Package } from '@alilc/lowcode-shared'; import { type Plugin } from './services/extension'; import { type ISchemaService } from './services/schema'; import { type IPackageManagementService } from './services/package'; @@ -6,8 +6,8 @@ import { type CodeRuntimeOptions } from './services/code-runtime'; import { type ModelDataSourceCreator } from './services/model'; export interface AppOptions { - schema: Spec.Project; - packages?: Spec.Package[]; + schema: Project; + packages?: Package[]; plugins?: Plugin[]; /** * 运行模式 diff --git a/packages/renderer-core/src/utils/value.ts b/packages/renderer-core/src/utils/value.ts index 7a10ffc27..f7d81f744 100644 --- a/packages/renderer-core/src/utils/value.ts +++ b/packages/renderer-core/src/utils/value.ts @@ -1,9 +1,9 @@ -import { type PlainObject } from '@alilc/lowcode-shared'; +import { type StringDictionary } from '@alilc/lowcode-shared'; import { isPlainObject, isEmpty } from 'lodash-es'; export function someValue( - obj: PlainObject | PlainObject[], - filter: (data: PlainObject) => boolean, + obj: StringDictionary | StringDictionary[], + filter: (data: StringDictionary) => boolean, ): boolean { if (Array.isArray(obj)) { return obj.some((item) => someValue(item, filter)); @@ -16,8 +16,8 @@ export function someValue( } export function mapValue( - obj: PlainObject, - filter: (obj: PlainObject) => boolean, + obj: StringDictionary, + filter: (obj: StringDictionary) => boolean, callback: (node: any, paths: Array) => any, ): any { if (!someValue(obj, filter)) return obj; @@ -33,7 +33,7 @@ export function mapValue( return callback(target, paths); } - const result: PlainObject = {}; + const result: StringDictionary = {}; for (const [key, value] of Object.entries(target)) { result[key] = mapping(value, [...paths, key]); diff --git a/packages/renderer-router/src/matcher.ts b/packages/renderer-router/src/matcher.ts index e5bac15bb..fd0727bc8 100644 --- a/packages/renderer-router/src/matcher.ts +++ b/packages/renderer-router/src/matcher.ts @@ -1,6 +1,6 @@ // refer from https://github.com/vuejs/router/blob/main/packages/router/src/matcher/index.ts -import { type PlainObject } from '@alilc/lowcode-shared'; +import { type StringDictionary } from '@alilc/lowcode-shared'; import { pick } from 'lodash-es'; import { createRouteRecordMatcher, type RouteRecordMatcher } from './utils/record-matcher'; import { @@ -18,7 +18,7 @@ export interface RouteRecordNormalized { name: RouteRecord['name']; path: RouteRecord['path']; page: string; - meta: PlainObject; + meta: StringDictionary; /** * {@link RouteRecord.redirect} */ diff --git a/packages/renderer-router/src/router.ts b/packages/renderer-router/src/router.ts index 2947eeb6f..304c39324 100644 --- a/packages/renderer-router/src/router.ts +++ b/packages/renderer-router/src/router.ts @@ -1,4 +1,4 @@ -import { type Spec } from '@alilc/lowcode-shared'; +import { type RouterConfig, type RouterApi } from '@alilc/lowcode-shared'; import { createBrowserHistory, createHashHistory, @@ -20,11 +20,11 @@ import type { import { type NavigationHookAfter, type NavigationGuard, guardToPromiseFn } from './guard'; import { createCallback } from './utils/callback'; -export interface RouterOptions extends Spec.RouterConfig, PathParserOptions { +export interface RouterOptions extends RouterConfig, PathParserOptions { routes: RouteRecord[]; } -export interface Router extends Spec.RouterApi { +export interface Router extends RouterApi { readonly options: RouterOptions; readonly history: RouterHistory; diff --git a/packages/renderer-router/src/types.ts b/packages/renderer-router/src/types.ts index bf694fa4b..0b9aa65fa 100644 --- a/packages/renderer-router/src/types.ts +++ b/packages/renderer-router/src/types.ts @@ -1,13 +1,17 @@ -import type { Spec, PlainObject } from '@alilc/lowcode-shared'; +import type { RouteRecord as SpecRouteRecord, StringDictionary } from '@alilc/lowcode-shared'; import type { PathParserOptions } from './utils/path-parser'; -export type RawRouteLocation = Spec.RawRouteLocation; -export type RouteLocation = Spec.RouteLocation; -export type RawLocation = Spec.RawLocation; -export type RawLocationOptions = Spec.RawLocationOptions; +import { + RawRouteLocation, + RouteLocation, + RawLocation, + RawLocationOptions, +} from '@alilc/lowcode-shared'; -export interface RouteRecord extends Spec.RouteRecord, PathParserOptions { - meta?: PlainObject; +export { RawRouteLocation, RouteLocation, RawLocation, RawLocationOptions }; + +export interface RouteRecord extends SpecRouteRecord, PathParserOptions { + meta?: StringDictionary; redirect?: | string | RawRouteLocation diff --git a/packages/shared/src/abilities/event.ts b/packages/shared/src/abilities/event.ts index 7ef4ddf92..876729a45 100644 --- a/packages/shared/src/abilities/event.ts +++ b/packages/shared/src/abilities/event.ts @@ -1,11 +1,49 @@ -import { Hookable, type HookKeys, type HookCallback } from 'hookable'; +import { Hookable, type HookKeys } from 'hookable'; -export type EventListener = HookCallback; +type ArrayT = T extends any[] ? T : [T]; + +export type Event = (listener: EventListener) => EventDisposable; +export type EventListener = (...arguments_: ArrayT) => Promise | void; export type EventDisposable = () => void; +export interface IEmitter { + on: Event; + emit(...args: ArrayT): void; + emitAsync(...args: ArrayT): Promise; + clear(): void; +} + +export class Emitter implements IEmitter { + private events: EventListener[] = []; + + on(fn: EventListener): EventDisposable { + this.events.push(fn); + + return () => { + this.events = this.events.filter((e) => e !== fn); + }; + } + + emit(...args: ArrayT) { + for (const event of this.events) { + event.call(null, ...args); + } + } + + async emitAsync(...args: ArrayT) { + for (const event of this.events) { + await event.call(null, ...args); + } + } + + clear() { + this.events.length = 0; + } +} + export interface IEventEmitter< - HooksT extends Record = Record, - HookNameT extends HookKeys = HookKeys, + EventT extends Record = Record, + EventNameT extends HookKeys = HookKeys, > { /** * 监听事件 @@ -13,14 +51,14 @@ export interface IEventEmitter< * @param event 事件名称 * @param listener 事件回调 */ - on(event: HookNameT, listener: HooksT[HookNameT]): EventDisposable; + on(event: EventNameT, listener: EventT[EventNameT]): EventDisposable; /** * 添加只运行一次的监听事件 * @param event 事件名称 * @param listener 事件回调 */ - once(event: HookNameT, listener: HooksT[HookNameT]): void; + once(event: EventNameT, listener: EventT[EventNameT]): void; /** * 触发事件 @@ -28,7 +66,7 @@ export interface IEventEmitter< * @param event 事件名称 * @param args 事件参数 */ - emit(event: HookNameT, ...args: any): Promise; + emit(event: EventNameT, ...args: any): Promise; /** * 取消监听事件 @@ -36,14 +74,14 @@ export interface IEventEmitter< * @param event 事件名称 * @param listener 事件回调 */ - off(event: HookNameT, listener: HooksT[HookNameT]): void; + off(event: EventNameT, listener: EventT[EventNameT]): void; /** * 监听事件,会在其他回调函数之前执行 * @param event 事件名称 * @param listener 事件回调 */ - prependListener(event: HookNameT, listener: HooksT[HookNameT]): EventDisposable; + prependListener(event: EventNameT, listener: EventT[EventNameT]): EventDisposable; /** * 清除所有事件监听 @@ -52,30 +90,30 @@ export interface IEventEmitter< } export class EventEmitter< - HooksT extends Record = Record, - HookNameT extends HookKeys = HookKeys, -> implements IEventEmitter + EventT extends Record = Record>, + EventNameT extends HookKeys = HookKeys, +> implements IEventEmitter { private namespace: string | undefined; - private hooks = new Hookable(); + private hooks = new Hookable(); constructor(namespace?: string) { this.namespace = namespace; } - on(event: HookNameT, listener: HooksT[HookNameT]): EventDisposable { + on(event: EventNameT, listener: EventT[EventNameT]): EventDisposable { return this.hooks.hook(event, listener); } - once(event: HookNameT, listener: HooksT[HookNameT]): void { + once(event: EventNameT, listener: EventT[EventNameT]): void { this.hooks.hookOnce(event, listener); } - async emit(event: HookNameT, ...args: any) { + async emit(event: EventNameT, ...args: any) { return this.hooks.callHook(event, ...args); } - off(event: HookNameT, listener: HooksT[HookNameT]): void { + off(event: EventNameT, listener: EventT[EventNameT]): void { this.hooks.removeHook(event, listener); } @@ -84,17 +122,11 @@ export class EventEmitter< * @param event 事件名称 * @param listener 事件回调 */ - prependListener(event: HookNameT, listener: HooksT[HookNameT]): EventDisposable { - return this.hooks.hook(`${event}:before` as HookNameT, listener); + prependListener(event: EventNameT, listener: EventT[EventNameT]): EventDisposable { + return this.hooks.hook(`${event}:before` as EventNameT, listener); } removeAll(): void { this.hooks.removeAllHooks(); } } - -export function createEventEmitter>( - namespace?: string, -): EventEmitter { - return new EventEmitter(namespace); -} diff --git a/packages/shared/src/abilities/instantiation/decorators.ts b/packages/shared/src/abilities/instantiation/decorators.ts new file mode 100644 index 000000000..50a6d49e5 --- /dev/null +++ b/packages/shared/src/abilities/instantiation/decorators.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from 'inversify'; +import { fluentProvide } from 'inversify-binding-decorators'; + +/** + * Identifies a service of type `T`. + */ +export interface ServiceIdentifier { + (...args: any[]): void; + type: T; +} + +export type Constructor = new (...args: any[]) => T; + +export function createDecorator(serviceId: string): ServiceIdentifier { + const id = ( + function (target: Constructor, targetKey: string, indexOrPropertyDescriptor: any): any { + return inject(serviceId)(target, targetKey, indexOrPropertyDescriptor); + } + ); + id.toString = () => serviceId; + + return id; +} + +export const Injectable = injectable; + +export function Provide(serviceId: ServiceIdentifier, isSingleTon?: boolean) { + const ret = fluentProvide(serviceId.toString()); + + if (isSingleTon) { + return ret.inSingletonScope().done(); + } + return ret.done(); +} diff --git a/packages/shared/src/abilities/instantiation/index.ts b/packages/shared/src/abilities/instantiation/index.ts index 50196be9c..a3c5ead51 100644 --- a/packages/shared/src/abilities/instantiation/index.ts +++ b/packages/shared/src/abilities/instantiation/index.ts @@ -1,59 +1,3 @@ import '@abraham/reflection'; -import { Container, inject, interfaces, injectable } from 'inversify'; -import { fluentProvide, buildProviderModule } from 'inversify-binding-decorators'; - -/** - * Identifies a service of type `T`. - */ -export interface ServiceIdentifier { - (...args: any[]): void; - type: T; -} - -export type Constructor = new (...args: any[]) => T; - -export function createDecorator(serviceId: string): ServiceIdentifier { - const id = ( - function (target: Constructor, targetKey: string, indexOrPropertyDescriptor: any): any { - return inject(serviceId)(target, targetKey, indexOrPropertyDescriptor); - } - ); - id.toString = () => serviceId; - - return id; -} - -export const Injectable = injectable; - -export function Provide(serviceId: ServiceIdentifier, isSingleTon?: boolean) { - const ret = fluentProvide(serviceId.toString()); - - if (isSingleTon) { - return ret.inSingletonScope().done(); - } - return ret.done(); -} - -export class InstantiationService { - private container: Container; - - constructor(options?: interfaces.ContainerOptions) { - this.container = new Container(options); - } - - get(serviceIdentifier: ServiceIdentifier) { - return this.container.get(serviceIdentifier); - } - - set(serviceIdentifier: ServiceIdentifier, constructor: Constructor) { - this.container.bind(serviceIdentifier).to(constructor); - } - - createInstance(App: T) { - return this.container.resolve>(App); - } - - bootstrapModules() { - this.container.load(buildProviderModule()); - } -} +export * from './instantiationService'; +export * from './decorators'; diff --git a/packages/shared/src/abilities/instantiation/instantiationService.ts b/packages/shared/src/abilities/instantiation/instantiationService.ts new file mode 100644 index 000000000..5ea7ed7ef --- /dev/null +++ b/packages/shared/src/abilities/instantiation/instantiationService.ts @@ -0,0 +1,46 @@ +import { Container, interfaces } from 'inversify'; +import { buildProviderModule } from 'inversify-binding-decorators'; +import { ServiceIdentifier, Constructor } from './decorators'; + +export interface InstanceAccessor { + get(id: ServiceIdentifier): T; +} + +export class InstantiationService { + container: Container; + + constructor(options?: interfaces.ContainerOptions) { + this.container = new Container(options); + } + + get(serviceIdentifier: ServiceIdentifier) { + return this.container.get(serviceIdentifier); + } + + /** + * Calls a function with a service accessor. + */ + invokeFunction( + fn: (accessor: InstanceAccessor, ...args: TS) => R, + ...args: TS + ): R { + const accessor: InstanceAccessor = { + get: (id) => { + return this.get(id); + }, + }; + return fn(accessor, ...args); + } + + set(serviceIdentifier: ServiceIdentifier, constructor: Constructor) { + this.container.bind(serviceIdentifier).to(constructor); + } + + createInstance(App: T) { + return this.container.resolve>(App); + } + + bootstrapModules() { + this.container.load(buildProviderModule()); + } +} diff --git a/packages/shared/src/abilities/storage.ts b/packages/shared/src/abilities/storage.ts index 14b21496a..798f0a20e 100644 --- a/packages/shared/src/abilities/storage.ts +++ b/packages/shared/src/abilities/storage.ts @@ -1,4 +1,4 @@ -import { PlainObject } from '../types'; +import { StringDictionary } from '../types'; /** * MapLike interface @@ -19,7 +19,9 @@ export interface IStore { /** * 统一存储接口 */ -export class KeyValueStore implements IStore { +export class KeyValueStore + implements IStore +{ private readonly store = new Map(); private setterValidation: ((key: K, value: O[K]) => boolean | string) | undefined; diff --git a/packages/shared/src/signals.ts b/packages/shared/src/signals.ts index e5e0acd98..8db0a2dad 100644 --- a/packages/shared/src/signals.ts +++ b/packages/shared/src/signals.ts @@ -4,7 +4,7 @@ * https://github.com/tc39/proposal-signals */ -import { AnyFunction, type PlainObject } from './types'; +import { AnyFunction, type StringDictionary } from './types'; import { ref, computed, @@ -299,7 +299,7 @@ function traverse(value: unknown, depth?: number, currentDepth = 0, seen?: Set void; + +export type AnyFunction = (...args: any[]) => any; + +export type PlainObject = Record; + +export type StringDictionary = Record; + +export type NumberDictionary = Record; diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index 3dac624f9..c6d1e2fc6 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -1,11 +1,3 @@ -import * as Spec from './specs'; - -export { Spec }; - -export * from './material'; - -export type VoidFunction = (...args: any[]) => void; - -export type AnyFunction = (...args: any[]) => any; - -export type PlainObject = Record; +export * from './specs'; +export * from './json'; +export * from './common'; diff --git a/packages/shared/src/types/json.ts b/packages/shared/src/types/json.ts new file mode 100644 index 000000000..e002ff5d9 --- /dev/null +++ b/packages/shared/src/types/json.ts @@ -0,0 +1,7 @@ +export type JSONValueType = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object'; + +export type JSONValue = number | string | boolean | null; + +export interface JSONObject { + [key: string]: JSONValue | JSONObject | JSONObject[]; +} diff --git a/packages/shared/src/types/material.ts b/packages/shared/src/types/material.ts deleted file mode 100644 index 06ee15d71..000000000 --- a/packages/shared/src/types/material.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Package } from './specs/asset-spec'; -import { ComponentTreeRoot } from './specs/lowcode-spec'; - -export interface ProCodeComponent extends Package { - package: string; - type: 'proCode'; - library: string; -} - -export interface LowCodeComponent extends Package { - id: string; - type: 'lowCode'; - componentName: string; - schema: ComponentTreeRoot; -} diff --git a/packages/shared/src/types/specs/asset-spec.ts b/packages/shared/src/types/specs/asset-spec.ts index 98629bb43..b1cb73351 100644 --- a/packages/shared/src/types/specs/asset-spec.ts +++ b/packages/shared/src/types/specs/asset-spec.ts @@ -2,7 +2,52 @@ * https://lowcode-engine.cn/site/docs/specs/assets-spec * 低代码引擎资产包协议规范 */ +import { StringDictionary } from '..'; import { ComponentTreeRoot } from './lowcode-spec'; +import { ComponentMetaData, Reference } from './material-spec'; + +/** + * 设计器资产包协议 + */ +export interface Assets { + /** + * 当前协议版本号 + */ + version: string; + /** + * 低代码编辑器中加载的资源列表 + */ + packages?: Package[]; + /** + * 所有组件的描述协议列表 + */ + components: ComponentMetaData[]; + /** + * 用于组件面板中的 tab 和 category 的显示顺序 + */ + sort?: { + /** + * 组件分组 + */ + groupList: string[]; + /** + * 组件面板中同一个 tab 下的不同区间用 category 区分 + */ + categoryList: string[]; + }; + /** + * 设计器中插件描述协议列表 + */ + plugins?: PluginDescription[]; + /** + * 设计器中设置器描述协议列表 + */ + setters?: PluginDescription[]; + /** + * 平台自定义扩展字段 + */ + extConfig?: StringDictionary; +} export interface Package { /** @@ -97,3 +142,45 @@ export interface MultiModeUrls { * 资源加载环境种类 */ export type LoadEnv = 'design' | 'runtime'; + +/** + * 插件声明 + */ +export interface PluginDescription { + /** + * 插件名称 + */ + name: string; + /** + * 插件标题 + */ + title?: string; + /** + * 插件类型 + */ + type?: string; + /** + * 插件描述 + */ + description?: string; + /** + * 插件文档地址 + */ + docUrl?: string; + /** + * 插件截图 + */ + screenshot?: string; + /** + * 插件相关的标签 + */ + tags?: string[]; + /** + * 插件关键字 + */ + keywords?: string[]; + /** + * 插件引用的资源信息 + */ + reference: Reference; +} diff --git a/packages/shared/src/types/specs/lowcode-spec.ts b/packages/shared/src/types/specs/lowcode-spec.ts index 96c22f957..f1dad1bb0 100644 --- a/packages/shared/src/types/specs/lowcode-spec.ts +++ b/packages/shared/src/types/specs/lowcode-spec.ts @@ -2,6 +2,8 @@ * https://lowcode-engine.cn/site/docs/specs/lowcode-spec * 低代码引擎搭建协议规范 */ +import { JSONObject, JSONValue } from '../json'; +import { Reference } from './material-spec'; /** * https://lowcode-engine.cn/site/docs/specs/lowcode-spec#2-%E5%8D%8F%E8%AE%AE%E7%BB%93%E6%9E%84 @@ -23,7 +25,7 @@ export interface Project { /** * 工具类扩展映射关系 */ - utils?: Util[]; + utils?: UtilDescription[]; /** * 国际化语料 */ @@ -90,39 +92,13 @@ export interface ProjectConfig { * https://lowcode-engine.cn/site/docs/specs/lowcode-spec#22-%E7%BB%84%E4%BB%B6%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BBa * 协议中用于描述 componentName 到公域组件映射关系的规范。 */ -export interface ComponentMap { +export interface ComponentMap extends Reference { /** * 协议中的组件名,对应包导出的组件名,是一个有效的 JS 标识符 */ componentName?: string; - /** - * npm 公域的 package name,唯一性 - */ - package: string; - /** - * package version - */ - version?: string; - /** - * 使用解构方式对模块进行导出 - */ - destructuring?: boolean; - /** - * 包导出的组件名 - */ - exportName?: string; - /** - * 下标子组件名称 - */ - subName?: string; - /** - * 包导出组件入口文件路径 - */ - main?: string; - /** - * proCode or lowCode - */ - devMode?: string; + + devMode?: 'lowCode' | 'proCode'; } /** @@ -342,7 +318,7 @@ export interface FunctionUtil { * https://lowcode-engine.cn/site/docs/specs/lowcode-spec#24-%E5%B7%A5%E5%85%B7%E7%B1%BB%E6%89%A9%E5%B1%95%E6%8F%8F%E8%BF%B0aa * 用于描述物料开发过程中,自定义扩展或引入的第三方工具类(例如:lodash 及 moment),增强搭建基础协议的扩展性,提供通用的工具类方法的配置方案及调用 API。 */ -export type Util = NPMUtil | FunctionUtil; +export type UtilDescription = NPMUtil | FunctionUtil; /** * https://lowcode-engine.cn/site/docs/specs/lowcode-spec#25-%E5%9B%BD%E9%99%85%E5%8C%96%E5%A4%9A%E8%AF%AD%E8%A8%80%E6%94%AF%E6%8C%81aa @@ -439,12 +415,6 @@ export interface PageConfig { config?: JSONObject; } -export type JSONValue = number | string | boolean | null; - -export interface JSONObject { - [key: string]: JSONValue | JSONObject | JSONObject[]; -} - export interface JSNode { type: string; [key: string]: any; diff --git a/packages/shared/src/types/specs/material-spec.ts b/packages/shared/src/types/specs/material-spec.ts index ce32121bc..2132d27e7 100644 --- a/packages/shared/src/types/specs/material-spec.ts +++ b/packages/shared/src/types/specs/material-spec.ts @@ -4,7 +4,8 @@ * 让组件针对不同的搭建平台接入时可以使用一份统一的描述内容,让组件在不同的业务中流通成为可能。 */ import { ComponentTree, ComponentNode } from './lowcode-spec'; -import { PlainObject } from '../index'; +import { StringDictionary } from '../index'; +import { Package } from './asset-spec'; export interface LowCodeComponentTree extends ComponentTree { componentName: 'Component'; @@ -13,15 +14,11 @@ export interface LowCodeComponentTree extends ComponentTree { /** * 组件基础信息 */ -export interface ComponentMetaData { +export interface ComponentMetaData { /** * 组件名 */ componentName: string; - /** - * unique id - */ - uri?: string; /** * 组件名称 */ @@ -67,22 +64,19 @@ export interface ComponentMetaData * 组件研发模式 */ devMode?: 'proCode' | 'lowCode'; - /** - * npm 源引入完整描述对象 + * 引用完整描述对象 */ - npm?: NpmInfo; - + reference?: Reference; /** * 低代码组件 schema - * @todo 待补充文档 */ schema?: LowCodeComponentTree; + /** * 可用片段 */ snippets?: Snippet[]; - /** * 组件属性信息 */ @@ -97,21 +91,21 @@ export interface ComponentMetaData } /** - * npm 源引入完整描述对象 + * 引用完整描述对象 */ -export interface NpmInfo { +export interface Reference { /** - * 源码组件名称 + * 引用资源的 id 标识 */ - componentName?: string; + id?: string; + /** + * 资源版本号 + */ + version: string; /** * 源码组件库名 */ - package: string; - /** - * 源码组件版本号 - */ - version?: string; + package?: string; /** * 是否解构 */ @@ -125,7 +119,7 @@ export interface NpmInfo { */ subName?: string; /** - * 组件路径 + * 引用的资源主入口 */ main?: string; } @@ -232,3 +226,16 @@ export interface Snippet { */ schema?: ComponentNode; } + +export interface ProCodeComponent extends Package { + package: string; + type: 'proCode'; + library: string; +} + +export interface LowCodeComponent extends Package { + id: string; + type: 'lowCode'; + componentName: string; + schema: LowCodeComponentTree; +} diff --git a/packages/shared/src/types/specs/runtime.ts b/packages/shared/src/types/specs/runtime.ts index 0290ffae8..7954a67aa 100644 --- a/packages/shared/src/types/specs/runtime.ts +++ b/packages/shared/src/types/specs/runtime.ts @@ -1,4 +1,4 @@ -import { AnyFunction, PlainObject } from '../index'; +import { AnyFunction, StringDictionary } from '../index'; /** * 在上述事件类型描述和变量类型描述中,在函数或 JS 表达式内,均可以通过 this 对象获取当前组件所在容器的实例化对象 @@ -9,7 +9,7 @@ export interface InstanceApi extends InstanceStateApi, Inst /** * 容器的 props 对象 */ - props?: PlainObject; + props?: StringDictionary; /** * ref 对应组件上配置的 ref 属性,用于唯一标识一个组件;若有同名的,则会返回第一个匹配的。 * @param ref 组件标识 @@ -24,7 +24,7 @@ export interface InstanceApi extends InstanceStateApi, Inst [methodName: string]: any; } -export interface InstanceStateApi { +export interface InstanceStateApi { /** * 实例的数据对象 state */ @@ -206,7 +206,7 @@ export interface RouteLocation { /** * 匹配到的路由记录元数据 */ - meta: PlainObject | undefined; + meta: StringDictionary | undefined; /** * 重定向之前的路由,在跳转到当前路径之前的路由记录 */ diff --git a/packages/shared/src/utils/callback.ts b/packages/shared/src/utils/callback.ts deleted file mode 100644 index 015b902d7..000000000 --- a/packages/shared/src/utils/callback.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { AnyFunction } from '../types'; - -export function createCallback() { - let events: T[] = []; - - function add(fn: T) { - events.push(fn); - - return () => { - events = events.filter((e) => e !== fn); - }; - } - - function remove(fn: T) { - events = events.filter((f) => fn !== f); - } - - function list() { - return [...events]; - } - - return { - add, - remove, - list, - clear() { - events.length = 0; - }, - }; -} diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index ad1a7a4cc..373d724c7 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -1,8 +1,7 @@ export * from './invariant'; export * from './is-promise'; export * from './unique-id'; -export * from './type-guards'; +export * from './types'; export * from './platform'; -export * from './callback'; export * from './async'; export * from './node'; diff --git a/packages/shared/src/utils/node.ts b/packages/shared/src/utils/node.ts index cc18ef1f3..2b622b940 100644 --- a/packages/shared/src/utils/node.ts +++ b/packages/shared/src/utils/node.ts @@ -3,14 +3,12 @@ * fork from: https://github.com/Rich-Harris/estree-walker */ -import { type PlainObject, type Spec } from '../types'; - -type Node = Spec.JSNode; +import { type StringDictionary, type JSNode } from '../types'; interface WalkerContext { skip: () => void; remove: () => void; - replace: (node: Node) => void; + replace: (node: JSNode) => void; } class WalkerBase { @@ -18,7 +16,7 @@ class WalkerBase { should_remove: boolean = false; - replacement: Node | null = null; + replacement: JSNode | null = null; context: WalkerContext; @@ -31,10 +29,10 @@ class WalkerBase { } replace( - parent: Node | null, - prop: keyof Node | null | undefined, + parent: JSNode | null, + prop: keyof JSNode | null | undefined, index: number | null | undefined, - node: Node, + node: JSNode, ) { if (parent && prop) { if (index != null) { @@ -46,8 +44,8 @@ class WalkerBase { } remove( - parent: Node | null | undefined, - prop: keyof Node | null | undefined, + parent: JSNode | null | undefined, + prop: keyof JSNode | null | undefined, index: number | null | undefined, ) { if (parent && prop) { @@ -62,8 +60,8 @@ class WalkerBase { export type SyncWalkerHandler = ( this: WalkerContext, - node: Node, - parent: Node | null, + node: JSNode, + parent: JSNode | null, key: PropertyKey | undefined, index: number | undefined, ) => void; @@ -81,11 +79,11 @@ export class SyncWalker extends WalkerBase { } visit( - node: Node, - parent: Node | null, - prop?: keyof Node | undefined, + node: JSNode, + parent: JSNode | null, + prop?: keyof JSNode | undefined, index?: number | undefined, - ): Node | null { + ): JSNode | null { if (node) { if (this.enter) { const _should_skip = this.should_skip; @@ -117,7 +115,7 @@ export class SyncWalker extends WalkerBase { if (removed) return null; } - let key: keyof Node; + let key: keyof JSNode; for (key in node) { const value = node[key] as unknown; @@ -172,18 +170,15 @@ export class SyncWalker extends WalkerBase { /** * Ducktype a node. - * - * @param {unknown} value - * @returns {value is Node} */ -export function isNode(value: unknown): value is Node { +export function isNode(value: unknown): value is JSNode { return ( value !== null && typeof value === 'object' && 'type' in value && typeof value.type === 'string' ); } export function walk( - ast: PlainObject, + ast: StringDictionary, { enter, leave }: { enter?: SyncWalkerHandler; leave?: SyncWalkerHandler } = {}, ) { const instance = new SyncWalker(enter, leave); diff --git a/packages/shared/src/utils/type-guards/index.ts b/packages/shared/src/utils/type-guards/index.ts deleted file mode 100644 index aa58622ee..000000000 --- a/packages/shared/src/utils/type-guards/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './spec'; -export * from './material'; diff --git a/packages/shared/src/utils/type-guards/material.ts b/packages/shared/src/utils/type-guards/material.ts deleted file mode 100644 index 5e58b72c7..000000000 --- a/packages/shared/src/utils/type-guards/material.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { LowCodeComponent, ProCodeComponent } from '../../types'; -import { isPlainObject } from 'lodash-es'; - -export function isLowCodeComponentPackage(v: unknown): v is LowCodeComponent { - return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema; -} - -export function isProCodeComponentPackage(v: unknown): v is ProCodeComponent { - return isPlainObject(v) && (v as any).package && (v as any).library; -} diff --git a/packages/shared/src/utils/type-guards/spec.ts b/packages/shared/src/utils/type-guards/spec.ts deleted file mode 100644 index da4ca4089..000000000 --- a/packages/shared/src/utils/type-guards/spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Spec } from '../../types'; -import { isPlainObject } from 'lodash-es'; - -export function isJSExpression(v: unknown): v is Spec.JSExpression { - return ( - isPlainObject(v) && (v as any).type === 'JSExpression' && typeof (v as any).value === 'string' - ); -} - -export function isJSFunction(v: unknown): v is Spec.JSFunction { - return ( - isPlainObject(v) && (v as any).type === 'JSFunction' && typeof (v as any).value === 'string' - ); -} - -export function isJSSlot(v: unknown): v is Spec.JSSlot { - return isPlainObject(v) && (v as any).type === 'JSSlot' && (v as any).value; -} - -export function isJSI18nNode(v: unknown): v is Spec.JSI18n { - return isPlainObject(v) && (v as any).type === 'i18n' && typeof (v as any).key === 'string'; -} - -export function isComponentNode(v: unknown): v is Spec.ComponentNode { - return isPlainObject(v) && (v as any).componentName; -} diff --git a/packages/shared/src/utils/types/index.ts b/packages/shared/src/utils/types/index.ts new file mode 100644 index 000000000..2fec07d05 --- /dev/null +++ b/packages/shared/src/utils/types/index.ts @@ -0,0 +1,2 @@ +export * as specTypes from './spec'; +export * as jsonTypes from './json'; diff --git a/packages/shared/src/utils/types/json.ts b/packages/shared/src/utils/types/json.ts new file mode 100644 index 000000000..92ee31f46 --- /dev/null +++ b/packages/shared/src/utils/types/json.ts @@ -0,0 +1,17 @@ +export function getDefaultValue(type: string | string[] | undefined): any { + const t = Array.isArray(type) ? (type)[0] : type; + switch (t) { + case 'boolean': + return false; + case 'number': + return 0; + case 'string': + return ''; + case 'array': + return []; + case 'object': + return {}; + default: + return null; + } +} diff --git a/packages/shared/src/utils/types/spec.ts b/packages/shared/src/utils/types/spec.ts new file mode 100644 index 000000000..7e742a585 --- /dev/null +++ b/packages/shared/src/utils/types/spec.ts @@ -0,0 +1,42 @@ +import type { + JSExpression, + JSFunction, + JSSlot, + JSI18n, + ComponentNode, + LowCodeComponent, + ProCodeComponent, +} from '../../types'; +import { isPlainObject } from 'lodash-es'; + +export function isJSExpression(v: unknown): v is JSExpression { + return ( + isPlainObject(v) && (v as any).type === 'JSExpression' && typeof (v as any).value === 'string' + ); +} + +export function isJSFunction(v: unknown): v is JSFunction { + return ( + isPlainObject(v) && (v as any).type === 'JSFunction' && typeof (v as any).value === 'string' + ); +} + +export function isJSSlot(v: unknown): v is JSSlot { + return isPlainObject(v) && (v as any).type === 'JSSlot' && (v as any).value; +} + +export function isJSI18nNode(v: unknown): v is JSI18n { + return isPlainObject(v) && (v as any).type === 'i18n' && typeof (v as any).key === 'string'; +} + +export function isComponentNode(v: unknown): v is ComponentNode { + return isPlainObject(v) && (v as any).componentName; +} + +export function isLowCodeComponentPackage(v: unknown): v is LowCodeComponent { + return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema; +} + +export function isProCodeComponentPackage(v: unknown): v is ProCodeComponent { + return isPlainObject(v) && (v as any).package && (v as any).library; +}