Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(expect): add exports to satisfy playwright #11816

Closed
wants to merge 7 commits into from
4 changes: 4 additions & 0 deletions packages/expect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
setState,
} from './jestMatchersObject';
import matchers from './matchers';
import print from './print';
import spyMatchers from './spyMatchers';
import toThrowMatchers, {
createMatcher as createThrowMatcher,
Expand Down Expand Up @@ -356,6 +357,9 @@ const makeThrowingMatcher = (

expect.extend = (matchers: MatchersObject): void =>
setMatchers(matchers, false, expect);
expect.matchers = matchers;

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;