diff --git a/packages/designer/jest.config.js b/packages/designer/jest.config.js index 1f136c9de..faaacb0f5 100644 --- a/packages/designer/jest.config.js +++ b/packages/designer/jest.config.js @@ -19,6 +19,7 @@ module.exports = { '!src/**/*.d.ts', '!src/icons/**', '!src/locale/**', + '!src/builtin-simulator/utils/**', '!src/document/node/exclusive-group.ts', '!**/node_modules/**', '!**/vendor/**', diff --git a/packages/designer/package.json b/packages/designer/package.json index fa9113580..d17d4a24a 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -27,6 +27,7 @@ "devDependencies": { "@ali/lowcode-test-mate": "^1.0.1", "@alib/build-scripts": "^0.1.29", + "@testing-library/react": "^11.2.2", "@types/classnames": "^2.2.7", "@types/jest": "^26.0.16", "@types/lodash": "^4.14.165", diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index fa1ee18e1..ebcdc46db 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -602,7 +602,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost !!item).join('-') || node?.componentMeta?.componentName || ''; - editor?.emit('desiger.builtinSimulator.contextmenu', { + editor?.emit('designer.builtinSimulator.contextmenu', { selected, }); }); @@ -817,6 +817,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost { @@ -193,7 +193,7 @@ export class Designer { this.setupSelection(); setupHistory(); }); - this.postEvent('designer.init', this); + this.postEvent('init', this); this.setupSelection(); setupHistory(); diff --git a/packages/designer/src/designer/scroller.ts b/packages/designer/src/designer/scroller.ts index 24070b306..6467fd494 100644 --- a/packages/designer/src/designer/scroller.ts +++ b/packages/designer/src/designer/scroller.ts @@ -18,18 +18,18 @@ export class ScrollTarget { } get scrollHeight(): number { - return ((this.doe || this.target) as any).scrollHeight; + return ((this.doc || this.target) as any).scrollHeight; } get scrollWidth(): number { - return ((this.doe || this.target) as any).scrollWidth; + return ((this.doc || this.target) as any).scrollWidth; } - private doe?: HTMLElement; + private doc?: HTMLElement; constructor(private target: Window | Element) { if (isWindow(target)) { - this.doe = target.document.documentElement; + this.doc = target.document.documentElement; } } } 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 new file mode 100644 index 000000000..b9622b015 --- /dev/null +++ b/packages/designer/tests/builtin-simulator/bem-tools/drag-resize-engine.test.ts @@ -0,0 +1,131 @@ +import '../../fixtures/window'; +import { 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 { Designer } from '../../../src/designer/designer'; +import DragResizeEngine from '../../../src/builtin-simulator/bem-tools/drag-resize-engine'; +import formSchema from '../../fixtures/schema/form'; +import divMetadata from '../../fixtures/component-metadata/div'; +import formMetadata from '../../fixtures/component-metadata/form'; +import otherMeta from '../../fixtures/component-metadata/other'; +import pageMetadata from '../../fixtures/component-metadata/page'; +import { fireEvent, createEvent } from '@testing-library/react'; +import { create } from 'lodash'; + +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 }); + 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 mockedBoostFn = jest + .fn((e) => { + return boostedNode; + }); + + // do nothing + resizeEngine.from(); + + const offFrom = resizeEngine.from(document, 'e', mockedBoostFn); + + 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 mockedDoc = document.createElement('iframe').contentWindow?.document; + project.mountSimulator({ + sensorAvailable: true, + contentDocument: document, + }); + + const mockedBoostFn = jest + .fn((e) => { + return doc.getNode('node_k1ow3cbn'); + }); + + const offFrom = resizeEngine.from(document, 'e', mockedBoostFn); + + // TODO: 想办法 mock 一个 iframe.currentDocument + fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); + }); +}); diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts index 178db6845..4c4e2672a 100644 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -11,22 +11,31 @@ import { assetItem, AssetType, } from '@ali/lowcode-utils'; +import { + Dragon, + isDragNodeObject, + isDragNodeDataObject, + isDragAnyObject, + isLocateEvent, + DragObjectType, + isShaken, + setShaken, +} from '../../src/designer/dragon'; 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 { getMockDocument, getMockWindow, getMockEvent, delayObxTick } from '../utils'; import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; import { fireEvent } from '@testing-library/react'; -const editor = new Editor(); - -describe('host 测试', () => { +describe('Host 测试', () => { let editor: Editor; let designer: Designer; let project: Project; let doc: DocumentModel; + let host: BuiltinSimulatorHost; beforeAll(() => { editor = new Editor(); @@ -37,6 +46,7 @@ describe('host 测试', () => { designer = new Designer({ editor }); project = designer.project; doc = project.createDocument(formSchema); + host = new BuiltinSimulatorHost(designer.project); }); afterEach(() => { @@ -44,13 +54,14 @@ describe('host 测试', () => { project.mountSimulator(undefined); designer._componentMetasMap.clear(); designer.purge(); + host.purge(); designer = null; project = null; + host = 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'); @@ -60,13 +71,16 @@ describe('host 测试', () => { renderEnv: 'rax', device: 'mobile', deviceClassName: 'mobile-rocks', - componentsAsset: [{ - type: AssetType.JSText, - content: 'console.log(1)', - }, { - type: AssetType.JSUrl, - content: '//path/to/js', - }], + componentsAsset: [ + { + type: AssetType.JSText, + content: 'console.log(1)', + }, + { + type: AssetType.JSUrl, + content: '//path/to/js', + }, + ], theme: { type: AssetType.CSSText, content: '.theme {font-size: 50px;}', @@ -76,13 +90,16 @@ describe('host 测试', () => { 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.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;}', @@ -96,22 +113,314 @@ describe('host 测试', () => { expect(host.getComponentContext).toThrow('Method not implemented.'); }); - it('connect', () => {}); - it('mountViewport', () => {}); - it('mountContentFrame', () => {}); - it('autorun', () => {}); - it('purge', () => {}); + 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: DragObjectType.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: DragObjectType.Node, + nodes: [], + }, + })).toBeUndefined(); + }); + it('locate,没有 document', () => { + project.removeDocument(doc); + expect(host.locate({ + dragObject: { + type: DragObjectType.Node, + nodes: [doc.getNode('page')], + }, + })).toBeNull(); + }); + it('locate', () => { + host.locate({ + dragObject: { + type: DragObjectType.Node, + nodes: [doc.getNode('page')], + }, + }) + }); }); describe('事件测试', () => { - it('setupDragAndClick', () => { + 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' }}, + }; + } + const mockFn = jest.fn(); + host.designer.editor.on('designer.builtinSimulator.contextmenu', mockFn); + fireEvent.contextMenu(document, {}); + // TODO: + // expect(mockFn).toHaveBeenCalledWith({ selected: 'Button' }); }); }); it('事件测试', async () => { - const host = new BuiltinSimulatorHost(designer.project); const mockDocument = getMockDocument(); const mockWindow = getMockWindow(mockDocument); const mockIframe = { @@ -124,11 +433,13 @@ describe('host 测试', () => { host.mountContentFrame(); expect(host._iframe).toBeUndefined(); - host.set('library', [{ - package: '@ali/vc-deep', - library: 'lib', - urls: ['a.js', 'b.js'] - }]); + host.set('library', [ + { + package: '@ali/vc-deep', + library: 'lib', + urls: ['a.js', 'b.js'], + }, + ]); host.componentsConsumer.consume(() => {}); host.injectionConsumer.consume(() => {}); @@ -161,11 +472,7 @@ describe('host 测试', () => { getMockEvent(mockDocument.createElement('div')), host, ); - mockDocument.triggerEventListener( - 'click', - getMockEvent(document.createElement('input')), - host, - ); + mockDocument.triggerEventListener('click', getMockEvent(document.createElement('input')), host); mockDocument.triggerEventListener( 'dblclick', getMockEvent(mockDocument.createElement('div')), @@ -176,5 +483,5 @@ describe('host 测试', () => { getMockEvent(mockDocument.createElement('div')), host, ); - }) + }); }); diff --git a/packages/designer/tests/designer/builtin-hotkey.test.ts b/packages/designer/tests/designer/builtin-hotkey.test.ts index ccdc5a527..493aa5546 100644 --- a/packages/designer/tests/designer/builtin-hotkey.test.ts +++ b/packages/designer/tests/designer/builtin-hotkey.test.ts @@ -1,4 +1,3 @@ -jest.mock('@ali/lowcode-utils'); import set from 'lodash/set'; import cloneDeep from 'lodash/cloneDeep'; import '../fixtures/window'; @@ -8,7 +7,6 @@ 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(); @@ -243,7 +241,6 @@ describe('快捷键测试', () => { 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'); diff --git a/packages/designer/tests/designer/dragon.test.ts b/packages/designer/tests/designer/dragon.test.ts index 76783c2ef..092ee4061 100644 --- a/packages/designer/tests/designer/dragon.test.ts +++ b/packages/designer/tests/designer/dragon.test.ts @@ -38,9 +38,12 @@ describe('Dragon 测试', () => { let doc: DocumentModel; let dragon: Dragon; - beforeEach(() => { + beforeAll(() => { editor = new Editor(); !globalContext.has(Editor) && globalContext.register(editor, Editor); + }); + + beforeEach(() => { designer = new Designer({ editor }); project = designer.project; doc = project.createDocument(formSchema); @@ -51,16 +54,15 @@ describe('Dragon 测试', () => { project.unload(); project.mountSimulator(undefined); designer.purge(); - editor = null; designer = null; project = null; dragon = null; }); it.skip('drag NodeData', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); dragon.onDragstart((e) => { console.log('start', e, e.originalEvent, e.originalEvent.clientX); @@ -95,15 +97,15 @@ describe('Dragon 测试', () => { }); it('mouse NodeData', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); - const offDragStart = dragon.onDragstart(dragStartMockedFn); + const offDragStart = dragon.onDragstart(dragStartMockFn); - const offDrag = dragon.onDrag(dragMockedFn); + const offDrag = dragon.onDrag(dragMockFn); - const offDragEnd = dragon.onDragend(dragEndMockedFn); + const offDragEnd = dragon.onDragend(dragEndMockFn); dragon.boost( { @@ -117,19 +119,19 @@ describe('Dragon 测试', () => { fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - expect(dragStartMockedFn).toHaveBeenCalledTimes(1); - expect(dragMockedFn).toHaveBeenCalledTimes(2); - expect(dragEndMockedFn).toHaveBeenCalledTimes(1); + expect(dragStartMockFn).toHaveBeenCalledTimes(1); + expect(dragMockFn).toHaveBeenCalledTimes(2); + expect(dragEndMockFn).toHaveBeenCalledTimes(1); }); it('mouse Node', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); - const offDragStart = dragon.onDragstart(dragStartMockedFn); - const offDrag = dragon.onDrag(dragMockedFn); - const offDragEnd = dragon.onDragend(dragEndMockedFn); + const offDragStart = dragon.onDragstart(dragStartMockFn); + const offDrag = dragon.onDrag(dragMockFn); + const offDragEnd = dragon.onDragend(dragEndMockFn); dragon.boost( { @@ -140,23 +142,23 @@ describe('Dragon 测试', () => { ); // mouseDown 模式正常不会触发 dragStart 事件,除非 shaken 型 - expect(dragStartMockedFn).not.toHaveBeenCalled(); + expect(dragStartMockFn).not.toHaveBeenCalled(); fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockedFn).toHaveBeenCalledTimes(1); - expect(dragMockedFn).toHaveBeenCalledTimes(1); + expect(dragStartMockFn).toHaveBeenCalledTimes(1); + expect(dragMockFn).toHaveBeenCalledTimes(1); fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockedFn).toHaveBeenCalledTimes(2); + expect(dragMockFn).toHaveBeenCalledTimes(2); expect(dragon.dragging).toBeTruthy(); fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - expect(dragEndMockedFn).toHaveBeenCalledTimes(1); + expect(dragEndMockFn).toHaveBeenCalledTimes(1); offDragStart(); offDrag(); offDragEnd(); - dragMockedFn.mockClear(); + dragMockFn.mockClear(); dragon.boost( { @@ -168,17 +170,17 @@ describe('Dragon 测试', () => { fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragMockedFn).not.toHaveBeenCalled(); + expect(dragMockFn).not.toHaveBeenCalled(); }); it('mouse Node & esc', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); - const offDragStart = dragon.onDragstart(dragStartMockedFn); - const offDrag = dragon.onDrag(dragMockedFn); - const offDragEnd = dragon.onDragend(dragEndMockedFn); + const offDragStart = dragon.onDragstart(dragStartMockFn); + const offDrag = dragon.onDrag(dragMockFn); + const offDragEnd = dragon.onDragend(dragEndMockFn); dragon.boost( { @@ -193,13 +195,13 @@ describe('Dragon 测试', () => { }); it('mouse Node & copy', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); - const offDragStart = dragon.onDragstart(dragStartMockedFn); - const offDrag = dragon.onDrag(dragMockedFn); - const offDragEnd = dragon.onDragend(dragEndMockedFn); + const offDragStart = dragon.onDragstart(dragStartMockFn); + const offDrag = dragon.onDrag(dragMockFn); + const offDragEnd = dragon.onDragend(dragEndMockFn); dragon.boost( { @@ -217,13 +219,13 @@ describe('Dragon 测试', () => { }); it('from', () => { - const dragStartMockedFn = jest.fn(); - const dragMockedFn = jest.fn(); - const dragEndMockedFn = jest.fn(); + const dragStartMockFn = jest.fn(); + const dragMockFn = jest.fn(); + const dragEndMockFn = jest.fn(); - const offDragStart = dragon.onDragstart(dragStartMockedFn); - const offDrag = dragon.onDrag(dragMockedFn); - const offDragEnd = dragon.onDragend(dragEndMockedFn); + const offDragStart = dragon.onDragstart(dragStartMockFn); + const offDrag = dragon.onDrag(dragMockFn); + const offDragEnd = dragon.onDragend(dragEndMockFn); const mockedBoostFn = jest .fn((e) => { return { @@ -237,37 +239,37 @@ describe('Dragon 测试', () => { // 无用 mouseDown,无效的按钮 fireEvent.mouseDown(document, { button: 2 }); - expect(dragStartMockedFn).not.toHaveBeenCalled(); + expect(dragStartMockFn).not.toHaveBeenCalled(); // 无用 mouseDown,无效的 dragObject fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - expect(dragStartMockedFn).not.toHaveBeenCalled(); + expect(dragStartMockFn).not.toHaveBeenCalled(); fireEvent.mouseDown(document, { clientX: 100, clientY: 100 }); - expect(dragStartMockedFn).not.toHaveBeenCalled(); + expect(dragStartMockFn).not.toHaveBeenCalled(); fireEvent.mouseMove(document, { clientX: 108, clientY: 108 }); - expect(dragStartMockedFn).toHaveBeenCalledTimes(1); - expect(dragMockedFn).toHaveBeenCalledTimes(1); + expect(dragStartMockFn).toHaveBeenCalledTimes(1); + expect(dragMockFn).toHaveBeenCalledTimes(1); fireEvent.mouseMove(document, { clientX: 110, clientY: 110 }); - expect(dragMockedFn).toHaveBeenCalledTimes(2); + expect(dragMockFn).toHaveBeenCalledTimes(2); expect(dragon.dragging).toBeTruthy(); fireEvent.mouseUp(document, { clientX: 118, clientY: 118 }); - expect(dragEndMockedFn).toHaveBeenCalledTimes(1); + expect(dragEndMockFn).toHaveBeenCalledTimes(1); offDragStart(); offDrag(); offDragEnd(); - dragMockedFn.mockClear(); + dragMockFn.mockClear(); fireEvent.mouseMove(document, { clientX: 100, clientY: 100 }); - expect(dragMockedFn).not.toHaveBeenCalled(); + expect(dragMockFn).not.toHaveBeenCalled(); offFrom(); fireEvent.mouseMove(document, { clientX: 100, clientY: 100 }); - expect(dragMockedFn).not.toHaveBeenCalled(); + expect(dragMockFn).not.toHaveBeenCalled(); }); it('addSensor / removeSensor', () => { diff --git a/packages/designer/tests/designer/scroller.test.ts b/packages/designer/tests/designer/scroller.test.ts new file mode 100644 index 000000000..a1be4d534 --- /dev/null +++ b/packages/designer/tests/designer/scroller.test.ts @@ -0,0 +1,159 @@ +import '../fixtures/window'; +import { 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 { ScrollTarget, Scroller } from '../../src/designer/scroller'; +import { + isRootNode, + isNode, + comparePosition, + contains, + insertChild, + insertChildren, + PositionNO, +} from '../../src/document/node/node'; +import { Designer } from '../../src/designer/designer'; +import { + Dragon, + isDragNodeObject, + isDragNodeDataObject, + isDragAnyObject, + isLocateEvent, + DragObjectType, + isShaken, + setShaken, +} from '../../src/designer/dragon'; +import formSchema from '../fixtures/schema/form'; +import divMetadata from '../fixtures/component-metadata/div'; +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'; + +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 }); + 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); + console.log(target.left, target.top, target.scrollHeight, target.scrollWidth); + }); + + }); + + 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/setting-top-entry.test.ts b/packages/designer/tests/designer/setting/setting-top-entry.test.ts index 906eb0720..f76501782 100644 --- a/packages/designer/tests/designer/setting/setting-top-entry.test.ts +++ b/packages/designer/tests/designer/setting/setting-top-entry.test.ts @@ -93,7 +93,7 @@ describe('setting-top-entry 测试', () => { const { currentDocument } = designer.project; const divNode = currentDocument?.getNode('div'); - console.log(divNode?.getPropValue('behavior')); + // console.log(divNode?.getPropValue('behavior')); const { settingEntry } = divNode!; expect(typeof settingEntry.getChildren).toBe('function'); diff --git a/packages/designer/tests/document/node/node.test.ts b/packages/designer/tests/document/node/node.test.ts index 80d57dbb6..0416b2a67 100644 --- a/packages/designer/tests/document/node/node.test.ts +++ b/packages/designer/tests/document/node/node.test.ts @@ -160,10 +160,10 @@ describe('Node 方法测试', () => { const pageMeta = designer.getComponentMeta('Page'); set(pageMeta, 'prototype.options.canDropIn', () => true); - const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), 1); + const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), { index: 1 }); expect(o).toEqual({ container: doc.rootNode, - ref: 1, + ref: { index: 1 }, }); }); diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index d2a8c2f30..ecc6023b4 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -370,7 +370,7 @@ describe('Prop 类测试', () => { // TODO: id 总是变,不好断言 expect(slotProp.code.includes('Button')).toBeTruthy(); - console.log(slotProp.export()); + slotProp.export(); expect(slotProp.export().value[0].componentName).toBe('Button'); expect(slotProp.export(TransformStage.Serilize).value[0].componentName).toBe('Button'); diff --git a/packages/designer/tests/fixtures/component-metadata/button.ts b/packages/designer/tests/fixtures/component-metadata/button.ts index f521d82f6..d0725efc3 100644 --- a/packages/designer/tests/fixtures/component-metadata/button.ts +++ b/packages/designer/tests/fixtures/component-metadata/button.ts @@ -221,10 +221,9 @@ export default { }, ], component: { - isContainer: true, nestingRule: { - parentWhitelist: 'Div', - childWhitelist: 'Div', + // parentWhitelist: 'Div', + // childWhitelist: 'Div', }, }, supports: {}, diff --git a/packages/designer/tests/fixtures/component-metadata/div.ts b/packages/designer/tests/fixtures/component-metadata/div.ts index ae1738754..941b90ef3 100644 --- a/packages/designer/tests/fixtures/component-metadata/div.ts +++ b/packages/designer/tests/fixtures/component-metadata/div.ts @@ -223,8 +223,8 @@ export default { component: { isContainer: true, nestingRule: { - parentWhitelist: 'Div', - childWhitelist: 'Div', + // parentWhitelist: 'Div', + // childWhitelist: 'Div', }, }, supports: {}, diff --git a/packages/designer/tests/fixtures/window.ts b/packages/designer/tests/fixtures/window.ts index d772d1ef4..4e0213d98 100644 --- a/packages/designer/tests/fixtures/window.ts +++ b/packages/designer/tests/fixtures/window.ts @@ -15,4 +15,6 @@ Object.defineProperty(window, 'matchMedia', { Object.defineProperty(window, 'React', { writable: true, value: {}, -}); \ No newline at end of file +}); + +window.scrollTo = () => {}; \ No newline at end of file diff --git a/packages/designer/tests/utils/bom.ts b/packages/designer/tests/utils/bom.ts index 9d25f8f0b..285000889 100644 --- a/packages/designer/tests/utils/bom.ts +++ b/packages/designer/tests/utils/bom.ts @@ -76,4 +76,36 @@ export function getMockWindow(doc?: MockDocument) { 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; } \ No newline at end of file diff --git a/packages/designer/tests/utils/renderer.ts b/packages/designer/tests/utils/renderer.ts index 256a8c651..cd91c15c5 100644 --- a/packages/designer/tests/utils/renderer.ts +++ b/packages/designer/tests/utils/renderer.ts @@ -2,7 +2,7 @@ export function getMockRenderer() { return { isSimulatorRenderer: true, run() { - console.log('renderer run'); + // console.log('renderer run'); } } } \ No newline at end of file