From e1bc733f21a1e707c46be0a07f3304214df63d95 Mon Sep 17 00:00:00 2001 From: frozenbonito Date: Sun, 2 May 2021 02:01:54 +0900 Subject: [PATCH] fix(jest-each): fix placeholder interpolation (#11364) --- CHANGELOG.md | 2 ++ packages/jest-each/src/__tests__/array.test.ts | 17 +++++++++++++---- packages/jest-each/src/table/array.ts | 15 +++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba07e937f8fc..37513c8a5370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ - `[jest-core]` Use `WeakRef` to hold timers when detecting open handles ([#11277](https://github.com/facebook/jest/pull/11277)) - `[jest-each]` [**BREAKING**] Ignore excess words in headings ([#8766](https://github.com/facebook/jest/pull/8766)) - `[jest-each]` Support array index with template strings ([#10763](https://github.com/facebook/jest/pull/10763)) +- `[jest-each]` Interpolate `%%` correctly ([#11364](https://github.com/facebook/jest/pull/11364)) +- `[jest-each]` Fix wrong interpolation when the value of array contains multiple `%` ([#11364](https://github.com/facebook/jest/pull/11364)) - `[jest-environment]` [**BREAKING**] Drop support for `runScript` for test environments ([#11155](https://github.com/facebook/jest/pull/11155)) - `[jest-environment-jsdom]` Use inner realm’s `ArrayBuffer` constructor ([#10885](https://github.com/facebook/jest/pull/10885)) - `[jest-environment-jsdom]` [**BREAKING**] Remove Node globals `setImmediate` and `clearImmediate` [#11222](https://github.com/facebook/jest/pull/11222) diff --git a/packages/jest-each/src/__tests__/array.test.ts b/packages/jest-each/src/__tests__/array.test.ts index e95b8e4e701d..c76609d80505 100644 --- a/packages/jest-each/src/__tests__/array.test.ts +++ b/packages/jest-each/src/__tests__/array.test.ts @@ -143,19 +143,22 @@ describe('jest-each', () => { ], ]); const testFunction = get(eachObject, keyPath); - testFunction('expected string: %s %d %s %s %d %j %s %j %d %d %#', noop); + testFunction( + 'expected string: %% %%s %s %d %s %s %d %j %s %j %d %d %#', + noop, + ); const globalMock = get(globalTestMocks, keyPath); expect(globalMock).toHaveBeenCalledTimes(2); expect(globalMock).toHaveBeenCalledWith( - `expected string: hello 1 null undefined 1.2 ${JSON.stringify({ + `expected string: % %s hello 1 null undefined 1.2 ${JSON.stringify({ foo: 'bar', })} () => {} [] Infinity NaN 0`, expectFunction, undefined, ); expect(globalMock).toHaveBeenCalledWith( - `expected string: world 1 null undefined 1.2 ${JSON.stringify({ + `expected string: % %s world 1 null undefined 1.2 ${JSON.stringify({ baz: 'qux', })} () => {} [] Infinity NaN 1`, expectFunction, @@ -400,12 +403,13 @@ describe('jest-each', () => { const eachObject = each.withGlobal(globalTestMocks)([ ['hello', '%d', 10, '%s', {foo: 'bar'}], ['world', '%i', 1991, '%p', {foo: 'bar'}], + ['joe', '%d %d', 10, '%%s', {foo: 'bar'}], ]); const testFunction = get(eachObject, keyPath); testFunction('expected string: %s %s %d %s %p', () => {}); const globalMock = get(globalTestMocks, keyPath); - expect(globalMock).toHaveBeenCalledTimes(2); + expect(globalMock).toHaveBeenCalledTimes(3); expect(globalMock).toHaveBeenCalledWith( 'expected string: hello %d 10 %s {"foo": "bar"}', expectFunction, @@ -416,6 +420,11 @@ describe('jest-each', () => { expectFunction, undefined, ); + expect(globalMock).toHaveBeenCalledWith( + 'expected string: joe %d %d 10 %%s {"foo": "bar"}', + expectFunction, + undefined, + ); }); }); }); diff --git a/packages/jest-each/src/table/array.ts b/packages/jest-each/src/table/array.ts index d8b3daf9661c..fe245e8e77bd 100644 --- a/packages/jest-each/src/table/array.ts +++ b/packages/jest-each/src/table/array.ts @@ -11,10 +11,11 @@ import type {Global} from '@jest/types'; import {format as pretty} from 'pretty-format'; import type {EachTests} from '../bind'; -const SUPPORTED_PLACEHOLDERS = /%[sdifjoOp%]/g; +const SUPPORTED_PLACEHOLDERS = /%[sdifjoOp]/g; const PRETTY_PLACEHOLDER = '%p'; const INDEX_PLACEHOLDER = '%#'; const PLACEHOLDER_PREFIX = '%'; +const ESCAPED_PLACEHOLDER_PREFIX = /%%/g; const JEST_EACH_PLACEHOLDER_ESCAPE = '@@__JEST_EACH_PLACEHOLDER_ESCAPE__@@'; export default (title: string, arrayTable: Global.ArrayTable): EachTests => @@ -46,17 +47,23 @@ const formatTitle = ( return interpolatePrettyPlaceholder(formattedTitle, normalisedValue); return util.format(formattedTitle, normalisedValue); - }, interpolateTitleIndex(title, rowIndex)) + }, interpolateTitleIndex(interpolateEscapedPlaceholders(title), rowIndex)) .replace(new RegExp(JEST_EACH_PLACEHOLDER_ESCAPE, 'g'), PLACEHOLDER_PREFIX); const normalisePlaceholderValue = (value: unknown) => - typeof value === 'string' && SUPPORTED_PLACEHOLDERS.test(value) - ? value.replace(PLACEHOLDER_PREFIX, JEST_EACH_PLACEHOLDER_ESCAPE) + typeof value === 'string' + ? value.replace( + new RegExp(PLACEHOLDER_PREFIX, 'g'), + JEST_EACH_PLACEHOLDER_ESCAPE, + ) : value; const getMatchingPlaceholders = (title: string) => title.match(SUPPORTED_PLACEHOLDERS) || []; +const interpolateEscapedPlaceholders = (title: string) => + title.replace(ESCAPED_PLACEHOLDER_PREFIX, JEST_EACH_PLACEHOLDER_ESCAPE); + const interpolateTitleIndex = (title: string, index: number) => title.replace(INDEX_PLACEHOLDER, index.toString());