From cbe5ed7692b94469d5d408b5a839a22b30c3a62c Mon Sep 17 00:00:00 2001 From: "lihao.ylh" Date: Mon, 15 Nov 2021 19:57:16 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E4=B8=80=E4=BA=9B=20?= =?UTF-8?q?ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/designer/src/document/node/index.ts | 1 - packages/designer/src/document/node/node.ts | 18 ++- .../src/document/node/props/prop-stash.ts | 77 ------------ .../designer/src/document/node/props/props.ts | 3 +- .../bem-tools/manager.test.tsx | 63 ++++++++++ .../designer/tests/document/node/node.test.ts | 112 +++++++++++++++++- .../fixtures/component-metadata/button.ts | 1 + .../tests/fixtures/component-metadata/div.ts | 5 +- .../designer/tests/utils-ut/invariant.test.ts | 1 + packages/designer/tests/utils-ut/slot.test.ts | 6 + 10 files changed, 201 insertions(+), 86 deletions(-) delete mode 100644 packages/designer/src/document/node/props/prop-stash.ts create mode 100644 packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx diff --git a/packages/designer/src/document/node/index.ts b/packages/designer/src/document/node/index.ts index c71b125db..75048d230 100644 --- a/packages/designer/src/document/node/index.ts +++ b/packages/designer/src/document/node/index.ts @@ -2,7 +2,6 @@ export * from './exclusive-group'; export * from './node'; export * from './node-children'; export * from './props/prop'; -export * from './props/prop-stash'; export * from './props/props'; export * from './transform-stage'; export * from './modal-nodes-manager'; diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 86bfda345..ad6172b96 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -142,9 +142,10 @@ export class Node { @computed get title(): string | I18nData | ReactElement { let t = this.getExtraProp('title'); - if (!t && this.componentMeta.descriptor) { - t = this.getProp(this.componentMeta.descriptor, false); - } + // TODO: 暂时走不到这个分支 + // if (!t && this.componentMeta.descriptor) { + // t = this.getProp(this.componentMeta.descriptor, false); + // } if (t) { const v = t.getAsString(); if (v) { @@ -175,7 +176,7 @@ export class Node { this._children.internalInitParent(); this.props.merge( this.upgradeProps(this.initProps(props || {})), - this.upgradeProps(extras || {}), + this.upgradeProps(extras), ); this.setupAutoruns(); } @@ -719,6 +720,7 @@ export class Node { const _extras_: { [key: string]: any } = { ...extras, }; + /* istanbul ignore next */ Object.keys(this._addons).forEach((key) => { const addon = this._addons[key]; if (addon) { @@ -1183,12 +1185,18 @@ export function getZLevelTop(child: Node, zLevel: number): Node | null { return r; } +/** + * 测试两个节点是否为包含关系 + * @param node1 测试的父节点 + * @param node2 测试的被包含节点 + * @returns 是否包含 + */ export function contains(node1: Node, node2: Node): boolean { if (node1 === node2) { return true; } - if (!node1.isParental || !node2.parent) { + if (!node1.isParental() || !node2.parent) { return false; } diff --git a/packages/designer/src/document/node/props/prop-stash.ts b/packages/designer/src/document/node/props/prop-stash.ts deleted file mode 100644 index 4c0da5be8..000000000 --- a/packages/designer/src/document/node/props/prop-stash.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { obx, autorun, untracked, computed, makeObservable, action } from '@ali/lowcode-editor-core'; -import { Prop, IPropParent, UNSET } from './prop'; -import { Props } from './props'; -import { Node } from '../node'; - -export type PendingItem = Prop[]; -export class PropStash implements IPropParent { - readonly isPropStash = true; - - @obx.shallow private space: Set = new Set(); - - @computed private get maps(): Map { - const maps = new Map(); - if (this.space.size > 0) { - this.space.forEach(prop => { - maps.set(prop.key, prop); - }); - } - return maps; - } - - private willPurge: () => void; - - readonly owner: Node; - - constructor(readonly props: Props, write: (item: Prop) => void) { - makeObservable(this); - this.owner = props.owner; - this.willPurge = autorun(() => { - if (this.space.size < 1) { - return; - } - const pending: Prop[] = []; - for (const prop of this.space) { - if (!prop.isUnset() && !prop.isVirtual()) { - this.space.delete(prop); - pending.push(prop); - } - } - if (pending.length > 0) { - untracked(() => { - for (const item of pending) { - write(item); - } - }); - } - }); - } - - @action - get(key: string | number): Prop { - let prop = this.maps.get(key); - if (!prop) { - prop = new Prop(this, UNSET, key); - this.space.add(prop); - } - return prop; - } - - @action - delete(prop: Prop) { - this.space.delete(prop); - prop.purge(); - } - - @action - clear() { - this.space.forEach(item => item.purge()); - this.space.clear(); - } - - @action - purge() { - this.willPurge(); - this.space.clear(); - } -} diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index e5f3e828d..b2cadab4c 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -228,9 +228,10 @@ export class Props implements IPropParent { */ @action deleteKey(key: string): void { - this.items = this.items.filter(item => { + this.items = this.items.filter((item, i) => { if (item.key === key) { item.purge(); + this.items.splice(i, 1); return false; } return true; diff --git a/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx b/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx new file mode 100644 index 000000000..0a337e2c0 --- /dev/null +++ b/packages/designer/tests/builtin-simulator/bem-tools/manager.test.tsx @@ -0,0 +1,63 @@ +import '../../fixtures/window'; +import { set, delayObxTick, delay } from '../../utils'; +import { Editor } from '@ali/lowcode-editor-core'; +import { Project } from '../../../src/project/project'; +import { DocumentModel } from '../../../src/document/document-model'; +import { + isRootNode, + Node, + isNode, + comparePosition, + contains, + insertChild, + insertChildren, + PositionNO, +} from '../../../src/document/node/node'; +import { Designer } from '../../../src/designer/designer'; +import { BemToolsManager } from '../../../src/builtin-simulator/bem-tools/manager'; +import formSchema from '../../fixtures/schema/form'; + +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 }); + // 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/document/node/node.test.ts b/packages/designer/tests/document/node/node.test.ts index ee3928a64..49a17481f 100644 --- a/packages/designer/tests/document/node/node.test.ts +++ b/packages/designer/tests/document/node/node.test.ts @@ -17,6 +17,8 @@ import { 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 otherMeta from '../../fixtures/component-metadata/other'; import pageMetadata from '../../fixtures/component-metadata/page'; @@ -47,6 +49,28 @@ describe('Node 方法测试', () => { 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); @@ -149,6 +173,22 @@ describe('Node 方法测试', () => { 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); @@ -195,6 +235,10 @@ describe('Node 方法测试', () => { 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 } }); @@ -208,6 +252,15 @@ describe('Node 方法测试', () => { 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')!; @@ -278,6 +331,19 @@ describe('Node 方法测试', () => { 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')!; @@ -330,8 +396,24 @@ describe('Node 方法测试', () => { 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); @@ -378,12 +460,39 @@ describe('Node 方法测试', () => { 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); + const callbacks = form.componentMeta.getMetadata().experimental?.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, form); + + form.didDropOut(textField); + expect(fn2).toHaveBeenCalledWith(textField, form); + }); + 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', () => { @@ -410,13 +519,14 @@ describe('Node 方法测试', () => { 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(); - // TODO: 其实这句测试的代码没懂 expect(contains(firstBtn, page)).toBeFalsy(); + expect(contains(firstBtn, content)).toBeFalsy(); expect(contains(firstCard, firstBtn)).toBeFalsy(); expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter); diff --git a/packages/designer/tests/fixtures/component-metadata/button.ts b/packages/designer/tests/fixtures/component-metadata/button.ts index f58abf88f..7950151ee 100644 --- a/packages/designer/tests/fixtures/component-metadata/button.ts +++ b/packages/designer/tests/fixtures/component-metadata/button.ts @@ -253,6 +253,7 @@ export default { // parentWhitelist: 'Div', // childWhitelist: 'Div', }, + descriptor: 'xTitle' }, supports: {}, }, diff --git a/packages/designer/tests/fixtures/component-metadata/div.ts b/packages/designer/tests/fixtures/component-metadata/div.ts index 941b90ef3..bf880bd4a 100644 --- a/packages/designer/tests/fixtures/component-metadata/div.ts +++ b/packages/designer/tests/fixtures/component-metadata/div.ts @@ -230,7 +230,10 @@ export default { supports: {}, }, experimental: { - callbacks: {}, + callbacks: { + onNodeAdd: (dragment, self) => { console.log(dragment); }, + onNodeRemove: (dragment, self) => { console.log(dragment); } + }, initials: [ { name: 'behavior', diff --git a/packages/designer/tests/utils-ut/invariant.test.ts b/packages/designer/tests/utils-ut/invariant.test.ts index 1d19cebf6..6b38e934f 100644 --- a/packages/designer/tests/utils-ut/invariant.test.ts +++ b/packages/designer/tests/utils-ut/invariant.test.ts @@ -4,4 +4,5 @@ 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/slot.test.ts b/packages/designer/tests/utils-ut/slot.test.ts index d69f65c22..079db30a1 100644 --- a/packages/designer/tests/utils-ut/slot.test.ts +++ b/packages/designer/tests/utils-ut/slot.test.ts @@ -21,10 +21,14 @@ const mockNode = { }] }; +// 没有 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', () => { @@ -34,4 +38,6 @@ it('removeSlot', () => { expect(mockNode.slots).toHaveLength(1); expect(removeSlot(mockNode, 'heihei')).toBeTruthy(); expect(mockNode.slots).toHaveLength(0); + + expect(removeSlot(mockNode2, 'xixi')).toBeFalsy(); });