test: 补充一些 ut

This commit is contained in:
lihao.ylh 2021-11-15 19:57:16 +08:00 committed by lianjie.lj
parent b07436d444
commit cbe5ed7692
10 changed files with 201 additions and 86 deletions

View File

@ -2,7 +2,6 @@ export * from './exclusive-group';
export * from './node'; export * from './node';
export * from './node-children'; export * from './node-children';
export * from './props/prop'; export * from './props/prop';
export * from './props/prop-stash';
export * from './props/props'; export * from './props/props';
export * from './transform-stage'; export * from './transform-stage';
export * from './modal-nodes-manager'; export * from './modal-nodes-manager';

View File

@ -142,9 +142,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
@computed get title(): string | I18nData | ReactElement { @computed get title(): string | I18nData | ReactElement {
let t = this.getExtraProp('title'); let t = this.getExtraProp('title');
if (!t && this.componentMeta.descriptor) { // TODO: 暂时走不到这个分支
t = this.getProp(this.componentMeta.descriptor, false); // if (!t && this.componentMeta.descriptor) {
} // t = this.getProp(this.componentMeta.descriptor, false);
// }
if (t) { if (t) {
const v = t.getAsString(); const v = t.getAsString();
if (v) { if (v) {
@ -175,7 +176,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this._children.internalInitParent(); this._children.internalInitParent();
this.props.merge( this.props.merge(
this.upgradeProps(this.initProps(props || {})), this.upgradeProps(this.initProps(props || {})),
this.upgradeProps(extras || {}), this.upgradeProps(extras),
); );
this.setupAutoruns(); this.setupAutoruns();
} }
@ -719,6 +720,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
const _extras_: { [key: string]: any } = { const _extras_: { [key: string]: any } = {
...extras, ...extras,
}; };
/* istanbul ignore next */
Object.keys(this._addons).forEach((key) => { Object.keys(this._addons).forEach((key) => {
const addon = this._addons[key]; const addon = this._addons[key];
if (addon) { if (addon) {
@ -1183,12 +1185,18 @@ export function getZLevelTop(child: Node, zLevel: number): Node | null {
return r; return r;
} }
/**
*
* @param node1
* @param node2
* @returns
*/
export function contains(node1: Node, node2: Node): boolean { export function contains(node1: Node, node2: Node): boolean {
if (node1 === node2) { if (node1 === node2) {
return true; return true;
} }
if (!node1.isParental || !node2.parent) { if (!node1.isParental() || !node2.parent) {
return false; return false;
} }

View File

@ -1,77 +0,0 @@
import { obx, autorun, untracked, computed, makeObservable, action } from '@ali/lowcode-editor-core';
import { Prop, IPropParent, UNSET } from './prop';
import { Props } from './props';
import { Node } from '../node';
export type PendingItem = Prop[];
export class PropStash implements IPropParent {
readonly isPropStash = true;
@obx.shallow private space: Set<Prop> = new Set();
@computed private get maps(): Map<string | number, Prop> {
const maps = new Map();
if (this.space.size > 0) {
this.space.forEach(prop => {
maps.set(prop.key, prop);
});
}
return maps;
}
private willPurge: () => void;
readonly owner: Node;
constructor(readonly props: Props, write: (item: Prop) => void) {
makeObservable(this);
this.owner = props.owner;
this.willPurge = autorun(() => {
if (this.space.size < 1) {
return;
}
const pending: Prop[] = [];
for (const prop of this.space) {
if (!prop.isUnset() && !prop.isVirtual()) {
this.space.delete(prop);
pending.push(prop);
}
}
if (pending.length > 0) {
untracked(() => {
for (const item of pending) {
write(item);
}
});
}
});
}
@action
get(key: string | number): Prop {
let prop = this.maps.get(key);
if (!prop) {
prop = new Prop(this, UNSET, key);
this.space.add(prop);
}
return prop;
}
@action
delete(prop: Prop) {
this.space.delete(prop);
prop.purge();
}
@action
clear() {
this.space.forEach(item => item.purge());
this.space.clear();
}
@action
purge() {
this.willPurge();
this.space.clear();
}
}

View File

@ -228,9 +228,10 @@ export class Props implements IPropParent {
*/ */
@action @action
deleteKey(key: string): void { deleteKey(key: string): void {
this.items = this.items.filter(item => { this.items = this.items.filter((item, i) => {
if (item.key === key) { if (item.key === key) {
item.purge(); item.purge();
this.items.splice(i, 1);
return false; return false;
} }
return true; return true;

View File

@ -0,0 +1,63 @@
import '../../fixtures/window';
import { set, delayObxTick, delay } from '../../utils';
import { Editor } from '@ali/lowcode-editor-core';
import { Project } from '../../../src/project/project';
import { DocumentModel } from '../../../src/document/document-model';
import {
isRootNode,
Node,
isNode,
comparePosition,
contains,
insertChild,
insertChildren,
PositionNO,
} from '../../../src/document/node/node';
import { Designer } from '../../../src/designer/designer';
import { BemToolsManager } from '../../../src/builtin-simulator/bem-tools/manager';
import formSchema from '../../fixtures/schema/form';
describe('Node 方法测试', () => {
let editor: Editor;
let designer: Designer;
// let project: Project;
// let doc: DocumentModel;
let manager: BemToolsManager;
beforeEach(() => {
editor = new Editor();
designer = new Designer({ editor });
// project = designer.project;
// doc = new DocumentModel(project, formSchema);
manager = new BemToolsManager(designer);
});
afterEach(() => {
// project.unload();
designer.purge();
editor = null;
designer = null;
// project = null;
});
it('addBemTools / removeBemTools / getAllBemTools', () => {
manager.addBemTools({
name: 't1',
item: (props: any) => { return <div />; },
});
expect(manager.getAllBemTools().length).toBe(1);
expect(() => {
manager.addBemTools({
name: 't1',
item: (props: any) => { return <div />; },
});
}).toThrow(/already exists/);
manager.removeBemTools('t2');
expect(manager.getAllBemTools().length).toBe(1);
manager.removeBemTools('t1');
expect(manager.getAllBemTools().length).toBe(0);
});
});

View File

@ -17,6 +17,8 @@ import {
import { Designer } from '../../../src/designer/designer'; import { Designer } from '../../../src/designer/designer';
import formSchema from '../../fixtures/schema/form'; import formSchema from '../../fixtures/schema/form';
import divMetadata from '../../fixtures/component-metadata/div'; import divMetadata from '../../fixtures/component-metadata/div';
import dialogMetadata from '../../fixtures/component-metadata/dialog';
import btnMetadata from '../../fixtures/component-metadata/button';
import formMetadata from '../../fixtures/component-metadata/form'; import formMetadata from '../../fixtures/component-metadata/form';
import otherMeta from '../../fixtures/component-metadata/other'; import otherMeta from '../../fixtures/component-metadata/other';
import pageMetadata from '../../fixtures/component-metadata/page'; import pageMetadata from '../../fixtures/component-metadata/page';
@ -47,6 +49,28 @@ describe('Node 方法测试', () => {
it('condition group', () => {}); it('condition group', () => {});
it('getExtraProp / setExtraProp', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!;
expect(firstBtn.getExtraProp('non-existing', false)).toBeNull();
firstBtn.setExtraProp('xxx', '1111');
expect(firstBtn.getExtraProp('xxx', false).getValue()).toBe('1111');
});
it('import(leaf)', () => {
const form = doc.getNode('node_k1ow3cbo');
form.insert({ componentName: 'Leaf', children: '111' });
const leaf = form.getChildren().get(2);
expect(leaf.getPropValue('children')).toBe('111');
leaf.import({ componentName: 'Leaf', children: '222' });
expect(leaf.getPropValue('children')).toBe('222');
leaf.import({ componentName: 'Leaf', children: { type: 'JSExpression', value: 'state.x' } });
expect(leaf.getPropValue('children')).toEqual({ type: 'JSExpression', value: 'state.x' });
});
it('hasCondition', () => { it('hasCondition', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.getExtraProp('condition')?.setValue(undefined); firstBtn.getExtraProp('condition')?.setValue(undefined);
@ -149,6 +173,22 @@ describe('Node 方法测试', () => {
expect(o).toBeNull(); expect(o).toBeNull();
}); });
it('放入模态节点', () => {
designer.createComponentMeta(pageMetadata);
designer.createComponentMeta(dialogMetadata);
const dialog = doc.createNode({ componentName: 'Dialog' });
const o = doc.rootNode?.getSuitablePlace(dialog, 1);
expect(o.container).toBe(doc.rootNode);
expect(o.ref).toBe(1);
});
it('包含 focusNode', () => {
const o = doc.rootNode?.getSuitablePlace(doc.rootNode);
expect(o.container).toBe(doc.rootNode);
});
it.skip('非 root 节点,不能放入子节点', () => { it.skip('非 root 节点,不能放入子节点', () => {
designer.createComponentMeta(formMetadata); designer.createComponentMeta(formMetadata);
designer.createComponentMeta(pageMetadata); designer.createComponentMeta(pageMetadata);
@ -195,6 +235,10 @@ describe('Node 方法测试', () => {
it('removeChild / replaceWith / replaceChild', () => { it('removeChild / replaceWith / replaceChild', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
const form = doc.getNode('node_k1ow3cbo');
// 不符合条件的节点直接返回
expect(firstBtn.replaceChild(form, { componentName: 'Button', props: { x: 1 } })).toBe(form);
firstBtn.select(); firstBtn.select();
firstBtn.parent?.replaceChild(firstBtn, { componentName: 'Button', props: { x: 1 } }); firstBtn.parent?.replaceChild(firstBtn, { componentName: 'Button', props: { x: 1 } });
@ -208,6 +252,15 @@ describe('Node 方法测试', () => {
expect(firstBtn.parent?.getChildren()?.get(1)?.getPropValue('y')).toBe(1); expect(firstBtn.parent?.getChildren()?.get(1)?.getPropValue('y')).toBe(1);
}); });
it('schema', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!;
const schema = firstBtn.schema;
schema.props.size = 'large';
firstBtn.schema = schema;
expect(firstBtn.getPropValue('size')).toBe('large');
});
describe('插入相关方法', () => { describe('插入相关方法', () => {
it('insertBefore / onChildrenChange', () => { it('insertBefore / onChildrenChange', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
@ -278,6 +331,19 @@ describe('Node 方法测试', () => {
expect(mockFn).not.toHaveBeenCalled(); expect(mockFn).not.toHaveBeenCalled();
}); });
it('RGL / getRGL', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.isRGLContainer = true;
expect(firstBtn.isRGLContainer).toBeTruthy();
const rgl = firstBtn.getRGL();
expect(rgl.isContainerNode).toBeFalsy();
expect(rgl.isEmptyNode).toBeTruthy();
expect(rgl.isRGLContainerNode).toBeTruthy();
expect(rgl.isRGLNode).toBeFalsy();
expect(rgl.isRGL).toBeTruthy();
});
it('onPropChange', () => { it('onPropChange', () => {
const mockFn = jest.fn(); const mockFn = jest.fn();
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
@ -330,8 +396,24 @@ describe('Node 方法测试', () => {
expect(doc.getNode('form')?.isValidComponent()).toBeFalsy(); expect(doc.getNode('form')?.isValidComponent()).toBeFalsy();
}); });
it('title', () => {
designer.createComponentMeta(btnMetadata);
const btn = doc.getNode('node_k1ow3cbn');
// 从 componentMeta 中获取到 title 值
expect(btn.title).toEqual({ type: 'i18n', 'zh-CN': '按钮', 'en-US': 'Button' } );
// 从 extraProp 中获取值
btn.setExtraProp('title', 'hello button');
expect(btn.title).toBe('hello button');
// btn.props.deleteKey('___title___');
// 从 componentMeta descriptor 指向的 key 获取 title
// btn.setPropValue('xTitle', 'title from descriptor')
// expect(btn.title).toBe('title from descriptor');
});
it('isEmpty / getIndex / getIcon', () => { it('isEmpty / getIndex / getIcon', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
// expect(firstBtn.children).toBeNull();
expect(firstBtn.isEmpty()).toBeTruthy(); expect(firstBtn.isEmpty()).toBeTruthy();
expect(firstBtn.index).toBe(0); expect(firstBtn.index).toBe(0);
expect(firstBtn.getIndex()).toBe(0); expect(firstBtn.getIndex()).toBe(0);
@ -378,12 +460,39 @@ describe('Node 方法测试', () => {
expect(doc.rootNode.toString()).toBe('page'); expect(doc.rootNode.toString()).toBe('page');
}); });
it('lock', () => {
const form = doc.getNode('node_k1ow3cbo');
expect(form.isLocked).toBeFalsy();
form.lock(true);
expect(form.isLocked).toBeTruthy();
form.lock(false);
expect(form.isLocked).toBeFalsy();
form.lock();
expect(form.isLocked).toBeTruthy();
});
it('didDropIn / didDropOut', () => {
const form = doc.getNode('node_k1ow3cbo');
designer.createComponentMeta(divMetadata);
const callbacks = form.componentMeta.getMetadata().experimental?.callbacks;
const fn1 = callbacks.onNodeAdd = jest.fn();
const fn2 = callbacks.onNodeRemove = jest.fn();
const textField = doc.getNode('node_k1ow3cc9');
form.didDropIn(textField);
expect(fn1).toHaveBeenCalledWith(textField, form);
form.didDropOut(textField);
expect(fn2).toHaveBeenCalledWith(textField, form);
});
it('hover', () => { it('hover', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.hover(true); firstBtn.hover(true);
expect(doc.designer.detecting.current).toBe(firstBtn); expect(doc.designer.detecting.current).toBe(firstBtn);
firstBtn.hover(false); firstBtn.hover(false);
expect(doc.designer.detecting.current).toBeNull(); expect(doc.designer.detecting.current).toBeNull();
firstBtn.hover();
expect(doc.designer.detecting.current).toBe(firstBtn);
}); });
it('getRect', () => { it('getRect', () => {
@ -410,13 +519,14 @@ describe('Node 方法测试', () => {
it('contains / comparePosition', () => { it('contains / comparePosition', () => {
const page = doc.getNode('page')!; const page = doc.getNode('page')!;
const content = doc.getNode('node_k1ow3cbb')!;
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
const secondBtn = doc.getNode('node_k1ow3cbp')!; const secondBtn = doc.getNode('node_k1ow3cbp')!;
const firstCard = doc.getNode('node_k1ow3cbj')!; const firstCard = doc.getNode('node_k1ow3cbj')!;
expect(contains(firstBtn, firstBtn)).toBeTruthy(); expect(contains(firstBtn, firstBtn)).toBeTruthy();
expect(contains(firstBtn, secondBtn)).toBeFalsy(); expect(contains(firstBtn, secondBtn)).toBeFalsy();
// TODO: 其实这句测试的代码没懂
expect(contains(firstBtn, page)).toBeFalsy(); expect(contains(firstBtn, page)).toBeFalsy();
expect(contains(firstBtn, content)).toBeFalsy();
expect(contains(firstCard, firstBtn)).toBeFalsy(); expect(contains(firstCard, firstBtn)).toBeFalsy();
expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter); expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter);

View File

@ -253,6 +253,7 @@ export default {
// parentWhitelist: 'Div', // parentWhitelist: 'Div',
// childWhitelist: 'Div', // childWhitelist: 'Div',
}, },
descriptor: 'xTitle'
}, },
supports: {}, supports: {},
}, },

View File

@ -230,7 +230,10 @@ export default {
supports: {}, supports: {},
}, },
experimental: { experimental: {
callbacks: {}, callbacks: {
onNodeAdd: (dragment, self) => { console.log(dragment); },
onNodeRemove: (dragment, self) => { console.log(dragment); }
},
initials: [ initials: [
{ {
name: 'behavior', name: 'behavior',

View File

@ -4,4 +4,5 @@ import { invariant } from '../../src/utils/invariant';
it('invariant', () => { it('invariant', () => {
expect(() => invariant(true)).not.toThrow(); expect(() => invariant(true)).not.toThrow();
expect(() => invariant(false, 'abc', 'xxx')).toThrow(/Invariant failed:/); expect(() => invariant(false, 'abc', 'xxx')).toThrow(/Invariant failed:/);
expect(() => invariant(false, 'abc')).toThrow(/Invariant failed:/);
}); });

View File

@ -21,10 +21,14 @@ const mockNode = {
}] }]
}; };
// 没有 slots
const mockNode2 = {};
it('includeSlot', () => { it('includeSlot', () => {
expect(includeSlot(mockNode, 'haha')).toBeTruthy(); expect(includeSlot(mockNode, 'haha')).toBeTruthy();
expect(includeSlot(mockNode, 'heihei')).toBeTruthy(); expect(includeSlot(mockNode, 'heihei')).toBeTruthy();
expect(includeSlot(mockNode, 'xixi')).toBeFalsy(); expect(includeSlot(mockNode, 'xixi')).toBeFalsy();
expect(includeSlot(mockNode2, 'xixi')).toBeFalsy();
}); });
it('removeSlot', () => { it('removeSlot', () => {
@ -34,4 +38,6 @@ it('removeSlot', () => {
expect(mockNode.slots).toHaveLength(1); expect(mockNode.slots).toHaveLength(1);
expect(removeSlot(mockNode, 'heihei')).toBeTruthy(); expect(removeSlot(mockNode, 'heihei')).toBeTruthy();
expect(mockNode.slots).toHaveLength(0); expect(mockNode.slots).toHaveLength(0);
expect(removeSlot(mockNode2, 'xixi')).toBeFalsy();
}); });