test: 补充部分单测

This commit is contained in:
lihao.ylh 2021-10-09 15:04:05 +08:00
parent 5d33a2a9ec
commit d53ba538b1
7 changed files with 215 additions and 51 deletions

View File

@ -6,7 +6,7 @@ module.exports = {
// // '^.+\\.(ts|tsx)$': 'ts-jest', // // '^.+\\.(ts|tsx)$': 'ts-jest',
// // '^.+\\.(js|jsx)$': 'babel-jest', // // '^.+\\.(js|jsx)$': 'babel-jest',
// }, // },
// testMatch: ['**/builtin-hotkey.test.ts'], testMatch: ['**/bugs/*.test.ts'],
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [ transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`, `/node_modules/(?!${esModules})/`,

View File

@ -0,0 +1,72 @@
// @ts-nocheck
import { Editor } from '@ali/lowcode-editor-core';
import { isJSBlock, TransformStage } from '@ali/lowcode-types';
import { isPlainObject, isVariable } from '@ali/lowcode-utils';
import '../fixtures/window';
import { Designer } from '../../src/designer/designer';
import { DocumentModel } from '../../src/document/document-model';
import { Project } from '../../src/project/project';
import formSchema from '../fixtures/schema/form';
/**
* bug
* Prop setValue dispose Prop Node dispose
* dispose variable JSExpression
*
* propsReducer Init / Upgrade props Node dispose Prop
*/
function upgradePropsReducer(props: any): any {
if (!props || !isPlainObject(props)) {
return props;
}
if (isJSBlock(props)) {
if (props.value.componentName === 'Slot') {
return {
type: 'JSSlot',
title: (props.value.props as any)?.slotTitle,
name: (props.value.props as any)?.slotName,
value: props.value.children,
};
} else {
return props.value;
}
}
if (isVariable(props)) {
return {
type: 'JSExpression',
value: props.variable,
mock: props.value,
};
}
const newProps: any = {};
Object.keys(props).forEach((key) => {
if (/^__slot__/.test(key) && props[key] === true) {
return;
}
newProps[key] = upgradePropsReducer(props[key]);
});
return newProps;
}
describe('Node 方法测试', () => {
let editor: Editor;
let designer: Designer;
let project: Project;
let doc: DocumentModel;
it('原始 prop 值是 variable 结构,通过一个 propsReducer 转成了 JSExpression 结构', () => {
editor = new Editor();
designer = new Designer({ editor });
designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade);
project = designer.project;
doc = new DocumentModel(project, formSchema);
const form = doc.getNode('form');
expect(form.getPropValue('dataSource')).toEqual({
type: 'JSExpression',
value: 'state.formData',
})
});
});

View File

@ -0,0 +1,6 @@
背景:
在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。
至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。
做法:
Bugs 文件夹每个文件记录一个 bug 修复的场景测试~

View File

@ -1,4 +1,4 @@
// @ts-ignore // @ts-nocheck
import '../../fixtures/window'; import '../../fixtures/window';
import { set, delayObxTick, delay } from '../../utils'; import { set, delayObxTick, delay } from '../../utils';
import { Editor } from '@ali/lowcode-editor-core'; import { Editor } from '@ali/lowcode-editor-core';
@ -24,7 +24,6 @@ import rootHeaderMetadata from '../../fixtures/component-metadata/root-header';
import rootContentMetadata from '../../fixtures/component-metadata/root-content'; import rootContentMetadata from '../../fixtures/component-metadata/root-content';
import rootFooterMetadata from '../../fixtures/component-metadata/root-footer'; import rootFooterMetadata from '../../fixtures/component-metadata/root-footer';
describe('Node 方法测试', () => { describe('Node 方法测试', () => {
let editor: Editor; let editor: Editor;
let designer: Designer; let designer: Designer;
@ -185,12 +184,16 @@ describe('Node 方法测试', () => {
it('null', () => { it('null', () => {
expect( expect(
doc.rootNode?.getSuitablePlace.call({ contains: () => false, isContainer: () => false, isRoot: () => false }), doc.rootNode?.getSuitablePlace.call({
contains: () => false,
isContainer: () => false,
isRoot: () => false,
}),
).toBeNull(); ).toBeNull();
}); });
}); });
it('removeChild / replaceWith / replaceChild / onChildrenChange / mergeChildren', () => { it('removeChild / replaceWith / replaceChild', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.select(); firstBtn.select();
@ -254,7 +257,7 @@ describe('Node 方法测试', () => {
}); });
}); });
it('setVisible / getVisible / onVisibleChange', async () => { it('setVisible / getVisible / onVisibleChange', () => {
const mockFn = jest.fn(); const mockFn = jest.fn();
const firstBtn = doc.getNode('node_k1ow3cbn')!; const firstBtn = doc.getNode('node_k1ow3cbn')!;
const off = firstBtn.onVisibleChange(mockFn); const off = firstBtn.onVisibleChange(mockFn);
@ -265,7 +268,6 @@ describe('Node 方法测试', () => {
firstBtn.setVisible(false); firstBtn.setVisible(false);
await delayObxTick();
expect(firstBtn.getVisible()).toBeFalsy(); expect(firstBtn.getVisible()).toBeFalsy();
expect(mockFn).toHaveBeenCalledTimes(2); expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(false); expect(mockFn).toHaveBeenCalledWith(false);
@ -273,7 +275,22 @@ describe('Node 方法测试', () => {
off(); off();
mockFn.mockClear(); mockFn.mockClear();
firstBtn.setVisible(true); firstBtn.setVisible(true);
await delayObxTick(); expect(mockFn).not.toHaveBeenCalled();
});
it('onPropChange', () => {
const mockFn = jest.fn();
const firstBtn = doc.getNode('node_k1ow3cbn')!;
const off = firstBtn.onPropChange(mockFn);
firstBtn.setPropValue('x', 1);
expect(mockFn).toHaveBeenCalledTimes(1);
firstBtn.setPropValue('x', 2);
expect(mockFn).toHaveBeenCalledTimes(2);
off();
mockFn.mockClear();
firstBtn.setPropValue('x', 3);
expect(mockFn).not.toHaveBeenCalled(); expect(mockFn).not.toHaveBeenCalled();
}); });
@ -292,7 +309,9 @@ describe('Node 方法测试', () => {
const pageMeta = designer.getComponentMeta('Page'); const pageMeta = designer.getComponentMeta('Page');
const autorunMockFn = jest.fn(); const autorunMockFn = jest.fn();
set(pageMeta, '_transformedMetadata.experimental.autoruns', [{ name: 'a', autorun: autorunMockFn }]); set(pageMeta, '_transformedMetadata.experimental.autoruns', [
{ name: 'a', autorun: autorunMockFn },
]);
const initialChildrenMockFn = jest.fn(); const initialChildrenMockFn = jest.fn();
set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn); set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn);
doc.createNode({ componentName: 'Page', props: { a: 1 } }); doc.createNode({ componentName: 'Page', props: { a: 1 } });

View File

@ -200,9 +200,11 @@ describe('Prop 类测试', () => {
// 更新 slot // 更新 slot
slotProp.setValue({ slotProp.setValue({
type: 'JSSlot', type: 'JSSlot',
value: [{ value: [
{
componentName: 'Form', componentName: 'Form',
}] },
],
}); });
expect(slotNodeImportMockFn).toBeCalled(); expect(slotNodeImportMockFn).toBeCalled();
@ -276,19 +278,20 @@ describe('Prop 类测试', () => {
expect(prop.get('z.z1')?.getValue()).toBe(1); expect(prop.get('z.z1')?.getValue()).toBe(1);
expect(prop.get('z.z2')?.getValue()).toBe('str'); expect(prop.get('z.z2')?.getValue()).toBe('str');
const fromStashProp = prop.get('l'); const newlyCreatedProp = prop.get('l', true);
const fromStashNestedProp = prop.get('m.m1'); const newlyCreatedNestedProp = prop.get('m.m1', true);
fromStashProp.setValue('fromStashProp'); newlyCreatedProp.setValue('newlyCreatedProp');
fromStashNestedProp?.setValue('fromStashNestedProp'); newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
await delayObxTick(); expect(prop.get('l').getValue()).toBe('newlyCreatedProp');
expect(prop.get('l').getValue()).toBe('fromStashProp'); expect(prop.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
expect(prop.get('m.m1').getValue()).toBe('fromStashNestedProp');
const newlyCreatedNestedProp2 = prop.get('m.m2', true);
// .m2 的值为 undefined导出时将会被移除
expect(prop.get('m').getValue()).toEqual({ m1: 'newlyCreatedNestedProp' });
}); });
it('export', () => { it('export', () => {
// TODO: 需要访问一下才能触发构造 _items
prop.items;
expect(prop.export()).toEqual({ expect(prop.export()).toEqual({
a: 1, a: 1,
b: 'str', b: 'str',

View File

@ -2,20 +2,28 @@
import '../../../fixtures/window'; import '../../../fixtures/window';
import { set, delayObxTick } from '../../../utils'; import { set, delayObxTick } from '../../../utils';
import { Editor } from '@ali/lowcode-editor-core'; import { Editor } from '@ali/lowcode-editor-core';
import { Props, getConvertedExtraKey, getOriginalExtraKey, Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/props'; import {
Props,
getConvertedExtraKey,
getOriginalExtraKey,
Prop,
isProp,
isValidArrayIndex,
} from '../../../../src/document/node/props/props';
import { Designer } from '../../../../src/designer/designer'; import { Designer } from '../../../../src/designer/designer';
import { Project } from '../../../../src/project/project'; import { Project } from '../../../../src/project/project';
import { DocumentModel } from '../../../../src/document/document-model'; import { DocumentModel } from '../../../../src/document/document-model';
import { TransformStage } from '@ali/lowcode-types'; import { TransformStage } from '@ali/lowcode-types';
const mockedOwner = { componentName: 'Page' }; const mockedOwner = { componentName: 'Page' };
describe('Props 类测试', () => { describe('Props 类测试', () => {
let props: Props; let props: Props;
beforeEach(() => { beforeEach(() => {
props = new Props(mockedOwner, { props = new Props(
mockedOwner,
{
a: 1, a: 1,
b: 'str', b: 'str',
c: true, c: true,
@ -27,7 +35,9 @@ describe('Props 类测试', () => {
z1: 1, z1: 1,
z2: 'str', z2: 'str',
}, },
}, { condition: true }); },
{ condition: true },
);
}); });
afterEach(() => { afterEach(() => {
props.purge(); props.purge();
@ -49,7 +59,6 @@ describe('Props 类测试', () => {
z2: 'str', z2: 'str',
}); });
expect(props.getPropValue('a')).toBe(1); expect(props.getPropValue('a')).toBe(1);
props.setPropValue('a', 2); props.setPropValue('a', 2);
expect(props.getPropValue('a')).toBe(2); expect(props.getPropValue('a')).toBe(2);
@ -59,14 +68,15 @@ describe('Props 类测试', () => {
expect(props.get('z.z1')?.getValue()).toBe(1); expect(props.get('z.z1')?.getValue()).toBe(1);
expect(props.get('z.z2')?.getValue()).toBe('str'); expect(props.get('z.z2')?.getValue()).toBe('str');
const fromStashProp = props.get('l', true); const notCreatedProp = props.get('i');
const fromStashNestedProp = props.get('m.m1', true); expect(notCreatedProp).toBeNull();
fromStashProp.setValue('fromStashProp'); const newlyCreatedProp = props.get('l', true);
fromStashNestedProp?.setValue('fromStashNestedProp'); const newlyCreatedNestedProp = props.get('m.m1', true);
newlyCreatedProp.setValue('newlyCreatedProp');
newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
await delayObxTick(); expect(props.get('l').getValue()).toBe('newlyCreatedProp');
expect(props.get('l').getValue()).toBe('fromStashProp'); expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
expect(props.get('m.m1').getValue()).toBe('fromStashNestedProp');
}); });
it('export', () => { it('export', () => {
@ -119,11 +129,66 @@ describe('Props 类测试', () => {
}); });
}); });
it('export - remove undefined items', () => {
props.import(
{
a: 1,
},
{ loop: false },
);
props.setPropValue('x', undefined);
expect(props.export()).toEqual({
props: {
a: 1,
},
extras: {
loop: false,
},
});
props.setPropValue('x', 2);
expect(props.export()).toEqual({
props: {
a: 1,
x: 2,
},
extras: {
loop: false,
},
});
props.setPropValue('y.z', undefined);
expect(props.export()).toEqual({
props: {
a: 1,
x: 2,
},
extras: {
loop: false,
},
});
props.setPropValue('y.z', 2);
expect(props.export()).toEqual({
props: {
a: 1,
x: 2,
y: { z: 2 },
},
extras: {
loop: false,
},
});
});
it('import', () => { it('import', () => {
props.import({ props.import(
{
x: 1, x: 1,
y: true, y: true,
}, { loop: false }); },
{ loop: false },
);
expect(props.export()).toEqual({ expect(props.export()).toEqual({
props: { props: {
x: 1, x: 1,
@ -173,19 +238,19 @@ describe('Props 类测试', () => {
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.forEach(item => { props.forEach((item) => {
mockedFn(); mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.map(item => { props.map((item) => {
return mockedFn(); return mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear(); mockedFn.mockClear();
props.filter(item => { props.filter((item) => {
return mockedFn(); return mockedFn();
}); });
expect(mockedFn).toHaveBeenCalledTimes(6); expect(mockedFn).toHaveBeenCalledTimes(6);
@ -229,7 +294,6 @@ describe('Props 类测试', () => {
}); });
}); });
describe('其他函数', () => { describe('其他函数', () => {
it('getConvertedExtraKey', () => { it('getConvertedExtraKey', () => {
expect(getConvertedExtraKey()).toBe(''); expect(getConvertedExtraKey()).toBe('');