Skip to content

Commit

Permalink
chore(expect): add all print utils to main expect export (expect.prin…
Browse files Browse the repository at this point in the history
…t) (print utils from src/print)
  • Loading branch information
mrienstra committed Sep 9, 2021
1 parent e3140f5 commit 142f85f
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 159 deletions.
10 changes: 2 additions & 8 deletions packages/expect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ import {
setState,
} from './jestMatchersObject';
import matchers from './matchers';
import {
printReceivedStringContainExpectedResult,
printReceivedStringContainExpectedSubstring,
} from './print';
import print from './print';
import spyMatchers from './spyMatchers';
import toThrowMatchers, {
createMatcher as createThrowMatcher,
Expand Down Expand Up @@ -362,10 +359,7 @@ expect.extend = (matchers: MatchersObject): void =>
setMatchers(matchers, false, expect);
expect.matchers = matchers;

expect.print = {
printReceivedStringContainExpectedResult,
printReceivedStringContainExpectedSubstring,
};
expect.print = print;

expect.anything = anything;
expect.any = any;
Expand Down
33 changes: 12 additions & 21 deletions packages/expect/src/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@ import {
stringify,
} from 'jest-matcher-utils';
import {equals} from './jasmineUtils';
import {
printCloseTo,
printExpectedConstructorName,
printExpectedConstructorNameNot,
printReceivedArrayContainExpectedItem,
printReceivedConstructorName,
printReceivedConstructorNameNot,
printReceivedStringContainExpectedResult,
printReceivedStringContainExpectedSubstring,
} from './print';
import print from './print';
import type {MatcherState, MatchersObject} from './types';
import {
getObjectSubset,
Expand Down Expand Up @@ -183,14 +174,14 @@ const matchers: MatchersObject = {
? ''
: `Received: ${printReceived(received)}\n` +
'\n' +
printCloseTo(receivedDiff, expectedDiff, precision, isNot))
print.closeTo(receivedDiff, expectedDiff, precision, isNot))
: () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
`Expected: ${printExpected(expected)}\n` +
`Received: ${printReceived(received)}\n` +
'\n' +
printCloseTo(receivedDiff, expectedDiff, precision, isNot);
print.closeTo(receivedDiff, expectedDiff, precision, isNot);

return {message, pass};
},
Expand Down Expand Up @@ -302,10 +293,10 @@ const matchers: MatchersObject = {
? () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
printExpectedConstructorNameNot('Expected constructor', expected) +
print.expectedConstructorNameNot('Expected constructor', expected) +
(typeof received.constructor === 'function' &&
received.constructor !== expected
? printReceivedConstructorNameNot(
? print.receivedConstructorNameNot(
'Received constructor',
received.constructor,
expected,
Expand All @@ -314,14 +305,14 @@ const matchers: MatchersObject = {
: () =>
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
printExpectedConstructorName('Expected constructor', expected) +
print.expectedConstructorName('Expected constructor', expected) +
(isPrimitive(received) || Object.getPrototypeOf(received) === null
? `\nReceived value has no prototype\nReceived value: ${printReceived(
received,
)}`
: typeof received.constructor !== 'function'
? `\nReceived value: ${printReceived(received)}`
: printReceivedConstructorName(
: print.receivedConstructorName(
'Received constructor',
received.constructor,
));
Expand Down Expand Up @@ -509,7 +500,7 @@ const matchers: MatchersObject = {
)}\n` +
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
isNot
? printReceivedStringContainExpectedSubstring(
? print.receivedStringContainExpectedSubstring(
received,
index,
String(expected).length,
Expand Down Expand Up @@ -539,7 +530,7 @@ const matchers: MatchersObject = {
)}\n` +
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
isNot && Array.isArray(received)
? printReceivedArrayContainExpectedItem(received, index)
? print.receivedArrayContainExpectedItem(received, index)
: printReceived(received)
}` +
(!isNot &&
Expand Down Expand Up @@ -595,7 +586,7 @@ const matchers: MatchersObject = {
)}\n` +
`${printLabel(labelReceived)}${isNot ? ' ' : ''}${
isNot && Array.isArray(received)
? printReceivedArrayContainExpectedItem(received, index)
? print.receivedArrayContainExpectedItem(received, index)
: printReceived(received)
}`
);
Expand Down Expand Up @@ -859,15 +850,15 @@ const matchers: MatchersObject = {
? matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
`Expected substring: not ${printExpected(expected)}\n` +
`Received string: ${printReceivedStringContainExpectedSubstring(
`Received string: ${print.receivedStringContainExpectedSubstring(
received,
received.indexOf(expected),
expected.length,
)}`
: matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
`Expected pattern: not ${printExpected(expected)}\n` +
`Received string: ${printReceivedStringContainExpectedResult(
`Received string: ${print.receivedStringContainExpectedResult(
received,
typeof expected.exec === 'function'
? expected.exec(received)
Expand Down
194 changes: 89 additions & 105 deletions packages/expect/src/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,116 +15,12 @@ import {
printReceived,
stringify,
} from 'jest-matcher-utils';
import type {PrintObject} from './types';

// Format substring but do not enclose in double quote marks.
// The replacement is compatible with pretty-format package.
const printSubstring = (val: string): string => val.replace(/"|\\/g, '\\$&');

export const printReceivedStringContainExpectedSubstring = (
received: string,
start: number,
length: number, // not end
): string =>
RECEIVED_COLOR(
'"' +
printSubstring(received.slice(0, start)) +
INVERTED_COLOR(printSubstring(received.slice(start, start + length))) +
printSubstring(received.slice(start + length)) +
'"',
);

export const printReceivedStringContainExpectedResult = (
received: string,
result: RegExpExecArray | null,
): string =>
result === null
? printReceived(received)
: printReceivedStringContainExpectedSubstring(
received,
result.index,
result[0].length,
);

// The serialized array is compatible with pretty-format package min option.
// However, items have default stringify depth (instead of depth - 1)
// so expected item looks consistent by itself and enclosed in the array.
export const printReceivedArrayContainExpectedItem = (
received: Array<unknown>,
index: number,
): string =>
RECEIVED_COLOR(
'[' +
received
.map((item, i) => {
const stringified = stringify(item);
return i === index ? INVERTED_COLOR(stringified) : stringified;
})
.join(', ') +
']',
);

export const printCloseTo = (
receivedDiff: number,
expectedDiff: number,
precision: number,
isNot: boolean,
): string => {
const receivedDiffString = stringify(receivedDiff);
const expectedDiffString = receivedDiffString.includes('e')
? // toExponential arg is number of digits after the decimal point.
expectedDiff.toExponential(0)
: 0 <= precision && precision < 20
? // toFixed arg is number of digits after the decimal point.
// It may be a value between 0 and 20 inclusive.
// Implementations may optionally support a larger range of values.
expectedDiff.toFixed(precision + 1)
: stringify(expectedDiff);

return (
`Expected precision: ${isNot ? ' ' : ''} ${stringify(precision)}\n` +
`Expected difference: ${isNot ? 'not ' : ''}< ${EXPECTED_COLOR(
expectedDiffString,
)}\n` +
`Received difference: ${isNot ? ' ' : ''} ${RECEIVED_COLOR(
receivedDiffString,
)}`
);
};

export const printExpectedConstructorName = (
label: string,
expected: Function,
): string => printConstructorName(label, expected, false, true) + '\n';

export const printExpectedConstructorNameNot = (
label: string,
expected: Function,
): string => printConstructorName(label, expected, true, true) + '\n';

export const printReceivedConstructorName = (
label: string,
received: Function,
): string => printConstructorName(label, received, false, false) + '\n';

// Do not call function if received is equal to expected.
export const printReceivedConstructorNameNot = (
label: string,
received: Function,
expected: Function,
): string =>
typeof expected.name === 'string' &&
expected.name.length !== 0 &&
typeof received.name === 'string' &&
received.name.length !== 0
? printConstructorName(label, received, true, false) +
` ${
Object.getPrototypeOf(received) === expected
? 'extends'
: 'extends … extends'
} ${EXPECTED_COLOR(expected.name)}` +
'\n'
: printConstructorName(label, received, false, false) + '\n';

const printConstructorName = (
label: string,
constructor: Function,
Expand All @@ -140,3 +36,91 @@ const printConstructorName = (
? EXPECTED_COLOR(constructor.name)
: RECEIVED_COLOR(constructor.name)
}`;

const print: PrintObject = {
closeTo: (receivedDiff, expectedDiff, precision, isNot) => {
const receivedDiffString = stringify(receivedDiff);
const expectedDiffString = receivedDiffString.includes('e')
? // toExponential arg is number of digits after the decimal point.
expectedDiff.toExponential(0)
: 0 <= precision && precision < 20
? // toFixed arg is number of digits after the decimal point.
// It may be a value between 0 and 20 inclusive.
// Implementations may optionally support a larger range of values.
expectedDiff.toFixed(precision + 1)
: stringify(expectedDiff);

return (
`Expected precision: ${isNot ? ' ' : ''} ${stringify(precision)}\n` +
`Expected difference: ${isNot ? 'not ' : ''}< ${EXPECTED_COLOR(
expectedDiffString,
)}\n` +
`Received difference: ${isNot ? ' ' : ''} ${RECEIVED_COLOR(
receivedDiffString,
)}`
);
},

expectedConstructorName: (label, expected) =>
printConstructorName(label, expected, false, true) + '\n',

expectedConstructorNameNot: (label, expected) =>
printConstructorName(label, expected, true, true) + '\n',

// The serialized array is compatible with pretty-format package min option.
// However, items have default stringify depth (instead of depth - 1)
// so expected item looks consistent by itself and enclosed in the array.
receivedArrayContainExpectedItem: (received, index) =>
RECEIVED_COLOR(
'[' +
received
.map((item, i) => {
const stringified = stringify(item);
return i === index ? INVERTED_COLOR(stringified) : stringified;
})
.join(', ') +
']',
),

receivedConstructorName: (label, received) =>
printConstructorName(label, received, false, false) + '\n',

// Do not call function if received is equal to expected.
receivedConstructorNameNot: (label, received, expected): string =>
typeof expected.name === 'string' &&
expected.name.length !== 0 &&
typeof received.name === 'string' &&
received.name.length !== 0
? printConstructorName(label, received, true, false) +
` ${
Object.getPrototypeOf(received) === expected
? 'extends'
: 'extends … extends'
} ${EXPECTED_COLOR(expected.name)}` +
'\n'
: printConstructorName(label, received, false, false) + '\n',

receivedStringContainExpectedResult: (received, result) =>
result === null
? printReceived(received)
: print.receivedStringContainExpectedSubstring(
received,
result.index,
result[0].length,
),

receivedStringContainExpectedSubstring: (
received,
start,
length, // not end
) =>
RECEIVED_COLOR(
'"' +
printSubstring(received.slice(0, start)) +
INVERTED_COLOR(printSubstring(received.slice(start, start + length))) +
printSubstring(received.slice(start + length)) +
'"',
),
};

export default print;
Loading

0 comments on commit 142f85f

Please sign in to comment.