Skip to content

Commit eb5f1aa

Browse files
committed
fix: custom expect messages for expect.extend matchers
1 parent eebafb5 commit eb5f1aa

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

packages/expect/src/jest-extend.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function getMatcherState(
2727
const obj = assertion._obj
2828
const isNot = util.flag(assertion, 'negate') as boolean
2929
const promise = util.flag(assertion, 'promise') || ''
30+
const customMessage = util.flag(assertion, 'message') as string | undefined
3031
const jestUtils = {
3132
...getMatcherUtils(),
3233
diff,
@@ -52,6 +53,7 @@ function getMatcherState(
5253
state: matcherState,
5354
isNot,
5455
obj,
56+
customMessage,
5557
}
5658
}
5759

@@ -73,7 +75,7 @@ function JestExtendPlugin(
7375
this: Chai.AssertionStatic & Chai.Assertion,
7476
...args: any[]
7577
) {
76-
const { state, isNot, obj } = getMatcherState(this, expect)
78+
const { state, isNot, obj, customMessage } = getMatcherState(this, expect)
7779

7880
const result = expectAssertion.call(state, obj, ...args)
7981

@@ -85,15 +87,21 @@ function JestExtendPlugin(
8587
const thenable = result as PromiseLike<SyncExpectationResult>
8688
return thenable.then(({ pass, message, actual, expected }) => {
8789
if ((pass && isNot) || (!pass && !isNot)) {
88-
throw new JestExtendError(message(), actual, expected)
90+
const errorMessage = customMessage != null
91+
? customMessage
92+
: message()
93+
throw new JestExtendError(errorMessage, actual, expected)
8994
}
9095
})
9196
}
9297

9398
const { pass, message, actual, expected } = result as SyncExpectationResult
9499

95100
if ((pass && isNot) || (!pass && !isNot)) {
96-
throw new JestExtendError(message(), actual, expected)
101+
const errorMessage = customMessage != null
102+
? `${customMessage}: ${message()}`
103+
: message()
104+
throw new JestExtendError(errorMessage, actual, expected)
97105
}
98106
}
99107

test/core/test/expect.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,39 @@ describe('Temporal equality', () => {
389389
})
390390
})
391391
})
392+
393+
describe('expect with custom message', () => {
394+
test('built-in matcher throws custom message on failure', () => {
395+
expect(() => expect(1, 'custom message').toBe(2)).toThrow('custom message')
396+
})
397+
398+
test('custom matcher with expect.extend throws custom message on failure', () => {
399+
expect.extend({
400+
toBeFoo(actual) {
401+
const { isNot } = this
402+
return {
403+
pass: actual === 'foo',
404+
message: () => `${actual} is${isNot ? ' not' : ''} foo`,
405+
}
406+
},
407+
})
408+
expect(() => (expect('bar', 'custom message') as any).toBeFoo()).toThrow('custom message')
409+
})
410+
411+
test('custom matcher passes with custom message when assertion succeeds', () => {
412+
expect.extend({
413+
toBeFoo(actual) {
414+
const { isNot } = this
415+
return {
416+
pass: actual === 'foo',
417+
message: () => `${actual} is${isNot ? ' not' : ''} foo`,
418+
}
419+
},
420+
})
421+
expect(() => (expect('foo', 'custom message') as any).toBeFoo()).not.toThrow()
422+
})
423+
424+
test('empty custom message falls back to default matcher message', () => {
425+
expect(() => expect(1, '').toBe(2)).toThrow('expected 1 to be 2 // Object.is equality')
426+
})
427+
})

0 commit comments

Comments
 (0)