mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 17:08:14 +00:00
test: 补充一些 ut
This commit is contained in:
parent
b07436d444
commit
cbe5ed7692
@ -2,7 +2,6 @@ export * from './exclusive-group';
|
||||
export * from './node';
|
||||
export * from './node-children';
|
||||
export * from './props/prop';
|
||||
export * from './props/prop-stash';
|
||||
export * from './props/props';
|
||||
export * from './transform-stage';
|
||||
export * from './modal-nodes-manager';
|
||||
|
||||
@ -142,9 +142,10 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
|
||||
@computed get title(): string | I18nData | ReactElement {
|
||||
let t = this.getExtraProp('title');
|
||||
if (!t && this.componentMeta.descriptor) {
|
||||
t = this.getProp(this.componentMeta.descriptor, false);
|
||||
}
|
||||
// TODO: 暂时走不到这个分支
|
||||
// if (!t && this.componentMeta.descriptor) {
|
||||
// t = this.getProp(this.componentMeta.descriptor, false);
|
||||
// }
|
||||
if (t) {
|
||||
const v = t.getAsString();
|
||||
if (v) {
|
||||
@ -175,7 +176,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
this._children.internalInitParent();
|
||||
this.props.merge(
|
||||
this.upgradeProps(this.initProps(props || {})),
|
||||
this.upgradeProps(extras || {}),
|
||||
this.upgradeProps(extras),
|
||||
);
|
||||
this.setupAutoruns();
|
||||
}
|
||||
@ -719,6 +720,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
const _extras_: { [key: string]: any } = {
|
||||
...extras,
|
||||
};
|
||||
/* istanbul ignore next */
|
||||
Object.keys(this._addons).forEach((key) => {
|
||||
const addon = this._addons[key];
|
||||
if (addon) {
|
||||
@ -1183,12 +1185,18 @@ export function getZLevelTop(child: Node, zLevel: number): Node | null {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试两个节点是否为包含关系
|
||||
* @param node1 测试的父节点
|
||||
* @param node2 测试的被包含节点
|
||||
* @returns 是否包含
|
||||
*/
|
||||
export function contains(node1: Node, node2: Node): boolean {
|
||||
if (node1 === node2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!node1.isParental || !node2.parent) {
|
||||
if (!node1.isParental() || !node2.parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -228,9 +228,10 @@ export class Props implements IPropParent {
|
||||
*/
|
||||
@action
|
||||
deleteKey(key: string): void {
|
||||
this.items = this.items.filter(item => {
|
||||
this.items = this.items.filter((item, i) => {
|
||||
if (item.key === key) {
|
||||
item.purge();
|
||||
this.items.splice(i, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
@ -17,6 +17,8 @@ import {
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
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 otherMeta from '../../fixtures/component-metadata/other';
|
||||
import pageMetadata from '../../fixtures/component-metadata/page';
|
||||
@ -47,6 +49,28 @@ describe('Node 方法测试', () => {
|
||||
|
||||
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', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.getExtraProp('condition')?.setValue(undefined);
|
||||
@ -149,6 +173,22 @@ describe('Node 方法测试', () => {
|
||||
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 节点,不能放入子节点', () => {
|
||||
designer.createComponentMeta(formMetadata);
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
@ -195,6 +235,10 @@ describe('Node 方法测试', () => {
|
||||
|
||||
it('removeChild / replaceWith / replaceChild', () => {
|
||||
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.parent?.replaceChild(firstBtn, { componentName: 'Button', props: { x: 1 } });
|
||||
@ -208,6 +252,15 @@ describe('Node 方法测试', () => {
|
||||
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('插入相关方法', () => {
|
||||
it('insertBefore / onChildrenChange', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
@ -278,6 +331,19 @@ describe('Node 方法测试', () => {
|
||||
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', () => {
|
||||
const mockFn = jest.fn();
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
@ -330,8 +396,24 @@ describe('Node 方法测试', () => {
|
||||
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', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
// expect(firstBtn.children).toBeNull();
|
||||
expect(firstBtn.isEmpty()).toBeTruthy();
|
||||
expect(firstBtn.index).toBe(0);
|
||||
expect(firstBtn.getIndex()).toBe(0);
|
||||
@ -378,12 +460,39 @@ describe('Node 方法测试', () => {
|
||||
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', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.hover(true);
|
||||
expect(doc.designer.detecting.current).toBe(firstBtn);
|
||||
firstBtn.hover(false);
|
||||
expect(doc.designer.detecting.current).toBeNull();
|
||||
firstBtn.hover();
|
||||
expect(doc.designer.detecting.current).toBe(firstBtn);
|
||||
});
|
||||
|
||||
it('getRect', () => {
|
||||
@ -410,13 +519,14 @@ describe('Node 方法测试', () => {
|
||||
|
||||
it('contains / comparePosition', () => {
|
||||
const page = doc.getNode('page')!;
|
||||
const content = doc.getNode('node_k1ow3cbb')!;
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
const firstCard = doc.getNode('node_k1ow3cbj')!;
|
||||
expect(contains(firstBtn, firstBtn)).toBeTruthy();
|
||||
expect(contains(firstBtn, secondBtn)).toBeFalsy();
|
||||
// TODO: 其实这句测试的代码没懂
|
||||
expect(contains(firstBtn, page)).toBeFalsy();
|
||||
expect(contains(firstBtn, content)).toBeFalsy();
|
||||
expect(contains(firstCard, firstBtn)).toBeFalsy();
|
||||
|
||||
expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter);
|
||||
|
||||
@ -253,6 +253,7 @@ export default {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
descriptor: 'xTitle'
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
|
||||
@ -230,7 +230,10 @@ export default {
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
|
||||
@ -4,4 +4,5 @@ import { invariant } from '../../src/utils/invariant';
|
||||
it('invariant', () => {
|
||||
expect(() => invariant(true)).not.toThrow();
|
||||
expect(() => invariant(false, 'abc', 'xxx')).toThrow(/Invariant failed:/);
|
||||
expect(() => invariant(false, 'abc')).toThrow(/Invariant failed:/);
|
||||
});
|
||||
@ -21,10 +21,14 @@ const mockNode = {
|
||||
}]
|
||||
};
|
||||
|
||||
// 没有 slots
|
||||
const mockNode2 = {};
|
||||
|
||||
it('includeSlot', () => {
|
||||
expect(includeSlot(mockNode, 'haha')).toBeTruthy();
|
||||
expect(includeSlot(mockNode, 'heihei')).toBeTruthy();
|
||||
expect(includeSlot(mockNode, 'xixi')).toBeFalsy();
|
||||
expect(includeSlot(mockNode2, 'xixi')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('removeSlot', () => {
|
||||
@ -34,4 +38,6 @@ it('removeSlot', () => {
|
||||
expect(mockNode.slots).toHaveLength(1);
|
||||
expect(removeSlot(mockNode, 'heihei')).toBeTruthy();
|
||||
expect(mockNode.slots).toHaveLength(0);
|
||||
|
||||
expect(removeSlot(mockNode2, 'xixi')).toBeFalsy();
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user