import '../fixtures/window'; import { Editor, globalContext } from '@alilc/lowcode-editor-core'; import { AssetType, } from '@alilc/lowcode-utils'; import { DragObjectType, } from '@alilc/lowcode-types'; import { Project } from '../../src/project/project'; import pageMetadata from '../fixtures/component-metadata/page'; import { Designer } from '../../src/designer/designer'; import { DocumentModel } from '../../src/document/document-model'; import formSchema from '../fixtures/schema/form'; import { getMockDocument, getMockWindow, getMockEvent, delayObxTick } from '../utils'; import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; import { fireEvent } from '@testing-library/react'; import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; describe('Host 测试', () => { let editor: Editor; let designer: Designer; let project: Project; let doc: DocumentModel; let host: BuiltinSimulatorHost; beforeAll(() => { editor = new Editor(); !globalContext.has(Editor) && globalContext.register(editor, Editor); }); beforeEach(() => { designer = new Designer({ editor, shellModelFactory }); project = designer.project; designer.createComponentMeta(pageMetadata); doc = project.createDocument(formSchema); host = new BuiltinSimulatorHost(designer.project); }); afterEach(() => { project.unload(); project.mountSimulator(undefined); designer._componentMetasMap.clear(); designer.purge(); host.purge(); designer = null; project = null; host = null; }); describe('基础方法测试', () => { it('setProps / get / set', async () => { expect(host.currentDocument).toBe(designer.project.currentDocument); expect(host.renderEnv).toBe('default'); expect(host.device).toBe('default'); expect(host.deviceClassName).toBeUndefined(); expect(host.requestHandlersMap).toBeNull(); host.setProps({ renderEnv: 'rax', device: 'mobile', deviceClassName: 'mobile-rocks', componentsAsset: [ { type: AssetType.JSText, content: 'console.log(1)', }, { type: AssetType.JSUrl, content: '//path/to/js', }, ], theme: { type: AssetType.CSSText, content: '.theme {font-size: 50px;}', }, requestHandlersMap: {}, }); expect(host.renderEnv).toBe('rax'); expect(host.device).toBe('mobile'); expect(host.deviceClassName).toBe('mobile-rocks'); expect(host.componentsAsset).toEqual([ { type: AssetType.JSText, content: 'console.log(1)', }, { type: AssetType.JSUrl, content: '//path/to/js', }, ]); expect(host.theme).toEqual({ type: AssetType.CSSText, content: '.theme {font-size: 50px;}', }); expect(host.componentsMap).toEqual(designer.componentsMap); expect(host.requestHandlersMap).toEqual({}); host.set('renderEnv', 'vue'); expect(host.renderEnv).toBe('vue'); expect(host.getComponentContext).toThrow('Method not implemented.'); }); it('connect', () => { const mockFn = jest.fn(); const mockRenderer = { isSimulatorRenderer: true }; host.connect(mockRenderer, mockFn); expect(host.renderer).toEqual(mockRenderer); // await delayObxTick(); expect(mockFn).toHaveBeenCalled(); }); it('mountViewport', () => { const mockBounds = { top: 10, bottom: 100, left: 10, right: 100, }; host.mountViewport({ getBoundingClientRect() { return mockBounds; }, }); expect(host.viewport.bounds).toEqual(mockBounds); }); it('autorun', () => { const mockFn = jest.fn(); host.autorun(mockFn); expect(mockFn).toHaveBeenCalled(); }); it('purge', () => { host.purge(); }); it('isEnter', () => { const mockBounds = { top: 10, bottom: 100, left: 10, right: 100, }; host.mountViewport({ getBoundingClientRect() { return mockBounds; }, }); expect( host.isEnter({ globalX: 5, globalY: 50, }), ).toBeFalsy(); expect( host.isEnter({ globalX: 115, globalY: 50, }), ).toBeFalsy(); expect( host.isEnter({ globalX: 50, globalY: 50, }), ).toBeTruthy(); expect( host.isEnter({ globalX: 50, globalY: 5, }), ).toBeFalsy(); expect( host.isEnter({ globalX: 50, globalY: 150, }), ).toBeFalsy(); expect( host.isEnter({ globalX: 150, globalY: 150, }), ).toBeFalsy(); }); it('fixEvent', () => { expect(host.fixEvent({ fixed: true, clientX: 1 })).toEqual({ fixed: true, clientX: 1 }); }); it('findDOMNodes', () => { host.connect({ findDOMNodes: () => { return null; }, }, () => {}); expect(host.findDOMNodes()).toBeNull(); const mockElems = [document.createElement('div')]; host.connect({ findDOMNodes: () => { return mockElems; }, }, () => {}); expect(host.findDOMNodes({})).toBe(mockElems); expect(host.findDOMNodes({}, 'xxx')).toBeNull(); expect(host.findDOMNodes({}, 'div')).toEqual(mockElems); }); it('getClosestNodeInstance', () => { const mockFn = jest.fn(() => { return { node: {}, nodeId: 'id', docId: 'docId', }; }); host.connect({ getClosestNodeInstance: mockFn, }, () => {}); expect(host.getClosestNodeInstance()).toEqual({ node: {}, nodeId: 'id', docId: 'docId', }); }); it('getNodeInstanceFromElement', () => { expect(host.getNodeInstanceFromElement()).toBeNull(); host.getClosestNodeInstance = () => { return null; }; expect(host.getNodeInstanceFromElement({})).toBeNull(); host.getClosestNodeInstance = () => { return { docId: project.currentDocument.id, nodeId: 'xxx', }; }; expect(host.getNodeInstanceFromElement({})).toBeTruthy(); }); it('getDropContainer', () => { host.getNodeInstanceFromElement = () => { return { node: doc.rootNode, }; }; host.getDropContainer({ target: {}, dragObject: { type: 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('notFoundComponent', () => { expect(host.locate({ dragObject: { type: DragObjectType.Node, nodes: [doc.getNode('form')], }, })).toBeUndefined(); }) it('locate', () => { host.locate({ dragObject: { type: DragObjectType.Node, nodes: [doc.getNode('page')], }, }); }); }); describe('事件测试', () => { it('setupDragAndClick', () => {}); it('setupContextMenu', async () => { const mockDocument = getMockDocument(); const mockWindow = getMockWindow(mockDocument); const mockIframe = { contentWindow: mockWindow, contentDocument: mockDocument, dispatchEvent() {}, }; host.set('library', [ { package: '@ali/vc-deep', library: 'lib', urls: ['a.js', 'b.js'], }, ]); host.componentsConsumer.consume(() => {}); host.injectionConsumer.consume(() => {}); await host.mountContentFrame(mockIframe); host.setupContextMenu(); host.getNodeInstanceFromElement = () => { return { node: { componentMeta: { componentName: 'Button', getMetadata() { return {} } }, contains() {} }, }; }; const mockFn = jest.fn(); host.designer.editor.on('designer.builtinSimulator.contextmenu', mockFn); fireEvent.contextMenu(document, {}); // TODO: // expect(mockFn).toHaveBeenCalledWith({ selected: 'Button' }); }); }); it('事件测试', async () => { const mockDocument = getMockDocument(); const mockWindow = getMockWindow(mockDocument); const mockIframe = { contentWindow: mockWindow, contentDocument: mockDocument, dispatchEvent() {}, }; // 非法分支测试 host.mountContentFrame(); expect(host._iframe).toBeUndefined(); host.set('library', [ { package: '@ali/vc-deep', library: 'lib', urls: ['a.js', 'b.js'], }, ]); host.componentsConsumer.consume(() => {}); host.injectionConsumer.consume(() => {}); await host.mountContentFrame(mockIframe); expect(host.contentWindow).toBe(mockWindow); mockDocument.triggerEventListener( 'mouseover', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener( 'mouseleave', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener( 'mousedown', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener( 'mouseup', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener( 'mousemove', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener('click', getMockEvent(document.createElement('input')), host); mockDocument.triggerEventListener( 'dblclick', getMockEvent(mockDocument.createElement('div')), host, ); mockDocument.triggerEventListener( 'contextmenu', getMockEvent(mockDocument.createElement('div')), host, ); }); });