From 46c5bf90acbd2c69e909ab2505b007ff0c5f8bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Wed, 25 Nov 2020 21:32:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=97=A0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E9=85=8D=E7=BD=AE=E7=9A=84=E8=AE=BE=E7=BD=AE=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E5=BD=A2=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-preset-vision/jest.config.js | 1 + packages/editor-preset-vision/package.json | 2 +- .../src/bundle/prototype.ts | 4 +- packages/editor-preset-vision/src/env.ts | 5 + .../deep-value-parser.test.ts.snap | 50 ++++ .../tests/master/bus.test.ts | 246 ++++++++++++++++++ .../tests/master/deep-value-parser.test.ts | 84 ++++++ .../tests/master/env.test.ts | 114 ++++++++ .../tests/master/symbols.test.ts | 15 ++ .../tests/props-reducer/init-node.test.ts | 3 + .../tests/vision-api/exchange.test.ts | 5 + .../tests/vision-api/project.test.ts | 2 +- .../settings/settings-primary-pane.tsx | 10 + 13 files changed, 537 insertions(+), 4 deletions(-) create mode 100644 packages/editor-preset-vision/tests/master/__snapshots__/deep-value-parser.test.ts.snap create mode 100644 packages/editor-preset-vision/tests/master/bus.test.ts create mode 100644 packages/editor-preset-vision/tests/master/deep-value-parser.test.ts create mode 100644 packages/editor-preset-vision/tests/master/env.test.ts create mode 100644 packages/editor-preset-vision/tests/master/symbols.test.ts diff --git a/packages/editor-preset-vision/jest.config.js b/packages/editor-preset-vision/jest.config.js index 5b9f59ab5..31a6eab6e 100644 --- a/packages/editor-preset-vision/jest.config.js +++ b/packages/editor-preset-vision/jest.config.js @@ -19,6 +19,7 @@ module.exports = { 'src/**/*.{ts,tsx}', '!src/**/*.d.ts', '!src/base/**', + '!src/fields/**', '!src/prop.ts', '!**/node_modules/**', '!**/vendor/**', diff --git a/packages/editor-preset-vision/package.json b/packages/editor-preset-vision/package.json index cda756045..86b2a127f 100644 --- a/packages/editor-preset-vision/package.json +++ b/packages/editor-preset-vision/package.json @@ -40,7 +40,7 @@ "react-dom": "^16.8.1" }, "devDependencies": { - "@ali/lowcode-test-mate": "^1.0.0", + "@ali/lowcode-test-mate": "^1.0.1", "@alib/build-scripts": "^0.1.18", "@types/domready": "^1.0.0", "@types/events": "^3.0.0", diff --git a/packages/editor-preset-vision/src/bundle/prototype.ts b/packages/editor-preset-vision/src/bundle/prototype.ts index 2c3f7b44c..c68c7c996 100644 --- a/packages/editor-preset-vision/src/bundle/prototype.ts +++ b/packages/editor-preset-vision/src/bundle/prototype.ts @@ -104,8 +104,8 @@ registerMetadataTransducer( let top: FieldConfig[]; let bottom: FieldConfig[]; if (combined) { - top = combined?.[0].items || combined; - bottom = combined?.[combined.length - 1].items || combined; + top = combined?.[0]?.items || combined; + bottom = combined?.[combined.length - 1]?.items || combined; } else if (props) { top = props; bottom = props; diff --git a/packages/editor-preset-vision/src/env.ts b/packages/editor-preset-vision/src/env.ts index 4e267ee24..786c1f74a 100644 --- a/packages/editor-preset-vision/src/env.ts +++ b/packages/editor-preset-vision/src/env.ts @@ -80,6 +80,11 @@ export class Env { }; } + clear() { + this.envs = {}; + this.featureMap = {}; + } + getAliSchemaVersion() { return ALI_SCHEMA_VERSION; } diff --git a/packages/editor-preset-vision/tests/master/__snapshots__/deep-value-parser.test.ts.snap b/packages/editor-preset-vision/tests/master/__snapshots__/deep-value-parser.test.ts.snap new file mode 100644 index 000000000..0dd46763f --- /dev/null +++ b/packages/editor-preset-vision/tests/master/__snapshots__/deep-value-parser.test.ts.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`deepValueParser 测试 designMode: design 1`] = ` +Object { + "a": "111", + "arr": Array [ + "111", + "111", + ], + "b": "222", + "c": "中文", + "slot": Object { + "type": "JSSlot", + "value": Array [ + Object { + "componentName": "Div", + "props": Object {}, + }, + ], + }, +} +`; + +exports[`deepValueParser 测试 designMode: live 1`] = ` +Object { + "a": Object { + "mock": "111", + "type": "JSExpression", + "value": "state.a", + }, + "arr": Array [ + Object { + "mock": "111", + "type": "JSExpression", + "value": "state.a", + }, + Object { + "mock": "111", + "type": "JSExpression", + "value": "state.b", + }, + ], + "b": Object { + "mock": "222", + "type": "JSExpression", + "value": "state.b", + }, + "c": "中文", +} +`; diff --git a/packages/editor-preset-vision/tests/master/bus.test.ts b/packages/editor-preset-vision/tests/master/bus.test.ts new file mode 100644 index 000000000..fcd07b097 --- /dev/null +++ b/packages/editor-preset-vision/tests/master/bus.test.ts @@ -0,0 +1,246 @@ +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 bus from '../../src/bus'; +import { editor } from '../../src/editor'; + +describe('bus 测试', () => { + afterEach(() => { + bus.unsub('evt1'); + }); + it('sub / pub 测试', () => { + const mockFn1 = jest.fn(); + const off1 = bus.sub('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.pub('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + off1(); + + bus.pub('evt1', evtData); + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('on / emit 测试', () => { + const mockFn1 = jest.fn(); + const off1 = bus.on('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + off1(); + + bus.emit('evt1', evtData); + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('once / emit 测试', () => { + const mockFn1 = jest.fn(); + bus.once('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + bus.emit('evt1', evtData); + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('once / emit 测试,调用解绑函数', () => { + const mockFn1 = jest.fn(); + const off1 = bus.once('evt1', mockFn1); + + off1(); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).not.toHaveBeenCalled(); + }); + + it('removeListener 测试', () => { + const mockFn1 = jest.fn(); + bus.on('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + bus.removeListener('evt1', mockFn1); + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('unsub 测试 - 只有一个 handler', () => { + const mockFn1 = jest.fn(); + bus.on('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + bus.unsub('evt1', mockFn1); + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('unsub 测试 - 只 unsub 一个 handler', () => { + const mockFn1 = jest.fn(); + const mockFn2 = jest.fn(); + bus.on('evt1', mockFn1); + bus.on('evt1', mockFn2); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + + bus.unsub('evt1', mockFn1); + const evtData2 = { a: 2 }; + bus.emit('evt1', evtData2); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(2); + expect(mockFn2).toHaveBeenLastCalledWith(evtData2); + }); + + it('unsub 测试 - 多个 handler', () => { + const mockFn1 = jest.fn(); + const mockFn2 = jest.fn(); + bus.on('evt1', mockFn1); + bus.on('evt1', mockFn2); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + + bus.unsub('evt1'); + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + }); + + it('off 测试 - 只有一个 handler', () => { + const mockFn1 = jest.fn(); + bus.on('evt1', mockFn1); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + + bus.off('evt1', mockFn1); + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + }); + + it('off 测试 - 只 off 一个 handler', () => { + const mockFn1 = jest.fn(); + const mockFn2 = jest.fn(); + bus.on('evt1', mockFn1); + bus.on('evt1', mockFn2); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + + bus.off('evt1', mockFn1); + const evtData2 = { a: 2 }; + bus.emit('evt1', evtData2); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(2); + expect(mockFn2).toHaveBeenLastCalledWith(evtData2); + }); + + it('off 测试 - 多个 handler', () => { + const mockFn1 = jest.fn(); + const mockFn2 = jest.fn(); + bus.on('evt1', mockFn1); + bus.on('evt1', mockFn2); + + const evtData = { a: 1 }; + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + + bus.off('evt1'); + bus.emit('evt1', evtData); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData); + expect(mockFn2).toHaveBeenCalledTimes(1); + expect(mockFn2).toHaveBeenCalledWith(evtData); + }); + + it('简单测试(dummy)', () => { + bus.getEmitter(); + }); + + describe('editor 事件转发', () => { + const fwdEvtMap = { + 've.hotkey.callback.call': 'hotkey.callback.call', + 've.history.back': 'history.back', + 've.history.forward': 'history.forward', + 'node.prop.change': 'node.prop.change', + }; + + Object.keys(fwdEvtMap).forEach(veEventName => { + it(`${veEventName} 测试`, () => { + const mockFn1 = jest.fn(); + const evtData1 = { a: 1 }; + bus.on(veEventName, mockFn1); + + editor.emit(fwdEvtMap[veEventName], evtData1); + + expect(mockFn1).toHaveBeenCalledTimes(1); + expect(mockFn1).toHaveBeenCalledWith(evtData1); + }); + }); + }); +}); diff --git a/packages/editor-preset-vision/tests/master/deep-value-parser.test.ts b/packages/editor-preset-vision/tests/master/deep-value-parser.test.ts new file mode 100644 index 000000000..8d2c27b80 --- /dev/null +++ b/packages/editor-preset-vision/tests/master/deep-value-parser.test.ts @@ -0,0 +1,84 @@ +import '../fixtures/window'; +import { deepValueParser } from '../../src/deep-value-parser'; +import { editor } from '../../src/editor'; + +describe('deepValueParser 测试', () => { + it('null & undefined', () => { + expect(deepValueParser()).toBeNull; + expect(deepValueParser()).toBeUndefined; + }); + + it('designMode: design', () => { + expect(deepValueParser({ + a: { + type: 'variable', + variable: 'state.a', + value: '111', + }, + b: { + type: 'JSExpression', + value: 'state.b', + mock: '222', + }, + c: { + type: 'i18n', + use: 'zh_CN', + zh_CN: '中文', + en_US: 'eng', + }, + slot: { + type: 'JSSlot', + value: [{ + componentName: 'Div', + props: {}, + }], + }, + arr: [ + { + type: 'variable', + variable: 'state.a', + value: '111', + }, + { + type: 'variable', + variable: 'state.b', + value: '111', + }, + ], + })).toMatchSnapshot(); + }); + + it('designMode: live', () => { + editor.set('designMode', 'live'); + expect(deepValueParser({ + a: { + type: 'variable', + variable: 'state.a', + value: '111', + }, + b: { + type: 'JSExpression', + value: 'state.b', + mock: '222', + }, + c: { + type: 'i18n', + use: 'zh_CN', + zh_CN: '中文', + en_US: 'eng', + }, + arr: [ + { + type: 'variable', + variable: 'state.a', + value: '111', + }, + { + type: 'variable', + variable: 'state.b', + value: '111', + }, + ], + })).toMatchSnapshot(); + }); +}); diff --git a/packages/editor-preset-vision/tests/master/env.test.ts b/packages/editor-preset-vision/tests/master/env.test.ts new file mode 100644 index 000000000..1cc77730f --- /dev/null +++ b/packages/editor-preset-vision/tests/master/env.test.ts @@ -0,0 +1,114 @@ +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 env from '../../src/env'; +import bus from 'editor-preset-vision/src/bus'; +import { autorun } from '@ali/lowcode-editor-core'; + +describe('env 测试', () => { + describe('常规 API 测试', () => { + it('setEnv / getEnv / setEnvMap / set / get', () => { + expect(env.getEnv('xxx')).toBeUndefined; + + const mockFn1 = jest.fn(); + const off1 = env.onEnvChange(mockFn1); + + const envData = { a: 1 }; + env.setEnv('xxx', envData); + expect(env.getEnv('xxx')).toEqual(envData); + expect(env.get('xxx')).toEqual(envData); + expect(mockFn1).toHaveBeenCalled(); + expect(mockFn1).toHaveBeenCalledWith(env.envs, 'xxx', envData); + mockFn1.mockClear(); + + // 设置相同的值 + env.setEnv('xxx', envData); + expect(env.getEnv('xxx')).toEqual(envData); + expect(env.get('xxx')).toEqual(envData); + expect(mockFn1).not.toHaveBeenCalled(); + mockFn1.mockClear(); + + // 设置另一个 envName + const envData2 = { b: 1 }; + env.set('yyy', envData2); + expect(env.getEnv('yyy')).toEqual(envData2); + expect(env.get('yyy')).toEqual(envData2); + expect(mockFn1).toHaveBeenCalled(); + expect(mockFn1).toHaveBeenCalledWith(env.envs, 'yyy', envData2); + mockFn1.mockClear(); + + env.setEnvMap({ + zzz: { a: 1, b: 1 }, + }); + expect(env.getEnv('xxx')).toBeUndefined; + expect(env.getEnv('yyy')).toBeUndefined; + expect(env.getEnv('zzz')).toEqual({ a: 1, b: 1 }); + expect(mockFn1).toHaveBeenCalled(); + expect(mockFn1).toHaveBeenCalledWith(env.envs); + mockFn1.mockClear(); + + // 解绑事件 + off1(); + env.setEnvMap({ + zzz: { a: 1, b: 1 }, + }); + expect(mockFn1).not.toHaveBeenCalled(); + mockFn1.mockClear(); + }); + + it('setLocale / getLocale', () => { + expect(env.getLocale()).toBe('zh_CN'); + env.setLocale('en_US'); + expect(env.getLocale()).toBe('en_US'); + }); + + it('setExpertMode / isExpertMode', () => { + expect(env.isExpertMode()).toBeFalsy; + env.setExpertMode('truthy value'); + expect(env.isExpertMode()).toBeTruthy; + }); + + it('getSupportFeatures / setSupportFeatures / supports', () => { + expect(env.getSupportFeatures()).toEqual({}); + env.setSupportFeatures({ + mobile: true, + pc: true, + }); + expect(env.getSupportFeatures()).toEqual({ + mobile: true, + pc: true, + }); + expect(env.supports('mobile')).toBeTruthy; + expect(env.supports('pc')).toBeTruthy; + expect(env.supports('iot')).toBeFalsy; + }); + + it('getAliSchemaVersion', () => { + expect(env.getAliSchemaVersion()).toBe('1.0.0'); + }); + + it('envs obx 测试', async () => { + const mockFn = jest.fn(); + env.clear(); + + autorun(() => { + mockFn(env.envs); + env.envs; + }); + + await new Promise(resolve => setTimeout(resolve, 16)); + + expect(mockFn).toHaveBeenCalledTimes(1); + expect(mockFn).toHaveBeenLastCalledWith({}); + + env.setEnv('abc', { a: 1 }); + + await new Promise(resolve => setTimeout(resolve, 16)); + expect(mockFn).toHaveBeenCalledTimes(2); + expect(mockFn).toHaveBeenLastCalledWith({ abc: { a: 1 } }); + }); + }); +}); diff --git a/packages/editor-preset-vision/tests/master/symbols.test.ts b/packages/editor-preset-vision/tests/master/symbols.test.ts new file mode 100644 index 000000000..35463ad8a --- /dev/null +++ b/packages/editor-preset-vision/tests/master/symbols.test.ts @@ -0,0 +1,15 @@ +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 symbols from '../../src/symbols'; + +describe('symbols 测试', () => { + it('API', () => { + symbols.create('abc'); + symbols.create('abc'); + symbols.get('abc'); + }); +}); diff --git a/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts b/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts index e69de29bb..05d27af2a 100644 --- a/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts +++ b/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts @@ -0,0 +1,3 @@ +it.skip('should ', () => { + +}); \ No newline at end of file diff --git a/packages/editor-preset-vision/tests/vision-api/exchange.test.ts b/packages/editor-preset-vision/tests/vision-api/exchange.test.ts index 972f49541..16c93b313 100644 --- a/packages/editor-preset-vision/tests/vision-api/exchange.test.ts +++ b/packages/editor-preset-vision/tests/vision-api/exchange.test.ts @@ -10,9 +10,14 @@ describe('VisualEngine.Exchange 相关 API 测试', () => { VisualEngine.Exchange.select(doc?.getNode('form')); expect(VisualEngine.Exchange.getSelected()?.componentName).toBe('Form'); expect(VisualEngine.Exchange.getSelected()?.id).toBe('form'); + + // clear selection + VisualEngine.Exchange.select(); + expect(VisualEngine.Exchange.getSelected()).toBeUndefined; }); it('onIntoView', () => { expect(typeof VisualEngine.Exchange.onIntoView).toBe('function'); + VisualEngine.Exchange.onIntoView(); }); }); diff --git a/packages/editor-preset-vision/tests/vision-api/project.test.ts b/packages/editor-preset-vision/tests/vision-api/project.test.ts index 6bb7cc566..bec071d51 100644 --- a/packages/editor-preset-vision/tests/vision-api/project.test.ts +++ b/packages/editor-preset-vision/tests/vision-api/project.test.ts @@ -4,7 +4,7 @@ import '../fixtures/window'; import formSchema from '../fixtures/schema/form'; import { Project } from '../../src'; -describe.skip('VisualEngine.Project 相关 API 测试', () => { +describe('VisualEngine.Project 相关 API 测试', () => { it('getSchema / setSchema 系列', () => { Project.setSchema({ componentsMap: {}, diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 6527cc690..d9f9c0651 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -120,6 +120,16 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any ); } + if (Array.isArray(settings.items) && settings.items.length === 0) { + return ( +
+
+

该组件暂无配置

+
+
+ ); + } + if (!settings.isSameComponent) { // todo: future support 获取设置项交集编辑 return (