mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 09:41:57 +00:00
fix: 解决 slot 在关闭时没有正常回收节点
fix: 解决同名 slot 在替换时没有正常回收节点 fix: findDOMNode 增强 chore: 在 build-plugins start 时开启 inline-source-map
This commit is contained in:
parent
1902da123b
commit
642a4042c4
@ -1,7 +1,7 @@
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
|
||||
module.exports = ({ onGetWebpackConfig }) => {
|
||||
module.exports = ({ context, onGetWebpackConfig }) => {
|
||||
onGetWebpackConfig((config) => {
|
||||
config.resolve.plugin('tsconfigpaths').use(TsconfigPathsPlugin, [
|
||||
{
|
||||
@ -23,5 +23,8 @@ module.exports = ({ onGetWebpackConfig }) => {
|
||||
|
||||
config.plugins.delete('hot');
|
||||
config.devServer.hot(false);
|
||||
if (context.command === 'start') {
|
||||
config.devtool('inline-source-map');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -33,8 +33,7 @@
|
||||
"build-plugin-component": "^0.2.10",
|
||||
"build-scripts-config": "^0.1.8",
|
||||
"jest": "^26.5.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash": "^4.17.20",
|
||||
"ts-jest": "^26.4.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
|
||||
@ -222,7 +222,7 @@ class Session {
|
||||
end() {
|
||||
if (this.isActive()) {
|
||||
this.clearTimer();
|
||||
console.info('session end');
|
||||
// console.info('session end');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import { ReactElement } from 'react';
|
||||
import { SettingTopEntry } from 'designer/src/designer';
|
||||
import { EventEmitter } from 'events';
|
||||
import { includeSlot, removeSlot } from '../../utils/slot';
|
||||
import { foreachReverse } from '../../utils/tree';
|
||||
|
||||
/**
|
||||
* 基础节点
|
||||
@ -594,7 +595,11 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
|
||||
import(data: Schema, checkId = false) {
|
||||
const { componentName, id, children, props, ...extras } = data;
|
||||
|
||||
if (this.isSlot()) {
|
||||
foreachReverse(this.children, (subNode: Node) => {
|
||||
subNode.remove(true, true);
|
||||
}, (iterable, idx) => (iterable as NodeChildren).get(idx));
|
||||
}
|
||||
if (this.isParental()) {
|
||||
this.props.import(props, extras);
|
||||
(this._children as NodeChildren).import(children, checkId);
|
||||
@ -709,12 +714,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
}
|
||||
|
||||
addSlot(slotNode: Node) {
|
||||
slotNode.internalSetParent(this as ParentalNode, true);
|
||||
const slotName = slotNode?.getExtraProp('name')?.getAsString();
|
||||
// 一个组件下的所有 slot,相同 slotName 的 slot 应该是唯一的
|
||||
if (includeSlot(this, slotName)) {
|
||||
removeSlot(this, slotName);
|
||||
}
|
||||
slotNode.internalSetParent(this as ParentalNode, true);
|
||||
this._slots.push(slotNode);
|
||||
}
|
||||
|
||||
@ -756,7 +761,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
this.purged = true;
|
||||
this.autoruns?.forEach((dispose) => dispose());
|
||||
this.props.purge();
|
||||
this.document.destroyNode(this);
|
||||
// this.document.destroyNode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -269,7 +269,7 @@ export class Prop implements IPropParent {
|
||||
this.stash.clear();
|
||||
}
|
||||
if (this._type !== 'slot' && this._slotNode) {
|
||||
this._slotNode.purge();
|
||||
this._slotNode.remove();
|
||||
this._slotNode = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ export function removeSlot(node: Node, slotName: string | undefined): boolean {
|
||||
const { slots = [] } = node;
|
||||
return slots.some((slot, idx) => {
|
||||
if (slotName && slotName === slot?.getExtraProp('name')?.getAsString()) {
|
||||
slot.remove();
|
||||
slots.splice(idx, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
55
packages/designer/tests/bugs/misc.ts
Normal file
55
packages/designer/tests/bugs/misc.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
// import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
it.todo('在同一个节点下,相同名称的 slot 只能有一个', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
@ -1,12 +1,11 @@
|
||||
import '../../fixtures/window';
|
||||
console.log('window.matchMedia', window.matchMedia);
|
||||
window.matchMedia('width=600px');
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
// const { DocumentModel } = require('../../../src/document/document-model');
|
||||
// const { Node } = require('../__mocks__/node');
|
||||
|
||||
describe('basic utility', () => {
|
||||
test.only('delegateMethod - useOriginMethodName', () => {
|
||||
describe.skip('basic utility', () => {
|
||||
test('delegateMethod - useOriginMethodName', () => {
|
||||
|
||||
const node = new DocumentModel({}, {
|
||||
componentName: 'Component',
|
||||
|
||||
@ -17,7 +17,7 @@ jest.mock('../../../src/document/document-model', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('basic utility', () => {
|
||||
describe.skip('basic utility', () => {
|
||||
test('delegateMethod - useOriginMethodName', () => {
|
||||
const dm = new DocumentModel({} as any, {} as any);
|
||||
console.log(dm.nextId);
|
||||
|
||||
245
packages/designer/tests/document/selection.test.ts
Normal file
245
packages/designer/tests/document/selection.test.ts
Normal file
@ -0,0 +1,245 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('选择区测试', () => {
|
||||
it('常规方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
|
||||
selection.select('node_k1ow3cbj');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj']);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cbj']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.selectAll(['node_k1ow3cbj', 'form']);
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj', 'form']);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cbj', 'form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.remove('node_k1ow3cbj');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.clear();
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual([]);
|
||||
expect(selection.selected).toEqual([]);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// 无选中时调用 clear,不再触发事件
|
||||
selection.clear();
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual([]);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('add 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.add('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// 再加一次相同的节点,不触发事件
|
||||
selection.add('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.add('form2');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'form2']);
|
||||
expect(selection.selected).toEqual(['form', 'form2']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('dispose 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
|
||||
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
selection.dispose();
|
||||
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']);
|
||||
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('dispose 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
|
||||
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
selection.dispose();
|
||||
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']);
|
||||
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('containsNode 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
expect(selection.has('form')).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('node_k1ow3cbj'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('node_k1ow3cb9'))).toBe(false);
|
||||
expect(selection.getNodes()).toEqual([currentDocument?.getNode('form')]);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.add('node_k1ow3cbj');
|
||||
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
|
||||
expect(selection.getTopNodes()).toEqual([currentDocument?.getNode('form')]);
|
||||
expect(selection.getTopNodes(true)).toEqual([currentDocument?.getNode('form')]);
|
||||
});
|
||||
|
||||
it('containsNode 方法 - excludeRoot: true', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('node_k1ow3cb9');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cb9']);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cb9']);
|
||||
expect(selection.has('node_k1ow3cb9')).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'), true)).toBe(false);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('containsNode 方法 - excludeRoot: true', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
const dispose = selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// dispose 后,selected 会被赋值,但是变更事件不会被触发
|
||||
dispose();
|
||||
selection.select('node_k1ow3cb9');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cb9']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
});
|
||||
272
packages/designer/tests/fixtures/component-metadata/div.ts
vendored
Normal file
272
packages/designer/tests/fixtures/component-metadata/div.ts
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
title: '容器',
|
||||
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||
devMode: 'procode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
};
|
||||
17
packages/designer/tests/fixtures/schema/form.ts
vendored
17
packages/designer/tests/fixtures/schema/form.ts
vendored
@ -1,6 +1,7 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
id: 'node_k1ow3cb9',
|
||||
title: 'hey, i\' a page!',
|
||||
props: {
|
||||
extensions: {
|
||||
启用页头: true,
|
||||
@ -111,7 +112,8 @@ export default {
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
id: 'node_k1ow3cbq',
|
||||
id: 'form',
|
||||
extraPropA: 'extraPropA',
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
@ -123,9 +125,15 @@ export default {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
slotA: '',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
@ -949,6 +957,13 @@ export default {
|
||||
},
|
||||
__style__: ':root {\n width: 80px;\n}',
|
||||
fieldId: 'button_k1ow3h1p',
|
||||
greeting: {
|
||||
// type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'Text',
|
||||
props: {},
|
||||
}]
|
||||
}
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
|
||||
30
packages/designer/tests/meta/component-meta.test.ts
Normal file
30
packages/designer/tests/meta/component-meta.test.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import divMeta from '../fixtures/component-metadata/div';
|
||||
import { ComponentMeta } from '../../src/component-meta';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getGlobalComponentActions: () => [],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('组件元数据处理', () => {
|
||||
it('构造函数', () => {
|
||||
const meta = new ComponentMeta(designer, divMeta);
|
||||
console.log(meta);
|
||||
});
|
||||
});
|
||||
564
packages/designer/tests/node/node.add.test.ts
Normal file
564
packages/designer/tests/node/node.add.test.ts
Normal file
@ -0,0 +1,564 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
import { EBADF } from 'constants';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
});
|
||||
it('基本的节点模型初始化,模型导出', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const pageNode = currentDocument?.getNode('node_k1ow3cb9');
|
||||
expect(pageNode?.getComponentName()).toBe('Page');
|
||||
expect(pageNode?.getIcon()).toBeUndefined;
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点深度', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('node_k1ow3cb9');
|
||||
const rootHeaderNode = getNode('node_k1ow3cba');
|
||||
const rootContentNode = getNode('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode('node_k1ow3cbc');
|
||||
const formNode = getNode('form');
|
||||
const cardNode = getNode('node_k1ow3cbj');
|
||||
const cardContentNode = getNode('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode('node_k1ow3cbw');
|
||||
const columnNode = getNode('node_k1ow3cbx');
|
||||
const textFieldNode = getNode('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.zLevel).toBe(0);
|
||||
expect(rootHeaderNode?.zLevel).toBe(1);
|
||||
expect(rootContentNode?.zLevel).toBe(1);
|
||||
expect(rootFooterNode?.zLevel).toBe(1);
|
||||
expect(formNode?.zLevel).toBe(2);
|
||||
expect(cardNode?.zLevel).toBe(3);
|
||||
expect(cardContentNode?.zLevel).toBe(4);
|
||||
expect(columnsLayoutNode?.zLevel).toBe(5);
|
||||
expect(columnNode?.zLevel).toBe(6);
|
||||
expect(textFieldNode?.zLevel).toBe(7);
|
||||
|
||||
expect(textFieldNode?.getZLevelTop(7)).toEqual(textFieldNode);
|
||||
expect(textFieldNode?.getZLevelTop(6)).toEqual(columnNode);
|
||||
expect(textFieldNode?.getZLevelTop(5)).toEqual(columnsLayoutNode);
|
||||
expect(textFieldNode?.getZLevelTop(4)).toEqual(cardContentNode);
|
||||
expect(textFieldNode?.getZLevelTop(3)).toEqual(cardNode);
|
||||
expect(textFieldNode?.getZLevelTop(2)).toEqual(formNode);
|
||||
expect(textFieldNode?.getZLevelTop(1)).toEqual(rootContentNode);
|
||||
expect(textFieldNode?.getZLevelTop(0)).toEqual(pageNode);
|
||||
|
||||
// 异常情况
|
||||
expect(textFieldNode?.getZLevelTop(8)).toBeNull;
|
||||
expect(textFieldNode?.getZLevelTop(-1)).toBeNull;
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点父子、兄弟相关方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('node_k1ow3cb9');
|
||||
const rootHeaderNode = getNode('node_k1ow3cba');
|
||||
const rootContentNode = getNode('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode('node_k1ow3cbc');
|
||||
const formNode = getNode('form');
|
||||
const cardNode = getNode('node_k1ow3cbj');
|
||||
const cardContentNode = getNode('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode('node_k1ow3cbw');
|
||||
const columnNode = getNode('node_k1ow3cbx');
|
||||
const textFieldNode = getNode('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.index).toBe(-1);
|
||||
expect(pageNode?.children.toString()).toBe('[object Array]');
|
||||
expect(pageNode?.children?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getChildren()?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getNode()).toBe(pageNode);
|
||||
|
||||
expect(rootFooterNode?.index).toBe(2);
|
||||
|
||||
expect(textFieldNode?.getParent()).toBe(columnNode);
|
||||
expect(columnNode?.getParent()).toBe(columnsLayoutNode);
|
||||
expect(columnsLayoutNode?.getParent()).toBe(cardContentNode);
|
||||
expect(cardContentNode?.getParent()).toBe(cardNode);
|
||||
expect(cardNode?.getParent()).toBe(formNode);
|
||||
expect(formNode?.getParent()).toBe(rootContentNode);
|
||||
expect(rootContentNode?.getParent()).toBe(pageNode);
|
||||
expect(rootContentNode?.prevSibling).toBe(rootHeaderNode);
|
||||
expect(rootContentNode?.nextSibling).toBe(rootFooterNode);
|
||||
|
||||
expect(pageNode?.isRoot()).toBe(true);
|
||||
expect(pageNode?.contains(textFieldNode)).toBe(true);
|
||||
expect(textFieldNode?.getRoot()).toBe(pageNode);
|
||||
expect(columnNode?.getRoot()).toBe(pageNode);
|
||||
expect(columnsLayoutNode?.getRoot()).toBe(pageNode);
|
||||
expect(cardContentNode?.getRoot()).toBe(pageNode);
|
||||
expect(cardNode?.getRoot()).toBe(pageNode);
|
||||
expect(formNode?.getRoot()).toBe(pageNode);
|
||||
expect(rootContentNode?.getRoot()).toBe(pageNode);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点新建、删除等事件', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
const createNode = currentDocument.createNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('node_k1ow3cb9');
|
||||
const nodeCreateHandler = jest.fn();
|
||||
currentDocument?.onNodeCreate(nodeCreateHandler);
|
||||
|
||||
const node = createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
}
|
||||
});
|
||||
currentDocument?.insertNode(pageNode, node);
|
||||
|
||||
expect(nodeCreateHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeCreateHandler.mock.calls[0][0]).toBe(node);
|
||||
expect(nodeCreateHandler.mock.calls[0][0].componentName).toBe('TextInput');
|
||||
expect(nodeCreateHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha');
|
||||
|
||||
const nodeDestroyHandler = jest.fn();
|
||||
currentDocument?.onNodeDestroy(nodeDestroyHandler);
|
||||
node.remove();
|
||||
expect(nodeDestroyHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0]).toBe(node);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0].componentName).toBe('TextInput');
|
||||
expect(nodeDestroyHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha');
|
||||
});
|
||||
|
||||
it.skip('基本的节点模型初始化,节点插入等方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const formNode = getNode('form');
|
||||
const node1 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
}
|
||||
});
|
||||
const node2 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei',
|
||||
}
|
||||
});
|
||||
const node3 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei2',
|
||||
}
|
||||
});
|
||||
const node4 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei3',
|
||||
}
|
||||
});
|
||||
|
||||
formNode?.insertBefore(node2);
|
||||
// formNode?.insertBefore(node1, node2);
|
||||
// formNode?.insertAfter(node3);
|
||||
// formNode?.insertAfter(node4, node3);
|
||||
|
||||
expect(formNode?.children?.get(0)).toBe(node1);
|
||||
expect(formNode?.children?.get(1)).toBe(node2);
|
||||
// expect(formNode?.children?.get(5)).toBe(node3);
|
||||
// expect(formNode?.children?.get(6)).toBe(node4);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点其他方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('node_k1ow3cb9');
|
||||
expect(pageNode?.isPage()).toBe(true);
|
||||
expect(pageNode?.isComponent()).toBe(false);
|
||||
expect(pageNode?.isSlot()).toBe(false);
|
||||
expect(pageNode?.title).toBe('hey, i\' a page!');
|
||||
});
|
||||
|
||||
describe('节点新增(insertNode)', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('场景一:插入 NodeSchema,不指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(formNode.children.length - 1);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,指定 index: 0', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument.nodesMap;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
}, 0);
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(0);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,指定 index: 1', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
}, 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(1);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,有 children', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'ParentNode',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'SubNode',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'SubNode2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('ParentNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(0)?.componentName).toBe('SubNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(1)?.componentName).toBe('SubNode2');
|
||||
});
|
||||
|
||||
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复,但关闭了 id 检测器', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景二:插入 Node 实例', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
const inputNode = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
currentDocument?.insertNode(formNode, inputNode);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景三:插入 JSExpression', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
type: 'JSExpression',
|
||||
value: 'just a expression'
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toEqual({
|
||||
// type: 'JSExpression',
|
||||
// value: 'just a expression'
|
||||
// });
|
||||
});
|
||||
it('场景四:插入 string', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, 'just a string');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toBe('just a string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('节点新增(insertNodes)', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('场景一:插入 NodeSchema,指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
currentDocument?.insertNodes(formNode, [
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha2',
|
||||
propB: 3
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
}
|
||||
], 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children.get(1);
|
||||
const insertedNode2 = formNode.children.get(2);
|
||||
expect(insertedNode1.componentName).toBe('TextInput');
|
||||
expect(insertedNode1.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3
|
||||
});
|
||||
expect(insertedNode2.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
});
|
||||
});
|
||||
|
||||
it('场景二:插入 Node 实例,指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
const createdNode1 = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha2',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
const createdNode2 = currentDocument?.createNode({
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
currentDocument?.insertNodes(formNode, [ createdNode1, createdNode2 ], 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children.get(1);
|
||||
const insertedNode2 = formNode.children.get(2);
|
||||
expect(insertedNode1.componentName).toBe('TextInput');
|
||||
expect(insertedNode1.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3
|
||||
});
|
||||
expect(insertedNode2.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
it('基本的 slot 创建', () => {
|
||||
const formSchemaWithSlot = set(cloneDeep(formSchema), 'children[0].children[0].props.title.type', 'JSSlot');
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchemaWithSlot,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增(1 + children.length)个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
61
packages/designer/tests/node/node.dragdrop.test.ts
Normal file
61
packages/designer/tests/node/node.dragdrop.test.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe.skip('节点拖拽测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
it('修改普通属性,string | number', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
456
packages/designer/tests/node/node.modify.test.ts
Normal file
456
packages/designer/tests/node/node.modify.test.ts
Normal file
@ -0,0 +1,456 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('读取普通属性,string | number | object', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
const sizeProp2 = formNode?.getProps().getProp('size');
|
||||
expect(sizeProp).toBe(sizeProp2);
|
||||
expect(sizeProp?.getAsString()).toBe('medium');
|
||||
expect(sizeProp?.getValue()).toBe('medium');
|
||||
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(true);
|
||||
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(1);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
|
||||
const idProp = formNode?.getExtraProp('extraPropA');
|
||||
expect(idProp?.getValue()).toBe('extraPropA');
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Node 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
formNode?.setPropValue('size', 'large');
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
formNode?.setPropValue('autoValidate', false);
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
formNode?.setPropValue('obj', {
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string'
|
||||
});
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
formNode?.setPropValue('obj.a', 3);
|
||||
formNode?.setPropValue('obj.b', false);
|
||||
formNode?.setPropValue('obj.c', 'string');
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Props 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const props = formNode?.getProps();
|
||||
props?.setPropValue('size', 'large');
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
props?.setPropValue('autoValidate', false);
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
props?.setPropValue('obj', {
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string'
|
||||
});
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
props?.setPropValue('obj.a', 3);
|
||||
props?.setPropValue('obj.b', false);
|
||||
props?.setPropValue('obj.c', 'string');
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Prop 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
sizeProp?.setValue('large');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
autoValidateProp?.setValue(false);
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
|
||||
const objProp = formNode?.getProp('obj');
|
||||
objProp?.setValue({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string'
|
||||
});
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
objAProp?.setValue(3);
|
||||
objBProp?.setValue(false);
|
||||
objCProp?.setValue('string');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('修改 slot 属性,初始存在 slot 属性名,正常生成节点模型', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true
|
||||
}
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(2);
|
||||
const firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
const secondChildNode = formNode?.slots[0].children?.get(1);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput1');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('haha');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(1);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(true);
|
||||
expect(secondChildNode?.componentName).toBe('TextInput2');
|
||||
expect(secondChildNode?.getPropValue('txt')).toBe('heihei');
|
||||
expect(secondChildNode?.getPropValue('num')).toBe(2);
|
||||
expect(secondChildNode?.getPropValue('bool')).toBe(false);
|
||||
});
|
||||
|
||||
it('修改 slot 属性,初始存在 slot 属性名,关闭 slot', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true
|
||||
}
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
|
||||
formNode?.setPropValue('slotA', '');
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length);
|
||||
expect(formNode?.slots).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('修改 slot 属性,初始存在 slot 属性名,同名覆盖 slot', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
name: 'slotA',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true
|
||||
}
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(2);
|
||||
|
||||
let firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput1');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('haha');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(1);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(true);
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
name: 'slotA',
|
||||
value: [{
|
||||
componentName: 'TextInput3',
|
||||
props: {
|
||||
txt: 'xixi',
|
||||
num: 3,
|
||||
bool: false
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(1);
|
||||
firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput3');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('xixi');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(3);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
122
packages/designer/tests/node/node.remove.test.ts
Normal file
122
packages/designer/tests/node/node.remove.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { experimental: null };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('节点模型删除测试', () => {
|
||||
it('删除叶子节点', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbn');
|
||||
// Button#1
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 1);
|
||||
|
||||
currentDocument?.removeNode(nodesMap.get('node_k1ow3cbp'));
|
||||
// Button#2
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 2);
|
||||
|
||||
currentDocument?.removeNode('unexisting_node');
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 2);
|
||||
});
|
||||
|
||||
it('删除叶子节点,带有 slot', () => {
|
||||
const formSchemaWithSlot = set(cloneDeep(formSchema),
|
||||
'children[1].children[0].children[2].children[1].props.greeting.type', 'JSSlot');
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchemaWithSlot,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbp');
|
||||
// Button + Slot + Text
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 3);
|
||||
});
|
||||
|
||||
it('删除分支节点', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbo');
|
||||
// Div + 2 * Button
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 3);
|
||||
});
|
||||
|
||||
it('删除分支节点,带有 slot', () => {
|
||||
const formSchemaWithSlot = set(cloneDeep(formSchema),
|
||||
'children[1].children[0].children[2].children[1].props.greeting.type', 'JSSlot');
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchemaWithSlot,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbo');
|
||||
// Div + 2 * Button + Slot + Text
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 5);
|
||||
});
|
||||
});
|
||||
@ -1,8 +1,8 @@
|
||||
import set from 'lodash.set';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/clonedeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
// import { Node } from '../../../src/document/node/node';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
@ -34,7 +34,11 @@ beforeAll(() => {
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
it('基本的节点模型初始化,模型导出', () => {
|
||||
beforeEach(() => {
|
||||
mockCreateSettingEntry.mockClear();
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,模型导出,初始化传入 schema', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
@ -56,129 +60,75 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
describe('节点新增(insertNode)', () => {
|
||||
let project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it.only('场景一:插入 NodeSchema', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('node_k1ow3cbq');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
it('基本的节点模型初始化,模型导出,project.open 传入 schema', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
it.only('场景一:插入 NodeSchema,有 children', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('node_k1ow3cbq');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it.only('场景一:插入 NodeSchema,id 与现有 schema 重复', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('node_k1ow3cbq');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it.only('场景一:插入 NodeSchema,id 与现有 schema 重复,但关闭了 id 检测器', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('node_k1ow3cbq');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景二:插入 Node 实例', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('node_k1ow3cbq');
|
||||
const inputNode = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3
|
||||
}
|
||||
});
|
||||
expect(inputNode.id).toBe('nodeschema-id2');
|
||||
currentDocument?.insertNode(formNode, inputNode);
|
||||
expect(nodesMap.get('nodeschema-id2').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景二:插入 JSExpression', () => {});
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
})
|
||||
it('project 卸载所有 document - unload()', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument, documents } = project;
|
||||
|
||||
it('block ❌ | component ❌ | slot ✅', () => {
|
||||
const formSchemaWithSlot = set(cloneDeep(formSchema), 'children[0].children[0].props.title.type', 'JSSlot');
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchemaWithSlot,
|
||||
],
|
||||
expect(documents).toHaveLength(1);
|
||||
expect(currentDocument).toBe(documents[0]);
|
||||
|
||||
project.unload();
|
||||
|
||||
expect(documents).toHaveLength(0);
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增 1 + children.length 个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
|
||||
|
||||
it('project 卸载指定 document - removeDocument()', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument, documents } = project;
|
||||
|
||||
expect(documents).toHaveLength(1);
|
||||
expect(currentDocument).toBe(documents[0]);
|
||||
|
||||
project.removeDocument(currentDocument);
|
||||
|
||||
expect(documents).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
it('基本的节点模型初始化,模型导出,初始化传入 schema', () => {
|
||||
const formSchemaWithSlot = set(cloneDeep(formSchema), 'children[0].children[0].props.title.type', 'JSSlot');
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchemaWithSlot,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument!;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增(1 + children.length)个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('多 document 测试', () => {
|
||||
|
||||
});
|
||||
});
|
||||
@ -127,7 +127,7 @@ designer.addPropsReducer((props, node) => {
|
||||
!isJSBlock(ov) &&
|
||||
!isJSSlot(ov) &&
|
||||
!isVariable(ov) &&
|
||||
isString(v)) {
|
||||
(isString(v) || isI18NObject(v))) {
|
||||
newProps[item.name] = convertToI18NObject(v);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@ -54,5 +54,5 @@
|
||||
"publishConfig": {
|
||||
"registry": "http://registry.npm.alibaba-inc.com"
|
||||
},
|
||||
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-9/build/index.html"
|
||||
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-10/build/index.html"
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ module.exports = {
|
||||
'no-shadow': 0,
|
||||
'no-prototype-builtins': 0,
|
||||
'array-callback-return': 0,
|
||||
'@typescript-eslint/member-ordering': 0
|
||||
'@typescript-eslint/member-ordering': 0,
|
||||
'react/no-find-dom-node', 0
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { ReactInstance } from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import { isElement } from '@ali/lowcode-utils';
|
||||
import { isDOMNode } from './is-dom-node';
|
||||
|
||||
@ -29,5 +30,5 @@ export function reactFindDOMNodes(elem: ReactInstance | null): Array<Element | T
|
||||
const elements: Array<Element | Text> = [];
|
||||
const fiberNode = (elem as any)[FIBER_KEY];
|
||||
elementsFromFiber(fiberNode.child, elements);
|
||||
return elements.length > 0 ? elements : null;
|
||||
return elements.length > 0 ? elements : [findDOMNode(elem)];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user