This commit is contained in:
Nathan Bierema 2020-09-11 12:37:46 -04:00
parent d1c222d847
commit 11add6a54c
39 changed files with 212 additions and 142 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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<EditorProps> {
if (this.props.onChange) {
this.cm.on('change', (doc, change) => {
this.props.onChange(doc.getValue(), change);
this.props.onChange!(doc.getValue(), change);
});
}
}

View File

@ -1,6 +0,0 @@
declare module 'simple-element-resize-detector' {
export default function (
element: HTMLElement,
handler: () => void
): HTMLIFrameElement;
}

View File

@ -25,14 +25,7 @@ describe('Editor', function () {
return range;
};
const wrapper = mount(
<Editor
value="var a = 1;"
onChange={() => {
//noop
}}
/>
);
const wrapper = mount(<Editor value="var a = 1;" />);
it('renders correctly', () => {
expect(mountToJson(wrapper)).toMatchSnapshot();

View File

@ -1,2 +1,3 @@
import DevtoolsInspector from './DevtoolsInspector';
export default DevtoolsInspector;
export { TabComponentProps } from './ActionPreview';

View File

@ -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"
]
}

View File

@ -0,0 +1 @@
lib

View File

@ -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'],
},
},
],
};

View File

@ -29,7 +29,7 @@ const testComponent = (props) => (
);
export default createDevTools(
<Inspector
<InspectorMonitor
tabs: defaultTabs => [...defaultTabs, { name: 'Test', component: testComponent }]
/>
);

View File

@ -0,0 +1,4 @@
{
"extends": "../../../../tsconfig.base.json",
"include": ["webpack.config.ts"]
}

View File

@ -1,4 +1,3 @@
import '@babel/polyfill';
import 'devui/lib/presets';
import React from 'react';
import { render } from 'react-dom';

View File

@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.react.base.json",
"include": ["../src", "src"]
}

View File

@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
};

View File

@ -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,50 +12,40 @@
"time travel",
"live edit"
],
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (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",
"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",
"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-dom": "^16.13.1",
"react-redux": "^7.2.1",
"react-router": "^5.2.0",
"redux": "^4.0.5",
"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"
"license": "MIT",
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (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/jsan": "^3.1.0",
"@types/object-path": "^0.11.0",
"@types/prop-types": "^15.7.3",
"devui": "^1.0.0-6",
"es6template": "^1.0.5",
"javascript-stringify": "^2.0.1",
@ -82,9 +56,25 @@
"react-icons": "^3.10.0",
"simple-diff": "^1.6.0"
},
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/test/setup.js"
]
"devDependencies": {
"connected-react-router": "^6.8.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.3",
"enzyme-to-json": "^3.5.0",
"history": "^4.10.1",
"immutable": "^4.0.0-rc.12",
"jest": "^26.2.2",
"lodash.shuffle": "^4.2.0",
"react-dom": "^16.13.1",
"react-redux": "^7.2.1",
"react-router": "^5.2.0",
"redux": "^4.0.5",
"redux-devtools": "^3.7.0",
"redux-devtools-dock-monitor": "^1.2.0",
"redux-devtools-inspector-monitor": "^0.14.0",
"redux-logger": "^3.0.6"
},
"peerDependencies": {
"redux-devtools-inspector-monitor": "^0.14.0"
}
}

View File

