mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
test: add renderer-core hoc/leaf single test
This commit is contained in:
parent
bf2c1e96a1
commit
11319e1ac9
@ -23,6 +23,8 @@ const jestConfig = {
|
|||||||
collectCoverageFrom: [
|
collectCoverageFrom: [
|
||||||
'src/**/*.ts',
|
'src/**/*.ts',
|
||||||
'src/**/*.tsx',
|
'src/**/*.tsx',
|
||||||
|
'!src/utils/logger.ts',
|
||||||
|
'!src/types/index.ts',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -216,24 +216,6 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
this.recordTime();
|
this.recordTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
get childrenMap(): any {
|
|
||||||
const map = new Map();
|
|
||||||
|
|
||||||
if (!this.hasChildren) {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.children.forEach((d: any) => {
|
|
||||||
if (Array.isArray(d)) {
|
|
||||||
map.set(d[0].props.componentId, d);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
map.set(d.props.componentId, d);
|
|
||||||
});
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultState() {
|
get defaultState() {
|
||||||
const {
|
const {
|
||||||
hidden = false,
|
hidden = false,
|
||||||
@ -253,18 +235,18 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
super(props, context);
|
super(props, context);
|
||||||
// 监听以下事件,当变化时更新自己
|
// 监听以下事件,当变化时更新自己
|
||||||
__debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
|
__debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
|
||||||
clearRerenderEvent(this.props.componentId);
|
clearRerenderEvent(componentCacheId);
|
||||||
const _leaf = this.leaf;
|
const _leaf = this.leaf;
|
||||||
this.initOnPropsChangeEvent(_leaf);
|
this.initOnPropsChangeEvent(_leaf);
|
||||||
this.initOnChildrenChangeEvent(_leaf);
|
this.initOnChildrenChangeEvent(_leaf);
|
||||||
this.initOnVisibleChangeEvent(_leaf);
|
this.initOnVisibleChangeEvent(_leaf);
|
||||||
this.curEventLeaf = _leaf;
|
this.curEventLeaf = _leaf;
|
||||||
|
|
||||||
cache.ref.set(props.componentId, {
|
cache.ref.set(componentCacheId, {
|
||||||
makeUnitRender: this.makeUnitRender,
|
makeUnitRender: this.makeUnitRender,
|
||||||
});
|
});
|
||||||
|
|
||||||
let cacheState = cache.state.get(props.componentId);
|
let cacheState = cache.state.get(componentCacheId);
|
||||||
if (!cacheState || cacheState.__tag !== props.__tag) {
|
if (!cacheState || cacheState.__tag !== props.__tag) {
|
||||||
cacheState = this.defaultState;
|
cacheState = this.defaultState;
|
||||||
}
|
}
|
||||||
@ -275,7 +257,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
private curEventLeaf: Node | undefined;
|
private curEventLeaf: Node | undefined;
|
||||||
|
|
||||||
setState(state: any) {
|
setState(state: any) {
|
||||||
cache.state.set(this.props.componentId, {
|
cache.state.set(componentCacheId, {
|
||||||
...this.state,
|
...this.state,
|
||||||
...state,
|
...state,
|
||||||
__tag: this.props.__tag,
|
__tag: this.props.__tag,
|
||||||
@ -489,7 +471,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
// TODO: 缓存同级其他元素的 children。
|
// TODO: 缓存同级其他元素的 children。
|
||||||
// 缓存二级 children Next 查询筛选组件有问题
|
// 缓存二级 children Next 查询筛选组件有问题
|
||||||
// 缓存一级 children Next Tab 组件有问题
|
// 缓存一级 children Next Tab 组件有问题
|
||||||
const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, scope, Comp); // this.childrenMap
|
const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, scope, Comp);
|
||||||
__debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`, nextChild);
|
__debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`, nextChild);
|
||||||
this.setState({
|
this.setState({
|
||||||
nodeChildren: nextChild,
|
nodeChildren: nextChild,
|
||||||
@ -531,7 +513,7 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get leaf(): Node | undefined {
|
get leaf(): Node | undefined {
|
||||||
return this.props._leaf || getNode(this.props.componentId);
|
return this.props._leaf || getNode(componentCacheId);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@ -1,97 +1,146 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`leafWrapper base 1`] = `
|
exports[`children this.props.children is array 1`] = `
|
||||||
<div
|
<div>
|
||||||
_leaf={
|
<div
|
||||||
Node {
|
content="content"
|
||||||
"emitter": EventEmitter {
|
>
|
||||||
"_events": Object {
|
content
|
||||||
"onChildrenChange": [Function],
|
</div>
|
||||||
"onPropChange": [Function],
|
<div
|
||||||
"onVisibleChange": [Function],
|
content="content"
|
||||||
},
|
>
|
||||||
"_eventsCount": 3,
|
|
||||||
"_maxListeners": undefined,
|
|
||||||
Symbol(kCapture): false,
|
|
||||||
},
|
|
||||||
"hasLoop": false,
|
|
||||||
"schema": Object {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
content
|
content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`leafWrapper change ___condition___ props 1`] = `
|
exports[`lifecycle leaf change and make componentWillReceiveProps 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
__tag="222"
|
||||||
|
content="content new leaf"
|
||||||
|
>
|
||||||
|
content new leaf
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lifecycle props change and make componentWillReceiveProps 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content="content"
|
||||||
|
>
|
||||||
|
content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lifecycle props change and make componentWillReceiveProps 2`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content="content 123"
|
||||||
|
>
|
||||||
|
content 123
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lifecycle props change and make componentWillReceiveProps 3`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
__tag="111"
|
||||||
|
content="content 123"
|
||||||
|
>
|
||||||
|
content 123
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`mini unit render leaf has a loop, render from parent 1`] = `
|
||||||
|
<div>
|
||||||
|
this is a new children
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`mini unit render make text props change 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content="content"
|
||||||
|
>
|
||||||
|
content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`mini unit render make text props change 2`] = `
|
||||||
<div
|
<div
|
||||||
_leaf={
|
newPropKey="newPropValue"
|
||||||
Node {
|
|
||||||
"emitter": EventEmitter {
|
|
||||||
"_events": Object {
|
|
||||||
"onChildrenChange": [Function],
|
|
||||||
"onPropChange": [Function],
|
|
||||||
"onVisibleChange": [Function],
|
|
||||||
},
|
|
||||||
"_eventsCount": 3,
|
|
||||||
"_maxListeners": undefined,
|
|
||||||
Symbol(kCapture): false,
|
|
||||||
},
|
|
||||||
"hasLoop": false,
|
|
||||||
"schema": Object {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`leafWrapper change ___condition___ props, but not hidden component 1`] = `
|
exports[`mini unit render parent is a mock leaf 1`] = `
|
||||||
<div
|
<div>
|
||||||
_leaf={
|
<div
|
||||||
Node {
|
content="new content to mock"
|
||||||
"emitter": EventEmitter {
|
>
|
||||||
"_events": Object {
|
new content to mock
|
||||||
"onChildrenChange": [Function],
|
|
||||||
"onPropChange": [Function],
|
|
||||||
"onVisibleChange": [Function],
|
|
||||||
},
|
|
||||||
"_eventsCount": 3,
|
|
||||||
"_maxListeners": undefined,
|
|
||||||
Symbol(kCapture): false,
|
|
||||||
},
|
|
||||||
"hasLoop": false,
|
|
||||||
"schema": Object {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
new content
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`leafWrapper change props 1`] = `
|
exports[`mini unit render props has new children 1`] = `
|
||||||
<div
|
<div>
|
||||||
_leaf={
|
children 01
|
||||||
Node {
|
children 02
|
||||||
"emitter": EventEmitter {
|
</div>
|
||||||
"_events": Object {
|
`;
|
||||||
"onChildrenChange": [Function],
|
|
||||||
"onPropChange": [Function],
|
exports[`onChildrenChange children is array string 1`] = `
|
||||||
"onVisibleChange": [Function],
|
<div>
|
||||||
},
|
onChildrenChange content 01
|
||||||
"_eventsCount": 3,
|
onChildrenChange content 02
|
||||||
"_maxListeners": undefined,
|
</div>
|
||||||
Symbol(kCapture): false,
|
`;
|
||||||
},
|
|
||||||
"hasLoop": false,
|
exports[`onPropChange change textNode [key:___condition___] props, but not hidden component 1`] = `
|
||||||
"schema": Object {},
|
<div>
|
||||||
}
|
<div
|
||||||
}
|
content="content"
|
||||||
>
|
>
|
||||||
<div>
|
content
|
||||||
new content
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`onPropChange change textNode [key:___condition___] props, hide textNode component 1`] = `<div />`;
|
||||||
|
|
||||||
|
exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content="content"
|
||||||
|
>
|
||||||
|
content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 2`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content={null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`onVisibleChange visible is false 1`] = `<div />`;
|
||||||
|
|
||||||
|
exports[`onVisibleChange visible is true 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
content="content"
|
||||||
|
>
|
||||||
|
content
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -6,12 +6,23 @@ import { leafWrapper } from '../../src/hoc/leaf';
|
|||||||
import components from '../utils/components';
|
import components from '../utils/components';
|
||||||
import Node from '../utils/node';
|
import Node from '../utils/node';
|
||||||
|
|
||||||
|
let rerenderCount = 0;
|
||||||
|
|
||||||
|
const nodeMap = new Map();
|
||||||
|
|
||||||
|
const makeSnapshot = (component) => {
|
||||||
|
let tree = component.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
const baseRenderer: any = {
|
const baseRenderer: any = {
|
||||||
__debug () {},
|
__debug () {},
|
||||||
__getComponentProps (schema: any) {
|
__getComponentProps (schema: any) {
|
||||||
return schema.props;
|
return schema.props;
|
||||||
},
|
},
|
||||||
__getSchemaChildrenVirtualDom () {},
|
__getSchemaChildrenVirtualDom (schema: any) {
|
||||||
|
return schema.children;
|
||||||
|
},
|
||||||
context: {
|
context: {
|
||||||
engine: {
|
engine: {
|
||||||
createElement,
|
createElement,
|
||||||
@ -19,110 +30,525 @@ const baseRenderer: any = {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
__host: {},
|
__host: {},
|
||||||
getNode: () => {},
|
getNode: (id) => nodeMap.get(id),
|
||||||
__container: () => {},
|
__container: {
|
||||||
|
rerender: () => {
|
||||||
|
rerenderCount = 1 + rerenderCount;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documentId: '01'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('leafWrapper', () => {
|
let Div, DivNode, Text, TextNode, component, textSchema, divSchema;
|
||||||
const Div = leafWrapper(components.Div as any, {
|
let id = 0;
|
||||||
schema: {
|
|
||||||
id: 'div',
|
beforeEach(() => {
|
||||||
|
textSchema = {
|
||||||
|
id: 'text' + id,
|
||||||
|
props: {
|
||||||
|
content: 'content'
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
divSchema = {
|
||||||
|
id: 'div' + id,
|
||||||
|
};
|
||||||
|
|
||||||
|
id++;
|
||||||
|
|
||||||
|
Div = leafWrapper(components.Div as any, {
|
||||||
|
schema: divSchema,
|
||||||
baseRenderer,
|
baseRenderer,
|
||||||
componentInfo: {},
|
componentInfo: {},
|
||||||
scope: {},
|
scope: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const DivNode = new Node({});
|
DivNode = new Node(divSchema);
|
||||||
const TextNode = new Node({});
|
TextNode = new Node(textSchema);
|
||||||
|
|
||||||
const Text = leafWrapper(components.Text as any, {
|
nodeMap.set(divSchema.id, DivNode);
|
||||||
schema: {
|
nodeMap.set(textSchema.id, TextNode);
|
||||||
id: 'div',
|
|
||||||
props: {
|
Text = leafWrapper(components.Text as any, {
|
||||||
content: 'content'
|
schema: textSchema,
|
||||||
}
|
|
||||||
},
|
|
||||||
baseRenderer,
|
baseRenderer,
|
||||||
componentInfo: {},
|
componentInfo: {},
|
||||||
scope: {},
|
scope: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const component = renderer.create(
|
component = renderer.create(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<Div _leaf={DivNode}>
|
<Div _leaf={DivNode}>
|
||||||
<Text _leaf={TextNode} content="content"></Text>
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
</Div>
|
</Div>
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('base', () => {
|
afterEach(() => {
|
||||||
let tree = component.toJSON();
|
component.unmount(component);
|
||||||
expect(tree).toMatchSnapshot();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('change props', () => {
|
describe('onPropChange', () => {
|
||||||
|
it('change textNode [key:content] props', () => {
|
||||||
TextNode.emitPropChange({
|
TextNode.emitPropChange({
|
||||||
key: 'content',
|
key: 'content',
|
||||||
newValue: 'new content',
|
newValue: 'new content',
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
let tree = component.toJSON();
|
const root = component.root;
|
||||||
expect(tree).toMatchSnapshot();
|
expect(root.findByType(components.Text).props.content).toEqual('new content')
|
||||||
});
|
});
|
||||||
|
|
||||||
it('change ___condition___ props', () => {
|
it('change textNode [key:___condition___] props, hide textNode component', () => {
|
||||||
|
// mock leaf?.export result
|
||||||
TextNode.schema.condition = false;
|
TextNode.schema.condition = false;
|
||||||
TextNode.emitPropChange({
|
TextNode.emitPropChange({
|
||||||
key: '___condition___',
|
key: '___condition___',
|
||||||
newValue: false,
|
newValue: false,
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
let tree = component.toJSON();
|
makeSnapshot(component);
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('change ___condition___ props, but not hidden component', () => {
|
it('change textNode [key:___condition___] props, but not hidden component', () => {
|
||||||
TextNode.schema.condition = true;
|
TextNode.schema.condition = true;
|
||||||
TextNode.emitPropChange({
|
TextNode.emitPropChange({
|
||||||
key: '___condition___',
|
key: '___condition___',
|
||||||
newValue: false,
|
newValue: false,
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
let tree = component.toJSON();
|
makeSnapshot(component);
|
||||||
expect(tree).toMatchSnapshot();
|
});
|
||||||
|
|
||||||
|
it('change textNode [key:content], content in this.props but not in leaf.export result', () => {
|
||||||
|
makeSnapshot(component);
|
||||||
|
|
||||||
|
delete TextNode.schema.props.content;
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: null,
|
||||||
|
} as any, true);
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
|
||||||
|
const root = component.root;
|
||||||
|
|
||||||
|
const TextInst = root.findByType(components.Text);
|
||||||
|
|
||||||
|
expect(TextInst.props.content).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('change textNode [key:___loop___], make rerender', () => {
|
||||||
|
expect(leafWrapper(components.Text as any, {
|
||||||
|
schema: textSchema,
|
||||||
|
baseRenderer,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
})).toEqual(Text);
|
||||||
|
|
||||||
|
const nextRerenderCount = rerenderCount + 1;
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: '___loop___',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect(rerenderCount).toBe(nextRerenderCount);
|
||||||
|
expect(leafWrapper(components.Text as any, {
|
||||||
|
schema: textSchema,
|
||||||
|
baseRenderer,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
})).not.toEqual(Text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('lifecycle', () => {
|
||||||
|
it('props change and make componentWillReceiveProps', () => {
|
||||||
|
makeSnapshot(component);
|
||||||
|
|
||||||
|
// 没有 __tag 标识
|
||||||
|
component.update((
|
||||||
|
<Div _leaf={DivNode}>
|
||||||
|
<Text _leaf={TextNode} content="content 123"></Text>
|
||||||
|
</Div>
|
||||||
|
));
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
|
||||||
|
// 有 __tag 标识
|
||||||
|
component.update((
|
||||||
|
<Div _leaf={DivNode}>
|
||||||
|
<Text _leaf={TextNode} __tag="111" content="content 123"></Text>
|
||||||
|
</Div>
|
||||||
|
));
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaf change and make componentWillReceiveProps', () => {
|
||||||
|
const newTextNodeLeaf = new Node(textSchema);
|
||||||
|
component.update((
|
||||||
|
<Div _leaf={DivNode}>
|
||||||
|
<Text _leaf={newTextNodeLeaf} __tag="222" content="content 123"></Text>
|
||||||
|
</Div>
|
||||||
|
));
|
||||||
|
|
||||||
|
newTextNodeLeaf.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'content new leaf',
|
||||||
|
});
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mini unit render', () => {
|
||||||
|
let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode;
|
||||||
|
beforeEach(() => {
|
||||||
|
miniRenderSchema = {
|
||||||
|
id: 'miniDiv' + id,
|
||||||
|
};
|
||||||
|
|
||||||
|
MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, {
|
||||||
|
schema: miniRenderSchema,
|
||||||
|
baseRenderer,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
MiniRenderDivNode = new Node(miniRenderSchema, {
|
||||||
|
componentMeta: {
|
||||||
|
isMinimalRenderUnit: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
TextNode = new Node(textSchema, {
|
||||||
|
parent: MiniRenderDivNode,
|
||||||
|
});
|
||||||
|
|
||||||
|
component = renderer.create(
|
||||||
|
// @ts-ignore
|
||||||
|
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
</MiniRenderDiv>
|
||||||
|
);
|
||||||
})
|
})
|
||||||
});
|
|
||||||
|
|
||||||
describe('loop', () => {
|
it('make text props change', () => {
|
||||||
const Div = leafWrapper(components.Div as any, {
|
if (!MiniRenderDivNode.schema.props) {
|
||||||
schema: {
|
MiniRenderDivNode.schema.props = {};
|
||||||
id: 'div',
|
}
|
||||||
},
|
MiniRenderDivNode.schema.props['newPropKey'] = 'newPropValue';
|
||||||
baseRenderer,
|
|
||||||
componentInfo: {},
|
makeSnapshot(component);
|
||||||
scope: {},
|
|
||||||
|
const inst = component.root;
|
||||||
|
|
||||||
|
const TextInst = inst.findByType(Text).children[0];
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect((TextInst as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||||
|
singleRender: false,
|
||||||
|
minimalUnitId: 'miniDiv' + id,
|
||||||
|
minimalUnitName: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
});
|
});
|
||||||
|
|
||||||
const DivNode = new Node({});
|
it('dont render mini render component', () => {
|
||||||
const TextNode = new Node({});
|
const TextNode = new Node(textSchema, {
|
||||||
|
parent: new Node({
|
||||||
|
id: 'random',
|
||||||
|
}, {
|
||||||
|
componentMeta: {
|
||||||
|
isMinimalRenderUnit: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const Text = leafWrapper(components.Text as any, {
|
renderer.create(
|
||||||
schema: {
|
// @ts-ignore
|
||||||
id: 'div',
|
<div>
|
||||||
props: {
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
content: 'content'
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextCount = rerenderCount + 1;
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect(rerenderCount).toBe(nextCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaf is a mock function', () => {
|
||||||
|
const TextNode = new Node(textSchema, {
|
||||||
|
parent: {
|
||||||
|
isEmpty: () => false,
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
baseRenderer,
|
|
||||||
componentInfo: {},
|
renderer.create(
|
||||||
scope: {},
|
// @ts-ignore
|
||||||
|
<div>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
const component = renderer.create(
|
it('change component leaf isRoot is true', () => {
|
||||||
// @ts-ignore
|
const TextNode = new Node(textSchema, {
|
||||||
<Div _leaf={DivNode}>
|
isRoot: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const component = renderer.create(
|
||||||
<Text _leaf={TextNode} content="content"></Text>
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
</Div>
|
);
|
||||||
);
|
|
||||||
|
const inst = component.root;
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||||
|
singleRender: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('change component leaf parent isRoot is true', () => {
|
||||||
|
const TextNode = new Node(textSchema, {
|
||||||
|
parent: new Node({
|
||||||
|
id: 'first-parent',
|
||||||
|
}, {
|
||||||
|
componentMeta: {
|
||||||
|
isMinimalRenderUnit: true,
|
||||||
|
},
|
||||||
|
parent: new Node({
|
||||||
|
id: 'rootId',
|
||||||
|
}, {
|
||||||
|
isRoot: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const component = renderer.create(
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
);
|
||||||
|
|
||||||
|
const inst = component.root;
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||||
|
singleRender: false,
|
||||||
|
minimalUnitId: 'first-parent',
|
||||||
|
minimalUnitName: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parent is a mock leaf', () => {
|
||||||
|
const MiniRenderDivNode = {};
|
||||||
|
|
||||||
|
const component = renderer.create(
|
||||||
|
// @ts-ignore
|
||||||
|
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
</MiniRenderDiv>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'new content to mock',
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('props has new children', () => {
|
||||||
|
MiniRenderDivNode.schema.props.children = [
|
||||||
|
'children 01',
|
||||||
|
'children 02',
|
||||||
|
];
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: 'props'
|
||||||
|
});
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaf has a loop, render from parent', () => {
|
||||||
|
MiniRenderDivNode = new Node(miniRenderSchema, {});
|
||||||
|
|
||||||
|
TextNode = new Node(textSchema, {
|
||||||
|
parent: MiniRenderDivNode,
|
||||||
|
hasLoop: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
component = renderer.create(
|
||||||
|
// @ts-ignore
|
||||||
|
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
</MiniRenderDiv>
|
||||||
|
);
|
||||||
|
|
||||||
|
MiniRenderDivNode.schema.children = ['this is a new children'];
|
||||||
|
|
||||||
|
TextNode.emitPropChange({
|
||||||
|
key: 'content',
|
||||||
|
newValue: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('component cache', () => {
|
||||||
|
it('get different component with same is and different doc id', () => {
|
||||||
|
const baseRenderer02 = {
|
||||||
|
...baseRenderer,
|
||||||
|
props: {
|
||||||
|
...baseRenderer.props,
|
||||||
|
documentId: '02',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const Div3 = leafWrapper(components.Div as any, {
|
||||||
|
schema: divSchema,
|
||||||
|
baseRenderer: baseRenderer02,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Div).not.toEqual(Div3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get component again and get ths cache component', () => {
|
||||||
|
const Div2 = leafWrapper(components.Div as any, {
|
||||||
|
schema: divSchema,
|
||||||
|
baseRenderer,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Div).toEqual(Div2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onVisibleChange', () => {
|
||||||
|
it('visible is false', () => {
|
||||||
|
TextNode.emitVisibleChange(false);
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('visible is true', () => {
|
||||||
|
TextNode.emitVisibleChange(true);
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('children', () => {
|
||||||
|
it('this.props.children is array', () => {
|
||||||
|
const component = renderer.create(
|
||||||
|
// @ts-ignore
|
||||||
|
<Div _leaf={DivNode}>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
</Div>
|
||||||
|
);
|
||||||
|
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onChildrenChange', () => {
|
||||||
|
it('children is array string', () => {
|
||||||
|
DivNode.schema.children = [
|
||||||
|
'onChildrenChange content 01',
|
||||||
|
'onChildrenChange content 02'
|
||||||
|
]
|
||||||
|
DivNode.emitChildrenChange();
|
||||||
|
makeSnapshot(component);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('not render leaf', () => {
|
||||||
|
let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode;
|
||||||
|
beforeEach(() => {
|
||||||
|
miniRenderSchema = {
|
||||||
|
id: 'miniDiv' + id,
|
||||||
|
};
|
||||||
|
|
||||||
|
MiniRenderDivNode = new Node(miniRenderSchema, {
|
||||||
|
componentMeta: {
|
||||||
|
isMinimalRenderUnit: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
nodeMap.set(miniRenderSchema.id, MiniRenderDivNode);
|
||||||
|
|
||||||
|
MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, {
|
||||||
|
schema: miniRenderSchema,
|
||||||
|
baseRenderer,
|
||||||
|
componentInfo: {},
|
||||||
|
scope: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
TextNode = new Node(textSchema, {
|
||||||
|
parent: MiniRenderDivNode,
|
||||||
|
});
|
||||||
|
|
||||||
|
component = renderer.create(
|
||||||
|
<Text _leaf={TextNode} content="content"></Text>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('onPropsChange', () => {
|
||||||
|
const nextCount = rerenderCount + 1;
|
||||||
|
|
||||||
|
MiniRenderDivNode.emitPropChange({
|
||||||
|
key: 'any',
|
||||||
|
newValue: 'any',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(rerenderCount).toBe(nextCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('onChildrenChange', () => {
|
||||||
|
const nextCount = rerenderCount + 1;
|
||||||
|
|
||||||
|
MiniRenderDivNode.emitChildrenChange({
|
||||||
|
key: 'any',
|
||||||
|
newValue: 'any',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(rerenderCount).toBe(nextCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('onVisibleChange', () => {
|
||||||
|
const nextCount = rerenderCount + 1;
|
||||||
|
|
||||||
|
MiniRenderDivNode.emitVisibleChange(true);
|
||||||
|
|
||||||
|
expect(rerenderCount).toBe(nextCount);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1026,7 +1026,20 @@ exports[`JSExpression JSSlot has loop 1`] = `
|
|||||||
forwardRef={[Function]}
|
forwardRef={[Function]}
|
||||||
useFieldIdAsDomId={false}
|
useFieldIdAsDomId={false}
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
|
__id="node_ocl1ao1o7w4"
|
||||||
|
__style__=":root {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}"
|
||||||
|
behavior="NORMAL"
|
||||||
|
className="text_l1ao7pfb"
|
||||||
|
content="这是一个低代码业务组件~"
|
||||||
|
fieldId="text_l1ao7lvp"
|
||||||
|
forwardRef={[Function]}
|
||||||
|
maxLine={0}
|
||||||
|
showTitle={false}
|
||||||
|
>
|
||||||
这是一个低代码业务组件~
|
这是一个低代码业务组件~
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1044,7 +1057,20 @@ exports[`JSExpression JSSlot has loop 1`] = `
|
|||||||
forwardRef={[Function]}
|
forwardRef={[Function]}
|
||||||
useFieldIdAsDomId={false}
|
useFieldIdAsDomId={false}
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
|
__id="node_ocl1ao1o7w4"
|
||||||
|
__style__=":root {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}"
|
||||||
|
behavior="NORMAL"
|
||||||
|
className="text_l1ao7pfb"
|
||||||
|
content="这是一个低代码业务组件~"
|
||||||
|
fieldId="text_l1ao7lvp"
|
||||||
|
forwardRef={[Function]}
|
||||||
|
maxLine={0}
|
||||||
|
showTitle={false}
|
||||||
|
>
|
||||||
这是一个低代码业务组件~
|
这是一个低代码业务组件~
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1062,7 +1088,20 @@ exports[`JSExpression JSSlot has loop 1`] = `
|
|||||||
forwardRef={[Function]}
|
forwardRef={[Function]}
|
||||||
useFieldIdAsDomId={false}
|
useFieldIdAsDomId={false}
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
|
__id="node_ocl1ao1o7w4"
|
||||||
|
__style__=":root {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}"
|
||||||
|
behavior="NORMAL"
|
||||||
|
className="text_l1ao7pfb"
|
||||||
|
content="这是一个低代码业务组件~"
|
||||||
|
fieldId="text_l1ao7lvp"
|
||||||
|
forwardRef={[Function]}
|
||||||
|
maxLine={0}
|
||||||
|
showTitle={false}
|
||||||
|
>
|
||||||
这是一个低代码业务组件~
|
这是一个低代码业务组件~
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,6 +11,16 @@ jest.mock('zen-logger', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('lodash', () => {
|
||||||
|
const original = jest.requireActual('lodash');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
debounce: (fn) => () => fn(),
|
||||||
|
throttle: (fn) => () => fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export const mockConsoleWarn = jest.fn();
|
export const mockConsoleWarn = jest.fn();
|
||||||
console.warn = mockConsoleWarn;
|
console.warn = mockConsoleWarn;
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
|
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
|
||||||
|
|
||||||
const Div = (props: any) => (<div {...props}>{props.children}</div>);
|
const Div = ({_leaf, ...rest}: any) => (<div {...rest}>{rest.children}</div>);
|
||||||
|
|
||||||
const Text = (props: any) => (<div>{props.content}</div>);
|
const MiniRenderDiv = ({_leaf, ...rest}: any) => {
|
||||||
|
return (
|
||||||
|
<div {...rest}>
|
||||||
|
{rest.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Text = ({_leaf, ...rest}: any) => (<div {...rest}>{rest.content}</div>);
|
||||||
|
|
||||||
const SlotComponent = (props: any) => props.mobileSlot;
|
const SlotComponent = (props: any) => props.mobileSlot;
|
||||||
|
|
||||||
@ -24,6 +32,7 @@ const components = {
|
|||||||
Div,
|
Div,
|
||||||
SlotComponent,
|
SlotComponent,
|
||||||
Text,
|
Text,
|
||||||
|
MiniRenderDiv,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default components;
|
export default components;
|
||||||
|
|||||||
@ -6,16 +6,47 @@ export default class Node {
|
|||||||
schema: any = {
|
schema: any = {
|
||||||
props: {},
|
props: {},
|
||||||
};
|
};
|
||||||
hasLoop = false;
|
|
||||||
|
|
||||||
constructor(schema: any) {
|
componentMeta = {};
|
||||||
|
|
||||||
|
parent;
|
||||||
|
|
||||||
|
hasLoop = () => this._hasLoop;
|
||||||
|
|
||||||
|
id;
|
||||||
|
|
||||||
|
_isRoot: false;
|
||||||
|
|
||||||
|
_hasLoop: false;
|
||||||
|
|
||||||
|
constructor(schema: any, info: any = {}) {
|
||||||
this.emitter = new EventEmitter();
|
this.emitter = new EventEmitter();
|
||||||
this.schema = schema;
|
const {
|
||||||
|
componentMeta,
|
||||||
|
parent,
|
||||||
|
isRoot,
|
||||||
|
hasLoop,
|
||||||
|
} = info;
|
||||||
|
this.schema = {
|
||||||
|
props: {},
|
||||||
|
...schema,
|
||||||
|
};
|
||||||
|
this.componentMeta = componentMeta || {};
|
||||||
|
this.parent = parent;
|
||||||
|
this.id = schema.id;
|
||||||
|
this._isRoot = isRoot;
|
||||||
|
this._hasLoop = hasLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
mockLoop() {
|
isRoot = () => this._isRoot;
|
||||||
this.hasLoop = true;
|
|
||||||
}
|
// componentMeta() {
|
||||||
|
// return this.componentMeta;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// mockLoop() {
|
||||||
|
// // this.hasLoop = true;
|
||||||
|
// }
|
||||||
|
|
||||||
onChildrenChange(fn: any) {
|
onChildrenChange(fn: any) {
|
||||||
this.emitter.on('onChildrenChange', fn);
|
this.emitter.on('onChildrenChange', fn);
|
||||||
@ -24,6 +55,10 @@ export default class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitChildrenChange() {
|
||||||
|
this.emitter?.emit('onChildrenChange', {});
|
||||||
|
}
|
||||||
|
|
||||||
onPropChange(fn: any) {
|
onPropChange(fn: any) {
|
||||||
this.emitter.on('onPropChange', fn);
|
this.emitter.on('onPropChange', fn);
|
||||||
return () => {
|
return () => {
|
||||||
@ -31,11 +66,14 @@ export default class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitPropChange(val: PropChangeOptions) {
|
emitPropChange(val: PropChangeOptions, skip?: boolean) {
|
||||||
this.schema.props = {
|
if (!skip) {
|
||||||
...this.schema.props,
|
this.schema.props = {
|
||||||
[val.key + '']: val.newValue,
|
...this.schema.props,
|
||||||
|
[val.key + '']: val.newValue,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emitter?.emit('onPropChange', val);
|
this.emitter?.emit('onPropChange', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user