From d49535da03a356b7820875e9bf67baffc1013b7d Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 13 Sep 2020 00:02:24 -0400 Subject: [PATCH] feature(redux-devtools-test-generator): convert to TypeScript (#634) * stash * stash * compiles * Tests * finish * fix packages * somemore * update snapshot --- package.json | 2 + packages/devui/package.json | 11 +- packages/devui/src/Dialog/Dialog.tsx | 4 +- packages/devui/src/Editor/Editor.tsx | 4 +- .../src/simple-element-resize-detector.ts | 6 - packages/devui/tests/Editor.test.tsx | 9 +- .../tests/__snapshots__/Editor.test.tsx.snap | 1 - .../demo/src/js/DevTools.tsx | 2 +- .../demo/src/js/reducers.ts | 29 +-- .../package.json | 3 - .../src/index.ts | 3 + .../redux-devtools-test-generator/.babelrc | 8 +- .../.eslintignore | 1 + .../.eslintrc.js | 37 +++ .../redux-devtools-test-generator/README.md | 2 +- .../demo/config/tsconfig.json | 7 + .../config/webpack.config.ts} | 62 ++--- .../demo/src/js/{DemoApp.jsx => DemoApp.tsx} | 101 ++++++-- .../src/js/{DevTools.jsx => DevTools.tsx} | 33 ++- .../demo/src/js/getOptions.js | 11 - .../demo/src/js/getOptions.ts | 20 ++ .../demo/src/js/{index.js => index.tsx} | 27 +- .../demo/src/js/reducers.js | 125 ---------- .../demo/src/js/reducers.ts | 232 ++++++++++++++++++ .../demo/tsconfig.json | 7 + .../jest.config.js | 4 + .../package.json | 105 ++++---- .../{TestGenerator.js => TestGenerator.tsx} | 136 ++++++---- .../src/es6template.ts | 6 + .../src/{index.js => index.tsx} | 82 ++++--- .../src/redux/ava/index.js | 18 -- .../src/redux/ava/index.ts | 21 ++ .../redux/ava/{template.js => template.ts} | 0 .../src/redux/jest/index.js | 18 -- .../src/redux/jest/index.ts | 20 ++ .../redux/jest/{template.js => template.ts} | 0 .../src/redux/mocha/index.js | 21 -- .../src/redux/mocha/index.ts | 23 ++ .../redux/mocha/{template.js => template.ts} | 0 .../src/redux/tape/index.js | 19 -- .../src/redux/tape/index.ts | 22 ++ .../redux/tape/{template.js => template.ts} | 0 .../src/simple-diff.ts | 64 +++++ .../src/{templateForm.js => templateForm.ts} | 14 +- .../src/types.ts | 23 ++ .../src/vanilla/ava/index.js | 18 -- .../src/vanilla/ava/index.ts | 20 ++ .../vanilla/ava/{template.js => template.ts} | 0 .../src/vanilla/jest/index.js | 18 -- .../src/vanilla/jest/index.ts | 20 ++ .../vanilla/jest/{template.js => template.ts} | 0 .../src/vanilla/mocha/index.js | 20 -- .../src/vanilla/mocha/index.ts | 27 ++ .../mocha/{template.js => template.ts} | 0 .../src/vanilla/tape/index.js | 19 -- .../src/vanilla/tape/index.ts | 21 ++ .../vanilla/tape/{template.js => template.ts} | 0 ...nerator.spec.js => TestGenerator.spec.tsx} | 34 ++- ...ec.js.snap => TestGenerator.spec.tsx.snap} | 0 ...{assertions.spec.js => assertions.spec.ts} | 8 +- .../test/{setup.js => setup.ts} | 0 .../test/tsconfig.json | 4 + .../tsconfig.json | 8 + yarn.lock | 40 +-- 64 files changed, 998 insertions(+), 602 deletions(-) delete mode 100644 packages/devui/src/simple-element-resize-detector.ts create mode 100644 packages/redux-devtools-test-generator/.eslintignore create mode 100644 packages/redux-devtools-test-generator/.eslintrc.js create mode 100644 packages/redux-devtools-test-generator/demo/config/tsconfig.json rename packages/redux-devtools-test-generator/{webpack.config.js => demo/config/webpack.config.ts} (62%) rename packages/redux-devtools-test-generator/demo/src/js/{DemoApp.jsx => DemoApp.tsx} (55%) rename packages/redux-devtools-test-generator/demo/src/js/{DevTools.jsx => DevTools.tsx} (53%) delete mode 100644 packages/redux-devtools-test-generator/demo/src/js/getOptions.js create mode 100644 packages/redux-devtools-test-generator/demo/src/js/getOptions.ts rename packages/redux-devtools-test-generator/demo/src/js/{index.js => index.tsx} (74%) delete mode 100644 packages/redux-devtools-test-generator/demo/src/js/reducers.js create mode 100644 packages/redux-devtools-test-generator/demo/src/js/reducers.ts create mode 100644 packages/redux-devtools-test-generator/demo/tsconfig.json create mode 100644 packages/redux-devtools-test-generator/jest.config.js rename packages/redux-devtools-test-generator/src/{TestGenerator.js => TestGenerator.tsx} (52%) create mode 100644 packages/redux-devtools-test-generator/src/es6template.ts rename packages/redux-devtools-test-generator/src/{index.js => index.tsx} (73%) delete mode 100644 packages/redux-devtools-test-generator/src/redux/ava/index.js create mode 100644 packages/redux-devtools-test-generator/src/redux/ava/index.ts rename packages/redux-devtools-test-generator/src/redux/ava/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/redux/jest/index.js create mode 100644 packages/redux-devtools-test-generator/src/redux/jest/index.ts rename packages/redux-devtools-test-generator/src/redux/jest/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/redux/mocha/index.js create mode 100644 packages/redux-devtools-test-generator/src/redux/mocha/index.ts rename packages/redux-devtools-test-generator/src/redux/mocha/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/redux/tape/index.js create mode 100644 packages/redux-devtools-test-generator/src/redux/tape/index.ts rename packages/redux-devtools-test-generator/src/redux/tape/{template.js => template.ts} (100%) create mode 100644 packages/redux-devtools-test-generator/src/simple-diff.ts rename packages/redux-devtools-test-generator/src/{templateForm.js => templateForm.ts} (76%) create mode 100644 packages/redux-devtools-test-generator/src/types.ts delete mode 100644 packages/redux-devtools-test-generator/src/vanilla/ava/index.js create mode 100644 packages/redux-devtools-test-generator/src/vanilla/ava/index.ts rename packages/redux-devtools-test-generator/src/vanilla/ava/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/vanilla/jest/index.js create mode 100644 packages/redux-devtools-test-generator/src/vanilla/jest/index.ts rename packages/redux-devtools-test-generator/src/vanilla/jest/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/vanilla/mocha/index.js create mode 100644 packages/redux-devtools-test-generator/src/vanilla/mocha/index.ts rename packages/redux-devtools-test-generator/src/vanilla/mocha/{template.js => template.ts} (100%) delete mode 100644 packages/redux-devtools-test-generator/src/vanilla/tape/index.js create mode 100644 packages/redux-devtools-test-generator/src/vanilla/tape/index.ts rename packages/redux-devtools-test-generator/src/vanilla/tape/{template.js => template.ts} (100%) rename packages/redux-devtools-test-generator/test/{TestGenerator.spec.js => TestGenerator.spec.tsx} (82%) rename packages/redux-devtools-test-generator/test/__snapshots__/{TestGenerator.spec.js.snap => TestGenerator.spec.tsx.snap} (100%) rename packages/redux-devtools-test-generator/test/{assertions.spec.js => assertions.spec.ts} (87%) rename packages/redux-devtools-test-generator/test/{setup.js => setup.ts} (100%) create mode 100644 packages/redux-devtools-test-generator/test/tsconfig.json create mode 100644 packages/redux-devtools-test-generator/tsconfig.json diff --git a/package.json b/package.json index 730f78de..967619de 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,14 @@ "babel-loader": "^8.1.0", "clean-webpack-plugin": "^3.0.0", "cross-env": "^7.0.2", + "css-loader": "^4.2.1", "eslint": "^7.6.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-jest": "^23.20.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.20.5", + "file-loader": "^6.0.0", "fork-ts-checker-webpack-plugin": "^5.1.0", "html-webpack-plugin": "^4.3.0", "jest": "^26.2.2", diff --git a/packages/devui/package.json b/packages/devui/package.json index b09a5b82..ad161345 100755 --- a/packages/devui/package.json +++ b/packages/devui/package.json @@ -36,6 +36,14 @@ "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { + "@types/base16": "^1.0.2", + "@types/codemirror": "^0.0.97", + "@types/prop-types": "^15.7.3", + "@types/react-jsonschema-form": "^1.7.4", + "@types/react-select": "^3.0.19", + "@types/redux-devtools-themes": "^1.0.0", + "@types/simple-element-resize-detector": "^1.3.0", + "@types/styled-components": "^5.1.2", "base16": "^1.0.0", "codemirror": "^5.56.0", "color": "^3.1.2", @@ -51,11 +59,8 @@ "devDependencies": { "@storybook/addon-essentials": "^6.0.21", "@storybook/react": "^6.0.21", - "@types/codemirror": "^0.0.97", "@types/enzyme": "^3.10.5", "@types/enzyme-adapter-react-16": "^1.0.6", - "@types/react-jsonschema-form": "^1.7.4", - "@types/react-select": "^3.0.19", "csstype": "^3.0.2", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.3", diff --git a/packages/devui/src/Dialog/Dialog.tsx b/packages/devui/src/Dialog/Dialog.tsx index dc058b4e..6ae80aa9 100644 --- a/packages/devui/src/Dialog/Dialog.tsx +++ b/packages/devui/src/Dialog/Dialog.tsx @@ -44,13 +44,13 @@ function isForm

(rest?: FormProps

): rest is FormProps

{ } export default class Dialog

extends (PureComponent || Component)< - DialogProps | (DialogProps & FormProps

) + DialogProps | (Omit & FormProps

) > { submitButton?: HTMLInputElement | null; onSubmit = () => { if (this.submitButton) this.submitButton.click(); - else this.props.onSubmit(); + else (this.props.onSubmit as () => void)(); }; getFormButtonRef: React.RefCallback = (node) => { diff --git a/packages/devui/src/Editor/Editor.tsx b/packages/devui/src/Editor/Editor.tsx index ab8a53cf..c78ca961 100644 --- a/packages/devui/src/Editor/Editor.tsx +++ b/packages/devui/src/Editor/Editor.tsx @@ -23,7 +23,7 @@ export interface EditorProps { theme?: Base16Theme; foldGutter: boolean; autofocus: boolean; - onChange: (value: string, change: CodeMirror.EditorChangeLinkedList) => void; + onChange?: (value: string, change: CodeMirror.EditorChangeLinkedList) => void; } /** @@ -47,7 +47,7 @@ export default class Editor extends Component { if (this.props.onChange) { this.cm.on('change', (doc, change) => { - this.props.onChange(doc.getValue(), change); + this.props.onChange!(doc.getValue(), change); }); } } diff --git a/packages/devui/src/simple-element-resize-detector.ts b/packages/devui/src/simple-element-resize-detector.ts deleted file mode 100644 index 66c7c0c0..00000000 --- a/packages/devui/src/simple-element-resize-detector.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'simple-element-resize-detector' { - export default function ( - element: HTMLElement, - handler: () => void - ): HTMLIFrameElement; -} diff --git a/packages/devui/tests/Editor.test.tsx b/packages/devui/tests/Editor.test.tsx index c5f38229..fd922108 100644 --- a/packages/devui/tests/Editor.test.tsx +++ b/packages/devui/tests/Editor.test.tsx @@ -25,14 +25,7 @@ describe('Editor', function () { return range; }; - const wrapper = mount( - { - //noop - }} - /> - ); + const wrapper = mount(); it('renders correctly', () => { expect(mountToJson(wrapper)).toMatchSnapshot(); diff --git a/packages/devui/tests/__snapshots__/Editor.test.tsx.snap b/packages/devui/tests/__snapshots__/Editor.test.tsx.snap index af747ac8..e1b1b93e 100644 --- a/packages/devui/tests/__snapshots__/Editor.test.tsx.snap +++ b/packages/devui/tests/__snapshots__/Editor.test.tsx.snap @@ -7,7 +7,6 @@ exports[`Editor renders correctly 1`] = ` lineNumbers={true} lineWrapping={false} mode="javascript" - onChange={[Function]} readOnly={false} value="var a = 1;" > diff --git a/packages/redux-devtools-inspector-monitor/demo/src/js/DevTools.tsx b/packages/redux-devtools-inspector-monitor/demo/src/js/DevTools.tsx index cf48d1d3..bae66bd0 100644 --- a/packages/redux-devtools-inspector-monitor/demo/src/js/DevTools.tsx +++ b/packages/redux-devtools-inspector-monitor/demo/src/js/DevTools.tsx @@ -2,10 +2,10 @@ import React from 'react'; import { connect } from 'react-redux'; import { createDevTools } from 'redux-devtools'; import DockMonitor from 'redux-devtools-dock-monitor'; +import { Location } from 'history'; import DevtoolsInspector from '../../../src/DevtoolsInspector'; import getOptions from './getOptions'; import { base16Themes } from '../../../src/utils/createStylingFromTheme'; -import { Location } from 'history'; import { DemoAppState } from './reducers'; const CustomComponent = () => ( diff --git a/packages/redux-devtools-inspector-monitor/demo/src/js/reducers.ts b/packages/redux-devtools-inspector-monitor/demo/src/js/reducers.ts index 7d2e9941..3fdbe4fc 100644 --- a/packages/redux-devtools-inspector-monitor/demo/src/js/reducers.ts +++ b/packages/redux-devtools-inspector-monitor/demo/src/js/reducers.ts @@ -194,7 +194,7 @@ const createRootReducer = ( ): Reducer => combineReducers({ router: connectRouter(history) as Reducer, - timeoutUpdateEnabled: (state = false, action: DemoAppAction) => + timeoutUpdateEnabled: (state = false, action) => action.type === 'TOGGLE_TIMEOUT_UPDATE' ? action.timeoutUpdateEnabled : state, @@ -207,7 +207,7 @@ const createRootReducer = ( // noop } ) => state, - array: (state = [], action: DemoAppAction) => + array: (state = [], action) => action.type === 'PUSH' ? [...state, Math.random()] : action.type === 'POP' @@ -215,13 +215,13 @@ const createRootReducer = ( : action.type === 'REPLACE' ? [Math.random(), ...state.slice(1)] : state, - hugeArrays: (state = [], action: DemoAppAction) => + hugeArrays: (state = [], action) => action.type === 'PUSH_HUGE_ARRAY' ? [...state, ...HUGE_ARRAY] : state, - hugeObjects: (state = [], action: DemoAppAction) => + hugeObjects: (state = [], action) => action.type === 'ADD_HUGE_OBJECT' ? [...state, HUGE_OBJECT] : state, - iterators: (state = [], action: DemoAppAction) => + iterators: (state = [], action) => action.type === 'ADD_ITERATOR' ? [...state, createIterator()] : state, - nested: (state = NESTED, action: DemoAppAction) => + nested: (state = NESTED, action) => action.type === 'CHANGE_NESTED' ? { ...state, @@ -238,29 +238,26 @@ const createRootReducer = ( }, } : state, - recursive: (state: { obj?: unknown }[] = [], action: DemoAppAction) => + recursive: (state: { obj?: unknown }[] = [], action) => action.type === 'ADD_RECURSIVE' ? [...state, { ...RECURSIVE }] : state, - immutables: ( - state: Immutable.Map[] = [], - action: DemoAppAction - ) => + immutables: (state: Immutable.Map[] = [], action) => action.type === 'ADD_IMMUTABLE_MAP' ? [...state, IMMUTABLE_MAP] : state, - maps: (state: Map[] = [], action: DemoAppAction) => + maps: (state: Map[] = [], action) => action.type === 'ADD_NATIVE_MAP' ? [...state, NATIVE_MAP] : state, - immutableNested: (state = IMMUTABLE_NESTED, action: DemoAppAction) => + immutableNested: (state = IMMUTABLE_NESTED, action) => action.type === 'CHANGE_IMMUTABLE_NESTED' ? state.updateIn( ['long', 'nested', 0, 'path', 'to', 'a'], (str: string) => str + '!' ) : state, - addFunction: (state = null, action: DemoAppAction) => + addFunction: (state = null, action) => action.type === 'ADD_FUNCTION' ? { f: FUNC } : state, - addSymbol: (state = null, action: DemoAppAction) => + addSymbol: (state = null, action) => action.type === 'ADD_SYMBOL' ? { s: window.Symbol('symbol'), error: new Error('TEST') } : state, - shuffleArray: (state = DEFAULT_SHUFFLE_ARRAY, action: DemoAppAction) => + shuffleArray: (state = DEFAULT_SHUFFLE_ARRAY, action) => action.type === 'SHUFFLE_ARRAY' ? shuffle(state) : state, }); diff --git a/packages/redux-devtools-inspector-monitor/package.json b/packages/redux-devtools-inspector-monitor/package.json index acc6ecaa..1d828391 100644 --- a/packages/redux-devtools-inspector-monitor/package.json +++ b/packages/redux-devtools-inspector-monitor/package.json @@ -53,11 +53,9 @@ "redux-devtools-themes": "^1.0.0" }, "devDependencies": { - "@babel/runtime": "^7.11.2", "@types/dateformat": "^3.0.1", "@types/hex-rgba": "^1.0.0", "@types/history": "^4.7.7", - "@types/lodash.debounce": "^4.0.6", "@types/lodash.shuffle": "^4.2.6", "@types/react": "^16.9.46", "@types/react-dragula": "^1.1.0", @@ -67,7 +65,6 @@ "connected-react-router": "^6.8.0", "history": "^4.10.1", "immutable": "^4.0.0-rc.12", - "lodash.isequalwith": "^4.4.0", "lodash.shuffle": "^4.2.0", "react": "^16.13.1", "react-bootstrap": "^1.3.0", diff --git a/packages/redux-devtools-inspector-monitor/src/index.ts b/packages/redux-devtools-inspector-monitor/src/index.ts index d6ea9d8f..fdddf57d 100644 --- a/packages/redux-devtools-inspector-monitor/src/index.ts +++ b/packages/redux-devtools-inspector-monitor/src/index.ts @@ -1,2 +1,5 @@ import DevtoolsInspector from './DevtoolsInspector'; export default DevtoolsInspector; +export { Tab, TabComponentProps } from './ActionPreview'; +export { DevtoolsInspectorState } from './redux'; +export { base16Themes } from './utils/createStylingFromTheme'; diff --git a/packages/redux-devtools-test-generator/.babelrc b/packages/redux-devtools-test-generator/.babelrc index 772c0ded..7a337702 100644 --- a/packages/redux-devtools-test-generator/.babelrc +++ b/packages/redux-devtools-test-generator/.babelrc @@ -1,7 +1,11 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"], + "presets": [ + "@babel/preset-env", + "@babel/preset-react", + "@babel/preset-typescript" + ], "plugins": [ "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-do-expressions" + "@babel/plugin-transform-runtime" ] } diff --git a/packages/redux-devtools-test-generator/.eslintignore b/packages/redux-devtools-test-generator/.eslintignore new file mode 100644 index 00000000..a65b4177 --- /dev/null +++ b/packages/redux-devtools-test-generator/.eslintignore @@ -0,0 +1 @@ +lib diff --git a/packages/redux-devtools-test-generator/.eslintrc.js b/packages/redux-devtools-test-generator/.eslintrc.js new file mode 100644 index 00000000..03a5a706 --- /dev/null +++ b/packages/redux-devtools-test-generator/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + extends: '../../.eslintrc', + overrides: [ + { + files: ['*.ts', '*.tsx'], + extends: '../../eslintrc.ts.react.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + }, + { + files: ['demo/**/*.ts', 'demo/**/*.tsx'], + extends: '../../eslintrc.ts.react.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./demo/tsconfig.json'], + }, + }, + { + files: ['demo/config/webpack.config.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./demo/config/tsconfig.json'], + }, + }, + { + files: ['test/*.ts', 'test/*.tsx'], + extends: '../../eslintrc.ts.react.jest.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./test/tsconfig.json'], + }, + }, + ], +}; diff --git a/packages/redux-devtools-test-generator/README.md b/packages/redux-devtools-test-generator/README.md index 53d66669..7348f513 100644 --- a/packages/redux-devtools-test-generator/README.md +++ b/packages/redux-devtools-test-generator/README.md @@ -29,7 +29,7 @@ const testComponent = (props) => ( ); export default createDevTools( - [...defaultTabs, { name: 'Test', component: testComponent }] /> ); diff --git a/packages/redux-devtools-test-generator/demo/config/tsconfig.json b/packages/redux-devtools-test-generator/demo/config/tsconfig.json new file mode 100644 index 00000000..7fd5a7b8 --- /dev/null +++ b/packages/redux-devtools-test-generator/demo/config/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "resolveJsonModule": true + }, + "include": ["webpack.config.ts"] +} diff --git a/packages/redux-devtools-test-generator/webpack.config.js b/packages/redux-devtools-test-generator/demo/config/webpack.config.ts similarity index 62% rename from packages/redux-devtools-test-generator/webpack.config.js rename to packages/redux-devtools-test-generator/demo/config/webpack.config.ts index 12d29a8a..d0b4137b 100644 --- a/packages/redux-devtools-test-generator/webpack.config.js +++ b/packages/redux-devtools-test-generator/demo/config/webpack.config.ts @@ -1,14 +1,14 @@ -const path = require('path'); -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); - -const pkg = require('./package.json'); +import * as path from 'path'; +import * as webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; +import pkg from '../../package.json'; const isProduction = process.env.NODE_ENV === 'production'; module.exports = { - devtool: 'eval-source-map', + mode: process.env.NODE_ENV || 'development', entry: isProduction ? ['./demo/src/js/index'] : [ @@ -20,39 +20,14 @@ module.exports = { path: path.join(__dirname, 'demo/dist'), filename: 'js/bundle.js', }, - plugins: [ - new CleanWebpackPlugin(), - new HtmlWebpackPlugin({ - inject: true, - template: 'demo/src/index.html', - package: pkg, - }), - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV), - }, - }), - ].concat( - isProduction - ? [ - new webpack.optimize.UglifyJsPlugin({ - compress: { warnings: false }, - output: { comments: false }, - }), - ] - : [new webpack.HotModuleReplacementPlugin()] - ), - resolve: { - extensions: ['.js', '.jsx'], - }, module: { rules: [ { - test: /\.jsx?$/, + test: /\.(js|ts)x?$/, loader: 'babel-loader', include: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'demo/src/js'), + path.join(__dirname, '../../src'), + path.join(__dirname, '../src/js'), ], }, { @@ -66,6 +41,22 @@ module.exports = { }, ], }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + plugins: [ + new CleanWebpackPlugin(), + new HtmlWebpackPlugin({ + inject: true, + template: 'demo/src/index.html', + package: pkg, + }), + new ForkTsCheckerWebpackPlugin({ + typescript: { + configFile: 'demo/tsconfig.json', + }, + }), + ].concat(isProduction ? [] : [new webpack.HotModuleReplacementPlugin()]), devServer: isProduction ? null : { @@ -78,4 +69,5 @@ module.exports = { }, historyApiFallback: true, }, + devtool: 'eval-source-map', }; diff --git a/packages/redux-devtools-test-generator/demo/src/js/DemoApp.jsx b/packages/redux-devtools-test-generator/demo/src/js/DemoApp.tsx similarity index 55% rename from packages/redux-devtools-test-generator/demo/src/js/DemoApp.jsx rename to packages/redux-devtools-test-generator/demo/src/js/DemoApp.tsx index 8b6cb822..90b4f0cb 100644 --- a/packages/redux-devtools-test-generator/demo/src/js/DemoApp.jsx +++ b/packages/redux-devtools-test-generator/demo/src/js/DemoApp.tsx @@ -1,11 +1,35 @@ -import React from 'react'; +import React, { CSSProperties } from 'react'; import { connect } from 'react-redux'; -import pkg from '../../../package.json'; import { Button, Toolbar, Spacer } from 'devui'; -import getOptions from './getOptions'; import { push as pushRoute } from 'connected-react-router'; +import pkg from '../../../package.json'; +import getOptions from './getOptions'; +import { DemoAppState } from './reducers'; +import { + AddFunctionAction, + AddHugeObjectAction, + AddImmutableMapAction, + AddIteratorAction, + AddRecursiveAction, + AddSymbolAction, + ChangeImmutableNestedAction, + ChangeNestedAction, + HugePayloadAction, + IncrementAction, + PopAction, + PushAction, + PushHugeArrayAction, + ReplaceAction, + ShuffleArrayAction, + TimeoutUpdateAction, + ToggleTimeoutUpdateAction, +} from './reducers'; -const styles = { +const styles: { + wrapper: CSSProperties; + muted: CSSProperties; + link: CSSProperties; +} = { wrapper: { height: '100vh', width: '450px', @@ -24,7 +48,30 @@ const styles = { const ROOT = '/'; // process.env.NODE_ENV === 'production' ? '/' : '/'; -class DemoApp extends React.Component { +interface Props + extends Omit { + toggleTimeoutUpdate: (timeoutUpdateEnabled: boolean) => void; + timeoutUpdate: () => void; + increment: () => void; + push: () => void; + pop: () => void; + replace: () => void; + changeNested: () => void; + pushHugeArray: () => void; + addIterator: () => void; + addHugeObject: () => void; + addRecursive: () => void; + addImmutableMap: () => void; + changeImmutableNested: () => void; + hugePayload: () => void; + addFunction: () => void; + addSymbol: () => void; + shuffleArray: () => void; +} + +class DemoApp extends React.Component { + timeout?: number; + render() { const options = getOptions(this.props.router.location); @@ -48,7 +95,7 @@ class DemoApp extends React.Component { - + @@ -98,36 +145,40 @@ class DemoApp extends React.Component { this.props.toggleTimeoutUpdate(enabled); if (enabled) { - this.timeout = setInterval(this.props.timeoutUpdate, 1000); + this.timeout = window.setInterval(this.props.timeoutUpdate, 1000); } else { clearTimeout(this.timeout); } }; } -export default connect((state) => state, { - toggleTimeoutUpdate: (timeoutUpdateEnabled) => ({ +export default connect((state: DemoAppState) => state, { + toggleTimeoutUpdate: ( + timeoutUpdateEnabled: boolean + ): ToggleTimeoutUpdateAction => ({ type: 'TOGGLE_TIMEOUT_UPDATE', timeoutUpdateEnabled, }), - timeoutUpdate: () => ({ type: 'TIMEOUT_UPDATE' }), - increment: () => ({ type: 'INCREMENT' }), - push: () => ({ type: 'PUSH' }), - pop: () => ({ type: 'POP' }), - replace: () => ({ type: 'REPLACE' }), - changeNested: () => ({ type: 'CHANGE_NESTED' }), - pushHugeArray: () => ({ type: 'PUSH_HUGE_ARRAY' }), - addIterator: () => ({ type: 'ADD_ITERATOR' }), - addHugeObect: () => ({ type: 'ADD_HUGE_OBJECT' }), - addRecursive: () => ({ type: 'ADD_RECURSIVE' }), - addImmutableMap: () => ({ type: 'ADD_IMMUTABLE_MAP' }), - changeImmutableNested: () => ({ type: 'CHANGE_IMMUTABLE_NESTED' }), - hugePayload: () => ({ + timeoutUpdate: (): TimeoutUpdateAction => ({ type: 'TIMEOUT_UPDATE' }), + increment: (): IncrementAction => ({ type: 'INCREMENT' }), + push: (): PushAction => ({ type: 'PUSH' }), + pop: (): PopAction => ({ type: 'POP' }), + replace: (): ReplaceAction => ({ type: 'REPLACE' }), + changeNested: (): ChangeNestedAction => ({ type: 'CHANGE_NESTED' }), + pushHugeArray: (): PushHugeArrayAction => ({ type: 'PUSH_HUGE_ARRAY' }), + addIterator: (): AddIteratorAction => ({ type: 'ADD_ITERATOR' }), + addHugeObject: (): AddHugeObjectAction => ({ type: 'ADD_HUGE_OBJECT' }), + addRecursive: (): AddRecursiveAction => ({ type: 'ADD_RECURSIVE' }), + addImmutableMap: (): AddImmutableMapAction => ({ type: 'ADD_IMMUTABLE_MAP' }), + changeImmutableNested: (): ChangeImmutableNestedAction => ({ + type: 'CHANGE_IMMUTABLE_NESTED', + }), + hugePayload: (): HugePayloadAction => ({ type: 'HUGE_PAYLOAD', payload: Array.from({ length: 10000 }).map((_, i) => i), }), - addFunction: () => ({ type: 'ADD_FUNCTION' }), - addSymbol: () => ({ type: 'ADD_SYMBOL' }), - shuffleArray: () => ({ type: 'SHUFFLE_ARRAY' }), + addFunction: (): AddFunctionAction => ({ type: 'ADD_FUNCTION' }), + addSymbol: (): AddSymbolAction => ({ type: 'ADD_SYMBOL' }), + shuffleArray: (): ShuffleArrayAction => ({ type: 'SHUFFLE_ARRAY' }), pushRoute, })(DemoApp); diff --git a/packages/redux-devtools-test-generator/demo/src/js/DevTools.jsx b/packages/redux-devtools-test-generator/demo/src/js/DevTools.tsx similarity index 53% rename from packages/redux-devtools-test-generator/demo/src/js/DevTools.jsx rename to packages/redux-devtools-test-generator/demo/src/js/DevTools.tsx index 5fc41048..d34e7b35 100644 --- a/packages/redux-devtools-test-generator/demo/src/js/DevTools.jsx +++ b/packages/redux-devtools-test-generator/demo/src/js/DevTools.tsx @@ -1,12 +1,18 @@ import React from 'react'; import { connect } from 'react-redux'; import { createDevTools } from 'redux-devtools'; -import InspectorMonitor from 'redux-devtools-inspector-monitor'; +import InspectorMonitor, { + base16Themes, + Tab, +} from 'redux-devtools-inspector-monitor'; import DockMonitor from 'redux-devtools-dock-monitor'; +import { Location } from 'history'; import getOptions from './getOptions'; import TestGenerator from '../../../src'; +import { DemoAppState } from './reducers'; +import { Action } from 'redux'; -export const getDevTools = (location) => +export const getDevTools = (location: { search: string }) => createDevTools( changeMonitorKey="ctrl-m" > [ - { - name: 'Test', - component: TestGenerator, - }, - ...defaultTabs, - ]} + tabs={(defaultTabs) => + [ + { + name: 'Test', + component: TestGenerator, + }, + ...defaultTabs, + ] as Tab>[] + } /> ); -const UnconnectedDevTools = ({ location }) => { +const UnconnectedDevTools = ({ location }: { location: Location }) => { const DevTools = getDevTools(location); return ; }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: DemoAppState) => ({ location: state.router.location, }); diff --git a/packages/redux-devtools-test-generator/demo/src/js/getOptions.js b/packages/redux-devtools-test-generator/demo/src/js/getOptions.js deleted file mode 100644 index b26e8222..00000000 --- a/packages/redux-devtools-test-generator/demo/src/js/getOptions.js +++ /dev/null @@ -1,11 +0,0 @@ -export default function getOptions(location) { - return { - useExtension: location.search.indexOf('ext') !== -1, - supportImmutable: location.search.indexOf('immutable') !== -1, - theme: do { - const match = location.search.match(/theme=([^&]+)/); - match ? match[1] : 'inspector'; - }, - dark: location.search.indexOf('dark') !== -1, - }; -} diff --git a/packages/redux-devtools-test-generator/demo/src/js/getOptions.ts b/packages/redux-devtools-test-generator/demo/src/js/getOptions.ts new file mode 100644 index 00000000..2b8a0071 --- /dev/null +++ b/packages/redux-devtools-test-generator/demo/src/js/getOptions.ts @@ -0,0 +1,20 @@ +export interface Options { + useExtension: boolean; + supportImmutable: boolean; + theme: string; + dark: boolean; +} + +export default function getOptions(location: { search: string }) { + return { + useExtension: location.search.indexOf('ext') !== -1, + supportImmutable: location.search.indexOf('immutable') !== -1, + theme: getTheme(), + dark: location.search.indexOf('dark') !== -1, + }; +} + +function getTheme() { + const match = /theme=([^&]+)/.exec(location.search); + return match ? match[1] : 'inspector'; +} diff --git a/packages/redux-devtools-test-generator/demo/src/js/index.js b/packages/redux-devtools-test-generator/demo/src/js/index.tsx similarity index 74% rename from packages/redux-devtools-test-generator/demo/src/js/index.js rename to packages/redux-devtools-test-generator/demo/src/js/index.tsx index da3e7cfb..419e93d4 100644 --- a/packages/redux-devtools-test-generator/demo/src/js/index.js +++ b/packages/redux-devtools-test-generator/demo/src/js/index.tsx @@ -1,22 +1,27 @@ -import '@babel/polyfill'; import 'devui/lib/presets'; import React from 'react'; import { render } from 'react-dom'; import { Container } from 'devui'; -import DemoApp from './DemoApp'; import { Provider } from 'react-redux'; -import createRootReducer from './reducers'; -import { createStore, applyMiddleware, compose } from 'redux'; +import { + createStore, + applyMiddleware, + compose, + StoreEnhancer, + StoreEnhancerStoreCreator, +} from 'redux'; import logger from 'redux-logger'; import { Route } from 'react-router'; import { createBrowserHistory } from 'history'; import { ConnectedRouter, routerMiddleware } from 'connected-react-router'; import { persistState } from 'redux-devtools'; +import DemoApp from './DemoApp'; +import createRootReducer from './reducers'; import getOptions from './getOptions'; import { ConnectedDevTools, getDevTools } from './DevTools'; function getDebugSessionKey() { - const matches = window.location.href.match(/[?&]debug_session=([^&#]+)\b/); + const matches = /[?&]debug_session=([^&#]+)\b/.exec(window.location.href); return matches && matches.length > 0 ? matches[1] : null; } @@ -30,21 +35,23 @@ const DevTools = getDevTools(window.location); const history = createBrowserHistory(); const useDevtoolsExtension = - !!window.__REDUX_DEVTOOLS_EXTENSION__ && + !!((window as unknown) as { __REDUX_DEVTOOLS_EXTENSION__: unknown }) && getOptions(window.location).useExtension; const enhancer = compose( applyMiddleware(logger, routerMiddleware(history)), - (...args) => { + (next: StoreEnhancerStoreCreator) => { const instrument = useDevtoolsExtension - ? window.__REDUX_DEVTOOLS_EXTENSION__() + ? ((window as unknown) as { + __REDUX_DEVTOOLS_EXTENSION__(): StoreEnhancer; + }).__REDUX_DEVTOOLS_EXTENSION__() : DevTools.instrument(); - return instrument(...args); + return instrument(next); }, persistState(getDebugSessionKey()) ); -const store = createStore(createRootReducer(history), {}, enhancer); +const store = createStore(createRootReducer(history), enhancer); render( diff --git a/packages/redux-devtools-test-generator/demo/src/js/reducers.js b/packages/redux-devtools-test-generator/demo/src/js/reducers.js deleted file mode 100644 index c82bc80c..00000000 --- a/packages/redux-devtools-test-generator/demo/src/js/reducers.js +++ /dev/null @@ -1,125 +0,0 @@ -import Immutable from 'immutable'; -import shuffle from 'lodash.shuffle'; -import { combineReducers } from 'redux'; -import { connectRouter } from 'connected-react-router'; - -const NESTED = { - long: { - nested: [ - { - path: { - to: { - a: 'key', - }, - }, - }, - ], - }, -}; - -const IMMUTABLE_NESTED = Immutable.fromJS(NESTED); - -/* eslint-disable babel/new-cap */ - -const IMMUTABLE_MAP = Immutable.Map({ - map: Immutable.Map({ a: 1, b: 2, c: 3 }), - list: Immutable.List(['a', 'b', 'c']), - set: Immutable.Set(['a', 'b', 'c']), - stack: Immutable.Stack(['a', 'b', 'c']), - seq: Immutable.Seq([1, 2, 3, 4, 5, 6, 7, 8]), -}); - -/* eslint-enable babel/new-cap */ - -const HUGE_ARRAY = Array.from({ length: 5000 }).map((_, key) => ({ - str: 'key ' + key, -})); - -const HUGE_OBJECT = Array.from({ length: 5000 }).reduce( - (o, _, key) => ((o['key ' + key] = 'item ' + key), o), - {} -); - -const FUNC = function (a, b, c) { - return a + b + c; -}; - -const RECURSIVE = {}; -RECURSIVE.obj = RECURSIVE; - -function createIterator() { - const iterable = {}; - iterable[window.Symbol.iterator] = function* iterator() { - for (var i = 0; i < 333; i++) { - yield 'item ' + i; - } - }; - - return iterable; -} - -const DEFAULT_SHUFFLE_ARRAY = [0, 1, null, { id: 1 }, { id: 2 }, 'string']; - -const createRootReducer = (history) => - combineReducers({ - router: connectRouter(history), - timeoutUpdateEnabled: (state = false, action) => - action.type === 'TOGGLE_TIMEOUT_UPDATE' - ? action.timeoutUpdateEnabled - : state, - store: (state = 0, action) => - action.type === 'INCREMENT' ? state + 1 : state, - undefined: (state = { val: undefined }) => state, - null: (state = null) => state, - func: (state = () => {}) => state, - array: (state = [], action) => - action.type === 'PUSH' - ? [...state, Math.random()] - : action.type === 'POP' - ? state.slice(0, state.length - 1) - : action.type === 'REPLACE' - ? [Math.random(), ...state.slice(1)] - : state, - hugeArrays: (state = [], action) => - action.type === 'PUSH_HUGE_ARRAY' ? [...state, ...HUGE_ARRAY] : state, - hugeObjects: (state = [], action) => - action.type === 'ADD_HUGE_OBJECT' ? [...state, HUGE_OBJECT] : state, - iterators: (state = [], action) => - action.type === 'ADD_ITERATOR' ? [...state, createIterator()] : state, - nested: (state = NESTED, action) => - action.type === 'CHANGE_NESTED' - ? { - ...state, - long: { - nested: [ - { - path: { - to: { - a: state.long.nested[0].path.to.a + '!', - }, - }, - }, - ], - }, - } - : state, - recursive: (state = [], action) => - action.type === 'ADD_RECURSIVE' ? [...state, { ...RECURSIVE }] : state, - immutables: (state = [], action) => - action.type === 'ADD_IMMUTABLE_MAP' ? [...state, IMMUTABLE_MAP] : state, - immutableNested: (state = IMMUTABLE_NESTED, action) => - action.type === 'CHANGE_IMMUTABLE_NESTED' - ? state.updateIn( - ['long', 'nested', 0, 'path', 'to', 'a'], - (str) => str + '!' - ) - : state, - addFunction: (state = null, action) => - action.type === 'ADD_FUNCTION' ? { f: FUNC } : state, - addSymbol: (state = null, action) => - action.type === 'ADD_SYMBOL' ? { s: window.Symbol('symbol') } : state, - shuffleArray: (state = DEFAULT_SHUFFLE_ARRAY, action) => - action.type === 'SHUFFLE_ARRAY' ? shuffle(state) : state, - }); - -export default createRootReducer; diff --git a/packages/redux-devtools-test-generator/demo/src/js/reducers.ts b/packages/redux-devtools-test-generator/demo/src/js/reducers.ts new file mode 100644 index 00000000..a98fe86d --- /dev/null +++ b/packages/redux-devtools-test-generator/demo/src/js/reducers.ts @@ -0,0 +1,232 @@ +import Immutable from 'immutable'; +import shuffle from 'lodash.shuffle'; +import { combineReducers, Reducer } from 'redux'; +import { + connectRouter, + LocationChangeAction, + RouterState, +} from 'connected-react-router'; +import { History } from 'history'; + +type Nested = { long: { nested: { path: { to: { a: string } } }[] } }; + +const NESTED = { + long: { + nested: [ + { + path: { + to: { + a: 'key', + }, + }, + }, + ], + }, +}; + +const IMMUTABLE_NESTED = Immutable.fromJS(NESTED); + +const IMMUTABLE_MAP = Immutable.Map({ + map: Immutable.Map({ a: 1, b: 2, c: 3 }), + list: Immutable.List(['a', 'b', 'c']), + set: Immutable.Set(['a', 'b', 'c']), + stack: Immutable.Stack(['a', 'b', 'c']), + seq: Immutable.Seq([1, 2, 3, 4, 5, 6, 7, 8]), +}); + +const HUGE_ARRAY = Array.from({ length: 5000 }).map((_, key) => ({ + str: `key ${key}`, +})); + +const HUGE_OBJECT = Array.from({ length: 5000 }).reduce( + (o: { [key: string]: string }, _, key) => ( + (o[`key ${key}`] = `item ${key}`), o + ), + {} +); + +const FUNC = function (a: number, b: number, c: number) { + return a + b + c; +}; + +const RECURSIVE: { obj?: unknown } = {}; +RECURSIVE.obj = RECURSIVE; + +function createIterator() { + const iterable: { [Symbol.iterator](): IterableIterator } = { + [Symbol.iterator]: function* iterator() { + for (let i = 0; i < 333; i++) { + yield `item ${i}`; + } + }, + }; + + return iterable; +} + +const DEFAULT_SHUFFLE_ARRAY = [0, 1, null, { id: 1 }, { id: 2 }, 'string']; + +export interface ToggleTimeoutUpdateAction { + type: 'TOGGLE_TIMEOUT_UPDATE'; + timeoutUpdateEnabled: boolean; +} +export interface TimeoutUpdateAction { + type: 'TIMEOUT_UPDATE'; +} +export interface IncrementAction { + type: 'INCREMENT'; +} +export interface PushAction { + type: 'PUSH'; +} +export interface PopAction { + type: 'POP'; +} +export interface ReplaceAction { + type: 'REPLACE'; +} +export interface ChangeNestedAction { + type: 'CHANGE_NESTED'; +} +export interface PushHugeArrayAction { + type: 'PUSH_HUGE_ARRAY'; +} +export interface AddIteratorAction { + type: 'ADD_ITERATOR'; +} +export interface AddHugeObjectAction { + type: 'ADD_HUGE_OBJECT'; +} +export interface AddRecursiveAction { + type: 'ADD_RECURSIVE'; +} +export interface AddImmutableMapAction { + type: 'ADD_IMMUTABLE_MAP'; +} +export interface ChangeImmutableNestedAction { + type: 'CHANGE_IMMUTABLE_NESTED'; +} +export interface HugePayloadAction { + type: 'HUGE_PAYLOAD'; + payload: number[]; +} +export interface AddFunctionAction { + type: 'ADD_FUNCTION'; +} +export interface AddSymbolAction { + type: 'ADD_SYMBOL'; +} +export interface ShuffleArrayAction { + type: 'SHUFFLE_ARRAY'; +} +type DemoAppAction = + | ToggleTimeoutUpdateAction + | TimeoutUpdateAction + | IncrementAction + | PushAction + | PopAction + | ReplaceAction + | ChangeNestedAction + | PushHugeArrayAction + | AddIteratorAction + | AddHugeObjectAction + | AddRecursiveAction + | AddImmutableMapAction + | ChangeImmutableNestedAction + | HugePayloadAction + | AddFunctionAction + | AddSymbolAction + | ShuffleArrayAction + | LocationChangeAction; + +export interface DemoAppState { + router: RouterState; + timeoutUpdateEnabled: boolean; + store: number; + undefined: { val: undefined }; + null: null; + func: () => void; + array: number[]; + hugeArrays: { str: string }[]; + hugeObjects: { [key: string]: string }[]; + iterators: { [Symbol.iterator](): IterableIterator }[]; + nested: Nested; + recursive: { obj?: unknown }[]; + immutables: Immutable.Map[]; + immutableNested: Immutable.Map; + addFunction: { f: (a: number, b: number, c: number) => number } | null; + addSymbol: { s: symbol; error: Error } | null; + shuffleArray: unknown[]; +} + +const createRootReducer = ( + history: History +): Reducer => + combineReducers({ + router: connectRouter(history) as Reducer, + timeoutUpdateEnabled: (state = false, action) => + action.type === 'TOGGLE_TIMEOUT_UPDATE' + ? action.timeoutUpdateEnabled + : state, + store: (state = 0, action) => + action.type === 'INCREMENT' ? state + 1 : state, + undefined: (state = { val: undefined }) => state, + null: (state = null) => state, + func: ( + state = () => { + // noop + } + ) => state, + array: (state = [], action) => + action.type === 'PUSH' + ? [...state, Math.random()] + : action.type === 'POP' + ? state.slice(0, state.length - 1) + : action.type === 'REPLACE' + ? [Math.random(), ...state.slice(1)] + : state, + hugeArrays: (state = [], action) => + action.type === 'PUSH_HUGE_ARRAY' ? [...state, ...HUGE_ARRAY] : state, + hugeObjects: (state = [], action) => + action.type === 'ADD_HUGE_OBJECT' ? [...state, HUGE_OBJECT] : state, + iterators: (state = [], action) => + action.type === 'ADD_ITERATOR' ? [...state, createIterator()] : state, + nested: (state = NESTED, action) => + action.type === 'CHANGE_NESTED' + ? { + ...state, + long: { + nested: [ + { + path: { + to: { + a: state.long.nested[0].path.to.a + '!', + }, + }, + }, + ], + }, + } + : state, + recursive: (state = [], action) => + action.type === 'ADD_RECURSIVE' ? [...state, { ...RECURSIVE }] : state, + immutables: (state = [], action) => + action.type === 'ADD_IMMUTABLE_MAP' ? [...state, IMMUTABLE_MAP] : state, + immutableNested: (state = IMMUTABLE_NESTED, action) => + action.type === 'CHANGE_IMMUTABLE_NESTED' + ? state.updateIn( + ['long', 'nested', 0, 'path', 'to', 'a'], + (str: string) => str + '!' + ) + : state, + addFunction: (state = null, action) => + action.type === 'ADD_FUNCTION' ? { f: FUNC } : state, + addSymbol: (state = null, action) => + action.type === 'ADD_SYMBOL' + ? { s: window.Symbol('symbol'), error: new Error('TEST') } + : state, + shuffleArray: (state = DEFAULT_SHUFFLE_ARRAY, action) => + action.type === 'SHUFFLE_ARRAY' ? shuffle(state) : state, + }); + +export default createRootReducer; diff --git a/packages/redux-devtools-test-generator/demo/tsconfig.json b/packages/redux-devtools-test-generator/demo/tsconfig.json new file mode 100644 index 00000000..420884ce --- /dev/null +++ b/packages/redux-devtools-test-generator/demo/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.react.base.json", + "compilerOptions": { + "resolveJsonModule": true + }, + "include": ["../src", "src"] +} diff --git a/packages/redux-devtools-test-generator/jest.config.js b/packages/redux-devtools-test-generator/jest.config.js new file mode 100644 index 00000000..547c49dd --- /dev/null +++ b/packages/redux-devtools-test-generator/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + preset: 'ts-jest', + setupFilesAfterEnv: ['/test/setup.ts'], +}; diff --git a/packages/redux-devtools-test-generator/package.json b/packages/redux-devtools-test-generator/package.json index d852e133..31684bd0 100644 --- a/packages/redux-devtools-test-generator/package.json +++ b/packages/redux-devtools-test-generator/package.json @@ -2,22 +2,6 @@ "name": "redux-devtools-test-generator", "version": "0.6.2", "description": "Generate tests for redux devtools.", - "main": "lib/index.js", - "files": [ - "lib" - ], - "scripts": { - "start": "webpack-dev-server", - "clean": "rimraf lib", - "build": "babel src --out-dir lib", - "test": "jest --no-cache", - "prepare": "npm run clean && npm run build", - "prepublishOnly": "npm run test && npm run clean && npm run build" - }, - "repository": { - "type": "git", - "url": "https://github.com/reduxjs/redux-devtools.git" - }, "keywords": [ "redux", "devtools", @@ -28,35 +12,64 @@ "time travel", "live edit" ], - "author": "Mihail Diordiev (https://github.com/zalmoxisus)", - "license": "MIT", + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-test-generator", "bugs": { "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "homepage": "https://github.com/reduxjs/redux-devtools", + "license": "MIT", + "author": "Mihail Diordiev (https://github.com/zalmoxisus)", + "files": [ + "lib", + "src" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/reduxjs/redux-devtools.git" + }, + "scripts": { + "start": "webpack-dev-server --config demo/config/webpack.config.ts", + "build": "npm run build:types && npm run build:js", + "build:types": "tsc --emitDeclarationOnly", + "build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline", + "clean": "rimraf lib", + "test": "jest", + "lint": "eslint . --ext .ts,.tsx", + "lint:fix": "eslint . --ext .ts,.tsx --fix", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch", + "preversion": "npm run type-check && npm run lint && npm run test", + "prepublishOnly": "npm run clean && npm run build" + }, + "dependencies": { + "@types/prop-types": "^15.7.3", + "devui": "^1.0.0-6", + "es6template": "^1.0.5", + "javascript-stringify": "^2.0.1", + "jsan": "^3.1.13", + "object-path": "^0.11.4", + "prop-types": "^15.7.2", + "react-icons": "^3.10.0", + "simple-diff": "^1.6.0" + }, "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-do-expressions": "^7.10.4", - "@babel/polyfill": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "babel-loader": "^8.1.0", - "clean-webpack-plugin": "^3.0.0", + "@types/history": "^4.7.7", + "@types/jsan": "^3.1.0", + "@types/lodash.shuffle": "^4.2.6", + "@types/object-path": "^0.11.0", + "@types/react": "^16.9.46", + "@types/react-router": "^5.1.8", + "@types/redux-logger": "^3.0.8", "connected-react-router": "^6.8.0", - "css-loader": "^4.2.1", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.3", "enzyme-to-json": "^3.5.0", - "expect": "^26.2.0", - "file-loader": "^6.0.0", "history": "^4.10.1", - "html-webpack-plugin": "^4.3.0", "immutable": "^4.0.0-rc.12", "jest": "^26.2.2", - "lodash.isequalwith": "^4.4.0", "lodash.shuffle": "^4.2.0", + "react": "^16.13.1", "react-dom": "^16.13.1", "react-redux": "^7.2.1", "react-router": "^5.2.0", @@ -64,27 +77,11 @@ "redux-devtools": "^3.7.0", "redux-devtools-dock-monitor": "^1.2.0", "redux-devtools-inspector-monitor": "^0.14.0", - "redux-logger": "^3.0.6", - "rimraf": "^3.0.2", - "seamless-immutable": "^7.1.4", - "style-loader": "^1.2.1", - "webpack": "^4.44.1", - "webpack-dev-server": "^3.11.0" + "redux-logger": "^3.0.6" }, - "dependencies": { - "devui": "^1.0.0-6", - "es6template": "^1.0.5", - "javascript-stringify": "^2.0.1", - "jsan": "^3.1.13", - "object-path": "^0.11.4", - "prop-types": "^15.7.2", - "react": "^16.13.1", - "react-icons": "^3.10.0", - "simple-diff": "^1.6.0" - }, - "jest": { - "setupFilesAfterEnv": [ - "/test/setup.js" - ] + "peerDependencies": { + "@types/react": "^16.3.18", + "react": "^16.3.0", + "redux-devtools-inspector-monitor": "^0.14.0" } } diff --git a/packages/redux-devtools-test-generator/src/TestGenerator.js b/packages/redux-devtools-test-generator/src/TestGenerator.tsx similarity index 52% rename from packages/redux-devtools-test-generator/src/TestGenerator.js rename to packages/redux-devtools-test-generator/src/TestGenerator.tsx index 755c7f6d..d8b240f4 100644 --- a/packages/redux-devtools-test-generator/src/TestGenerator.js +++ b/packages/redux-devtools-test-generator/src/TestGenerator.tsx @@ -1,38 +1,54 @@ -import React, { PureComponent, Component } from 'react'; +import React, { PureComponent, Component, ReactNode } from 'react'; import PropTypes from 'prop-types'; import { stringify } from 'javascript-stringify'; import objectPath from 'object-path'; import jsan from 'jsan'; -import diff from 'simple-diff'; +import diff, { Event } from 'simple-diff'; import es6template from 'es6template'; import { Editor } from 'devui'; +import { TabComponentProps } from 'redux-devtools-inspector-monitor'; +import { Action } from 'redux'; +import { AssertionLocals, DispatcherLocals, WrapLocals } from './types'; -export const fromPath = (path) => +export const fromPath = (path: (string | number)[]) => path.map((a) => (typeof a === 'string' ? `.${a}` : `[${a}]`)).join(''); -function getState(s, defaultValue) { +function getState( + s: { state: S; error?: string } | undefined, + // eslint-disable-next-line @typescript-eslint/ban-types + defaultValue?: {} +) { if (!s) return defaultValue; return JSON.parse(jsan.stringify(s.state)); } -export function compare(s1, s2, cb, defaultValue) { - const paths = []; // Already processed - function generate({ type, newPath, newValue, newIndex }) { - let curState; - let path = fromPath(newPath); +export function compare( + s1: { state: S; error?: string } | undefined, + s2: { state: S; error?: string }, + cb: (value: { path: string; curState: number | string | undefined }) => void, + // eslint-disable-next-line @typescript-eslint/ban-types + defaultValue?: {} +) { + const paths: string[] = []; // Already processed + function generate( + event: Event | { type: 'move-item'; newPath: (string | number)[] } + ) { + let curState: number | string | undefined; + let path = fromPath(event.newPath); - if (type === 'remove-item' || type === 'move-item') { + if (event.type === 'remove-item' || event.type === 'move-item') { if (paths.length && paths.indexOf(path) !== -1) return; paths.push(path); - const v = objectPath.get(s2.state, newPath); + // eslint-disable-next-line @typescript-eslint/ban-types + const v = objectPath.get((s2.state as unknown) as object, event.newPath); curState = v.length; path += '.length'; - } else if (type === 'add-item') { - generate({ type: 'move-item', newPath }); - path += `[${newIndex}]`; - curState = stringify(newValue); + } else if (event.type === 'add-item') { + generate({ type: 'move-item', newPath: event.newPath }); + path += `[${event.newIndex}]`; + curState = stringify(event.newValue); } else { - curState = stringify(newValue); + curState = stringify(event.newValue); } // console.log(`expect(store${path}).toEqual(${curState});`); @@ -45,17 +61,34 @@ export function compare(s1, s2, cb, defaultValue) { ).forEach(generate); } -export default class TestGenerator extends (PureComponent || Component) { - getMethod(action) { - let type = action.type; +interface Props> + extends Omit, 'monitorState' | 'updateMonitorState'> { + name?: string; + isVanilla?: boolean; + wrap?: string | ((locals: WrapLocals) => string); + dispatcher?: string | ((locals: DispatcherLocals) => string); + assertion?: string | ((locals: AssertionLocals) => string); + useCodemirror: boolean; + indentation?: number; + header?: ReactNode; +} + +export default class TestGenerator< + S, + A extends Action +> extends (PureComponent || Component)> { + getMethod(action: A) { + let type: string = action.type as string; if (type[0] === '┗') type = type.substr(1).trim(); - let args = action.arguments; - if (args) args = args.map((arg) => stringify(arg)).join(','); - else args = ''; + const args = ((action as unknown) as { arguments: unknown[] }).arguments + ? ((action as unknown) as { arguments: unknown[] }).arguments + .map((arg) => stringify(arg)) + .join(',') + : ''; return `${type}(${args})`; } - getAction(action) { + getAction(action: A) { if (action.type === '@@INIT') return '{}'; return stringify(action); } @@ -76,7 +109,7 @@ export default class TestGenerator extends (PureComponent || Component) { if (typeof assertion === 'string') assertion = es6template.compile(assertion); if (typeof wrap === 'string') { - const ident = wrap.match(/\n.+\$\{assertions}/); + const ident = /\n.+\$\{assertions}/.exec(wrap); if (ident) indentation = ident[0].length - 13; wrap = es6template.compile(wrap); } @@ -94,21 +127,30 @@ export default class TestGenerator extends (PureComponent || Component) { else i = computedStates.length - 1; const startIdx = i > 0 ? i : 1; - const addAssertions = ({ path, curState }) => { - r += space + assertion({ path, curState }) + '\n'; + const addAssertions = ({ + path, + curState, + }: { + path: string; + curState: number | string | undefined; + }) => { + r += `${space}${(assertion as (locals: AssertionLocals) => string)({ + path, + curState, + })}\n`; }; while (actions[i]) { if ( !isVanilla || /* eslint-disable-next-line no-useless-escape */ - /^┗?\s?[a-zA-Z0-9_@.\[\]-]+?$/.test(actions[i].action.type) + /^┗?\s?[a-zA-Z0-9_@.\[\]-]+?$/.test(actions[i].action.type as string) ) { if (isFirst) isFirst = false; else r += space; - if (!isVanilla || actions[i].action.type[0] !== '@') { + if (!isVanilla || (actions[i].action.type as string)[0] !== '@') { r += - dispatcher({ + (dispatcher as (locals: DispatcherLocals) => string)({ action: !isVanilla ? this.getAction(actions[i].action) : this.getMethod(actions[i].action), @@ -131,7 +173,7 @@ export default class TestGenerator extends (PureComponent || Component) { } } i++; - if (i > selectedActionId) break; + if (i > selectedActionId!) break; } r = r.trim(); @@ -139,11 +181,14 @@ export default class TestGenerator extends (PureComponent || Component) { if (!isVanilla) r = wrap({ name, assertions: r }); else { r = wrap({ - name: /^[a-zA-Z0-9_-]+?$/.test(name) ? name : 'Store', + name: /^[a-zA-Z0-9_-]+?$/.test(name as string) ? name : 'Store', actionName: (selectedActionId === null || selectedActionId > 0) && actions[startIdx] - ? actions[startIdx].action.type.replace(/[^a-zA-Z0-9_-]+/, '') + ? (actions[startIdx].action.type as string).replace( + /[^a-zA-Z0-9_-]+/, + '' + ) : 'should return the initial state', initialState: stringify(computedStates[startIdx - 1].state), assertions: r, @@ -167,25 +212,10 @@ export default class TestGenerator extends (PureComponent || Component) { return ; } + + static defaultProps = { + useCodemirror: true, + selectedActionId: null, + startActionId: null, + }; } - -TestGenerator.propTypes = { - name: PropTypes.string, - isVanilla: PropTypes.bool, - computedStates: PropTypes.array, - actions: PropTypes.object, - selectedActionId: PropTypes.number, - startActionId: PropTypes.number, - wrap: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - dispatcher: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - assertion: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - useCodemirror: PropTypes.bool, - indentation: PropTypes.number, - header: PropTypes.element, -}; - -TestGenerator.defaultProps = { - useCodemirror: true, - selectedActionId: null, - startActionId: null, -}; diff --git a/packages/redux-devtools-test-generator/src/es6template.ts b/packages/redux-devtools-test-generator/src/es6template.ts new file mode 100644 index 00000000..9828d0ef --- /dev/null +++ b/packages/redux-devtools-test-generator/src/es6template.ts @@ -0,0 +1,6 @@ +declare module 'es6template' { + const _default: { + compile(template: string): (locals: Locals) => string; + }; + export default _default; +} diff --git a/packages/redux-devtools-test-generator/src/index.js b/packages/redux-devtools-test-generator/src/index.tsx similarity index 73% rename from packages/redux-devtools-test-generator/src/index.js rename to packages/redux-devtools-test-generator/src/index.tsx index afd6bfc7..011946df 100644 --- a/packages/redux-devtools-test-generator/src/index.js +++ b/packages/redux-devtools-test-generator/src/index.tsx @@ -8,16 +8,22 @@ import { Notification, Dialog, } from 'devui'; -import { formSchema, uiSchema, defaultFormData } from './templateForm'; import { MdAdd } from 'react-icons/md'; import { MdEdit } from 'react-icons/md'; +import { Action } from 'redux'; +import { + DevtoolsInspectorState, + TabComponentProps, +} from 'redux-devtools-inspector-monitor'; +import { formSchema, uiSchema, defaultFormData } from './templateForm'; import TestGenerator from './TestGenerator'; import jestTemplate from './redux/jest/template'; import mochaTemplate from './redux/mocha/template'; import tapeTemplate from './redux/tape/template'; import avaTemplate from './redux/ava/template'; +import { Template } from './types'; -export const getDefaultTemplates = (/* lib */) => +export const getDefaultTemplates = (/* lib */): Template[] => /* if (lib === 'redux') { return [mochaTemplate, tapeTemplate, avaTemplate]; @@ -26,15 +32,27 @@ export const getDefaultTemplates = (/* lib */) => */ [jestTemplate, mochaTemplate, tapeTemplate, avaTemplate]; -export default class TestTab extends Component { - constructor(props) { - super(props); - this.state = { dialogStatus: null }; - } +interface TestGeneratorMonitorState { + hideTip?: boolean; + selected?: number; + templates?: Template[]; +} - getPersistedState = () => this.props.monitorState.testGenerator || {}; +interface State { + dialogStatus: 'Add' | 'Edit' | null; +} - handleSelectTemplate = (selectedTemplate) => { +export default class TestTab> extends Component< + TabComponentProps, + State +> { + state: State = { dialogStatus: null }; + + getPersistedState = (): TestGeneratorMonitorState => + (this.props.monitorState as { testGenerator?: TestGeneratorMonitorState }) + .testGenerator || {}; + + handleSelectTemplate = (selectedTemplate: Template) => { const { templates = getDefaultTemplates() } = this.getPersistedState(); this.updateState({ selected: templates.indexOf(selectedTemplate) }); }; @@ -47,7 +65,7 @@ export default class TestTab extends Component { this.setState({ dialogStatus: null }); }; - handleSubmit = ({ formData: template }) => { + handleSubmit = ({ formData: template }: { formData: Template }) => { const { templates = getDefaultTemplates(), selected = 0, @@ -90,13 +108,15 @@ export default class TestTab extends Component { this.setState({ dialogStatus: 'Edit' }); }; - updateState = (newState) => { + updateState = (newState: TestGeneratorMonitorState) => { this.props.updateMonitorState({ testGenerator: { - ...this.props.monitorState.testGenerator, + ...(this.props.monitorState as { + testGenerator?: TestGeneratorMonitorState; + }).testGenerator, ...newState, }, - }); + } as Partial); }; render() { @@ -128,7 +148,7 @@ export default class TestTab extends Component { {!assertion ? ( No template for tests specified. ) : ( - isVanilla={false} assertion={assertion} dispatcher={dispatcher} @@ -142,7 +162,7 @@ export default class TestTab extends Component { )} {dialogStatus && ( -

open title={`${dialogStatus} test template`} onDismiss={this.handleCloseDialog} @@ -164,20 +184,20 @@ export default class TestTab extends Component { ); } -} -TestTab.propTypes = { - monitorState: PropTypes.shape({ - testGenerator: PropTypes.shape({ - templates: PropTypes.array, - selected: PropTypes.number, - hideTip: PropTypes.bool, - }), - }).isRequired, - /* - options: PropTypes.shape({ - lib: PropTypes.string - }).isRequired, - */ - updateMonitorState: PropTypes.func.isRequired, -}; + static propTypes = { + monitorState: PropTypes.shape({ + testGenerator: PropTypes.shape({ + templates: PropTypes.array, + selected: PropTypes.number, + hideTip: PropTypes.bool, + }), + }).isRequired, + /* + options: PropTypes.shape({ + lib: PropTypes.string + }).isRequired, + */ + updateMonitorState: PropTypes.func.isRequired, + }; +} diff --git a/packages/redux-devtools-test-generator/src/redux/ava/index.js b/packages/redux-devtools-test-generator/src/redux/ava/index.js deleted file mode 100644 index d0b8e97d..00000000 --- a/packages/redux-devtools-test-generator/src/redux/ava/index.js +++ /dev/null @@ -1,18 +0,0 @@ -export const name = 'Ava template'; - -export const dispatcher = ({ action, prevState }) => - `state = reducers(${prevState}, ${action});`; - -export const assertion = ({ curState }) => `t.deepEqual(state, ${curState});`; - -export const wrap = ({ assertions }) => - `import test from 'ava'; -import reducers from '../../reducers'; - -test('reducers', (t) => { - let state; - ${assertions} -}); -`; - -export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/ava/index.ts b/packages/redux-devtools-test-generator/src/redux/ava/index.ts new file mode 100644 index 00000000..960e8e9d --- /dev/null +++ b/packages/redux-devtools-test-generator/src/redux/ava/index.ts @@ -0,0 +1,21 @@ +import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types'; + +export const name = 'Ava template'; + +export const dispatcher = ({ action, prevState }: DispatcherLocals) => + `state = reducers(${prevState!}, ${action!});`; + +export const assertion = ({ curState }: AssertionLocals) => + `t.deepEqual(state, ${curState!});`; + +export const wrap = ({ assertions }: WrapLocals) => + `import test from 'ava'; +import reducers from '../../reducers'; + +test('reducers', (t) => { + let state; + ${assertions} +}); +`; + +export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/ava/template.js b/packages/redux-devtools-test-generator/src/redux/ava/template.ts similarity index 100% rename from packages/redux-devtools-test-generator/src/redux/ava/template.js rename to packages/redux-devtools-test-generator/src/redux/ava/template.ts diff --git a/packages/redux-devtools-test-generator/src/redux/jest/index.js b/packages/redux-devtools-test-generator/src/redux/jest/index.js deleted file mode 100644 index 8c4b625a..00000000 --- a/packages/redux-devtools-test-generator/src/redux/jest/index.js +++ /dev/null @@ -1,18 +0,0 @@ -export const name = 'Jest template'; - -export const dispatcher = ({ action, prevState }) => - `state = reducers(${prevState}, ${action});`; - -export const assertion = ({ curState }) => - `expect(state).toEqual(${curState});`; - -export const wrap = ({ assertions }) => - `import reducers from '../../reducers'; - -test('reducers', () => { - let state; - ${assertions} -}); -`; - -export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/jest/index.ts b/packages/redux-devtools-test-generator/src/redux/jest/index.ts new file mode 100644 index 00000000..379eb3fb --- /dev/null +++ b/packages/redux-devtools-test-generator/src/redux/jest/index.ts @@ -0,0 +1,20 @@ +import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types'; + +export const name = 'Jest template'; + +export const dispatcher = ({ action, prevState }: DispatcherLocals) => + `state = reducers(${prevState!}, ${action!});`; + +export const assertion = ({ curState }: AssertionLocals) => + `expect(state).toEqual(${curState!});`; + +export const wrap = ({ assertions }: WrapLocals) => + `import reducers from '../../reducers'; + +test('reducers', () => { + let state; + ${assertions} +}); +`; + +export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/jest/template.js b/packages/redux-devtools-test-generator/src/redux/jest/template.ts similarity index 100% rename from packages/redux-devtools-test-generator/src/redux/jest/template.js rename to packages/redux-devtools-test-generator/src/redux/jest/template.ts diff --git a/packages/redux-devtools-test-generator/src/redux/mocha/index.js b/packages/redux-devtools-test-generator/src/redux/mocha/index.js deleted file mode 100644 index e1df405e..00000000 --- a/packages/redux-devtools-test-generator/src/redux/mocha/index.js +++ /dev/null @@ -1,21 +0,0 @@ -export const name = 'Mocha template'; - -export const dispatcher = ({ action, prevState }) => - `state = reducers(${prevState}, ${action});`; - -export const assertion = ({ curState }) => - `expect(state).toEqual(${curState});`; - -export const wrap = ({ assertions }) => - `import expect from 'expect'; -import reducers from '../../reducers'; - -describe('reducers', () => { - it('should handle actions', () => { - let state; - ${assertions} - }); -}); -`; - -export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/mocha/index.ts b/packages/redux-devtools-test-generator/src/redux/mocha/index.ts new file mode 100644 index 00000000..b8c944aa --- /dev/null +++ b/packages/redux-devtools-test-generator/src/redux/mocha/index.ts @@ -0,0 +1,23 @@ +import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types'; + +export const name = 'Mocha template'; + +export const dispatcher = ({ action, prevState }: DispatcherLocals) => + `state = reducers(${prevState!}, ${action!});`; + +export const assertion = ({ curState }: AssertionLocals) => + `expect(state).toEqual(${curState!});`; + +export const wrap = ({ assertions }: WrapLocals) => + `import expect from 'expect'; +import reducers from '../../reducers'; + +describe('reducers', () => { + it('should handle actions', () => { + let state; + ${assertions} + }); +}); +`; + +export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/mocha/template.js b/packages/redux-devtools-test-generator/src/redux/mocha/template.ts similarity index 100% rename from packages/redux-devtools-test-generator/src/redux/mocha/template.js rename to packages/redux-devtools-test-generator/src/redux/mocha/template.ts diff --git a/packages/redux-devtools-test-generator/src/redux/tape/index.js b/packages/redux-devtools-test-generator/src/redux/tape/index.js deleted file mode 100644 index 037ce601..00000000 --- a/packages/redux-devtools-test-generator/src/redux/tape/index.js +++ /dev/null @@ -1,19 +0,0 @@ -export const name = 'Tape template'; - -export const dispatcher = ({ action, prevState }) => - `state = reducers(${prevState}, ${action});`; - -export const assertion = ({ curState }) => `t.deepEqual(state, ${curState});`; - -export const wrap = ({ assertions }) => - `import test from 'tape'; -import reducers from '../../reducers'; - -test('reducers', (t) => { - let state; - ${assertions} - t.end(); -}); -`; - -export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/tape/index.ts b/packages/redux-devtools-test-generator/src/redux/tape/index.ts new file mode 100644 index 00000000..5a8a843a --- /dev/null +++ b/packages/redux-devtools-test-generator/src/redux/tape/index.ts @@ -0,0 +1,22 @@ +import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types'; + +export const name = 'Tape template'; + +export const dispatcher = ({ action, prevState }: DispatcherLocals) => + `state = reducers(${prevState!}, ${action!});`; + +export const assertion = ({ curState }: AssertionLocals) => + `t.deepEqual(state, ${curState!});`; + +export const wrap = ({ assertions }: WrapLocals) => + `import test from 'tape'; +import reducers from '../../reducers'; + +test('reducers', (t) => { + let state; + ${assertions} + t.end(); +}); +`; + +export default { name, assertion, dispatcher, wrap }; diff --git a/packages/redux-devtools-test-generator/src/redux/tape/template.js b/packages/redux-devtools-test-generator/src/redux/tape/template.ts similarity index 100% rename from packages/redux-devtools-test-generator/src/redux/tape/template.js rename to packages/redux-devtools-test-generator/src/redux/tape/template.ts diff --git a/packages/redux-devtools-test-generator/src/simple-diff.ts b/packages/redux-devtools-test-generator/src/simple-diff.ts new file mode 100644 index 00000000..e5fcafff --- /dev/null +++ b/packages/redux-devtools-test-generator/src/simple-diff.ts @@ -0,0 +1,64 @@ +declare module 'simple-diff' { + interface AddEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'add'; + oldValue: undefined; + newValue: unknown; + } + + interface RemoveEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'remove'; + oldValue: unknown; + newValue: undefined; + } + + interface ChangeEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'change'; + oldValue: unknown; + newValue: unknown; + } + + interface AddItemEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'add-item'; + oldIndex: -1; + curIndex: -1; + newIndex: number; + newValue: unknown; + } + + interface RemoveItemEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'remove-item'; + oldIndex: number; + curIndex: number; + newIndex: -1; + oldValue: unknown; + } + + interface MoveItemEvent { + oldPath: (string | number)[]; + newPath: (string | number)[]; + type: 'move-item'; + oldIndex: number; + curIndex: number; + newIndex: number; + } + + export type Event = + | AddEvent + | RemoveEvent + | ChangeEvent + | AddItemEvent + | RemoveItemEvent + | MoveItemEvent; + + export default function (oldObj: unknown, newObj: unknown): Event[]; +} diff --git a/packages/redux-devtools-test-generator/src/templateForm.js b/packages/redux-devtools-test-generator/src/templateForm.ts similarity index 76% rename from packages/redux-devtools-test-generator/src/templateForm.js rename to packages/redux-devtools-test-generator/src/templateForm.ts index b496a2d5..217c6008 100644 --- a/packages/redux-devtools-test-generator/src/templateForm.js +++ b/packages/redux-devtools-test-generator/src/templateForm.ts @@ -1,21 +1,23 @@ +import { Template } from './types'; + export const formSchema = { - type: 'object', + type: 'object' as const, required: ['name'], properties: { name: { - type: 'string', + type: 'string' as const, title: 'Template name', }, dispatcher: { - type: 'string', + type: 'string' as const, title: 'Dispatcher: ({ action, prevState }) => (`