2018-12-23 03:13:56 +03:00
|
|
|
import React, { PureComponent, Component } 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 es6template from 'es6template';
|
|
|
|
import { Editor } from 'devui';
|
|
|
|
|
2020-08-08 23:26:39 +03:00
|
|
|
export const fromPath = (path) =>
|
|
|
|
path.map((a) => (typeof a === 'string' ? `.${a}` : `[${a}]`)).join('');
|
2018-12-23 03:13:56 +03:00
|
|
|
|
|
|
|
function getState(s, 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);
|
|
|
|
|
|
|
|
if (type === 'remove-item' || type === 'move-item') {
|
|
|
|
if (paths.length && paths.indexOf(path) !== -1) return;
|
|
|
|
paths.push(path);
|
|
|
|
const v = objectPath.get(s2.state, newPath);
|
|
|
|
curState = v.length;
|
|
|
|
path += '.length';
|
|
|
|
} else if (type === 'add-item') {
|
|
|
|
generate({ type: 'move-item', newPath });
|
|
|
|
path += `[${newIndex}]`;
|
|
|
|
curState = stringify(newValue);
|
|
|
|
} else {
|
|
|
|
curState = stringify(newValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
// console.log(`expect(store${path}).toEqual(${curState});`);
|
|
|
|
cb({ path, curState });
|
|
|
|
}
|
|
|
|
|
2019-01-10 21:51:14 +03:00
|
|
|
diff(
|
|
|
|
getState(s1, defaultValue),
|
|
|
|
getState(s2, defaultValue) /* , { idProp: '*' } */
|
|
|
|
).forEach(generate);
|
2018-12-23 03:13:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export default class TestGenerator extends (PureComponent || Component) {
|
|
|
|
getMethod(action) {
|
|
|
|
let type = action.type;
|
|
|
|
if (type[0] === '┗') type = type.substr(1).trim();
|
|
|
|
let args = action.arguments;
|
2020-08-08 23:26:39 +03:00
|
|
|
if (args) args = args.map((arg) => stringify(arg)).join(',');
|
2018-12-23 03:13:56 +03:00
|
|
|
else args = '';
|
|
|
|
return `${type}(${args})`;
|
|
|
|
}
|
|
|
|
|
|
|
|
getAction(action) {
|
|
|
|
if (action.type === '@@INIT') return '{}';
|
|
|
|
return stringify(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
generateTest() {
|
|
|
|
const {
|
2019-01-10 21:51:14 +03:00
|
|
|
computedStates,
|
|
|
|
actions,
|
|
|
|
selectedActionId,
|
|
|
|
startActionId,
|
|
|
|
isVanilla,
|
2020-08-08 23:26:39 +03:00
|
|
|
name,
|
2018-12-23 03:13:56 +03:00
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
if (!actions || !computedStates || computedStates.length < 1) return '';
|
|
|
|
|
|
|
|
let { wrap, assertion, dispatcher, indentation } = this.props;
|
2019-01-10 21:51:14 +03:00
|
|
|
if (typeof assertion === 'string')
|
|
|
|
assertion = es6template.compile(assertion);
|
2018-12-23 03:13:56 +03:00
|
|
|
if (typeof wrap === 'string') {
|
|
|
|
const ident = wrap.match(/\n.+\$\{assertions}/);
|
|
|
|
if (ident) indentation = ident[0].length - 13;
|
|
|
|
wrap = es6template.compile(wrap);
|
|
|
|
}
|
2019-01-10 21:51:14 +03:00
|
|
|
if (typeof dispatcher === 'string')
|
|
|
|
dispatcher = es6template.compile(dispatcher);
|
2018-12-23 03:13:56 +03:00
|
|
|
|
|
|
|
let space = '';
|
|
|
|
if (indentation) space = Array(indentation).join(' ');
|
|
|
|
|
|
|
|
let r = '';
|
|
|
|
let isFirst = true;
|
|
|
|
let i;
|
|
|
|
if (startActionId !== null) i = startActionId;
|
|
|
|
else if (selectedActionId !== null) i = selectedActionId;
|
|
|
|
else i = computedStates.length - 1;
|
|
|
|
const startIdx = i > 0 ? i : 1;
|
|
|
|
|
|
|
|
const addAssertions = ({ path, curState }) => {
|
|
|
|
r += space + assertion({ path, curState }) + '\n';
|
|
|
|
};
|
|
|
|
|
|
|
|
while (actions[i]) {
|
2019-01-10 21:51:14 +03:00
|
|
|
if (
|
|
|
|
!isVanilla ||
|
|
|
|
/* eslint-disable-next-line no-useless-escape */
|
|
|
|
/^┗?\s?[a-zA-Z0-9_@.\[\]-]+?$/.test(actions[i].action.type)
|
|
|
|
) {
|
2018-12-23 03:13:56 +03:00
|
|
|
if (isFirst) isFirst = false;
|
|
|
|
else r += space;
|
|
|
|
if (!isVanilla || actions[i].action.type[0] !== '@') {
|
2019-01-10 21:51:14 +03:00
|
|
|
r +=
|
|
|
|
dispatcher({
|
|
|
|
action: !isVanilla
|
|
|
|
? this.getAction(actions[i].action)
|
|
|
|
: this.getMethod(actions[i].action),
|
|
|
|
prevState:
|
2020-08-08 23:26:39 +03:00
|
|
|
i > 0 ? stringify(computedStates[i - 1].state) : undefined,
|
2019-01-10 21:51:14 +03:00
|
|
|
}) + '\n';
|
2018-12-23 03:13:56 +03:00
|
|
|
}
|
|
|
|
if (!isVanilla) {
|
2019-01-10 21:51:14 +03:00
|
|
|
addAssertions({
|
|
|
|
path: '',
|
2020-08-08 23:26:39 +03:00
|
|
|
curState: stringify(computedStates[i].state),
|
2019-01-10 21:51:14 +03:00
|
|
|
});
|
2018-12-23 03:13:56 +03:00
|
|
|
} else {
|
2019-01-10 21:51:14 +03:00
|
|
|
compare(
|
|
|
|
computedStates[i - 1],
|
|
|
|
computedStates[i],
|
|
|
|
addAssertions,
|
|
|
|
isVanilla && {}
|
|
|
|
);
|
2018-12-23 03:13:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
if (i > selectedActionId) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = r.trim();
|
|
|
|
if (wrap) {
|
|
|
|
if (!isVanilla) r = wrap({ name, assertions: r });
|
|
|
|
else {
|
|
|
|
r = wrap({
|
|
|
|
name: /^[a-zA-Z0-9_-]+?$/.test(name) ? name : 'Store',
|
2019-01-10 21:51:14 +03:00
|
|
|
actionName:
|
|
|
|
(selectedActionId === null || selectedActionId > 0) &&
|
|
|
|
actions[startIdx]
|
|
|
|
? actions[startIdx].action.type.replace(/[^a-zA-Z0-9_-]+/, '')
|
|
|
|
: 'should return the initial state',
|
2018-12-23 03:13:56 +03:00
|
|
|
initialState: stringify(computedStates[startIdx - 1].state),
|
2020-08-08 23:26:39 +03:00
|
|
|
assertions: r,
|
2018-12-23 03:13:56 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const code = this.generateTest();
|
|
|
|
|
|
|
|
if (!this.props.useCodemirror) {
|
|
|
|
return (
|
|
|
|
<textarea
|
|
|
|
style={{ padding: '10px', width: '100%', height: '100%' }}
|
|
|
|
defaultValue={code}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return <Editor value={code} />;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TestGenerator.propTypes = {
|
|
|
|
name: PropTypes.string,
|
|
|
|
isVanilla: PropTypes.bool,
|
|
|
|
computedStates: PropTypes.array,
|
|
|
|
actions: PropTypes.object,
|
|
|
|
selectedActionId: PropTypes.number,
|
|
|
|
startActionId: PropTypes.number,
|
2019-01-10 21:51:14 +03:00
|
|
|
wrap: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
|
|
|
dispatcher: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
|
|
|
assertion: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
2018-12-23 03:13:56 +03:00
|
|
|
useCodemirror: PropTypes.bool,
|
|
|
|
indentation: PropTypes.number,
|
2020-08-08 23:26:39 +03:00
|
|
|
header: PropTypes.element,
|
2018-12-23 03:13:56 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
TestGenerator.defaultProps = {
|
|
|
|
useCodemirror: true,
|
|
|
|
selectedActionId: null,
|
2020-08-08 23:26:39 +03:00
|
|
|
startActionId: null,
|
2018-12-23 03:13:56 +03:00
|
|
|
};
|