From 431be9a0c03033675a517a62db686e4fe81f51f6 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Fri, 1 Mar 2019 18:16:16 +0000 Subject: [PATCH 01/16] Extract jest-each array table logic --- packages/jest-each/src/bind.ts | 58 +++------------------------ packages/jest-each/src/table/array.ts | 50 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 53 deletions(-) create mode 100644 packages/jest-each/src/table/array.ts diff --git a/packages/jest-each/src/bind.ts b/packages/jest-each/src/bind.ts index 173c26e159c1..fe7cc58d5bfa 100644 --- a/packages/jest-each/src/bind.ts +++ b/packages/jest-each/src/bind.ts @@ -6,23 +6,16 @@ * */ -import util from 'util'; import chalk from 'chalk'; import pretty from 'pretty-format'; import {isPrimitive} from 'jest-get-type'; import {ErrorWithStack} from 'jest-util'; -type Table = Array>; -type PrettyArgs = { - args: Array; - title: string; -}; +// TODO: renmae this +import arrayTableToX from './table/array'; const EXPECTED_COLOR = chalk.green; const RECEIVED_COLOR = chalk.red; -const SUPPORTED_PLACEHOLDERS = /%[sdifjoOp%]/g; -const PRETTY_PLACEHOLDER = '%p'; -const INDEX_PLACEHOLDER = '%#'; export default (cb: Function, supportsDone: boolean = true) => (...args: any) => function eachBind(title: string, test: Function, timeout?: number): void { @@ -72,13 +65,10 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => throw error; }); } - const table: Table = tableArg.every(Array.isArray) - ? tableArg - : tableArg.map(entry => [entry]); - return table.forEach((row, i) => + return arrayTableToX(title, tableArg).forEach(row => cb( - arrayFormat(title, i, ...row), - applyRestParams(supportsDone, row, test), + row.title, + applyRestParams(supportsDone, row.arguments, test), timeout, ), ); @@ -126,44 +116,6 @@ const isEmptyTable = (table: Array) => table.length === 0; const isEmptyString = (str: string) => typeof str === 'string' && str.trim() === ''; -const getPrettyIndexes = (placeholders: RegExpMatchArray) => - placeholders.reduce((indexes: Array, placeholder, index) => { - if (placeholder === PRETTY_PLACEHOLDER) { - indexes.push(index); - } - return indexes; - }, []); - -const arrayFormat = (title: string, rowIndex: number, ...args: Array) => { - const placeholders = title.match(SUPPORTED_PLACEHOLDERS) || []; - const prettyIndexes = getPrettyIndexes(placeholders); - - const {title: prettyTitle, args: remainingArgs} = args.reduce( - (acc: PrettyArgs, arg, index) => { - if (prettyIndexes.indexOf(index) !== -1) { - return { - args: acc.args, - title: acc.title.replace( - PRETTY_PLACEHOLDER, - pretty(arg, {maxDepth: 1, min: true}), - ), - }; - } - - return { - args: acc.args.concat([arg]), - title: acc.title, - }; - }, - {args: [], title}, - ); - - return util.format( - prettyTitle.replace(INDEX_PLACEHOLDER, rowIndex.toString()), - ...remainingArgs.slice(0, placeholders.length - prettyIndexes.length), - ); -}; - type Done = () => {}; const applyRestParams = ( diff --git a/packages/jest-each/src/table/array.ts b/packages/jest-each/src/table/array.ts new file mode 100644 index 000000000000..524ce21ef32b --- /dev/null +++ b/packages/jest-each/src/table/array.ts @@ -0,0 +1,50 @@ +import util from 'util'; +import pretty from 'pretty-format'; + +type Col = unknown; +type Row = Array; +type Table = Array; +type ArrayTable = Table | Row; + +// TODO: rename X and maybe arguments +type X = Array<{ + title: string; + arguments: Array; +}>; + +const SUPPORTED_PLACEHOLDERS = /%[sdifjoOp%]/g; +const PRETTY_PLACEHOLDER = '%p'; +const INDEX_PLACEHOLDER = '%#'; + +export default (title: string, arrayTable: ArrayTable): X => + normaliseTable(arrayTable).map((row, index) => ({ + title: formatTitle(title, row, index), + arguments: row, + })); + +const normaliseTable = (table: ArrayTable): Table => + isTable(table) ? (table as Table) : (table as Row).map(colToRow); + +const isTable = (table: ArrayTable): boolean => table.every(Array.isArray); + +const colToRow = (col: Col): Row => [col]; + +const formatTitle = (title: string, row: Row, rowIndex: number): string => + row.reduce((formattedTitle, value) => { + const [placeholder] = getMatchingPlaceholders(formattedTitle); + if (!placeholder) return formattedTitle; + + if (placeholder === PRETTY_PLACEHOLDER) + return interpolatePrettyPlaceholder(formattedTitle, value); + + return util.format(formattedTitle, value); + }, interpolateTitleIndex(title, rowIndex)); + +const getMatchingPlaceholders = (title: string) => + title.match(SUPPORTED_PLACEHOLDERS) || []; + +const interpolateTitleIndex = (title: string, index: number) => + title.replace(INDEX_PLACEHOLDER, index.toString()); + +const interpolatePrettyPlaceholder = (title: string, value: unknown) => + title.replace(PRETTY_PLACEHOLDER, pretty(value, {maxDepth: 1, min: true})); From e0ef99d5a8f36957302db8176ef78ce488b0c961 Mon Sep 17 00:00:00 2001 From: Matt Phillips Date: Fri, 1 Mar 2019 18:16:24 +0000 Subject: [PATCH 02/16] Extract jest-each template table logic --- packages/jest-each/src/bind.ts | 65 ++------------------- packages/jest-each/src/table/template.ts | 74 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 60 deletions(-) create mode 100644 packages/jest-each/src/table/template.ts diff --git a/packages/jest-each/src/bind.ts b/packages/jest-each/src/bind.ts index fe7cc58d5bfa..66d51b108c58 100644 --- a/packages/jest-each/src/bind.ts +++ b/packages/jest-each/src/bind.ts @@ -8,11 +8,11 @@ import chalk from 'chalk'; import pretty from 'pretty-format'; -import {isPrimitive} from 'jest-get-type'; import {ErrorWithStack} from 'jest-util'; // TODO: renmae this import arrayTableToX from './table/array'; +import templateTableToX from './table/template'; const EXPECTED_COLOR = chalk.green; const RECEIVED_COLOR = chalk.red; @@ -68,7 +68,7 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => return arrayTableToX(title, tableArg).forEach(row => cb( row.title, - applyRestParams(supportsDone, row.arguments, test), + applyArguments(supportsDone, row.arguments, test), timeout, ), ); @@ -78,7 +78,6 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => const data = args.slice(1); const keys = getHeadingKeys(templateStrings[0]); - const table = buildTable(data, keys.length, keys); const missingData = data.length % keys.length; @@ -102,12 +101,8 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => }); } - return table.forEach(row => - cb( - interpolate(title, row), - applyObjectParams(supportsDone, row, test), - timeout, - ), + return templateTableToX(title, keys, data).forEach(row => + cb(row.title, applyArguments(supportsDone, row.arguments, test), timeout), ); }; @@ -118,7 +113,7 @@ const isEmptyString = (str: string) => type Done = () => {}; -const applyRestParams = ( +const applyArguments = ( supportsDone: boolean, params: Array, test: Function, @@ -130,55 +125,5 @@ const applyRestParams = ( const getHeadingKeys = (headings: string): Array => headings.replace(/\s/g, '').split('|'); -const buildTable = ( - data: Array, - rowSize: number, - keys: Array, -): Array => - Array.from({length: data.length / rowSize}) - .map((_, index) => data.slice(index * rowSize, index * rowSize + rowSize)) - .map(row => - row.reduce( - (acc, value, index) => Object.assign(acc, {[keys[index]]: value}), - {}, - ), - ); - -const getMatchingKeyPaths = (title: string) => ( - matches: Array, - key: string, -) => matches.concat(title.match(new RegExp(`\\$${key}[\\.\\w]*`, 'g')) || []); - -const replaceKeyPathWithValue = (data: any) => ( - title: string, - match: string, -) => { - const keyPath = match.replace('$', '').split('.'); - const value = getPath(data, keyPath); - - if (isPrimitive(value)) { - return title.replace(match, String(value)); - } - return title.replace(match, pretty(value, {maxDepth: 1, min: true})); -}; - -const interpolate = (title: string, data: any) => - Object.keys(data) - .reduce(getMatchingKeyPaths(title), []) // aka flatMap - .reduce(replaceKeyPathWithValue(data), title); - -const applyObjectParams = (supportsDone: boolean, obj: any, test: Function) => - supportsDone && test.length > 1 - ? (done: Done) => test(obj, done) - : () => test(obj); - const pluralize = (word: string, count: number) => word + (count === 1 ? '' : 's'); - -const getPath = ( - o: {[key: string]: any}, - [head, ...tail]: Array, -): any => { - if (!head || !o.hasOwnProperty || !o.hasOwnProperty(head)) return o; - return getPath(o[head], tail); -}; diff --git a/packages/jest-each/src/table/template.ts b/packages/jest-each/src/table/template.ts new file mode 100644 index 000000000000..394bfff7618b --- /dev/null +++ b/packages/jest-each/src/table/template.ts @@ -0,0 +1,74 @@ +import pretty from 'pretty-format'; +import {isPrimitive} from 'jest-get-type'; + +type Col = unknown; +type Row = Array; +type Table = Array; +type Template = {[key: string]: unknown}; +type Templates = Array