mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-22 22:19:48 +03:00
compiles
This commit is contained in:
parent
c746f29e9d
commit
1d6d3867d2
|
@ -44,13 +44,13 @@ function isForm<P>(rest?: FormProps<P>): rest is FormProps<P> {
|
|||
}
|
||||
|
||||
export default class Dialog<P> extends (PureComponent || Component)<
|
||||
DialogProps | (DialogProps & FormProps<P>)
|
||||
DialogProps | (Omit<DialogProps, 'onSubmit'> & FormProps<P>)
|
||||
> {
|
||||
submitButton?: HTMLInputElement | null;
|
||||
|
||||
onSubmit = () => {
|
||||
if (this.submitButton) this.submitButton.click();
|
||||
else this.props.onSubmit();
|
||||
else (this.props.onSubmit as () => void)();
|
||||
};
|
||||
|
||||
getFormButtonRef: React.RefCallback<HTMLInputElement> = (node) => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import DevtoolsInspector from './DevtoolsInspector';
|
||||
export default DevtoolsInspector;
|
||||
export { TabComponentProps } from './ActionPreview';
|
||||
export { DevtoolsInspectorState } from './redux';
|
||||
|
|
|
@ -3,38 +3,49 @@ 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) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function getState<S>(s: { state: S; error?: string }, defaultValue: {}) {
|
||||
if (!s) return defaultValue;
|
||||
return JSON.parse(jsan.stringify(s.state));
|
||||
}
|
||||
|
||||
export function compare<S>(s1: S, s2: S, cb, defaultValue) {
|
||||
const paths = []; // Already processed
|
||||
function generate({ type, newPath, newValue, newIndex }) {
|
||||
let curState;
|
||||
let path = fromPath(newPath);
|
||||
export function compare<S>(
|
||||
s1: { state: S; error?: string },
|
||||
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});`);
|
||||
|
@ -51,9 +62,9 @@ interface Props<S, A extends Action<unknown>>
|
|||
extends Omit<TabComponentProps<S, A>, 'monitorState' | 'updateMonitorState'> {
|
||||
name?: string;
|
||||
isVanilla?: boolean;
|
||||
wrap?: unknown;
|
||||
dispatcher?: unknown;
|
||||
assertion?: unknown;
|
||||
wrap?: string | ((locals: WrapLocals) => string);
|
||||
dispatcher?: string | ((locals: DispatcherLocals) => string);
|
||||
assertion?: string | ((locals: AssertionLocals) => string);
|
||||
useCodemirror: boolean;
|
||||
indentation?: number;
|
||||
header?: ReactNode;
|
||||
|
@ -118,9 +129,12 @@ export default class TestGenerator<
|
|||
curState,
|
||||
}: {
|
||||
path: string;
|
||||
curState: string | undefined;
|
||||
curState: number | string | undefined;
|
||||
}) => {
|
||||
r += `${space}${assertion({ path, curState })}\n`;
|
||||
r += `${space}${(assertion as (locals: AssertionLocals) => string)({
|
||||
path,
|
||||
curState,
|
||||
})}\n`;
|
||||
};
|
||||
|
||||
while (actions[i]) {
|
||||
|
@ -133,7 +147,7 @@ export default class TestGenerator<
|
|||
else r += space;
|
||||
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),
|
||||
|
@ -164,7 +178,7 @@ export default class TestGenerator<
|
|||
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]
|
||||
|
@ -196,21 +210,6 @@ export default class TestGenerator<
|
|||
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,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
declare module 'es6template' {
|
||||
const _default: {
|
||||
compile<Locals>(template: string): (locals: Locals) => string;
|
||||
};
|
||||
export default _default;
|
||||
}
|
|
@ -11,7 +11,10 @@ import {
|
|||
import { MdAdd } from 'react-icons/md';
|
||||
import { MdEdit } from 'react-icons/md';
|
||||
import { Action } from 'redux';
|
||||
import { TabComponentProps } from 'redux-devtools-inspector-monitor';
|
||||
import {
|
||||
DevtoolsInspectorState,
|
||||
TabComponentProps,
|
||||
} from 'redux-devtools-inspector-monitor';
|
||||
import { formSchema, uiSchema, defaultFormData } from './templateForm';
|
||||
import TestGenerator from './TestGenerator';
|
||||
import jestTemplate from './redux/jest/template';
|
||||
|
@ -19,7 +22,6 @@ import mochaTemplate from './redux/mocha/template';
|
|||
import tapeTemplate from './redux/tape/template';
|
||||
import avaTemplate from './redux/ava/template';
|
||||
import { Template } from './types';
|
||||
import { DevtoolsInspectorState } from 'redux-devtools-inspector-monitor/lib/redux';
|
||||
|
||||
export const getDefaultTemplates = (/* lib */): Template[] =>
|
||||
/*
|
||||
|
@ -33,7 +35,7 @@ export const getDefaultTemplates = (/* lib */): Template[] =>
|
|||
interface TestGeneratorMonitorState {
|
||||
hideTip?: boolean;
|
||||
selected?: number;
|
||||
templates: Template[];
|
||||
templates?: Template[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -63,7 +65,7 @@ export default class TestTab<S, A extends Action<unknown>> extends Component<
|
|||
this.setState({ dialogStatus: null });
|
||||
};
|
||||
|
||||
handleSubmit = ({ formData: template }) => {
|
||||
handleSubmit = ({ formData: template }: { formData: Template }) => {
|
||||
const {
|
||||
templates = getDefaultTemplates(),
|
||||
selected = 0,
|
||||
|
@ -106,7 +108,7 @@ export default class TestTab<S, A extends Action<unknown>> extends Component<
|
|||
this.setState({ dialogStatus: 'Edit' });
|
||||
};
|
||||
|
||||
updateState = (newState) => {
|
||||
updateState = (newState: TestGeneratorMonitorState) => {
|
||||
this.props.updateMonitorState({
|
||||
testGenerator: {
|
||||
...(this.props.monitorState as {
|
||||
|
@ -146,7 +148,7 @@ export default class TestTab<S, A extends Action<unknown>> extends Component<
|
|||
{!assertion ? (
|
||||
<Notification>No template for tests specified.</Notification>
|
||||
) : (
|
||||
<TestGenerator
|
||||
<TestGenerator<S, A>
|
||||
isVanilla={false}
|
||||
assertion={assertion}
|
||||
dispatcher={dispatcher}
|
||||
|
@ -160,7 +162,7 @@ export default class TestTab<S, A extends Action<unknown>> extends Component<
|
|||
</Notification>
|
||||
)}
|
||||
{dialogStatus && (
|
||||
<Dialog
|
||||
<Dialog<Template>
|
||||
open
|
||||
title={`${dialogStatus} test template`}
|
||||
onDismiss={this.handleCloseDialog}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Ava template';
|
||||
|
||||
export const dispatcher = ({ action, prevState }) =>
|
||||
`state = reducers(${prevState}, ${action});`;
|
||||
export const dispatcher = ({ action, prevState }: DispatcherLocals) =>
|
||||
`state = reducers(${prevState!}, ${action!});`;
|
||||
|
||||
export const assertion = ({ curState }) => `t.deepEqual(state, ${curState});`;
|
||||
export const assertion = ({ curState }: AssertionLocals) =>
|
||||
`t.deepEqual(state, ${curState!});`;
|
||||
|
||||
export const wrap = ({ assertions }) =>
|
||||
export const wrap = ({ assertions }: WrapLocals) =>
|
||||
`import test from 'ava';
|
||||
import reducers from '../../reducers';
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Jest template';
|
||||
|
||||
export const dispatcher = ({ action, prevState }) =>
|
||||
`state = reducers(${prevState}, ${action});`;
|
||||
export const dispatcher = ({ action, prevState }: DispatcherLocals) =>
|
||||
`state = reducers(${prevState!}, ${action!});`;
|
||||
|
||||
export const assertion = ({ curState }) =>
|
||||
`expect(state).toEqual(${curState});`;
|
||||
export const assertion = ({ curState }: AssertionLocals) =>
|
||||
`expect(state).toEqual(${curState!});`;
|
||||
|
||||
export const wrap = ({ assertions }) =>
|
||||
export const wrap = ({ assertions }: WrapLocals) =>
|
||||
`import reducers from '../../reducers';
|
||||
|
||||
test('reducers', () => {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Mocha template';
|
||||
|
||||
export const dispatcher = ({ action, prevState }) =>
|
||||
`state = reducers(${prevState}, ${action});`;
|
||||
export const dispatcher = ({ action, prevState }: DispatcherLocals) =>
|
||||
`state = reducers(${prevState!}, ${action!});`;
|
||||
|
||||
export const assertion = ({ curState }) =>
|
||||
`expect(state).toEqual(${curState});`;
|
||||
export const assertion = ({ curState }: AssertionLocals) =>
|
||||
`expect(state).toEqual(${curState!});`;
|
||||
|
||||
export const wrap = ({ assertions }) =>
|
||||
export const wrap = ({ assertions }: WrapLocals) =>
|
||||
`import expect from 'expect';
|
||||
import reducers from '../../reducers';
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Tape template';
|
||||
|
||||
export const dispatcher = ({ action, prevState }) =>
|
||||
`state = reducers(${prevState}, ${action});`;
|
||||
export const dispatcher = ({ action, prevState }: DispatcherLocals) =>
|
||||
`state = reducers(${prevState!}, ${action!});`;
|
||||
|
||||
export const assertion = ({ curState }) => `t.deepEqual(state, ${curState});`;
|
||||
export const assertion = ({ curState }: AssertionLocals) =>
|
||||
`t.deepEqual(state, ${curState!});`;
|
||||
|
||||
export const wrap = ({ assertions }) =>
|
||||
export const wrap = ({ assertions }: WrapLocals) =>
|
||||
`import test from 'tape';
|
||||
import reducers from '../../reducers';
|
||||
|
||||
|
|
64
packages/redux-devtools-test-generator/src/simple-diff.ts
Normal file
64
packages/redux-devtools-test-generator/src/simple-diff.ts
Normal file
|
@ -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[];
|
||||
}
|
|
@ -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 }) => (`<template>`)',
|
||||
},
|
||||
assertion: {
|
||||
type: 'string',
|
||||
type: 'string' as const,
|
||||
title: 'Assertion: ({ curState }) => (`<template>`)',
|
||||
},
|
||||
wrap: {
|
||||
type: 'string',
|
||||
type: 'string' as const,
|
||||
title:
|
||||
'Wrap code: ({ name, initialState, assertions }) => (`<template>`)',
|
||||
},
|
||||
|
@ -34,7 +36,7 @@ export const uiSchema = {
|
|||
},
|
||||
};
|
||||
|
||||
export const defaultFormData = {
|
||||
export const defaultFormData: Template = {
|
||||
dispatcher: 'state = reducers(${prevState}, ${action});',
|
||||
assertion: 't.deepEqual(state, ${curState});',
|
||||
wrap: `test('reducers', (t) => {
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
export interface DispatcherLocals {
|
||||
action: string | undefined;
|
||||
prevState: string | undefined;
|
||||
}
|
||||
|
||||
export interface AssertionLocals {
|
||||
path: string;
|
||||
curState: number | string | undefined;
|
||||
}
|
||||
|
||||
export interface WrapLocals {
|
||||
name: string | undefined;
|
||||
assertions: string;
|
||||
actionName?: string;
|
||||
initialState?: string | undefined;
|
||||
}
|
||||
|
||||
export interface Template {
|
||||
name: string;
|
||||
name?: string;
|
||||
dispatcher: string;
|
||||
assertion: string;
|
||||
wrap: string;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Ava template';
|
||||
|
||||
export const dispatcher = ({ action }) => `${action};`;
|
||||
export const dispatcher = ({ action }: DispatcherLocals) => `${action!};`;
|
||||
|
||||
export const assertion = ({ path, curState }) =>
|
||||
`t.deepEqual(state${path}, ${curState});`;
|
||||
export const assertion = ({ path, curState }: AssertionLocals) =>
|
||||
`t.deepEqual(state${path}, ${curState!});`;
|
||||
|
||||
export const wrap = ({ name, initialState, assertions }) =>
|
||||
export const wrap = ({ name, initialState, assertions }: WrapLocals) =>
|
||||
`import test from 'ava';
|
||||
import ${name} from '../../stores/${name}';
|
||||
import ${name!} from '../../stores/${name!}';
|
||||
|
||||
test('${name}', (t) => {
|
||||
const store = new ${name}(${initialState});
|
||||
test('${name!}', (t) => {
|
||||
const store = new ${name!}(${initialState!});
|
||||
${assertions}
|
||||
});
|
||||
`;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Mocha template';
|
||||
|
||||
export const dispatcher = ({ action }) => `${action};`;
|
||||
export const dispatcher = ({ action }: DispatcherLocals) => `${action!};`;
|
||||
|
||||
export const assertion = ({ path, curState }) =>
|
||||
`expect(store${path}).toEqual(${curState});`;
|
||||
export const assertion = ({ path, curState }: AssertionLocals) =>
|
||||
`expect(store${path}).toEqual(${curState!});`;
|
||||
|
||||
export const wrap = ({ name, initialState, assertions }) =>
|
||||
export const wrap = ({ name, initialState, assertions }: WrapLocals) =>
|
||||
`import expect from 'expect';
|
||||
import ${name} from '../../stores/${name}';
|
||||
import ${name!} from '../../stores/${name!}';
|
||||
|
||||
test('${name}', (t) => {
|
||||
const store = new ${name}(${initialState});
|
||||
test('${name!}', (t) => {
|
||||
const store = new ${name!}(${initialState!});
|
||||
${assertions}
|
||||
});
|
||||
`;
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Mocha template';
|
||||
|
||||
export const dispatcher = ({ action }) => `${action};`;
|
||||
export const dispatcher = ({ action }: DispatcherLocals) => `${action!};`;
|
||||
|
||||
export const assertion = ({ path, curState }) =>
|
||||
`expect(store${path}).toEqual(${curState});`;
|
||||
export const assertion = ({ path, curState }: AssertionLocals) =>
|
||||
`expect(store${path}).toEqual(${curState!});`;
|
||||
|
||||
export const wrap = ({ name, actionName, initialState, assertions }) =>
|
||||
export const wrap = ({
|
||||
name,
|
||||
actionName,
|
||||
initialState,
|
||||
assertions,
|
||||
}: WrapLocals) =>
|
||||
`import expect from 'expect';
|
||||
import ${name} from '../../stores/${name}';
|
||||
import ${name!} from '../../stores/${name!}';
|
||||
|
||||
describe('${name}', () => {
|
||||
it('${actionName}', () => {
|
||||
const store = new ${name}(${initialState});
|
||||
describe('${name!}', () => {
|
||||
it('${actionName!}', () => {
|
||||
const store = new ${name!}(${initialState!});
|
||||
${assertions}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { AssertionLocals, DispatcherLocals, WrapLocals } from '../../types';
|
||||
|
||||
export const name = 'Tape template';
|
||||
|
||||
export const dispatcher = ({ action }) => `${action};`;
|
||||
export const dispatcher = ({ action }: DispatcherLocals) => `${action!};`;
|
||||
|
||||
export const assertion = ({ path, curState }) =>
|
||||
`t.deepEqual(state${path}, ${curState});`;
|
||||
export const assertion = ({ path, curState }: AssertionLocals) =>
|
||||
`t.deepEqual(state${path}, ${curState!});`;
|
||||
|
||||
export const wrap = ({ name, initialState, assertions }) =>
|
||||
export const wrap = ({ name, initialState, assertions }: WrapLocals) =>
|
||||
`import test from 'tape';
|
||||
import ${name} from '../../stores/${name}';
|
||||
import ${name!} from '../../stores/${name!}';
|
||||
|
||||
test('${name}', (t) => {
|
||||
const store = new ${name}(${initialState});
|
||||
test('${name!}', (t) => {
|
||||
const store = new ${name!}(${initialState!});
|
||||
${assertions}
|
||||
t.end();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user