diff --git a/__tests__/jest.setup.js b/__tests__/jest.setup.js new file mode 100644 index 000000000..fa9711707 --- /dev/null +++ b/__tests__/jest.setup.js @@ -0,0 +1,17 @@ +const originalWarn = console.warn.bind(console.warn); +global.beforeAll(() => { + console.warn = msg => { + // warning 先关了,实在太吵。 + // const str = msg.toString(); + // if ( + // str.includes('componentWillMount') || + // str.includes('componentWillReceiveProps') + // ) { + // return; + // } + // originalWarn(msg); + }; +}); +global.afterAll(() => { + console.warn = originalWarn; +}); diff --git a/__tests__/renderers/Form/__snapshots__/city.test.tsx.snap b/__tests__/renderers/Form/__snapshots__/city.test.tsx.snap index d7476c8fa..db0318e22 100644 --- a/__tests__/renderers/Form/__snapshots__/city.test.tsx.snap +++ b/__tests__/renderers/Form/__snapshots__/city.test.tsx.snap @@ -15,7 +15,9 @@ exports[`Renderer:city 1`] = ` - The form + + The form + @@ -28,6 +30,7 @@ exports[`Renderer:city 1`] = ` >
diff --git a/__tests__/renderers/Form/city.test.tsx b/__tests__/renderers/Form/city.test.tsx index 8ce5db202..32763c7e8 100644 --- a/__tests__/renderers/Form/city.test.tsx +++ b/__tests__/renderers/Form/city.test.tsx @@ -1,38 +1,40 @@ import React = require('react'); import {render, fireEvent} from 'react-testing-library'; import '../../../src/themes/default'; -import { - render as amisRender -} from '../../../src/index'; -import { makeEnv } from '../../helper'; +import {render as amisRender} from '../../../src/index'; +import {makeEnv, wait} from '../../helper'; test('Renderer:city', async () => { - const { - container, - getByText - } = render(amisRender({ + const {container, getByText} = render( + amisRender( + { type: 'form', api: '/api/xxx', controls: [ - { - type: 'city', - name: 'a', - label: 'city', - allowDistrict: true, - allowCity: true - } + { + type: 'city', + name: 'a', + label: 'city', + allowDistrict: true, + allowCity: true + } ], title: 'The form', actions: [] - }, {}, makeEnv({ - }))); + }, + {}, + makeEnv({}) + ) + ); - fireEvent.click(getByText('请选择')); - fireEvent.click(getByText('北京市')); - fireEvent.click(getByText('请选择')); - fireEvent.click(getByText('北京市市辖区')); - fireEvent.click(getByText('请选择')); - fireEvent.click(getByText('东城区')); + await wait(200); - expect(container).toMatchSnapshot(); -}); \ No newline at end of file + fireEvent.click(getByText('请选择')); + fireEvent.click(getByText('北京市')); + fireEvent.click(getByText('请选择')); + fireEvent.click(getByText('北京市市辖区')); + fireEvent.click(getByText('请选择')); + fireEvent.click(getByText('东城区')); + + expect(container).toMatchSnapshot(); +}); diff --git a/__tests__/stores/service.test.ts b/__tests__/stores/service.test.ts index 211894758..5cc6516b3 100644 --- a/__tests__/stores/service.test.ts +++ b/__tests__/stores/service.test.ts @@ -1,73 +1,95 @@ -import { getSnapshot, getEnv, onSnapshot } from 'mobx-state-tree'; -import { ServiceStore } from '../../src/store/service'; -import { RendererStore } from '../../src/store'; +import {getSnapshot, getEnv, onSnapshot} from 'mobx-state-tree'; +import {StoreNode} from '../../src/store/node'; +import {ServiceStore} from '../../src/store/service'; +import {RendererStore} from '../../src/store'; import omit = require('lodash/omit'); - test('store:ServiceStore', () => { - const store = ServiceStore.create({ - id: '1', - storeType: ServiceStore.name - }); + const store = ServiceStore.create({ + id: '1', + storeType: ServiceStore.name + }); - expect(getSnapshot(store)).toMatchSnapshot(); + expect(getSnapshot(store)).toMatchSnapshot(); }); - test('store:ServiceStore fetchInitData success', async () => { - const fetcher = jest.fn().mockImplementationOnce(() => Promise.resolve({ - ok: true, - data: { - a: 1, - b: 2 - } - })); - const mainStore = RendererStore.create({}, { - fetcher - }); - const states:Array = []; + const fetcher = jest.fn().mockImplementationOnce(() => + Promise.resolve({ + ok: true, + data: { + a: 1, + b: 2 + } + }) + ); + const isCancel = jest.fn(() => false); + const mainStore = RendererStore.create( + {}, + { + fetcher, + isCancel + } + ); + const states: Array = []; + const store = ServiceStore.create( + { + id: '1', + storeType: ServiceStore.name + }, + { + fetcher, + isCancel + } + ); + mainStore.addStore(store); - const store = ServiceStore.create({ - id: '1', - storeType: ServiceStore.name - }); - mainStore.addStore(store); + onSnapshot(store, snapshot => states.push(snapshot)); - onSnapshot(store, (snapshot) => states.push(snapshot)); + await store.fetchInitData('/api/xxx'); - await store.fetchInitData('/api/xxx'); + const ignoreUdatedAt = states.map(snapshot => omit(snapshot, ['updatedAt'])); + expect(ignoreUdatedAt).toMatchSnapshot(); - const ignoreUdatedAt = states.map(snapshot => omit(snapshot, ['updatedAt'])); - expect(ignoreUdatedAt).toMatchSnapshot(); - - expect(states.length).toBe(2); - expect(states[1].updatedAt).not.toEqual(states[0].updatedAt); + expect(states.length).toBe(2); + expect(states[1].updatedAt).not.toEqual(states[0].updatedAt); }); test('store:ServiceStore fetchInitData failed', async () => { - const fetcher = jest.fn().mockImplementationOnce(() => Promise.reject('Network Error')); - const notify = jest.fn(); - const isCancel = jest.fn(() => false); - const mainStore = RendererStore.create({}, { - fetcher, - notify, - isCancel - }); - const states:Array = []; + const fetcher = jest + .fn() + .mockImplementationOnce(() => Promise.reject('Network Error')); + const notify = jest.fn(); + const isCancel = jest.fn(() => false); + const mainStore = RendererStore.create( + {}, + { + fetcher, + notify, + isCancel + } + ); + const states: Array = []; + const store = ServiceStore.create( + { + id: '1', + storeType: ServiceStore.name + }, + { + fetcher, + notify, + isCancel + } + ); + mainStore.addStore(store); - const store = ServiceStore.create({ - id: '1', - storeType: ServiceStore.name - }); - mainStore.addStore(store); + onSnapshot(store, snapshot => states.push(snapshot)); - onSnapshot(store, (snapshot) => states.push(snapshot)); - - await store.fetchInitData('/api/xxx'); - expect(states).toMatchSnapshot(); - expect(notify).toHaveBeenCalled(); - expect(notify).toHaveBeenLastCalledWith("error", "Network Error"); - expect(isCancel).toHaveBeenCalled(); -}); \ No newline at end of file + await store.fetchInitData('/api/xxx'); + expect(states).toMatchSnapshot(); + expect(notify).toHaveBeenCalled(); + expect(notify).toHaveBeenLastCalledWith('error', 'Network Error'); + expect(isCancel).toHaveBeenCalled(); +}); diff --git a/fis-conf.js b/fis-conf.js index 7d81fbb6e..42f5029cf 100644 --- a/fis-conf.js +++ b/fis-conf.js @@ -110,24 +110,23 @@ fis.match('/docs/**.md', { parser: [ parserMarkdown, function (contents, file) { - return contents.replace(/\bhref=\\('|")(.+?)\\\1/g, function ( - _, - quota, - link - ) { - if (/\.md($|#)/.test(link) && !/^https?\:/.test(link)) { - let parts = link.split('#'); - parts[0] = parts[0].replace('.md', ''); + return contents.replace( + /\bhref=\\('|")(.+?)\\\1/g, + function (_, quota, link) { + if (/\.md($|#)/.test(link) && !/^https?\:/.test(link)) { + let parts = link.split('#'); + parts[0] = parts[0].replace('.md', ''); - if (parts[0][0] !== '/') { - parts[0] = path.resolve(path.dirname(file.subpath), parts[0]); + if (parts[0][0] !== '/') { + parts[0] = path.resolve(path.dirname(file.subpath), parts[0]); + } + + return 'href=\\' + quota + parts.join('#') + '\\' + quota; } - return 'href=\\' + quota + parts.join('#') + '\\' + quota; + return _; } - - return _; - }); + ); } ], isMod: true @@ -170,7 +169,14 @@ fis.match('{*.ts,*.jsx,*.tsx,/src/**.js,/src/**.ts}', { }), function (content) { - return content.replace(/\b[a-zA-Z_0-9$]+\.__uri\s*\(/g, '__uri('); + return content + .replace(/\b[a-zA-Z_0-9$]+\.__uri\s*\(/g, '__uri(') + .replace( + /return\s+(tslib_\d+)\.__importStar\(require\(('|")(.*?)\2\)\);/g, + function (_, tslib, quto, value) { + return `return new Promise(function(resolve){require(['${value}'], function(ret) {resolve(${tslib}.__importStar(ret));})});`; + } + ); } ], preprocessor: fis.plugin('js-require-css'), @@ -239,19 +245,26 @@ if (fis.project.currentMedia() === 'publish') { allowUmdGlobalAccess: true }), function (contents) { - return contents.replace( - /(?:\w+\.)?\b__uri\s*\(\s*('|")(.*?)\1\s*\)/g, - function (_, quote, value) { - let str = quote + value + quote; - return ( - '(function(){try {return __uri(' + - str + - ')} catch(e) {return ' + - str + - '}})()' - ); - } - ); + return contents + .replace( + /(?:\w+\.)?\b__uri\s*\(\s*('|")(.*?)\1\s*\)/g, + function (_, quote, value) { + let str = quote + value + quote; + return ( + '(function(){try {return __uri(' + + str + + ')} catch(e) {return ' + + str + + '}})()' + ); + } + ) + .replace( + /return\s+(tslib_\d+)\.__importStar\(require\(('|")(.*?)\2\)\);/g, + function (_, tslib, quto, value) { + return `return new Promise(function(resolve){require(['${value}'], function(ret) {resolve(${tslib}.__importStar(ret));})});`; + } + ); } ], preprocessor: null @@ -345,9 +358,15 @@ if (fis.project.currentMedia() === 'publish') { experimentalDecorators: true, sourceMap: false }), - function (content) { - return content.replace(/\b[a-zA-Z_0-9$]+\.__uri\s*\(/g, '__uri('); + return content + .replace(/\b[a-zA-Z_0-9$]+\.__uri\s*\(/g, '__uri(') + .replace( + /return\s+(tslib_\d+)\.__importStar\(require\(('|")(.*?)\2\)\);/g, + function (_, tslib, quto, value) { + return `return new Promise(function(resolve){require(['${value}'], function(ret) {resolve(${tslib}.__importStar(ret));})});`; + } + ); } ], preprocessor: fis.plugin('js-require-css'), @@ -521,24 +540,25 @@ if (fis.project.currentMedia() === 'publish') { parser: [ parserMarkdown, function (contents, file) { - return contents.replace(/\bhref=\\('|")(.+?)\\\1/g, function ( - _, - quota, - link - ) { - if (/\.md($|#)/.test(link) && !/^https?\:/.test(link)) { - let parts = link.split('#'); - parts[0] = parts[0].replace('.md', ''); + return contents.replace( + /\bhref=\\('|")(.+?)\\\1/g, + function (_, quota, link) { + if (/\.md($|#)/.test(link) && !/^https?\:/.test(link)) { + let parts = link.split('#'); + parts[0] = parts[0].replace('.md', ''); - if (parts[0][0] !== '/') { - parts[0] = path.resolve(path.dirname(file.subpath), parts[0]); + if (parts[0][0] !== '/') { + parts[0] = path.resolve(path.dirname(file.subpath), parts[0]); + } + + return ( + 'href=\\' + quota + '/amis' + parts.join('#') + '\\' + quota + ); } - return 'href=\\' + quota + '/amis' + parts.join('#') + '\\' + quota; + return _; } - - return _; - }); + ); } ] }); @@ -658,18 +678,17 @@ if (fis.project.currentMedia() === 'publish') { DocJs.getContent(), ExampleJs.getContent() ].join('\n'); - source.replace(/\bpath\b\s*\:\s*('|")(.*?)\1/g, function ( - _, - qutoa, - path - ) { - if (path === '*') { - return; - } + source.replace( + /\bpath\b\s*\:\s*('|")(.*?)\1/g, + function (_, qutoa, path) { + if (path === '*') { + return; + } - pages.push(path.replace(/^\//, '')); - return _; - }); + pages.push(path.replace(/^\//, '')); + return _; + } + ); const contents = indexHtml.getContent(); pages.forEach(function (path) { @@ -713,7 +732,30 @@ if (fis.project.currentMedia() === 'publish') { sourceMap: false, importHelpers: true, esModuleInterop: true - }) + }), + + function (contents) { + return contents + .replace( + /(?:\w+\.)?\b__uri\s*\(\s*('|")(.*?)\1\s*\)/g, + function (_, quote, value) { + let str = quote + value + quote; + return ( + '(function(){try {return __uri(' + + str + + ')} catch(e) {return ' + + str + + '}})()' + ); + } + ) + .replace( + /return\s+(tslib_\d+)\.__importStar\(require\(('|")(.*?)\2\)\);/g, + function (_, tslib, quto, value) { + return `return new Promise(function(resolve){require(['${value}'], function(ret) {resolve(${tslib}.__importStar(ret));})});`; + } + ); + } ] }); ghPages.match('*', { diff --git a/package.json b/package.json index c5292c373..1d101ac65 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "@types/async": "^2.0.45", "@types/classnames": "^2.2.3", "@types/dom-helpers": "^3.4.1", + "@types/echarts": "^4.9.2", "@types/history": "^4.6.0", "@types/hoist-non-react-statics": "^3.3.1", "@types/jest": "^24.9.1", @@ -183,9 +184,40 @@ "\\.(css|less|sass|scss)$": "/__mocks__/styleMock.js", "\\.(svg)$": "/__mocks__/svgMock.js" }, + "setupFilesAfterEnv": [ + "/__tests__/jest.setup.js" + ], "globals": { "ts-jest": { - "diagnostics": false + "diagnostics": false, + "tsconfig": { + "module": "commonjs", + "target": "es5", + "lib": [ + "es6", + "dom", + "ES2015" + ], + "sourceMap": true, + "jsx": "react", + "moduleResolution": "node", + "rootDir": ".", + "importHelpers": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceRoot": ".", + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": false, + "typeRoots": [ + "./node_modules/@types", + "./types" + ], + "skipLibCheck": true + } } } }, diff --git a/scss/components/form/_text.scss b/scss/components/form/_text.scss index 9510116b1..da09f538a 100644 --- a/scss/components/form/_text.scss +++ b/scss/components/form/_text.scss @@ -15,6 +15,7 @@ flex-grow: 1; line-height: 1; white-space: nowrap; + display: flex; > input { display: inline-block; diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index ab5d6b63d..fd070e152 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -176,9 +176,7 @@ export class Editor extends React.Component { } loadMonaco() { - (require as any)(['monaco-editor'], (monaco: any) => { - this.initMonaco(monaco); - }); + import('monaco-editor').then(monaco => this.initMonaco(monaco)); } initMonaco(monaco: any) { diff --git a/src/components/ResultBox.tsx b/src/components/ResultBox.tsx index 12846b5da..5509277f0 100644 --- a/src/components/ResultBox.tsx +++ b/src/components/ResultBox.tsx @@ -14,7 +14,7 @@ export interface ResultBoxProps onChange?: (value: string) => void; onResultClick?: (e: React.MouseEvent) => void; result?: Array | any; - itemRender: (value: any) => JSX.Element; + itemRender: (value: any) => JSX.Element | string; onResultChange?: (value: Array) => void; allowInput?: boolean; inputPlaceholder: string; diff --git a/src/components/condition-builder/index.tsx b/src/components/condition-builder/index.tsx index f8acf9a0b..c5272eb41 100644 --- a/src/components/condition-builder/index.tsx +++ b/src/components/condition-builder/index.tsx @@ -67,7 +67,7 @@ export class QueryBuilder extends React.Component { } @autobind - handleDragOver(e: React.DragEvent) { + handleDragOver(e: DragEvent) { e.preventDefault(); const item = (e.target as HTMLElement).closest('[data-id]') as HTMLElement; diff --git a/src/renderers/CRUD.tsx b/src/renderers/CRUD.tsx index a4cc58892..6a80b2d9c 100644 --- a/src/renderers/CRUD.tsx +++ b/src/renderers/CRUD.tsx @@ -1706,7 +1706,7 @@ export default class CRUD extends React.Component {