test(selection & dragon): add some test cases for increasing code coverage rate

This commit is contained in:
LeoYuan 袁力皓 2022-08-08 16:50:44 +08:00 committed by LeoYuan 袁力皓
parent b4d7d6d8c2
commit c50b07d3ec
9 changed files with 131 additions and 84 deletions

View File

@ -2,35 +2,7 @@ import { EventEmitter } from 'events';
import { ISimulatorHost } from '../../simulator'; import { ISimulatorHost } from '../../simulator';
import { Designer, Point } from '../../designer'; import { Designer, Point } from '../../designer';
import { cursor } from '@alilc/lowcode-utils'; import { cursor } from '@alilc/lowcode-utils';
// import Cursor from './cursor'; import { makeEventsHandler } from '../../utils/misc';
// import Pages from './pages';
function makeEventsHandler(
boostEvent: MouseEvent | DragEvent,
sensors: ISimulatorHost[],
): (fn: (sdoc: Document) => void) => void {
const topDoc = window.document;
const sourceDoc = boostEvent.view?.document || topDoc;
// TODO: optimize this logic, reduce listener
// const boostPrevented = boostEvent.defaultPrevented;
const docs = new Set<Document>();
// if (boostPrevented || isDragEvent(boostEvent)) {
docs.add(topDoc);
// }
docs.add(sourceDoc);
// if (sourceDoc !== topDoc || isDragEvent(boostEvent)) {
sensors.forEach(sim => {
const sdoc = sim.contentDocument;
if (sdoc) {
docs.add(sdoc);
}
});
// }
return (handle: (sdoc: Document) => void) => {
docs.forEach(doc => handle(doc));
};
}
// 拖动缩放 // 拖动缩放
export default class DragResizeEngine { export default class DragResizeEngine {
@ -73,6 +45,7 @@ export default class DragResizeEngine {
const masterSensors = this.getMasterSensors(); const masterSensors = this.getMasterSensors();
/* istanbul ignore next */
const createResizeEvent = (e: MouseEvent | DragEvent): Point => { const createResizeEvent = (e: MouseEvent | DragEvent): Point => {
const sourceDocument = e.view?.document; const sourceDocument = e.view?.document;

View File

@ -6,6 +6,7 @@ import { DropLocation } from './location';
import { Node, DocumentModel } from '../document'; import { Node, DocumentModel } from '../document';
import { ISimulatorHost, isSimulatorHost, NodeInstance, ComponentInstance } from '../simulator'; import { ISimulatorHost, isSimulatorHost, NodeInstance, ComponentInstance } from '../simulator';
import { Designer } from './designer'; import { Designer } from './designer';
import { makeEventsHandler } from '../utils/misc';
export interface LocateEvent { export interface LocateEvent {
readonly type: 'LocateEvent'; readonly type: 'LocateEvent';
@ -135,7 +136,7 @@ export function isShaken(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent)
); );
} }
function isInvalidPoint(e: any, last: any): boolean { export function isInvalidPoint(e: any, last: any): boolean {
return ( return (
e.clientX === 0 && e.clientX === 0 &&
e.clientY === 0 && e.clientY === 0 &&
@ -144,7 +145,7 @@ function isInvalidPoint(e: any, last: any): boolean {
); );
} }
function isSameAs(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent): boolean { export function isSameAs(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent): boolean {
return e1.clientY === e2.clientY && e1.clientX === e2.clientX; return e1.clientY === e2.clientY && e1.clientX === e2.clientX;
} }
@ -159,31 +160,6 @@ function getSourceSensor(dragObject: DragObject): ISimulatorHost | null {
return dragObject.nodes[0]?.document.simulator || null; return dragObject.nodes[0]?.document.simulator || null;
} }
/**
* make a handler that listen all sensors:document, avoid frame lost
*/
function makeEventsHandler(
boostEvent: MouseEvent | DragEvent,
sensors: ISimulatorHost[],
): (fn: (sdoc: Document) => void) => void {
const topDoc = window.document;
const sourceDoc = boostEvent.view?.document || topDoc;
// TODO: optimize this logic, reduce listener
const docs = new Set<Document>();
docs.add(topDoc);
docs.add(sourceDoc);
sensors.forEach((sim) => {
const sdoc = sim.contentDocument;
if (sdoc) {
docs.add(sdoc);
}
});
return (handle: (sdoc: Document) => void) => {
docs.forEach((doc) => handle(doc));
};
}
function isDragEvent(e: any): e is DragEvent { function isDragEvent(e: any): e is DragEvent {
return e?.type?.startsWith('drag'); return e?.type?.startsWith('drag');
} }
@ -325,6 +301,7 @@ export class Dragon {
const locateEvent = createLocateEvent(e); const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent); const sensor = chooseSensor(locateEvent);
/* istanbul ignore next */
if (isRGL) { if (isRGL) {
// 禁止被拖拽元素的阻断 // 禁止被拖拽元素的阻断
const nodeInst = dragObject.nodes[0].getDOMNode(); const nodeInst = dragObject.nodes[0].getDOMNode();
@ -429,6 +406,7 @@ export class Dragon {
// 发送drop事件 // 发送drop事件
if (e) { if (e) {
const { isRGL, rglNode } = getRGL(e); const { isRGL, rglNode } = getRGL(e);
/* istanbul ignore next */
if (isRGL && this._canDrop) { if (isRGL && this._canDrop) {
const tarNode = dragObject.nodes[0]; const tarNode = dragObject.nodes[0];
if (rglNode.id !== tarNode.id) { if (rglNode.id !== tarNode.id) {
@ -468,7 +446,7 @@ export class Dragon {
this._dragging = false; this._dragging = false;
try { try {
this.emitter.emit('dragend', { dragObject, copy }); this.emitter.emit('dragend', { dragObject, copy });
} catch (ex) { } catch (ex) /* istanbul ignore next */ {
exception = ex; exception = ex;
} }
} }
@ -489,6 +467,7 @@ export class Dragon {
doc.removeEventListener('keydown', checkcopy, false); doc.removeEventListener('keydown', checkcopy, false);
doc.removeEventListener('keyup', checkcopy, false); doc.removeEventListener('keyup', checkcopy, false);
}); });
/* istanbul ignore next */
if (exception) { if (exception) {
throw exception; throw exception;
} }
@ -509,7 +488,7 @@ export class Dragon {
if (!sourceDocument || sourceDocument === document) { if (!sourceDocument || sourceDocument === document) {
evt.globalX = e.clientX; evt.globalX = e.clientX;
evt.globalY = e.clientY; evt.globalY = e.clientY;
} /* istanbul ignore next */ else { } else /* istanbul ignore next */ {
// event from simulator sandbox // event from simulator sandbox
let srcSim: ISimulatorHost | undefined; let srcSim: ISimulatorHost | undefined;
const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null; const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null;
@ -616,6 +595,7 @@ export class Dragon {
} }
} }
/* istanbul ignore next */
private getMasterSensors(): ISimulatorHost[] { private getMasterSensors(): ISimulatorHost[] {
return Array.from( return Array.from(
new Set( new Set(

View File

@ -147,10 +147,11 @@ export class Selection {
if (n === PositionNO.Contains || n === PositionNO.TheSame) { if (n === PositionNO.Contains || n === PositionNO.TheSame) {
isTop = false; isTop = false;
break; break;
} } else if (n === PositionNO.ContainedBy) {
// node contains nodes[i], delete nodes[i] // node contains nodes[i], delete nodes[i]
if (n === PositionNO.ContainedBy) {
nodes.splice(i, 1); nodes.splice(i, 1);
} else {
isTop = false;
} }
} }
// node is top item, push to nodes // node is top item, push to nodes

View File

@ -1,4 +1,5 @@
import Viewport from '../builtin-simulator/viewport'; import Viewport from '../builtin-simulator/viewport';
import { ISimulatorHost } from '../simulator';
export function isElementNode(domNode: Element) { export function isElementNode(domNode: Element) {
return domNode.nodeType === Node.ELEMENT_NODE; return domNode.nodeType === Node.ELEMENT_NODE;
@ -29,3 +30,27 @@ export function isDOMNodeVisible(domNode: Element, viewport: Viewport) {
export function normalizeTriggers(triggers: string[]) { export function normalizeTriggers(triggers: string[]) {
return triggers.map((trigger: string) => trigger?.toUpperCase()); return triggers.map((trigger: string) => trigger?.toUpperCase());
} }
/**
* make a handler that listen all sensors:document, avoid frame lost
*/
export function makeEventsHandler(
boostEvent: MouseEvent | DragEvent,
sensors: ISimulatorHost[],
): (fn: (sdoc: Document) => void) => void {
const topDoc = window.document;
const sourceDoc = boostEvent.view?.document || topDoc;
const docs = new Set<Document>();
docs.add(topDoc);
docs.add(sourceDoc);
sensors.forEach((sim) => {
const sdoc = sim.contentDocument;
if (sdoc) {
docs.add(sdoc);
}
});
return (handle: (sdoc: Document) => void) => {
docs.forEach((doc) => handle(doc));
};
}

View File

@ -57,7 +57,8 @@ describe('DragResizeEngine 测试', () => {
}); });
// do nothing // do nothing
resizeEngine.from(); const noop = resizeEngine.from();
noop();
const offFrom = resizeEngine.from(document, 'e', mockedBoostFn); const offFrom = resizeEngine.from(document, 'e', mockedBoostFn);

View File

@ -1,14 +1,18 @@
import { Detecting } from '../../src/designer/detecting'; import { Detecting } from '../../src/designer/detecting';
it('Detecting 测试', () => { it('Detecting 测试', () => {
const fn = jest.fn();
const detecting = new Detecting(); const detecting = new Detecting();
detecting.onDetectingChange(fn);
expect(detecting.enable).toBeTruthy(); expect(detecting.enable).toBeTruthy();
const mockNode = { document }; const mockNode = { document };
detecting.capture(mockNode); detecting.capture(mockNode);
expect(fn).toHaveBeenCalledWith(detecting.current);
expect(detecting.current).toBe(mockNode); expect(detecting.current).toBe(mockNode);
detecting.release({});
detecting.release(mockNode); detecting.release(mockNode);
expect(detecting.current).toBeNull(); expect(detecting.current).toBeNull();

View File

@ -3,16 +3,6 @@ import { set } from '../utils';
import { Editor, globalContext } from '@alilc/lowcode-editor-core'; import { Editor, globalContext } from '@alilc/lowcode-editor-core';
import { Project } from '../../src/project/project'; import { Project } from '../../src/project/project';
import { DocumentModel } from '../../src/document/document-model'; import { DocumentModel } from '../../src/document/document-model';
import {
isRootNode,
Node,
isNode,
comparePosition,
contains,
insertChild,
insertChildren,
PositionNO,
} from '../../src/document/node/node';
import { Designer } from '../../src/designer/designer'; import { Designer } from '../../src/designer/designer';
import { import {
Dragon, Dragon,
@ -23,12 +13,10 @@ import {
DragObjectType, DragObjectType,
isShaken, isShaken,
setShaken, setShaken,
isInvalidPoint,
isSameAs,
} from '../../src/designer/dragon'; } from '../../src/designer/dragon';
import formSchema from '../fixtures/schema/form'; 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'; import { fireEvent } from '@testing-library/react';
describe('Dragon 测试', () => { describe('Dragon 测试', () => {
@ -273,9 +261,32 @@ describe('Dragon 测试', () => {
}); });
it('addSensor / removeSensor', () => { it('addSensor / removeSensor', () => {
const sensor = {}; const sensor = {
locate: () => {},
sensorAvailable: true,
isEnter: () => true,
fixEvent: () => {},
deactiveSensor: () => {},
};
const sensor2 = {};
dragon.addSensor(sensor); dragon.addSensor(sensor);
expect(dragon.sensors.length).toBe(1); expect(dragon.sensors.length).toBe(1);
expect(dragon.activeSensor).toBeUndefined();
dragon.boost(
{
type: DragObjectType.NodeData,
data: [{ componentName: 'Button' }],
},
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
);
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
expect(dragon.activeSensor).toBe(sensor);
// remove a non-existing sensor
dragon.removeSensor(sensor2);
expect(dragon.sensors.length).toBe(1);
dragon.removeSensor(sensor); dragon.removeSensor(sensor);
expect(dragon.sensors.length).toBe(0); expect(dragon.sensors.length).toBe(0);
}); });
@ -343,4 +354,16 @@ describe('导出的其他函数', () => {
setShaken(e); setShaken(e);
expect(isShaken(e)).toBeTruthy(); expect(isShaken(e)).toBeTruthy();
}); });
it('isInvalidPoint', () => {
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 1 })).toBeTruthy();
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 1, clientY: 6 })).toBeTruthy();
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 6 })).toBeTruthy();
expect(isInvalidPoint({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy();
});
it('isSameAs', () => {
expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 1, clientY: 1 })).toBeTruthy();
expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy();
});
}); });

View File

@ -67,6 +67,7 @@ describe('选择区测试', () => {
expect(selection.selected).toEqual(['node_k1ow3cbj', 'form']); expect(selection.selected).toEqual(['node_k1ow3cbj', 'form']);
selectionChangeHandler.mockClear(); selectionChangeHandler.mockClear();
selection.remove('node_k1ow3cbj_fake');
selection.remove('node_k1ow3cbj'); selection.remove('node_k1ow3cbj');
expect(selectionChangeHandler).toHaveBeenCalledTimes(1); expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']); expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
@ -141,7 +142,7 @@ describe('选择区测试', () => {
selectionChangeHandler.mockClear(); selectionChangeHandler.mockClear();
}); });
it('dispose 方法', () => { it('dispose 方法 - 选中的节点没有被删除的', () => {
const project = new Project(designer, { const project = new Project(designer, {
componentsTree: [ componentsTree: [
formSchema, formSchema,
@ -152,16 +153,13 @@ describe('选择区测试', () => {
const { currentDocument } = project; const { currentDocument } = project;
const { nodesMap, selection } = currentDocument!; const { nodesMap, selection } = currentDocument!;
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']); selection.selectAll(['form', 'node_k1ow3cbj']);
const selectionChangeHandler = jest.fn(); const selectionChangeHandler = jest.fn();
selection.onSelectionChange(selectionChangeHandler); selection.onSelectionChange(selectionChangeHandler);
selection.dispose(); selection.dispose();
expect(selectionChangeHandler).toHaveBeenCalledTimes(1); expect(selectionChangeHandler).not.toHaveBeenCalled();
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']);
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
selectionChangeHandler.mockClear();
}); });
it('containsNode 方法', () => { it('containsNode 方法', () => {
@ -242,4 +240,36 @@ describe('选择区测试', () => {
expect(selection.selected).toEqual(['page']); expect(selection.selected).toEqual(['page']);
selectionChangeHandler.mockClear(); selectionChangeHandler.mockClear();
}); });
it('getNodes', () => {
const project = new Project(designer, {
componentsTree: [
formSchema,
],
});
project.open();
const { currentDocument } = project;
const { selection } = currentDocument!;
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
// form2 is not a valid node
expect(selection.getNodes()).toHaveLength(2);
});
it('getTopNodes', () => {
const project = new Project(designer, {
componentsTree: [
formSchema,
],
});
project.open();
const { currentDocument } = project;
const { selection } = currentDocument!;
selection.selectAll(['node_k1ow3cbj', 'node_k1ow3cbo', 'form', 'node_k1ow3cbl', 'form2']);
// form2 is not a valid node, and node_k1ow3cbj is a child node of form
expect(selection.getTopNodes()).toHaveLength(1);
});
}); });

View File

@ -1,5 +1,5 @@
// @ts-nocheck // @ts-nocheck
import { isElementNode, isDOMNodeVisible, normalizeTriggers } from '../../src/utils/misc'; import { isElementNode, isDOMNodeVisible, normalizeTriggers, makeEventsHandler } from '../../src/utils/misc';
it('isElementNode', () => { it('isElementNode', () => {
expect(isElementNode(document.createElement('div'))).toBeTruthy(); expect(isElementNode(document.createElement('div'))).toBeTruthy();
@ -152,3 +152,13 @@ describe('isDOMNodeVisible', () => {
it('normalizeTriggers', () => { it('normalizeTriggers', () => {
expect(normalizeTriggers(['n', 'w'])).toEqual(['N', 'W']); expect(normalizeTriggers(['n', 'w'])).toEqual(['N', 'W']);
}); });
it('makeEventsHandler', () => {
const sensor = { contentDocument: document };
// no contentDocument
const sensor2 = {};
const bind = makeEventsHandler({ view: { document } } as any, [sensor, sensor2]);
const fn = jest.fn();
bind((doc) => fn(doc));
expect(fn).toHaveBeenCalledTimes(1);
});