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',
// // '^.+\\.(js|jsx)$': 'babel-jest',
// },
// testMatch: ['**/builtin-hotkey.test.ts'],
testMatch: ['**/bugs/*.test.ts'],
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [
`/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 { set, delayObxTick, delay } from '../../utils';
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 rootFooterMetadata from '../../fixtures/component-metadata/root-footer';
describe('Node 方法测试', () => {
let editor: Editor;
let designer: Designer;
@ -185,12 +184,16 @@ describe('Node 方法测试', () => {
it('null', () => {
expect(
doc.rootNode?.getSuitablePlace.call({ contains: () => false, isContainer: () => false, isRoot: () => false }),
doc.rootNode?.getSuitablePlace.call({
contains: () => false,
isContainer: () => false,
isRoot: () => false,
}),
).toBeNull();
});
});
it('removeChild / replaceWith / replaceChild / onChildrenChange / mergeChildren', () => {
it('removeChild / replaceWith / replaceChild', () => {
const firstBtn = doc.getNode('node_k1ow3cbn')!;
firstBtn.select();
@ -254,7 +257,7 @@ describe('Node 方法测试', () => {
});
});
it('setVisible / getVisible / onVisibleChange', async () => {
it('setVisible / getVisible / onVisibleChange', () => {
const mockFn = jest.fn();
const firstBtn = doc.getNode('node_k1ow3cbn')!;
const off = firstBtn.onVisibleChange(mockFn);
@ -265,7 +268,6 @@ describe('Node 方法测试', () => {
firstBtn.setVisible(false);
await delayObxTick();
expect(firstBtn.getVisible()).toBeFalsy();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(false);
@ -273,7 +275,22 @@ describe('Node 方法测试', () => {
off();
mockFn.mockClear();
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();
});
@ -292,7 +309,9 @@ describe('Node 方法测试', () => {
const pageMeta = designer.getComponentMeta('Page');
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();
set(pageMeta, '_transformedMetadata.experimental.initialChildren', initialChildrenMockFn);
doc.createNode({ componentName: 'Page', props: { a: 1 } });

View File

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

View File

@ -2,32 +2,42 @@
import '../../../fixtures/window';
import { set, delayObxTick } from '../../../utils';
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 { Project } from '../../../../src/project/project';
import { DocumentModel } from '../../../../src/document/document-model';
import { TransformStage } from '@ali/lowcode-types';
const mockedOwner = { componentName: 'Page' };
describe('Props 类测试', () => {
let props: Props;
beforeEach(() => {
props = new Props(mockedOwner, {
a: 1,
b: 'str',
c: true,
d: {
type: 'JSExpression',
value: 'state.a',
props = new Props(
mockedOwner,
{
a: 1,
b: 'str',
c: true,
d: {
type: 'JSExpression',
value: 'state.a',
},
z: {
z1: 1,
z2: 'str',
},
},
z: {
z1: 1,
z2: 'str',
},
}, { condition: true });
{ condition: true },
);
});
afterEach(() => {
props.purge();
@ -49,7 +59,6 @@ describe('Props 类测试', () => {
z2: 'str',
});
expect(props.getPropValue('a')).toBe(1);
props.setPropValue('a', 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.z2')?.getValue()).toBe('str');
const fromStashProp = props.get('l', true);
const fromStashNestedProp = props.get('m.m1', true);
fromStashProp.setValue('fromStashProp');
fromStashNestedProp?.setValue('fromStashNestedProp');
const notCreatedProp = props.get('i');
expect(notCreatedProp).toBeNull();
const newlyCreatedProp = props.get('l', true);
const newlyCreatedNestedProp = props.get('m.m1', true);
newlyCreatedProp.setValue('newlyCreatedProp');
newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
await delayObxTick();
expect(props.get('l').getValue()).toBe('fromStashProp');
expect(props.get('m.m1').getValue()).toBe('fromStashNestedProp');
expect(props.get('l').getValue()).toBe('newlyCreatedProp');
expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
});
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', () => {
props.import({
x: 1,
y: true,
}, { loop: false });
props.import(
{
x: 1,
y: true,
},
{ loop: false },
);
expect(props.export()).toEqual({
props: {
x: 1,
@ -173,19 +238,19 @@ describe('Props 类测试', () => {
expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear();
props.forEach(item => {
props.forEach((item) => {
mockedFn();
});
expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear();
props.map(item => {
props.map((item) => {
return mockedFn();
});
expect(mockedFn).toHaveBeenCalledTimes(6);
mockedFn.mockClear();
props.filter(item => {
props.filter((item) => {
return mockedFn();
});
expect(mockedFn).toHaveBeenCalledTimes(6);
@ -229,7 +294,6 @@ describe('Props 类测试', () => {
});
});
describe('其他函数', () => {
it('getConvertedExtraKey', () => {
expect(getConvertedExtraKey()).toBe('');