diff --git a/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap b/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap index 7a4d4b365..da09ca6c4 100644 --- a/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap +++ b/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap @@ -199,6 +199,8 @@ exports[`React Renderer render basic case 1`] = ` maxLength={null} onBlur={[Function]} onChange={[Function]} + onCompositionEnd={[Function]} + onCompositionStart={[Function]} onFocus={[Function]} onKeyDown={[Function]} placeholder="请选择" @@ -296,6 +298,8 @@ exports[`React Renderer render basic case 1`] = ` maxLength={null} onBlur={[Function]} onChange={[Function]} + onCompositionEnd={[Function]} + onCompositionStart={[Function]} onFocus={[Function]} onKeyDown={[Function]} placeholder="请选择" @@ -378,6 +382,8 @@ exports[`React Renderer render basic case 1`] = ` maxLength={null} onBlur={[Function]} onChange={[Function]} + onCompositionEnd={[Function]} + onCompositionStart={[Function]} onFocus={[Function]} onKeyDown={[Function]} readOnly={false} @@ -826,6 +832,8 @@ exports[`React Renderer render basic case 1`] = ` maxLength={null} onBlur={[Function]} onChange={[Function]} + onCompositionEnd={[Function]} + onCompositionStart={[Function]} onFocus={[Function]} onKeyDown={[Function]} readOnly={false} diff --git a/packages/renderer-core/build.test.json b/packages/renderer-core/build.test.json new file mode 100644 index 000000000..228c07e7f --- /dev/null +++ b/packages/renderer-core/build.test.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "build-plugin-component", + "@ali/lowcode-test-mate/plugin/index.ts" + ] +} diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js new file mode 100644 index 000000000..865d30116 --- /dev/null +++ b/packages/renderer-core/jest.config.js @@ -0,0 +1,20 @@ +const esModules = ['@recore/obx-react'].join('|'); + +module.exports = { + // transform: { + // '^.+\\.[jt]sx?$': 'babel-jest', + // // '^.+\\.(ts|tsx)$': 'ts-jest', + // // '^.+\\.(js|jsx)$': 'babel-jest', + // }, + // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], + transformIgnorePatterns: [ + `/node_modules/(?!${esModules})/`, + ], + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + collectCoverage: true, + collectCoverageFrom: [ + 'src/**/*.{ts,tsx}', + '!**/node_modules/**', + '!**/vendor/**', + ], +}; diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json index 132cd2e25..55beee7d7 100644 --- a/packages/renderer-core/package.json +++ b/packages/renderer-core/package.json @@ -10,8 +10,13 @@ "es" ], "scripts": { + "test": "build-scripts test --config build.test.json", "build": "build-scripts build --skip-demo" }, + "repository": { + "type": "git", + "url": "git@gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine.git" + }, "dependencies": { "@ali/b3-one": "^0.0.17", "@ali/bzb-request": "^2.6.0-beta.13", diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index 200bdb409..75041f7c6 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -32,7 +32,7 @@ import { compWrapper } from '../hoc'; import { IComponentConstruct, IComponentHoc, leafWrapper } from '../hoc/leaf'; import logger from '../utils/logger'; -export default function baseRenererFactory() { +export default function baseRendererFactory() { const { BaseRenderer: customBaseRenderer } = adapter.getRenderers(); if (customBaseRenderer) { @@ -712,7 +712,7 @@ export default function baseRenererFactory() { ) { return checkProps(props); } - if (isJSExpression(props)) { + if (isJSExpression(props) || isJSFunction(props)) { props = parseExpression(props, scope); // 只有当变量解析出来为模型结构的时候才会继续解析 if (!isSchema(props) && !isJSSlot(props)) return checkProps(props); diff --git a/packages/renderer-core/src/types/index.ts b/packages/renderer-core/src/types/index.ts index 8bee549f9..691deb74c 100644 --- a/packages/renderer-core/src/types/index.ts +++ b/packages/renderer-core/src/types/index.ts @@ -1,8 +1,7 @@ import { BuiltinSimulatorHost } from '@ali/lowcode-designer'; import { baseRendererFactory } from '../renderer'; -import baseRenererFactory from '../renderer/base'; -export type IBaseRenderer = ReturnType; +export type IBaseRenderer = ReturnType; export type IBaseRendererInstance = InstanceType>; export interface IProps { @@ -28,7 +27,7 @@ export interface IProps { export interface IState { engineRenderError?: boolean; - error?: Error + error?: Error; onCompGetRef: (schema: ISchema, ref: any) => void; onCompGetCtx: (schema: ISchema, ref: any) => void; customCreateElement: (...args: any) => any; @@ -57,7 +56,7 @@ export interface ComponentModel { export interface ISchema { componentName: string; props: any; - children: ComponentModel[] + children: ComponentModel[]; dataSource?: any; methods?: any; lifeCycles?: any; @@ -68,7 +67,7 @@ export interface IInfo { schema: ISchema; Comp: any; componentInfo?: any; - componentChildren?: any + componentChildren?: any; } export interface JSExpression { @@ -113,9 +112,9 @@ export interface IRendererModules { BaseRenderer?: new(...args: any) => IRenderer; PageRenderer: any; ComponentRenderer: any; - BlockRenderer?: any, - AddonRenderer?: any, - TempRenderer?: any, + BlockRenderer?: any; + AddonRenderer?: any; + TempRenderer?: any; DivRenderer?: any; } diff --git a/packages/renderer-core/src/utils/common.ts b/packages/renderer-core/src/utils/common.ts index 78bead5ac..5ce015bc1 100644 --- a/packages/renderer-core/src/utils/common.ts +++ b/packages/renderer-core/src/utils/common.ts @@ -1,5 +1,6 @@ /* eslint-disable no-new-func */ -import Debug from 'debug'; +// import Debug from 'debug'; +import logger from './logger'; import { forEach as _forEach, shallowEqual as _shallowEqual } from '@ali/b3-one/lib/obj'; import { serialize as serializeParams } from '@ali/b3-one/lib/url'; // moment对象配置 @@ -52,7 +53,7 @@ const EXPRESSION_TYPE = { const EXPRESSION_REG = /^\{\{(\{.*\}|.*?)\}\}$/; const hasSymbol = typeof Symbol === 'function' && Symbol.for; const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; -const debug = Debug('utils:index'); +// const debug = Debug('utils:index'); const ENV = { TBE: 'TBE', @@ -434,7 +435,8 @@ export function parseExpression(str: any, self: any) { const code = `with($scope || {}) { ${tarStr} }`; return new Function('$scope', code)(self); } catch (err) { - debug('parseExpression.error', err, str, self); + // debug('parseExpression.error', err, str, self); + logger.error('parseExpression.error', err, str, self); return undefined; } } diff --git a/packages/renderer-core/test/fixtures/schema/basic.ts b/packages/renderer-core/test/fixtures/schema/basic.ts new file mode 100644 index 000000000..cc587163a --- /dev/null +++ b/packages/renderer-core/test/fixtures/schema/basic.ts @@ -0,0 +1,567 @@ +export default { + componentName: 'Page', + id: 'node_dockcviv8fo1', + props: { + ref: 'outterView', + autoLoading: true, + style: { + padding: '0 5px 0 5px', + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + state: { + text: 'outter', + isShowDialog: false, + }, + css: 'body {font-size: 12px;} .botton{width:100px;color:#ff00ff}', + lifeCycles: { + componentDidMount: { + type: 'JSFunction', + value: "function() {\n console.log('did mount');\n }", + }, + componentWillUnmount: { + type: 'JSFunction', + value: "function() {\n console.log('will umount');\n }", + }, + }, + methods: { + testFunc: { + type: 'JSFunction', + value: "function() {\n console.log('test func');\n }", + }, + onClick: { + type: 'JSFunction', + value: 'function() {\n this.setState({\n isShowDialog: true\n })\n }', + }, + closeDialog: { + type: 'JSFunction', + value: 'function() {\n this.setState({\n isShowDialog: false\n })\n }', + }, + }, + children: [ + { + componentName: 'Box', + id: 'node_dockcy8n9xed', + props: { + style: { + backgroundColor: 'rgba(31,56,88,0.1)', + padding: '12px 12px 12px 12px', + }, + }, + children: [ + { + componentName: 'Box', + id: 'node_dockcy8n9xee', + props: { + style: { + padding: '12px 12px 12px 12px', + backgroundColor: '#ffffff', + }, + }, + children: [ + { + componentName: 'Breadcrumb', + id: 'node_dockcy8n9xef', + props: { + prefix: 'next-', + maxNode: 100, + component: 'nav', + }, + children: [ + { + componentName: 'Breadcrumb.Item', + id: 'node_dockcy8n9xeg', + props: { + prefix: 'next-', + children: '首页', + }, + }, + { + componentName: 'Breadcrumb.Item', + id: 'node_dockcy8n9xei', + props: { + prefix: 'next-', + children: '品质中台', + }, + }, + { + componentName: 'Breadcrumb.Item', + id: 'node_dockcy8n9xek', + props: { + prefix: 'next-', + children: '商家品质页面管理', + }, + }, + { + componentName: 'Breadcrumb.Item', + id: 'node_dockcy8n9xem', + props: { + prefix: 'next-', + children: '质检知识条配置', + }, + }, + ], + }, + ], + }, + { + componentName: 'Box', + id: 'node_dockcy8n9xeo', + props: { + style: { + marginTop: '12px', + backgroundColor: '#ffffff', + }, + }, + children: [ + { + componentName: 'Form', + id: 'node_dockcy8n9xep', + props: { + inline: true, + style: { + marginTop: '12px', + marginRight: '12px', + marginLeft: '12px', + }, + __events: [], + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_dockcy8n9xeq', + props: { + style: { + marginBottom: '0', + }, + label: '类目名:', + }, + children: [ + { + componentName: 'Select', + id: 'node_dockcy8n9xer', + props: { + mode: 'single', + hasArrow: true, + cacheValue: true, + style: { + width: '150px', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_dockcy8n9xes', + props: { + style: { + marginBottom: '0', + }, + label: '项目类型:', + }, + children: [ + { + componentName: 'Select', + id: 'node_dockcy8n9xet', + props: { + mode: 'single', + hasArrow: true, + cacheValue: true, + style: { + width: '200px', + }, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_dockcy8n9xeu', + props: { + style: { + marginBottom: '0', + }, + label: '项目 ID:', + }, + children: [ + { + componentName: 'Input', + id: 'node_dockcy8n9xev', + props: { + hasBorder: true, + size: 'medium', + autoComplete: 'off', + style: { + width: '200px', + }, + }, + }, + ], + }, + { + componentName: 'Button.Group', + id: 'node_dockcy8n9xew', + props: {}, + children: [ + { + componentName: 'Button', + id: 'node_dockcy8n9xex', + props: { + type: 'primary', + style: { + margin: '0 5px 0 5px', + }, + htmlType: 'submit', + children: '搜索', + }, + }, + { + componentName: 'Button', + id: 'node_dockcy8n9xe10', + props: { + type: 'normal', + style: { + margin: '0 5px 0 5px', + }, + htmlType: 'reset', + children: '清空', + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'Box', + id: 'node_dockcy8n9xe1f', + props: { + style: { + backgroundColor: '#ffffff', + paddingBottom: '24px', + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-end', + }, + }, + children: [ + { + componentName: 'Button', + id: 'node_dockd5nrh9p4', + props: { + type: 'primary', + size: 'medium', + htmlType: 'button', + component: 'button', + children: '新建配置', + style: {}, + __events: [ + { + type: 'componentEvent', + name: 'onClick', + relatedEventName: 'onClick', + }, + ], + onClick: { + type: 'JSFunction', + value: 'function(){ this.onClick() }', + }, + }, + }, + ], + }, + { + componentName: 'Box', + id: 'node_dockd5nrh9p5', + props: {}, + children: [ + { + componentName: 'Table', + id: 'node_dockjielosj1', + props: { + showMiniPager: true, + showActionBar: true, + actionBar: [ + { + title: '新增', + type: 'primary', + }, + { + title: '编辑', + }, + ], + columns: [ + { + dataKey: 'name', + width: 200, + align: 'center', + title: '姓名', + editType: 'text', + }, + { + dataKey: 'age', + width: 200, + align: 'center', + title: '年龄', + }, + { + dataKey: 'email', + width: 200, + align: 'center', + title: '邮箱', + }, + ], + data: [ + { + name: '王小', + id: '1', + age: 15000, + email: 'aaa@abc.com', + }, + { + name: '王中', + id: '2', + age: 25000, + email: 'bbb@abc.com', + }, + { + name: '王大', + id: '3', + age: 35000, + email: 'ccc@abc.com', + }, + ], + actionTitle: '操作', + actionWidth: 180, + actionType: 'link', + actionFixed: 'right', + actionHidden: false, + maxWebShownActionCount: 2, + actionColumn: [ + { + title: '编辑', + callback: { + type: 'JSFunction', + value: '(rowData, action, table) => {\n return table.editRow(rowData).then((row) => {\n console.log(row);\n });\n }', + }, + device: [ + 'desktop', + ], + }, + { + title: '保存', + callback: { + type: 'JSFunction', + value: '(rowData, action, table) => { \nreturn table.saveRow(rowData).then((row) => { \nconsole.log(row); \n}); \n}', + }, + mode: 'EDIT', + }, + ], + }, + }, + { + componentName: 'Box', + id: 'node_dockd5nrh9pg', + props: { + style: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-end', + }, + }, + children: [ + { + componentName: 'Pagination', + id: 'node_dockd5nrh9pf', + props: { + prefix: 'next-', + type: 'normal', + shape: 'normal', + size: 'medium', + defaultCurrent: 1, + total: 100, + pageShowCount: 5, + pageSize: 10, + pageSizePosition: 'start', + showJump: true, + style: {}, + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'Dialog', + id: 'node_dockcy8n9xe1h', + props: { + prefix: 'next-', + footerAlign: 'right', + footerActions: [ + 'ok', + 'cancel', + ], + closeable: 'esc,close', + hasMask: true, + align: 'cc cc', + minMargin: 40, + visible: { + type: 'JSExpression', + value: 'this.state.isShowDialog', + }, + title: '标题', + events: [], + __events: [ + { + type: 'componentEvent', + name: 'onCancel', + relatedEventName: 'closeDialog', + }, + { + type: 'componentEvent', + name: 'onClose', + relatedEventName: 'closeDialog', + }, + { + type: 'componentEvent', + name: 'onOk', + relatedEventName: 'testFunc', + }, + ], + onCancel: { + type: 'JSFunction', + value: 'function(){ this.closeDialog() }', + }, + onClose: { + type: 'JSFunction', + value: 'function(){ this.closeDialog() }', + }, + onOk: { + type: 'JSFunction', + value: 'function(){ this.testFunc() }', + }, + }, + children: [ + { + componentName: 'Form', + id: 'node_dockd5nrh9pi', + props: { + inline: false, + labelAlign: 'top', + labelTextAlign: 'right', + size: 'medium', + }, + children: [ + { + componentName: 'Form.Item', + id: 'node_dockd5nrh9pj', + props: { + style: { + marginBottom: '0', + minWidth: '200px', + minHeight: '28px', + }, + label: '商品类目', + }, + children: [ + { + componentName: 'Select', + id: 'node_dockd5nrh9pk', + props: { + mode: 'single', + hasArrow: true, + cacheValue: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_dockd5nrh9pl', + props: { + style: { + marginBottom: '0', + minWidth: '200px', + minHeight: '28px', + }, + label: '商品类目', + }, + children: [ + { + componentName: 'Select', + id: 'node_dockd5nrh9pm', + props: { + mode: 'single', + hasArrow: true, + cacheValue: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_dockd5nrh9pn', + props: { + style: { + marginBottom: '0', + minWidth: '200px', + minHeight: '28px', + }, + label: '商品类目', + asterisk: true, + }, + children: [ + { + componentName: 'Select', + id: 'node_dockd5nrh9po', + props: { + mode: 'single', + hasArrow: true, + cacheValue: true, + }, + }, + ], + }, + { + componentName: 'Form.Item', + id: 'node_dockd5nrh9pp', + props: { + style: { + marginBottom: '0', + minWidth: '200px', + minHeight: '28px', + }, + label: '商品类目', + }, + children: [ + { + componentName: 'Input', + id: 'node_dockd5nrh9pr', + props: { + hasBorder: true, + size: 'medium', + autoComplete: 'off', + }, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'ErrorComponent', + id: 'node_dockd5nrh9pr', + props: { + name: 'error', + }, + }, + ], +}; diff --git a/packages/renderer-core/test/renderer/__snapshots__/renderer.test.tsx.snap b/packages/renderer-core/test/renderer/__snapshots__/renderer.test.tsx.snap new file mode 100644 index 000000000..b253ac9c5 --- /dev/null +++ b/packages/renderer-core/test/renderer/__snapshots__/renderer.test.tsx.snap @@ -0,0 +1,1023 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Base Render renderComp 1`] = ` +
+
+
+ +
+
+
+
+
+ +
+
+ + + + + + + + 请选择 + + +   + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + + + + + 请选择 + + +   + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + + +
+
+
+ + +
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ 姓名 +
+
+
+ 年龄 +
+
+
+ 邮箱 +
+
+
+ 没有数据 +
+
+
+
+
+
+ +
+ + + + + + +
+ + + + 1 + + / + 10 + + + 到第 + + + + + + 页 + + +
+
+
+
+
+ + + + + + + + 请选择 + + +   + + + + + + + + + + + + + + +
+`; + +exports[`JSExpression JSExpression props 1`] = ` +
+
+
+`; + +exports[`JSExpression JSExpression props with loop 1`] = ` +
+
+
+
+`; + +exports[`JSExpression JSFunction props 1`] = ` +
+
+
+`; + +exports[`JSExpression JSFunction props with loop 1`] = ` +
+
+
+
+`; + +exports[`JSExpression base props 1`] = ` +
+
+
+`; diff --git a/packages/renderer-core/test/renderer/renderer.test.tsx b/packages/renderer-core/test/renderer/renderer.test.tsx new file mode 100644 index 000000000..2e5ff942d --- /dev/null +++ b/packages/renderer-core/test/renderer/renderer.test.tsx @@ -0,0 +1,241 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import schema from '../fixtures/schema/basic'; +import '../utils/mock-react-render'; +import rendererFactory from '../../src/renderer/renderer'; +import components from '../utils/components'; + +const Renderer = rendererFactory(); + +function getComp(schema, comp = null): Promise<{ + component, + inst, +}> { + return new Promise((resolve, reject) => { + const component = renderer.create( + // @ts-ignore + ); + + const componentInstance = component.root; + + setTimeout(() => { + resolve({ + inst: comp ? componentInstance.findAllByType(comp) : null, + component, + }); + }, 20); + }) +} + +beforeEach(() => { + +}); + +let componentSnapshot; + +afterEach(() => { + if (componentSnapshot) { + let tree = componentSnapshot.toJSON(); + expect(tree).toMatchSnapshot(); + componentSnapshot = null; + } +}); + +describe('Base Render', () => { + it('renderComp', () => { + const content = ( + // @ts-ignore + ); + const tree = renderer.create(content).toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); + +describe('JSExpression', () => { + it('base props', (done) => { + const schema = { + componentName: 'Page', + props: {}, + children: [ + { + componentName: "Div", + props: { + className: 'div-ut', + text: "123", + visible: true, + } + } + ] + }; + + getComp(schema, components.Div).then(({ component, inst }) => { + expect(inst[0].props.text).toBe('123'); + expect(inst[0].props.visible).toBeTruthy(); + + componentSnapshot = component; + done(); + }); + }); + + it('JSExpression props', (done) => { + const schema = { + componentName: 'Page', + props: {}, + state: { + isShowDialog: true, + }, + children: [ + { + componentName: "Div", + props: { + className: "div-ut", + visible: { + type: 'JSExpression', + value: 'this.state.isShowDialog', + }, + } + } + ] + }; + + getComp(schema, components.Div).then(({ component, inst }) => { + expect(inst[0].props.visible).toBeTruthy(); + componentSnapshot = component; + done(); + }); + }); + + it('JSExpression props with loop', (done) => { + const schema = { + componentName: 'Page', + props: {}, + state: { + isShowDialog: true, + }, + children: [ + { + componentName: "Div", + loop: [ + { + name: '1', + }, + { + name: '2' + } + ], + props: { + className: "div-ut", + name1: { + type: 'JSExpression', + value: 'this.item.name', + }, + name2: { + type: 'JSExpression', + value: 'item.name', + }, + } + } + ] + }; + + getComp(schema, components.Div).then(({ component, inst }) => { + // expect(inst[0].props.visible).toBeTruthy(); + expect(inst.length).toEqual(2); + [1, 2].forEach((i) => { + expect(inst[0].props[`name${i}`]).toBe('1'); + expect(inst[1].props[`name${i}`]).toBe('2'); + }) + componentSnapshot = component; + done(); + }); + }); + + it('JSFunction props with loop', (done) => { + const schema = { + componentName: 'Page', + props: {}, + state: { + isShowDialog: true, + }, + children: [ + { + componentName: "Div", + loop: [ + { + name: '1', + }, + { + name: '2' + } + ], + props: { + className: "div-ut", + onClick1: { + type: 'JSFunction', + value: '() => this.item.name', + }, + onClick2: { + type: 'JSFunction', + value: 'function(){ return this.item.name }', + }, + onClick3: { + type: 'JSFunction', + value: 'function(){ return item.name }', + }, + onClick4: { + type: 'JSFunction', + value: '() => item.name', + } + } + } + ] + }; + + getComp(schema, components.Div).then(({ component, inst }) => { + // expect(inst[0].props.visible).toBeTruthy(); + expect(inst.length).toEqual(2); + [1, 2, 3, 4].forEach((i) => { + expect(inst[0].props[`onClick${i}`]()).toBe('1'); + expect(inst[1].props[`onClick${i}`]()).toBe('2'); + }) + componentSnapshot = component; + done(); + }); + }); + + it('JSFunction props', (done) => { + const schema = { + componentName: 'Page', + props: {}, + state: { + isShowDialog: true, + }, + children: [ + { + componentName: "Div", + props: { + className: "div-ut", + onClick: { + type: 'JSExpression', + value: '() => this.state.isShowDialog', + }, + } + } + ] + }; + + getComp(schema, components.Div).then(({ component, inst }) => { + expect(!!inst[0].props.onClick).toBeTruthy(); + expect(inst[0].props.onClick()).toBeTruthy(); + + componentSnapshot = component; + done(); + });; + }); +}) \ No newline at end of file diff --git a/packages/renderer-core/test/utils/components.tsx b/packages/renderer-core/test/utils/components.tsx new file mode 100644 index 000000000..5fdadb2c3 --- /dev/null +++ b/packages/renderer-core/test/utils/components.tsx @@ -0,0 +1,22 @@ +import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next'; + +const Div = (props) => (
{props.children}
); + +const components = { + Box, + Breadcrumb, + 'Breadcrumb.Item': Breadcrumb.Item, + Form, + 'Form.Item': Form.Item, + Select, + Input, + Button, + 'Button.Group': Button.Group, + Table, + Pagination, + Dialog, + ErrorComponent: Select, + Div, +}; + +export default components; \ No newline at end of file diff --git a/packages/renderer-core/test/utils/mock-react-render.ts b/packages/renderer-core/test/utils/mock-react-render.ts new file mode 100644 index 000000000..d19f78e7f --- /dev/null +++ b/packages/renderer-core/test/utils/mock-react-render.ts @@ -0,0 +1,66 @@ +import React, { Component, PureComponent, createElement, createContext, forwardRef, ReactInstance, ContextType } from 'react'; +import ReactDOM from 'react-dom'; +import { + adapter, + pageRendererFactory, + componentRendererFactory, + blockRendererFactory, + addonRendererFactory, + tempRendererFactory, + rendererFactory, + types, +} from '../../src'; +import ConfigProvider from '@alifd/next/lib/config-provider'; + +window.React = React; +(window as any).ReactDom = ReactDOM; + +adapter.setRuntime({ + Component, + PureComponent, + createContext, + createElement, + forwardRef, + findDOMNode: ReactDOM.findDOMNode, +}); + +adapter.setRenderers({ + PageRenderer: pageRendererFactory(), + ComponentRenderer: componentRendererFactory(), + BlockRenderer: blockRendererFactory(), + AddonRenderer: addonRendererFactory(), + TempRenderer: tempRendererFactory(), + DivRenderer: blockRendererFactory(), +}); + +adapter.setConfigProvider(ConfigProvider); + +function factory() { + const Renderer = rendererFactory(); + return class ReactRenderer extends Renderer implements Component { + readonly props: types.IProps; + + context: ContextType; + + setState: ( + state: types.IState, + callback?: () => void, + ) => void; + + forceUpdate: (callback?: () => void) => void; + + refs: { + [key: string]: ReactInstance, + }; + + constructor(props: types.IProps, context: ContextType) { + super(props, context); + } + + isValidComponent(obj: any) { + return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component; + } + }; +} + +export default factory();