Skip to content

Commit

Permalink
Extract validation
Browse files Browse the repository at this point in the history
  • Loading branch information
mattphillips committed Mar 1, 2019
1 parent 880c655 commit e35fee6
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 71 deletions.
95 changes: 24 additions & 71 deletions packages/jest-each/src/bind.ts
Expand Up @@ -7,15 +7,11 @@
*/

import {DoneFn, ItBase} from '@jest/types/build/Global';
import chalk from 'chalk';
import pretty from 'pretty-format';
import {ErrorWithStack} from 'jest-util';

import convertArrayTable from './table/array';
import convertTemplateTable from './table/template';

const EXPECTED_COLOR = chalk.green;
const RECEIVED_COLOR = chalk.red;
import {validateArrayTable, validateTemplateTableHeadings} from './validation';

type EachTestFn = (...args: any[]) => Promise<any> | void | undefined;

Expand All @@ -32,87 +28,50 @@ export type EachTests = Array<{
arguments: Array<unknown>;
}>;

type Result = EachTests | string;

// TODO: update cb to ItBase | DescribeBase and deprecate supportsDone
export default (cb: ItBase, supportsDone: boolean = true) => (
table: EachTable,
...taggedTemplateData: TemplateData
) =>
function eachBind(title: string, test: EachTestFn, timeout?: number): void {
const result = isArrayTable(taggedTemplateData)
? buildArrayTests(title, table)
: buildTemplateTests(title, table, taggedTemplateData);

if (typeof result === 'string') {
const error = new ErrorWithStack(result, eachBind);
try {
const tests = isArrayTable(taggedTemplateData)
? buildArrayTests(title, table)
: buildTemplateTests(title, table, taggedTemplateData);

return tests.forEach(row =>
cb(
row.title,
applyArguments(supportsDone, row.arguments, test),
timeout,
),
);
} catch (e) {
const error = new ErrorWithStack(e, eachBind);
return cb(title, () => {
throw error;
});
}

return result.forEach(row =>
cb(row.title, applyArguments(supportsDone, row.arguments, test), timeout),
);
};

const isArrayTable = (data: TemplateData) => data.length === 0;

const buildArrayTests = (title: string, table: EachTable): Result => {
if (!Array.isArray(table)) {
return (
'`.each` must be called with an Array or Tagged Template Literal.\n\n' +
`Instead was called with: ${pretty(table, {
maxDepth: 1,
min: true,
})}\n`
);
}

if (isTaggedTemplateLiteral(table)) {
if (isEmptyString(table[0])) {
return 'Error: `.each` called with an empty Tagged Template Literal of table data.\n';
}

return 'Error: `.each` called with a Tagged Template Literal with no data, remember to interpolate with ${expression} syntax.\n';
}

if (isEmptyTable(table)) {
return 'Error: `.each` called with an empty Array of table data.\n';
}
return convertArrayTable(title, table);
const buildArrayTests = (title: string, table: EachTable): EachTests => {
validateArrayTable(table);
return convertArrayTable(title, table as ArrayTable);
};

const buildTemplateTests = (
title: string,
table: EachTable,
taggedTemplateData: TemplateData,
): Result => {
const keys = getHeadingKeys(table[0] as string);
const missingData = taggedTemplateData.length % keys.length;

if (missingData > 0) {
return (
'Not enough arguments supplied for given headings:\n' +
EXPECTED_COLOR(keys.join(' | ')) +
'\n\n' +
'Received:\n' +
RECEIVED_COLOR(pretty(taggedTemplateData)) +
'\n\n' +
`Missing ${RECEIVED_COLOR(missingData.toString())} ${pluralize(
'argument',
missingData,
)}`
);
}

return convertTemplateTable(title, keys, taggedTemplateData);
): EachTests => {
const headings = getHeadingKeys(table[0] as string);
validateTemplateTableHeadings(headings, taggedTemplateData);
return convertTemplateTable(title, headings, taggedTemplateData);
};

const isTaggedTemplateLiteral = (array: any) => array.raw !== undefined;
const isEmptyTable = (table: Array<any>) => table.length === 0;
const isEmptyString = (str: string | unknown) =>
typeof str === 'string' && str.trim() === '';
const getHeadingKeys = (headings: string): Array<string> =>
headings.replace(/\s/g, '').split('|');

const applyArguments = (
supportsDone: boolean,
Expand All @@ -122,9 +81,3 @@ const applyArguments = (
supportsDone && params.length < test.length
? (done: DoneFn) => test(...params, done)
: () => test(...params);

const getHeadingKeys = (headings: string): Array<string> =>
headings.replace(/\s/g, '').split('|');

const pluralize = (word: string, count: number) =>
word + (count === 1 ? '' : 's');
56 changes: 56 additions & 0 deletions packages/jest-each/src/validation.ts
@@ -0,0 +1,56 @@
import chalk from 'chalk';
import pretty from 'pretty-format';
import {TemplateData} from './bind';

const EXPECTED_COLOR = chalk.green;
const RECEIVED_COLOR = chalk.red;

export const validateArrayTable = (table: any) => {
if (!Array.isArray(table)) {
throw '`.each` must be called with an Array or Tagged Template Literal.\n\n' +
`Instead was called with: ${pretty(table, {
maxDepth: 1,
min: true,
})}\n`;
}

if (isTaggedTemplateLiteral(table)) {
if (isEmptyString(table[0])) {
throw 'Error: `.each` called with an empty Tagged Template Literal of table data.\n';
}

throw 'Error: `.each` called with a Tagged Template Literal with no data, remember to interpolate with ${expression} syntax.\n';
}

if (isEmptyTable(table)) {
throw 'Error: `.each` called with an empty Array of table data.\n';
}
};

const isTaggedTemplateLiteral = (array: any) => array.raw !== undefined;
const isEmptyTable = (table: Array<any>) => table.length === 0;
const isEmptyString = (str: string | unknown) =>
typeof str === 'string' && str.trim() === '';

export const validateTemplateTableHeadings = (
headings: Array<string>,
data: TemplateData,
) => {
const missingData = data.length % headings.length;

if (missingData > 0) {
throw 'Not enough arguments supplied for given headings:\n' +
EXPECTED_COLOR(headings.join(' | ')) +
'\n\n' +
'Received:\n' +
RECEIVED_COLOR(pretty(data)) +
'\n\n' +
`Missing ${RECEIVED_COLOR(missingData.toString())} ${pluralize(
'argument',
missingData,
)}`;
}
};

const pluralize = (word: string, count: number) =>
word + (count === 1 ? '' : 's');

0 comments on commit e35fee6

Please sign in to comment.