[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix: make `expect(..., message)` consistent as error message prefix (#10068)

Co-authored-by: Codex <noreply@openai.com>

authored by

Hiroshi Ogawa
Codex
and committed by
GitHub
(Apr 7, 2026, 9:32 PM +0200) a1b5f0f4 65c9d55e

+135 -24
+2 -6
packages/expect/src/jest-extend.ts
··· 106 106 const thenable = result as PromiseLike<SyncExpectationResult> 107 107 return thenable.then(({ pass, message, actual, expected, meta }) => { 108 108 if ((pass && isNot) || (!pass && !isNot)) { 109 - const errorMessage = customMessage != null 110 - ? customMessage 111 - : message() 109 + const errorMessage = (customMessage ? `${customMessage}: ` : '') + message() 112 110 throw new JestExtendError( 113 111 errorMessage, 114 112 actual, ··· 122 120 const { pass, message, actual, expected, meta } = result as SyncExpectationResult 123 121 124 122 if ((pass && isNot) || (!pass && !isNot)) { 125 - const errorMessage = customMessage != null 126 - ? customMessage 127 - : message() 123 + const errorMessage = (customMessage ? `${customMessage}: ` : '') + message() 128 124 throw new JestExtendError( 129 125 errorMessage, 130 126 actual,
+6
test/core/test/expect-poll.test.ts
··· 122 122 })) 123 123 }) 124 124 125 + test('custom message', async () => { 126 + await expect(() => 127 + expect.poll(() => 1, { timeout: 100, interval: 10, message: 'custom' }).toBe(2), 128 + ).rejects.toMatchInlineSnapshot(`[AssertionError: custom: expected 1 to be 2 // Object.is equality]`) 129 + }) 130 + 125 131 test('should set _isLastPollAttempt flag on last call', async () => { 126 132 const fn = vi.fn(function (this: object) { 127 133 return chai.util.flag(this, '_isLastPollAttempt')
+9 -9
test/core/test/expect.test.ts
··· 397 397 describe('expect with custom message', () => { 398 398 describe('built-in matchers', () => { 399 399 test('sync matcher throws custom message on failure', () => { 400 - expect(() => expect(1, 'custom message').toBe(2)).toThrow('custom message') 400 + expect(() => expect(1, 'custom message').toBe(2)).toThrowErrorMatchingInlineSnapshot(`[AssertionError: custom message: expected 1 to be 2 // Object.is equality]`) 401 401 }) 402 402 403 403 test('async rejects matcher throws custom message on failure', async ({ expect }) => { 404 404 const asyncAssertion = expect(Promise.reject(new Error('test error')), 'custom async message').rejects.toBe(2) 405 - await expect(asyncAssertion).rejects.toThrow('custom async message') 405 + await expect(asyncAssertion).rejects.toMatchInlineSnapshot(`[AssertionError: custom async message: expected Error: test error to be 2 // Object.is equality]`) 406 406 }) 407 407 408 408 test('async resolves matcher throws custom message on failure', async ({ expect }) => { 409 409 const asyncAssertion = expect(Promise.resolve(1), 'custom async message').resolves.toBe(2) 410 - await expect(asyncAssertion).rejects.toThrow('custom async message') 410 + await expect(asyncAssertion).rejects.toMatchInlineSnapshot(`[AssertionError: custom async message: expected 1 to be 2 // Object.is equality]`) 411 411 }) 412 412 413 413 test('not matcher throws custom message on failure', () => { 414 - expect(() => expect(1, 'custom message').not.toBe(1)).toThrow('custom message') 414 + expect(() => expect(1, 'custom message').not.toBe(1)).toThrowErrorMatchingInlineSnapshot(`[AssertionError: custom message: expected 1 not to be 1 // Object.is equality]`) 415 415 }) 416 416 }) 417 417 ··· 426 426 } 427 427 }, 428 428 }) 429 - expect(() => (expect('bar', 'custom message') as any).toBeFoo()).toThrow('custom message') 429 + expect(() => (expect('bar', 'custom message') as any).toBeFoo()).toThrowErrorMatchingInlineSnapshot(`[Error: custom message: bar is foo]`) 430 430 }) 431 431 432 432 test('sync custom matcher passes with custom message when assertion succeeds', ({ expect }) => { ··· 453 453 }, 454 454 }) 455 455 const asyncAssertion = (expect(Promise.resolve('bar'), 'custom async message') as any).toBeFoo() 456 - await expect(asyncAssertion).rejects.toThrow('custom async message') 456 + await expect(asyncAssertion).rejects.toMatchInlineSnapshot(`[Error: custom async message: bar is not foo]`) 457 457 }) 458 458 459 459 test('async custom matcher with not throws custom message on failure', async ({ expect }) => { ··· 467 467 }, 468 468 }) 469 469 const asyncAssertion = (expect(Promise.resolve('foo'), 'custom async message') as any).not.toBeFoo() 470 - await expect(asyncAssertion).rejects.toThrow('custom async message') 470 + await expect(asyncAssertion).rejects.toMatchInlineSnapshot(`[Error: custom async message: foo is not foo]`) 471 471 }) 472 472 }) 473 473 474 474 describe('edge cases', () => { 475 475 test('empty custom message falls back to default matcher message', () => { 476 - expect(() => expect(1, '').toBe(2)).toThrow('expected 1 to be 2 // Object.is equality') 476 + expect(() => expect(1, '').toBe(2)).toThrowErrorMatchingInlineSnapshot(`[AssertionError: expected 1 to be 2 // Object.is equality]`) 477 477 }) 478 478 479 479 test('undefined custom message falls back to default matcher message', () => { 480 - expect(() => expect(1, undefined as any).toBe(2)).toThrow('expected 1 to be 2 // Object.is equality') 480 + expect(() => expect(1, undefined as any).toBe(2)).toThrowErrorMatchingInlineSnapshot(`[AssertionError: expected 1 to be 2 // Object.is equality]`) 481 481 }) 482 482 }) 483 483 })
+108
test/snapshots/test/custom-matcher.test.ts
··· 403 403 `) 404 404 expect(result.fs.readFile('raw.txt')).toMatchInlineSnapshot(`"crazy long"`) 405 405 }) 406 + 407 + test('outer expect message is prefixed by jest-extend for Snapshots wrappers', async () => { 408 + const result = await runInlineTests({ 409 + 'basic.test.ts': ` 410 + import { test, expect, Snapshots } from 'vitest' 411 + 412 + const { 413 + toMatchInlineSnapshot, 414 + } = Snapshots 415 + 416 + expect.extend({ 417 + toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) { 418 + return toMatchInlineSnapshot.call(this, received.slice(0, 5), inlineSnapshot) 419 + }, 420 + }) 421 + 422 + test('custom snapshot matcher', () => { 423 + expect('abcdefghij', 'outer message').toMatchTrimmedInlineSnapshot(\`"wrong"\`) 424 + }) 425 + 426 + test('builtin', () => { 427 + expect('abcdefghij', 'outer message').toMatchInlineSnapshot(\`"wrong"\`) 428 + }) 429 + 430 + test('builtin properties mismatch', () => { 431 + expect({ value: 1 }, 'outer message').toMatchSnapshot({ 432 + value: expect.any(String), 433 + }) 434 + }) 435 + `, 436 + }, { 437 + update: 'none', 438 + }) 439 + expect(result.stderr).toMatchInlineSnapshot(` 440 + " 441 + ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯ 442 + 443 + FAIL basic.test.ts > custom snapshot matcher 444 + Error: outer message: Snapshot \`custom snapshot matcher 1\` mismatched 445 + 446 + Expected: ""wrong"" 447 + Received: ""abcde"" 448 + 449 + ❯ basic.test.ts:15:41 450 + 13| 451 + 14| test('custom snapshot matcher', () => { 452 + 15| expect('abcdefghij', 'outer message').toMatchTrimmedInlineSnapshot(\`… 453 + | ^ 454 + 16| }) 455 + 17| 456 + 457 + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯ 458 + 459 + FAIL basic.test.ts > builtin 460 + Error: outer message: Snapshot \`builtin 1\` mismatched 461 + 462 + Expected: ""wrong"" 463 + Received: ""abcdefghij"" 464 + 465 + ❯ basic.test.ts:19:41 466 + 17| 467 + 18| test('builtin', () => { 468 + 19| expect('abcdefghij', 'outer message').toMatchInlineSnapshot(\`"wrong"… 469 + | ^ 470 + 20| }) 471 + 21| 472 + 473 + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯ 474 + 475 + FAIL basic.test.ts > builtin properties mismatch 476 + Error: outer message: Snapshot properties mismatched 477 + 478 + - Expected 479 + + Received 480 + 481 + { 482 + - "value": Any<String>, 483 + + "value": 1, 484 + } 485 + 486 + ❯ basic.test.ts:23:41 487 + 21| 488 + 22| test('builtin properties mismatch', () => { 489 + 23| expect({ value: 1 }, 'outer message').toMatchSnapshot({ 490 + | ^ 491 + 24| value: expect.any(String), 492 + 25| }) 493 + 494 + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯ 495 + 496 + " 497 + `) 498 + expect(result.errorTree()).toMatchInlineSnapshot(` 499 + Object { 500 + "basic.test.ts": Object { 501 + "builtin": Array [ 502 + "outer message: Snapshot \`builtin 1\` mismatched", 503 + ], 504 + "builtin properties mismatch": Array [ 505 + "outer message: Snapshot properties mismatched", 506 + ], 507 + "custom snapshot matcher": Array [ 508 + "outer message: Snapshot \`custom snapshot matcher 1\` mismatched", 509 + ], 510 + }, 511 + } 512 + `) 513 + })
+10 -9
packages/vitest/src/integrations/snapshot/chai.ts
··· 89 89 received: utils.flag(this, 'object'), 90 90 ...normalizeArguments(propertiesOrHint, hint), 91 91 }) 92 - return assertMatchResult(result) 92 + return assertMatchResult(result, chai.util.flag(this, 'message')) 93 93 }), 94 94 ) 95 95 } ··· 108 108 filepath, 109 109 hint, 110 110 }) 111 - const assertPromise = resultPromise.then(result => assertMatchResult(result)) 111 + const assertPromise = resultPromise.then(result => 112 + assertMatchResult(result, chai.util.flag(this, 'message')), 113 + ) 112 114 return recordAsyncExpect( 113 115 getTest(this), 114 116 assertPromise, ··· 134 136 isInline: true, 135 137 ...normalizeInlineArguments(propertiesOrInlineSnapshot, inlineSnapshotOrHint, hint), 136 138 }) 137 - return assertMatchResult(result) 139 + return assertMatchResult(result, chai.util.flag(this, 'message')) 138 140 }), 139 141 ) 140 142 utils.addMethod( ··· 149 151 received: getError(received, promise), 150 152 ...normalizeArguments(propertiesOrHint, hint), 151 153 }) 152 - return assertMatchResult(result) 154 + return assertMatchResult(result, chai.util.flag(this, 'message')) 153 155 }), 154 156 ) 155 157 utils.addMethod( ··· 169 171 isInline: true, 170 172 ...normalizeInlineArguments(undefined, inlineSnapshotOrHint, hint), 171 173 }) 172 - return assertMatchResult(result) 174 + return assertMatchResult(result, chai.util.flag(this, 'message')) 173 175 }), 174 176 ) 175 177 utils.addMethod(chai.expect, 'addSnapshotSerializer', addSerializer) ··· 221 223 message: options.hint, 222 224 isInline: options.isInline, 223 225 inlineSnapshot: options.inlineSnapshot, 224 - errorMessage: chai.util.flag(assertion, 'message'), 225 226 // pass `assertionName` for inline snapshot stack probing 226 227 assertionName, 227 228 // set by async assertion (e.g. resolves/rejects) for inline snapshot stack probing ··· 246 247 return getSnapshotClient().match({ 247 248 received: options.received, 248 249 message: options.hint, 249 - errorMessage: chai.util.flag(assertion, 'message'), 250 250 rawSnapshot: { 251 251 file: rawSnapshotFile, 252 252 content: rawSnapshotContent ?? undefined, ··· 255 255 }) 256 256 } 257 257 258 - function assertMatchResult(result: SyncExpectationResult): void { 258 + function assertMatchResult(result: SyncExpectationResult, customMessage?: string): void { 259 259 if (!result.pass) { 260 - throw Object.assign(new Error(result.message()), { 260 + const errorMessage = (customMessage ? `${customMessage}: ` : '') + result.message() 261 + throw Object.assign(new Error(errorMessage), { 261 262 actual: result.actual, 262 263 expected: result.expected, 263 264 diffOptions: {