diff --git a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts index 96c92943a..22b9ceb09 100644 --- a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts +++ b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts @@ -103,7 +103,7 @@ LowcodeTypes.exact = (typesMap: any) => { const configs = Object.keys(typesMap).map(key => { return { name: key, - propType: typesMap[key].lowcodeType || 'any', + propType: typesMap[key]?.lowcodeType || 'any', }; }); return define(PropTypes.exact(typesMap), { @@ -117,7 +117,7 @@ LowcodeTypes.shape = (typesMap: any) => { const configs = Object.keys(typesMap).map(key => { return { name: key, - propType: typesMap[key].lowcodeType || 'any', + propType: typesMap[key]?.lowcodeType || 'any', }; }); return define(PropTypes.shape(typesMap), { diff --git a/packages/designer/src/designer/builtin-hotkey.ts b/packages/designer/src/designer/builtin-hotkey.ts index 27b33013d..d098a191d 100644 --- a/packages/designer/src/designer/builtin-hotkey.ts +++ b/packages/designer/src/designer/builtin-hotkey.ts @@ -8,7 +8,6 @@ function isInLiveEditing() { if (globalContext.has(Editor)) { return Boolean(globalContext.get(Editor).get('designer')?.project?.simulator?.liveEditing?.editing); } - return false; } function getNextForSelect(next: any, head?: any, parent?: any): any { diff --git a/packages/designer/tests/builtin-simulator/host.test.tsx b/packages/designer/tests/builtin-simulator/host.test.ts similarity index 56% rename from packages/designer/tests/builtin-simulator/host.test.tsx rename to packages/designer/tests/builtin-simulator/host.test.ts index f181f7138..178db6845 100644 --- a/packages/designer/tests/builtin-simulator/host.test.tsx +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -2,7 +2,7 @@ import React from 'react'; import set from 'lodash/set'; import cloneDeep from 'lodash/cloneDeep'; import '../fixtures/window'; -import { Editor } from '@ali/lowcode-editor-core'; +import { Editor, globalContext } from '@ali/lowcode-editor-core'; import { AssetLevel, Asset, @@ -14,65 +14,100 @@ import { import { Project } from '../../src/project/project'; import { Node } from '../../src/document/node/node'; import { Designer } from '../../src/designer/designer'; +import { DocumentModel } from '../../src/document/document-model'; import formSchema from '../fixtures/schema/form'; import { getMockDocument, getMockWindow, getMockEvent } from '../utils'; import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; -import { eq } from 'lodash'; +import { fireEvent } from '@testing-library/react'; const editor = new Editor(); describe('host 测试', () => { + let editor: Editor; let designer: Designer; - beforeEach(() => { - designer = new Designer({ editor }); - }); - afterEach(() => { - designer._componentMetasMap.clear(); - designer = null; + let project: Project; + let doc: DocumentModel; + + beforeAll(() => { + editor = new Editor(); + !globalContext.has(Editor) && globalContext.register(editor, Editor); }); - it('基础方法测试', async () => { - const host = new BuiltinSimulatorHost(designer.project); - expect(host.currentDocument).toBe(designer.project.currentDocument); - expect(host.renderEnv).toBe('default'); - expect(host.device).toBe('default'); - expect(host.deviceClassName).toBeUndefined(); - host.setProps({ - renderEnv: 'rax', - device: 'mobile', - deviceClassName: 'mobile-rocks', - componentsAsset: [{ + beforeEach(() => { + designer = new Designer({ editor }); + project = designer.project; + doc = project.createDocument(formSchema); + }); + + afterEach(() => { + project.unload(); + project.mountSimulator(undefined); + designer._componentMetasMap.clear(); + designer.purge(); + designer = null; + project = null; + }); + + describe('基础方法测试', () => { + it('setProps / get / set', async () => { + const host = new BuiltinSimulatorHost(designer.project); + 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', - }], - theme: { + }]); + expect(host.theme).toEqual({ type: AssetType.CSSText, content: '.theme {font-size: 50px;}', - } - }); - 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).toBe(designer.componentsMap); + }); + expect(host.componentsMap).toBe(designer.componentsMap); + expect(host.requestHandlersMap).toEqual({}); - host.set('renderEnv', 'vue'); - expect(host.renderEnv).toBe('vue'); + host.set('renderEnv', 'vue'); + expect(host.renderEnv).toBe('vue'); - expect(host.getComponentContext).toThrow('Method not implemented.'); + expect(host.getComponentContext).toThrow('Method not implemented.'); + }); + + it('connect', () => {}); + it('mountViewport', () => {}); + it('mountContentFrame', () => {}); + it('autorun', () => {}); + it('purge', () => {}); + + }); + + describe('事件测试', () => { + it('setupDragAndClick', () => { + + }); }); it('事件测试', async () => { diff --git a/packages/designer/tests/builtin-simulator/resource-consumer.test.ts b/packages/designer/tests/builtin-simulator/resource-consumer.test.ts new file mode 100644 index 000000000..5243112e1 --- /dev/null +++ b/packages/designer/tests/builtin-simulator/resource-consumer.test.ts @@ -0,0 +1,60 @@ +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 }); +}); \ No newline at end of file diff --git a/packages/designer/tests/builtin-simulator/parse-metadata.test.ts b/packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts similarity index 59% rename from packages/designer/tests/builtin-simulator/parse-metadata.test.ts rename to packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts index 50a1ba005..e0c67255c 100644 --- a/packages/designer/tests/builtin-simulator/parse-metadata.test.ts +++ b/packages/designer/tests/builtin-simulator/utils/parse-metadata.test.ts @@ -1,5 +1,5 @@ -import '../fixtures/window'; -import { parseMetadata } from '../../src/builtin-simulator/utils/parse-metadata'; +import '../../fixtures/window'; +import { parseMetadata } from '../../../src/builtin-simulator/utils/parse-metadata'; describe('parseMetadata', () => { it('parseMetadata', async () => { diff --git a/packages/designer/tests/builtin-simulator/path.test.ts b/packages/designer/tests/builtin-simulator/utils/path.test.ts similarity index 98% rename from packages/designer/tests/builtin-simulator/path.test.ts rename to packages/designer/tests/builtin-simulator/utils/path.test.ts index 170cb9f9f..be9ee6013 100644 --- a/packages/designer/tests/builtin-simulator/path.test.ts +++ b/packages/designer/tests/builtin-simulator/utils/path.test.ts @@ -7,7 +7,7 @@ import { removeVersion, resolveAbsoluatePath, joinPath, -} from '../../src/builtin-simulator/utils/path'; +} from '../../../src/builtin-simulator/utils/path'; describe('builtin-simulator/utils/path 测试', () => { it('isPackagePath', () => { diff --git a/packages/designer/tests/builtin-simulator/throttle.test.ts b/packages/designer/tests/builtin-simulator/utils/throttle.test.ts similarity index 75% rename from packages/designer/tests/builtin-simulator/throttle.test.ts rename to packages/designer/tests/builtin-simulator/utils/throttle.test.ts index fd2d7ce33..ffbf6c886 100644 --- a/packages/designer/tests/builtin-simulator/throttle.test.ts +++ b/packages/designer/tests/builtin-simulator/utils/throttle.test.ts @@ -1,5 +1,5 @@ -import '../fixtures/disable-raf'; -import { throttle } from '../../src/builtin-simulator/utils/throttle'; +import '../../fixtures/disable-raf'; +import { throttle } from '../../../src/builtin-simulator/utils/throttle'; const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/packages/designer/tests/builtin-simulator/viewport.test.ts b/packages/designer/tests/builtin-simulator/viewport.test.ts new file mode 100644 index 000000000..ce3534353 --- /dev/null +++ b/packages/designer/tests/builtin-simulator/viewport.test.ts @@ -0,0 +1,180 @@ +import '../fixtures/window'; +import { getMockWindow, set } from '../utils'; +import { Editor, globalContext } from '@ali/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 { fireEvent } from '@testing-library/react'; +import { getMockElement, delay } from '../utils'; + +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 }); + 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 new file mode 100644 index 000000000..1b73ecda5 --- /dev/null +++ b/packages/designer/tests/designer/active-tracker.test.ts @@ -0,0 +1,40 @@ +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(); +}); \ No newline at end of file diff --git a/packages/designer/tests/designer/builtin-hotkey.test.ts b/packages/designer/tests/designer/builtin-hotkey.test.ts index 637c3cbfc..ccdc5a527 100644 --- a/packages/designer/tests/designer/builtin-hotkey.test.ts +++ b/packages/designer/tests/designer/builtin-hotkey.test.ts @@ -1,3 +1,4 @@ +jest.mock('@ali/lowcode-utils'); import set from 'lodash/set'; import cloneDeep from 'lodash/cloneDeep'; import '../fixtures/window'; @@ -6,6 +7,8 @@ import { Designer } from '../../src/designer/designer'; import { Project } from '../../src/project/project'; import formSchema from '../fixtures/schema/form'; import '../../src/designer/builtin-hotkey'; +import { fireEvent } from '@testing-library/react'; +import { isFormEvent } from '@ali/lowcode-utils'; const editor = new Editor(); @@ -28,8 +31,7 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbj')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 39 }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 39 }); expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy(); }); @@ -38,8 +40,7 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 37 }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 37 }); expect(designer.currentSelection?.selected.includes('node_k1ow3cbj')).toBeTruthy(); }); @@ -48,8 +49,7 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 40 }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 40 }); expect(designer.currentSelection?.selected.includes('node_k1ow3cbo')).toBeTruthy(); }); @@ -58,8 +58,7 @@ describe('快捷键测试', () => { const secondCardNode = designer.currentDocument?.getNode('node_k1ow3cbm')!; secondCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 38 }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 38 }); expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy(); }); @@ -69,8 +68,7 @@ describe('快捷键测试', () => { const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!; firstButtonNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 39, altKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 39, altKey: true }); expect(firstButtonNode.prevSibling?.getId()).toBe('node_k1ow3cbp'); }); @@ -80,8 +78,7 @@ describe('快捷键测试', () => { const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; secondButtonNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 37, altKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 37, altKey: true }); expect(secondButtonNode.nextSibling?.getId()).toBe('node_k1ow3cbn'); }); @@ -91,8 +88,7 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 38, altKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 38, altKey: true }); }); // 将节点移入到兄弟节点中 @@ -100,8 +96,7 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 40, altKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 40, altKey: true }); }); // 撤销 @@ -114,8 +109,7 @@ describe('快捷键测试', () => { await new Promise(resolve => setTimeout(resolve, 1000)); - let event = new KeyboardEvent('keydown', { keyCode: 90, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); // 重新获取一次节点,因为 documentModel.import 是全画布刷新 secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; @@ -132,8 +126,7 @@ describe('快捷键测试', () => { await new Promise(resolve => setTimeout(resolve, 1000)); - let event = new KeyboardEvent('keydown', { keyCode: 90, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 90, metaKey: true }); // 重新获取一次节点,因为 documentModel.import 是全画布刷新 secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; @@ -141,8 +134,7 @@ describe('快捷键测试', () => { await new Promise(resolve => setTimeout(resolve, 1000)); - event = new KeyboardEvent('keydown', { keyCode: 89, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 89, metaKey: true }); // 重新获取一次节点,因为 documentModel.import 是全画布刷新 secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; @@ -153,19 +145,16 @@ describe('快捷键测试', () => { const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; firstCardNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 67, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); }); it('command + v', async () => { const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!; secondButtonNode.select(); - let event = new KeyboardEvent('keydown', { keyCode: 67, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 67, metaKey: true }); - event = new KeyboardEvent('keydown', { keyCode: 86, metaKey: true }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 86, metaKey: true }); await new Promise(resolve => setTimeout(resolve, 1000)); @@ -180,8 +169,7 @@ describe('快捷键测试', () => { expect(designer.currentSelection!.selected.includes('node_k1ow3cbp')).toBeTruthy(); - let event = new KeyboardEvent('keydown', { keyCode: 27 }); - document.dispatchEvent(event); + fireEvent.keyDown(document, { keyCode: 27 }); expect(designer.currentSelection!.selected.length).toBe(0); }); @@ -194,9 +182,110 @@ describe('快捷键测试', () => { expect(secondButtonNode.prevSibling.id).toBe('node_k1ow3cbn'); - let event = new KeyboardEvent('keydown', { keyCode: 46 }); - document.dispatchEvent(event); + 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', () => { + designer.currentDocument?.selection.select('page'); + // nothing happened + isFormEvent.mockReturnValue(true); + + 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'); + }); + }); }); diff --git a/packages/designer/tests/designer/designer.test.ts b/packages/designer/tests/designer/designer.test.ts new file mode 100644 index 000000000..0dbad8a83 --- /dev/null +++ b/packages/designer/tests/designer/designer.test.ts @@ -0,0 +1,404 @@ +import '../fixtures/window'; +import { Editor, globalContext } from '@ali/lowcode-editor-core'; +import { Project } from '../../src/project/project'; +import { DocumentModel } from '../../src/document/document-model'; +import { Designer } from '../../src/designer/designer'; +import { Dragon, DragObjectType } 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'; + +describe('Designer 测试', () => { + 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 }); + 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('DragObjectType.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, + 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; + + dragon.boost( + { + type: DragObjectType.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() {}, + }, + }; + 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('DragObjectType.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, + 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; + + dragon.boost( + { + type: DragObjectType.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() {}, + }, + }; + 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 }, TransformStage.Init)).toEqual({ num: 1 }); + // props 是数组 + expect(designer.transformProps([{ num: 1 }], TransformStage.Init)).toEqual([{ num: 1 }]); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Init); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Init); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Clone); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Serilize); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Render); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Save); + + designer.addPropsReducer((props, node) => { + props.num = props.num + 1; + return props; + }, TransformStage.Upgrade); + + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Init)).toEqual({ num: 3 }); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Clone)).toEqual({ num: 2 }); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Serilize)).toEqual({ num: 2 }); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Render)).toEqual({ num: 2 }); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Save)).toEqual({ num: 2 }); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Upgrade)).toEqual({ num: 2 }); + + designer.addPropsReducer((props, node) => { + throw new Error('calculate error'); + }, TransformStage.Upgrade); + expect(designer.transformProps({ num: 1 }, {}, TransformStage.Upgrade)).toEqual({ num: 2 }); + }); + + it('setProps', () => { + // 第一次设置 props + const initialProps = { + simulatorComponent: { isSimulatorComp: true }, + simulatorProps: { designMode: 'design' }, + suspensed: true, + componentMetadatas: [buttonMetadata, divMetadata], + }; + designer = new Designer({ editor, ...initialProps }); + + expect(designer.simulatorComponent).toEqual({ isSimulatorComp: true }); + expect(designer.simulatorProps).toEqual({ designMode: 'design' }); + expect(designer.suspensed).toBeTruthy(); + expect(designer._componentMetasMap.has('Div')).toBeTruthy(); + expect(designer._componentMetasMap.has('Button')).toBeTruthy(); + const { editor: editorFromDesigner, ...others } = designer.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._componentMetasMap.has('Button')).toBeTruthy(); + expect(designer._componentMetasMap.has('Div')).toBeTruthy(); + const { editor: editorFromDesigner2, ...others2 } = designer.props; + expect(others2).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('createLocation / clearLocation', () => { + const mockTarget = { + document: doc, + children: { + get(x) { + return x; + }, + insert() {}, + }, + }; + 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() {}, + }, + }, + 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 new file mode 100644 index 000000000..0e96e5bca --- /dev/null +++ b/packages/designer/tests/designer/detecting.test.ts @@ -0,0 +1,22 @@ +import { Detecting } from '../../src/designer/detecting'; + +it('Detecting 测试', () => { + const detecting = new Detecting(); + + expect(detecting.enable).toBeTruthy(); + + const mockNode = { document }; + detecting.capture(mockNode); + expect(detecting.current).toBe(mockNode); + + 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(); +}); \ No newline at end of file diff --git a/packages/designer/tests/designer/dragon.test.ts b/packages/designer/tests/designer/dragon.test.ts index a340f8a70..76783c2ef 100644 --- a/packages/designer/tests/designer/dragon.test.ts +++ b/packages/designer/tests/designer/dragon.test.ts @@ -30,7 +30,6 @@ import formMetadata from '../fixtures/component-metadata/form'; import otherMeta from '../fixtures/component-metadata/other'; import pageMetadata from '../fixtures/component-metadata/page'; import { fireEvent } from '@testing-library/react'; -import { clearScreenDown } from 'readline'; describe('Dragon 测试', () => { let editor: Editor; diff --git a/packages/designer/tests/designer/location.test.ts b/packages/designer/tests/designer/location.test.ts new file mode 100644 index 000000000..cf748a7c8 --- /dev/null +++ b/packages/designer/tests/designer/location.test.ts @@ -0,0 +1,196 @@ +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' : 'row'; + } + } + }) + .mockImplementationOnce(() => { + return { + getPropertyValue: (pName) => { + return pName === 'display' ? 'flex' : 'column'; + } + } + }); + expect(isRowContainer(getMockElement('div'))).toBeFalsy(); + 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(); + 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); +}); diff --git a/packages/designer/tests/utils/bom.ts b/packages/designer/tests/utils/bom.ts index fca5f73fc..9d25f8f0b 100644 --- a/packages/designer/tests/utils/bom.ts +++ b/packages/designer/tests/utils/bom.ts @@ -14,6 +14,7 @@ interface MockDocument extends Document { 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])); @@ -45,6 +46,7 @@ const mockCreateElement = jest.fn((tagName) => { addEventListener: mockAddEventListener, removeEventListener: mockRemoveEventListener, triggerEventListener: mockTriggerEventListener, + removeAttribute: mockRemoveAttribute, } })