From db6fa39aecf44c83ded34d303f755e86acfa3f4c Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 15:15:37 -0500 Subject: [PATCH 01/11] Add maxWidth configuration option --- packages/jest-schemas/src/index.ts | 1 + packages/pretty-format/src/index.ts | 5 +++++ packages/pretty-format/src/types.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/packages/jest-schemas/src/index.ts b/packages/jest-schemas/src/index.ts index b3f0a32eebe8..d0f6f4010165 100644 --- a/packages/jest-schemas/src/index.ts +++ b/packages/jest-schemas/src/index.ts @@ -15,6 +15,7 @@ const RawSnapshotFormat = Type.Partial( highlight: Type.Readonly(Type.Boolean()), indent: Type.Readonly(Type.Number({minimum: 0})), maxDepth: Type.Readonly(Type.Number({minimum: 0})), + maxWidth: Type.Readonly(Type.Number({minimum: 0})), min: Type.Readonly(Type.Boolean()), printBasicPrototype: Type.Readonly(Type.Boolean()), printFunctionName: Type.Readonly(Type.Boolean()), diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index 9543e6b354a8..329b2d67f9c3 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -404,6 +404,7 @@ export const DEFAULT_OPTIONS: Options = { highlight: false, indent: 2, maxDepth: Infinity, + maxWidth: Infinity, min: false, plugins: [], printBasicPrototype: true, @@ -506,6 +507,10 @@ const getConfig = (options?: OptionsReceived): Config => ({ options && options.maxDepth !== undefined ? options.maxDepth : DEFAULT_OPTIONS.maxDepth, + maxWidth: + options && options.maxWidth !== undefined + ? options.maxWidth + : DEFAULT_OPTIONS.maxWidth, min: options && options.min !== undefined ? options.min : DEFAULT_OPTIONS.min, plugins: options && options.plugins !== undefined diff --git a/packages/pretty-format/src/types.ts b/packages/pretty-format/src/types.ts index c26b77aee0f7..62e9a0b917ff 100644 --- a/packages/pretty-format/src/types.ts +++ b/packages/pretty-format/src/types.ts @@ -45,6 +45,7 @@ export type Config = { escapeString: boolean; indent: string; maxDepth: number; + maxWidth: number; min: boolean; plugins: Plugins; printBasicPrototype: boolean; From 2778b5581d939a3d089f972feaa2019defc87102 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 15:15:49 -0500 Subject: [PATCH 02/11] Add (failing) test for customized maxWidth --- .../src/__tests__/prettyFormat.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/pretty-format/src/__tests__/prettyFormat.test.ts b/packages/pretty-format/src/__tests__/prettyFormat.test.ts index ab9d0b9da214..873bd2a6a009 100644 --- a/packages/pretty-format/src/__tests__/prettyFormat.test.ts +++ b/packages/pretty-format/src/__tests__/prettyFormat.test.ts @@ -562,6 +562,22 @@ describe('prettyFormat()', () => { ); }); + it('can customize the max width', () => { + const val = Array(1_000_000).fill('x'); + expect(prettyFormat(val, {maxWidth: 5})).toEqual( + [ + 'Array [', + ' "x",', + ' "x",', + ' "x",', + ' "x",', + ' "x",', + ' ...', + ']', + ].join('\n'), + ); + }); + it('can customize the max depth', () => { const val = [ { From 8e93397a4b6cb12317953205a7393a0b8f92a152 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 15:30:35 -0500 Subject: [PATCH 03/11] Exit printListItems early if maxWidth exceeded --- packages/pretty-format/src/collections.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/pretty-format/src/collections.ts b/packages/pretty-format/src/collections.ts index 97fae10163ba..54091c90b428 100644 --- a/packages/pretty-format/src/collections.ts +++ b/packages/pretty-format/src/collections.ts @@ -147,6 +147,11 @@ export function printListItems( for (let i = 0; i < list.length; i++) { result += indentationNext; + if (i === config.maxWidth) { + result += '...'; + break; + } + if (i in list) { result += printer(list[i], config, indentationNext, depth, refs); } From 9fc0e9e6173f40c40bfbb099c3e11a9a86b6d92a Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 16:03:54 -0500 Subject: [PATCH 04/11] Support sets --- .../src/__tests__/prettyFormat.test.ts | 39 ++++++++++++------- packages/pretty-format/src/collections.ts | 12 ++++-- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/pretty-format/src/__tests__/prettyFormat.test.ts b/packages/pretty-format/src/__tests__/prettyFormat.test.ts index 873bd2a6a009..8364ef61534f 100644 --- a/packages/pretty-format/src/__tests__/prettyFormat.test.ts +++ b/packages/pretty-format/src/__tests__/prettyFormat.test.ts @@ -562,20 +562,31 @@ describe('prettyFormat()', () => { ); }); - it('can customize the max width', () => { - const val = Array(1_000_000).fill('x'); - expect(prettyFormat(val, {maxWidth: 5})).toEqual( - [ - 'Array [', - ' "x",', - ' "x",', - ' "x",', - ' "x",', - ' "x",', - ' ...', - ']', - ].join('\n'), - ); + describe('maxWidth option', () => { + it('applies to arrays', () => { + const val = Array(1_000_000).fill('x'); + expect(prettyFormat(val, {maxWidth: 5})).toEqual( + [ + 'Array [', + ' "x",', + ' "x",', + ' "x",', + ' "x",', + ' "x",', + ' ...', + ']', + ].join('\n'), + ); + }); + + it('applies to sets', () => { + const val = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + expect(prettyFormat(val, {maxWidth: 5})).toEqual( + ['Set {', ' 1,', ' 2,', ' 3,', ' 4,', ' 5,', ' ...', '}'].join( + '\n', + ), + ); + }); }); it('can customize the max depth', () => { diff --git a/packages/pretty-format/src/collections.ts b/packages/pretty-format/src/collections.ts index 54091c90b428..cc862066b54b 100644 --- a/packages/pretty-format/src/collections.ts +++ b/packages/pretty-format/src/collections.ts @@ -97,6 +97,7 @@ export function printIteratorValues( printer: Printer, ): string { let result = ''; + let width = 0; let current = iterator.next(); if (!current.done) { @@ -105,9 +106,14 @@ export function printIteratorValues( const indentationNext = indentation + config.indent; while (!current.done) { - result += - indentationNext + - printer(current.value, config, indentationNext, depth, refs); + result += indentationNext; + + if (width++ === config.maxWidth) { + result += '...'; + break; + } + + result += printer(current.value, config, indentationNext, depth, refs); current = iterator.next(); From c560f9a8dc44380fdc86b02f6a3de815fdfc1ce1 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 16:11:13 -0500 Subject: [PATCH 05/11] Support Maps --- .../src/__tests__/prettyFormat.test.ts | 26 +++++++++++++++++++ packages/pretty-format/src/collections.ts | 10 ++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/pretty-format/src/__tests__/prettyFormat.test.ts b/packages/pretty-format/src/__tests__/prettyFormat.test.ts index 8364ef61534f..d758d1737dfb 100644 --- a/packages/pretty-format/src/__tests__/prettyFormat.test.ts +++ b/packages/pretty-format/src/__tests__/prettyFormat.test.ts @@ -587,6 +587,32 @@ describe('prettyFormat()', () => { ), ); }); + + it('applies to maps', () => { + const val = new Map(); + val.set('a', 1); + val.set('b', 2); + val.set('c', 3); + val.set('d', 4); + val.set('e', 5); + val.set('f', 6); + val.set('g', 7); + val.set('h', 8); + val.set('i', 9); + val.set('j', 10); + expect(prettyFormat(val, {maxWidth: 5})).toEqual( + [ + 'Map {', + ' "a" => 1,', + ' "b" => 2,', + ' "c" => 3,', + ' "d" => 4,', + ' "e" => 5,', + ' ...', + '}', + ].join('\n'), + ); + }); }); it('can customize the max depth', () => { diff --git a/packages/pretty-format/src/collections.ts b/packages/pretty-format/src/collections.ts index cc862066b54b..64062ee4a8a2 100644 --- a/packages/pretty-format/src/collections.ts +++ b/packages/pretty-format/src/collections.ts @@ -43,6 +43,7 @@ export function printIteratorEntries( separator: string = ': ', ): string { let result = ''; + let width = 0; let current = iterator.next(); if (!current.done) { @@ -51,6 +52,13 @@ export function printIteratorEntries( const indentationNext = indentation + config.indent; while (!current.done) { + result += indentationNext; + + if (width++ === config.maxWidth) { + result += '...'; + break; + } + const name = printer( current.value[0], config, @@ -66,7 +74,7 @@ export function printIteratorEntries( refs, ); - result += indentationNext + name + separator + value; + result += name + separator + value; current = iterator.next(); From bacf8d5c5a9fdec7df877df48f16a8eef5f52635 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 16:15:37 -0500 Subject: [PATCH 06/11] Add maxWidth doc to pretty-format readme --- packages/pretty-format/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/pretty-format/README.md b/packages/pretty-format/README.md index c5cb0041dbfa..3dae52a159fe 100755 --- a/packages/pretty-format/README.md +++ b/packages/pretty-format/README.md @@ -75,6 +75,7 @@ console.log(prettyFormat(onClick, options)); | `highlight` | `boolean` | `false` | highlight syntax with colors in terminal (some plugins) | | `indent` | `number` | `2` | spaces in each level of indentation | | `maxDepth` | `number` | `Infinity` | levels to print in arrays, objects, elements, and so on | +| `maxWidth` | `number` | `Infinity` | number of elements to print in arrays, sets, and so on | | `min` | `boolean` | `false` | minimize added space: no indentation nor line breaks | | `plugins` | `array` | `[]` | plugins to serialize application-specific data types | | `printBasicPrototype` | `boolean` | `false` | print the prototype for plain objects and arrays | From 62ee8450947f384aa43a6f61e9160a5be4b42de6 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 16:31:23 -0500 Subject: [PATCH 07/11] Limit width in matcher's stringify --- .../src/__tests__/index.test.ts | 17 +++++++++++++++++ packages/jest-matcher-utils/src/index.ts | 18 ++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/jest-matcher-utils/src/__tests__/index.test.ts b/packages/jest-matcher-utils/src/__tests__/index.test.ts index 9ed9cc9e17e3..37fb9fafc130 100644 --- a/packages/jest-matcher-utils/src/__tests__/index.test.ts +++ b/packages/jest-matcher-utils/src/__tests__/index.test.ts @@ -97,6 +97,23 @@ describe('stringify()', () => { expect(stringify(big)).toBe(prettyFormat(big, {maxDepth: 1, min: true})); expect(stringify(small)).toBe(prettyFormat(small, {min: true})); }); + + test('reduces maxWidth if stringifying very large arrays', () => { + const big: any = []; + const small: any = []; + const testString = Array(1000).join('x'); + + for (let i = 0; i < 100; i += 1) { + big[i] = testString; + } + + for (let i = 0; i < 3; i += 1) { + small[i] = testString; + } + + expect(stringify(big)).toBe(prettyFormat(big, {maxWidth: 5, min: true})); + expect(stringify(small)).toBe(prettyFormat(small, {min: true})); + }); }); describe('ensureNumbers()', () => { diff --git a/packages/jest-matcher-utils/src/index.ts b/packages/jest-matcher-utils/src/index.ts index 7210d37c624c..cdbad1a6c79f 100644 --- a/packages/jest-matcher-utils/src/index.ts +++ b/packages/jest-matcher-utils/src/index.ts @@ -89,13 +89,18 @@ export const SUGGEST_TO_CONTAIN_EQUAL = chalk.dim( 'Looks like you wanted to test for object/array equality with the stricter `toContain` matcher. You probably need to use `toContainEqual` instead.', ); -export const stringify = (object: unknown, maxDepth: number = 10): string => { +export const stringify = ( + object: unknown, + maxDepth: number = 10, + maxWidth: number = 10, +): string => { const MAX_LENGTH = 10000; let result; try { result = prettyFormat(object, { maxDepth, + maxWidth, min: true, plugins: PLUGINS, }); @@ -103,14 +108,19 @@ export const stringify = (object: unknown, maxDepth: number = 10): string => { result = prettyFormat(object, { callToJSON: false, maxDepth, + maxWidth, min: true, plugins: PLUGINS, }); } - return result.length >= MAX_LENGTH && maxDepth > 1 - ? stringify(object, Math.floor(maxDepth / 2)) - : result; + if (result.length >= MAX_LENGTH && maxDepth > 1) { + return stringify(object, Math.floor(maxDepth / 2), maxWidth); + } else if (result.length >= MAX_LENGTH && maxWidth > 1) { + return stringify(object, maxDepth, Math.floor(maxWidth / 2)); + } else { + return result; + } }; export const highlightTrailingWhitespace = (text: string): string => From 7a7d9596cdc1e5293e577473214487d60326916c Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 17:26:50 -0500 Subject: [PATCH 08/11] Use unicode ellipsis --- packages/pretty-format/src/__tests__/prettyFormat.test.ts | 6 +++--- packages/pretty-format/src/collections.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pretty-format/src/__tests__/prettyFormat.test.ts b/packages/pretty-format/src/__tests__/prettyFormat.test.ts index d758d1737dfb..73b9237d54b6 100644 --- a/packages/pretty-format/src/__tests__/prettyFormat.test.ts +++ b/packages/pretty-format/src/__tests__/prettyFormat.test.ts @@ -573,7 +573,7 @@ describe('prettyFormat()', () => { ' "x",', ' "x",', ' "x",', - ' ...', + ' …', ']', ].join('\n'), ); @@ -582,7 +582,7 @@ describe('prettyFormat()', () => { it('applies to sets', () => { const val = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); expect(prettyFormat(val, {maxWidth: 5})).toEqual( - ['Set {', ' 1,', ' 2,', ' 3,', ' 4,', ' 5,', ' ...', '}'].join( + ['Set {', ' 1,', ' 2,', ' 3,', ' 4,', ' 5,', ' …', '}'].join( '\n', ), ); @@ -608,7 +608,7 @@ describe('prettyFormat()', () => { ' "c" => 3,', ' "d" => 4,', ' "e" => 5,', - ' ...', + ' …', '}', ].join('\n'), ); diff --git a/packages/pretty-format/src/collections.ts b/packages/pretty-format/src/collections.ts index 64062ee4a8a2..0495f35be557 100644 --- a/packages/pretty-format/src/collections.ts +++ b/packages/pretty-format/src/collections.ts @@ -55,7 +55,7 @@ export function printIteratorEntries( result += indentationNext; if (width++ === config.maxWidth) { - result += '...'; + result += '…'; break; } @@ -117,7 +117,7 @@ export function printIteratorValues( result += indentationNext; if (width++ === config.maxWidth) { - result += '...'; + result += '…'; break; } @@ -162,7 +162,7 @@ export function printListItems( result += indentationNext; if (i === config.maxWidth) { - result += '...'; + result += '…'; break; } From b8ecccae39c6e7877b326815e3b8c82f465fd19a Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 17:33:30 -0500 Subject: [PATCH 09/11] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1856c8f98ad..e3064fd47ce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `[jest-resolver]` [**BREAKING**] Add support for `package.json` `exports` ([11961](https://github.com/facebook/jest/pull/11961)) - `[@jes/schemas]` New module for JSON schemas for Jest's config ([#12384](https://github.com/facebook/jest/pull/12384)) - `[jest-worker]` [**BREAKING**] Allow only absolute `workerPath` ([#12343](https://github.com/facebook/jest/pull/12343)) +- `[pretty-format]` New `maxWidth` parameter ([#12402](https://github.com/facebook/jest/pull/12402)) ### Fixes @@ -21,6 +22,7 @@ - `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#12397](https://github.com/facebook/jest/pull/12397)) - `[jest-environment-jsdom]` Make `jsdom` accessible to extending environments again ([#12232](https://github.com/facebook/jest/pull/12232)) - `[jest-jasmine2, jest-types]` [**BREAKING**] Move all `jasmine` specific types from `@jest/types` to its own package ([#12125](https://github.com/facebook/jest/pull/12125)) +- `[jest-matcher-utils]` Pass maxWidth to pretty-format to avoid printing every element in arrays by default ([#12402](https://github.com/facebook/jest/pull/12402)) ### Chore & Maintenance From 82c5d3cf48d414106523c8773ba57e4b34f9f7f5 Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 15 Feb 2022 17:40:17 -0500 Subject: [PATCH 10/11] Refactor to use safe navigation & null coalescing prefer to use these operators where possible on the options object Refactor requested during review --- packages/pretty-format/src/index.ts | 56 ++++++++--------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index 329b2d67f9c3..3d9d4ce90564 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -466,60 +466,34 @@ const getColorsEmpty = (): Colors => }, Object.create(null)); const getPrintFunctionName = (options?: OptionsReceived) => - options && options.printFunctionName !== undefined - ? options.printFunctionName - : DEFAULT_OPTIONS.printFunctionName; + options?.printFunctionName ?? DEFAULT_OPTIONS.printFunctionName; const getEscapeRegex = (options?: OptionsReceived) => - options && options.escapeRegex !== undefined - ? options.escapeRegex - : DEFAULT_OPTIONS.escapeRegex; + options?.escapeRegex ?? DEFAULT_OPTIONS.escapeRegex; const getEscapeString = (options?: OptionsReceived) => - options && options.escapeString !== undefined - ? options.escapeString - : DEFAULT_OPTIONS.escapeString; + options?.escapeString ?? DEFAULT_OPTIONS.escapeString; const getConfig = (options?: OptionsReceived): Config => ({ - callToJSON: - options && options.callToJSON !== undefined - ? options.callToJSON - : DEFAULT_OPTIONS.callToJSON, - colors: - options && options.highlight - ? getColorsHighlight(options) - : getColorsEmpty(), + callToJSON: options?.callToJSON ?? DEFAULT_OPTIONS.callToJSON, + colors: options?.highlight ? getColorsHighlight(options) : getColorsEmpty(), compareKeys: - options && typeof options.compareKeys === 'function' + typeof options?.compareKeys === 'function' ? options.compareKeys : DEFAULT_OPTIONS.compareKeys, escapeRegex: getEscapeRegex(options), escapeString: getEscapeString(options), - indent: - options && options.min - ? '' - : createIndent( - options && options.indent !== undefined - ? options.indent - : DEFAULT_OPTIONS.indent, - ), - maxDepth: - options && options.maxDepth !== undefined - ? options.maxDepth - : DEFAULT_OPTIONS.maxDepth, - maxWidth: - options && options.maxWidth !== undefined - ? options.maxWidth - : DEFAULT_OPTIONS.maxWidth, - min: options && options.min !== undefined ? options.min : DEFAULT_OPTIONS.min, - plugins: - options && options.plugins !== undefined - ? options.plugins - : DEFAULT_OPTIONS.plugins, + indent: options?.min + ? '' + : createIndent(options?.indent ?? DEFAULT_OPTIONS.indent), + maxDepth: options?.maxDepth ?? DEFAULT_OPTIONS.maxDepth, + maxWidth: options?.maxWidth ?? DEFAULT_OPTIONS.maxWidth, + min: options?.min ?? DEFAULT_OPTIONS.min, + plugins: options?.plugins ?? DEFAULT_OPTIONS.plugins, printBasicPrototype: options?.printBasicPrototype ?? true, printFunctionName: getPrintFunctionName(options), - spacingInner: options && options.min ? ' ' : '\n', - spacingOuter: options && options.min ? '' : '\n', + spacingInner: options?.min ? ' ' : '\n', + spacingOuter: options?.min ? '' : '\n', }); function createIndent(indent: number): string { From 7ea67194daec8bdd4f25ae2fb25b1bd9d6577c73 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 16 Feb 2022 00:57:19 +0100 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3064fd47ce1..6629b8e64368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ - `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#12397](https://github.com/facebook/jest/pull/12397)) - `[jest-environment-jsdom]` Make `jsdom` accessible to extending environments again ([#12232](https://github.com/facebook/jest/pull/12232)) - `[jest-jasmine2, jest-types]` [**BREAKING**] Move all `jasmine` specific types from `@jest/types` to its own package ([#12125](https://github.com/facebook/jest/pull/12125)) -- `[jest-matcher-utils]` Pass maxWidth to pretty-format to avoid printing every element in arrays by default ([#12402](https://github.com/facebook/jest/pull/12402)) +- `[jest-matcher-utils]` Pass maxWidth to `pretty-format` to avoid printing every element in arrays by default ([#12402](https://github.com/facebook/jest/pull/12402)) ### Chore & Maintenance