UNPKG

lazy-jest

Version:

Jest util for lazy person like me to generate mass test snapshots with few configurations.

205 lines (195 loc) 5.39 kB
// @flow const enumerateArrayCases = require('./caseGenerator/enumerateArrayCases'); /** @module testFunction */ const formatArgCaseDesc = (args) => { const strArgs = args.map(arg => { if (typeof arg === 'undefined') { return 'undefined'; } if (typeof arg === 'string') { return `"${arg}"`; } if ((Array.isArray(arg), typeof arg === 'object')) { return JSON.stringify(arg); } return arg.toString(); }); return `- (${strArgs.join(',')})`; }; const mayThrowWrapper = async (func, args = []) => { try { const ret = func.apply(null, args); if (ret.then) { return await ret; } return ret; } catch (e) { console.warn(e); return 'error'; } }; const testNoArgs = (func) => { test('- ()', async () => { const ret = await mayThrowWrapper(func); expect(ret).toMatchSnapshot(); }); }; const testArgList = (func, argsList = []) => { test.each(argsList.map(args => [args]))( '- %j', async (args) => { const ret = await mayThrowWrapper(func, args); expect(ret).toMatchSnapshot(); } ); } /** * * @param {Function} func * @param {Array.<Array.<*>>} [argsCases] * @param {string} [testCaption] * @example * testFunction(targetFunction, [ * [1, 2], * [0, -1], * [0], * [] * ], 'custom combination test of targetFunction()'); */ const testFunction = ( func, argsCases = [], testCaption = `${func.name || 'function'}()` ) => { if (!argsCases) { return; } describe(testCaption, () => { if (argsCases.length) { testArgList(func, argsCases); } else { testNoArgs(func); } }); }; /** * Test function with given argument configurations by using invalid case for one of the argument, and first valid case for others. * This process will go through all arguments in config list, and all the invalid cases. * @private * @param {Function} func Target function * @param {ArgConfig[]} argsConfig */ const testInvalidArgs = (func, argsConfig) => { const argNames = argsConfig.map(conf => conf.name); const testConfigs = argsConfig.map((conf, i) => { const cases = enumerateArrayCases(argsConfig, i); return cases && cases.length ? [ `argument <${conf.name}> is invalid`, cases ] : null; }).filter(conf => !!conf); if (testConfigs.length) { describe.each(testConfigs)( '%s', (_name, cases) => { testArgList(func, cases); } ); } }; /** * Test function with all combinations of valid arguments. * @private * @param {Function} func Target function * @param {ArgConfig[]} argsConfig */ const testValidArgs = (func, argsConfig) => { const cases = enumerateArrayCases(argsConfig); if (cases.length) { testArgList(func, cases); } else { testNoArgs(func); } }; const getArgNames = (argsConfig) => argsConfig.map(conf => conf.name); /** * Catch snapshot of a function. It will do following tests: * - If no compulsory arguments defined, it will test empty argument case * - Test compulsory arguments first, and add in optional argument test cases one by one * - Test invalid cases by using arguments with one invalid argument, and first valid case for others * - Test all valid combinations of arguments * @param {Function} func Target function * @param {Args|ArgConfig[]} argsConfig * @param {string} [testCaption] Test description * @example * const emptyFunc = () => {}; * enumerateArgsTestFunction(emptyFunc); * * const simpleFunc = (param) => param; * enumerateArgsTestFunction( * simpleFunc, * configArgs().arg('param', [1], { invalidCases: [0] }) * ]); * * const funcHasOptional = (param, opts) => opts || param; * enumerateArgsTestFunction( * funcHasOpts, * configArgs() * .arg('param', [1], { invalidCases: [0] }) * .arg('opts', [2], { invalidCases: [0], optional: true }) * ); * * const complexFunc = ({ param, opts }) => opts || param; * enumerateArgsTestFunction( * funcHasOpts, * configArgs().objectArg( * 'param', * configArgs() * .arg('param', [1], { invalidCases: [0] }) * .arg('opts', [2], { invalidCases: [0], optional: true }) * ) * ); */ const enumerateArgsTestFunction = ( func, argsConfig, testCaption = `${func.name || 'function'}()` ) => { const CONF = Array.isArray(argsConfig) ? argsConfig : argsConfig.value; let optionalStartIndex = CONF.length; for ( let i = CONF.length; --i && CONF[i] && CONF[i].optional; ) { optionalStartIndex = i; } const optionalArgs = CONF.slice(optionalStartIndex); const compulsoryArgs = CONF.slice(0, optionalStartIndex); const testConfig = optionalArgs.reduce((config, optionalArg) => { const [lastArgs, last] = config[config.length - 1]; const newArgs = last.concat(optionalArg); return [ ...config, [getArgNames(newArgs), newArgs] ]; }, [[getArgNames(compulsoryArgs), compulsoryArgs]]); describe(testCaption, () => { if (testConfig.length) { describe.each(testConfig)( '- (%p)', (_argNames, config) => { if (config.length) { testInvalidArgs(func, config); testValidArgs(func, config); } else { testNoArgs(func); } } ); } else { testNoArgs(func); } }); }; module.exports = { testFunction, enumerateArgsTestFunction };