@ -1,4 +1,4 @@
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';
@ -6,6 +6,8 @@ import jsan from 'jsan';
import diff from 'simple-diff';
import es6template from 'es6template';
import { Editor } from 'devui';
import { TabComponentProps } from 'redux-devtools-inspector-monitor';
import { Action } from 'redux';
export const fromPath = (path) =>
path.map((a) => (typeof a === 'string' ? `.${a}` : `[${a}]`)).join('');
@ -15,7 +17,7 @@ function getState(s, defaultValue) {
return JSON.parse(jsan.stringify(s.state));
}
export function compare(s1, s2, cb, defaultValue) {
export function compare<S>(s1: S, s2: S, cb, defaultValue) {
const paths = []; // Already processed
function generate({ type, newPath, newValue, newIndex }) {
let curState;
@ -45,17 +47,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<S, A extends Action<unknown>>
extends Omit<TabComponentProps<S, A>, 'monitorState' | 'updateMonitorState'> {
name?: string;
isVanilla?: boolean;
wrap?: unknown;
dispatcher?: unknown;
assertion?: unknown;
useCodemirror: boolean;
indentation?: number;
header?: ReactNode;
}
export default class TestGenerator<
S,
A extends Action<unknown>
> extends (PureComponent || Component)<Props<S, A>> {
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 +95,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,19 +113,25 @@ 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: string | undefined;
}) => {
r += `${space}${assertion({ 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({
action: !isVanilla
@ -131,7 +156,7 @@ export default class TestGenerator extends (PureComponent || Component) {
}
}
i++;
if (i > selectedActionId) break;
if (i > selectedActionId!) break;
}
r = r.trim();
@ -143,7 +168,10 @@ export default class TestGenerator extends (PureComponent || Component) {
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 +195,25 @@ export default class TestGenerator extends (PureComponent || Component) {
return <Editor value={code} />;
}
static 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,
};
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,
};

View File

@ -0,0 +1,6 @@
export interface Template {
name: string;
dispatcher: string;
assertion: string;
wrap: string;
}

View File

@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.react.base.json",
"include": ["../src", "."]
}

View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.react.base.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}

View File

@ -387,14 +387,6 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-decorators" "^7.10.4"
"@babel/plugin-proposal-do-expressions@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-do-expressions/-/plugin-proposal-do-expressions-7.10.4.tgz#9a5190f3bf4818f83e41d673ee517ff76cf8e4ed"
integrity sha512-Gcc2wLVeMceRdP6m9tdDygP01lbUVmaQGBRoIRJZxzPfB5VTiUgmn1jGfORgqbEVgUpG0IQm/z4q5Y/qzG+8JQ==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-do-expressions" "^7.10.4"
"@babel/plugin-proposal-dynamic-import@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e"
@ -530,13 +522,6 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-do-expressions@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-do-expressions/-/plugin-syntax-do-expressions-7.10.4.tgz#0c7ebb749500c6bfa99a9f926db3bfd6cdbaded9"
integrity sha512-HyvaTg1aiwGo2I+Pu0nyurRMjIP7J89GpuZ2mcQ0fhO6Jt3BnyhEPbNJFG1hRE99NAPNfPYh93/7HO+GPVkTKg==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
@ -977,14 +962,6 @@
"@babel/helper-create-regexp-features-plugin" "^7.10.4"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/polyfill@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.10.4.tgz#915e5bfe61490ac0199008e35ca9d7d151a8e45a"
integrity sha512-8BYcnVqQ5kMD2HXoHInBH7H1b/uP3KdnwCYXOqFnXqguOyuu443WXusbIUbWEfY3Z0Txk0M1uG/8YuAMhNl6zg==
dependencies:
core-js "^2.6.5"
regenerator-runtime "^0.13.4"
"@babel/preset-env@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796"
@ -3653,6 +3630,11 @@
resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4"
integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA==
"@types/object-path@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@types/object-path/-/object-path-0.11.0.tgz#0b744309b2573dc8bf867ef589b6288be998e602"
integrity sha512-/tuN8jDbOXcPk+VzEVZzzAgw1Byz7s/itb2YI10qkSyy6nykJH02DuhfrflxVdAdE7AZ91h5X6Cn0dmVdFw2TQ==
"@types/overlayscrollbars@^1.9.0":
version "1.12.0"
resolved "https://registry.yarnpkg.com/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz#98456caceca8ad73bd5bb572632a585074e70764"
@ -3828,6 +3810,11 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
"@types/simple-element-resize-detector@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/simple-element-resize-detector/-/simple-element-resize-detector-1.3.0.tgz#19b40d71fefa1876ac5d4ba585197ef438946353"
integrity sha512-z89ForrCNg+4uwTHjwBCM9LjcsXYC/4O8u3tSi+82v2LCbfiYFpkjH/qQVkDewFBK6FUG7RRV7jw78EGs2maoQ==
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"