import fs, { readFileSync } from 'node:fs' import { join } from 'node:path' import { playwright } from '@vitest/browser-playwright' import { expect, test } from 'vitest' import { editFile, runInlineTests, runVitest } from '../../test-utils' import { extractInlineSnaphsots } from './utils' test('custom snapshot matcher', async () => { const root = join(import.meta.dirname, 'fixtures/custom-matcher') const testFile = join(root, 'basic.test.ts') const snapshotFile = join(root, '__snapshots__/basic.test.ts.snap') const rawSnapshotFile = join(root, '__snapshots__/raw.txt') // remove snapshots fs.rmSync(join(root, '__snapshots__'), { recursive: true, force: true }) editFile(testFile, s => s.replace(/(toMatchCustom(?:Async)?InlineSnapshot)\(`[^`]*`\)/g, '$1()')) // create snapshots from scratch let result = await runVitest({ root, update: 'new' }) expect(result.stderr).toMatchInlineSnapshot(`""`) expect(readFileSync(snapshotFile, 'utf-8')).toMatchInlineSnapshot(` "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[\`file 1\`] = \` { "length": 6, "reversed": "ahahah", } \`; exports[\`properties 1 1\`] = \` { "length": 6, "reversed": "opopop", } \`; exports[\`properties 2 1\`] = \` { "length": toSatisfy<[Function lessThan10]>, "reversed": "epepep", } \`; " `) expect(readFileSync(rawSnapshotFile, 'utf-8')).toMatchInlineSnapshot(` "{ "length": 6, "reversed": "ihihih", }" `) expect(extractInlineSnaphsots(readFileSync(testFile, 'utf-8'))).toMatchInlineSnapshot(` " expect(\`hehehe\`).toMatchCustomInlineSnapshot(\` { "length": 6, "reversed": "eheheh", } \`) expect(\`huhuhu\`).toMatchCustomAsyncInlineSnapshot(\` { "length": 6, "reversed": "uhuhuh", } \`) " `) expect(result.errorTree()).toMatchInlineSnapshot(` { "basic.test.ts": { "async inline": "passed", "file": "passed", "inline": "passed", "properties 1": "passed", "properties 2": "passed", "raw": "passed", }, } `) // edit tests to introduce snapshot errors editFile(testFile, s => s .replace('`hahaha`', '`hahaha-edit`') .replace('`popopo`', '`popopo-edit`') .replace('`pepepe`', '`pepepe-edit`') .replace('`hihihi`', '`hihihi-edit`') .replace('`huhuhu`', '`huhuhu-edit`') .replace('`hehehe`', '`hehehe-edit`')) result = await runVitest({ root, update: 'none' }) expect(result.stderr).toMatchInlineSnapshot(` " ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯ FAIL basic.test.ts > file Error: [custom error] Snapshot \`file 1\` mismatched - Expected + Received { - "length": 6, + "length": 11, - "reversed": "ahahah", + "reversed": "tide-ahahah", } ❯ basic.test.ts:66:25 64| 65| test('file', () => { 66| expect(\`hahaha-edit\`).toMatchCustomSnapshot() | ^ 67| }) 68| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯ FAIL basic.test.ts > properties 1 Error: [custom error] Snapshot properties mismatched - Expected + Received { - "length": 6, + "length": 11, + "reversed": "tide-opopop", } ❯ basic.test.ts:70:25 68| 69| test('properties 1', () => { 70| expect(\`popopo-edit\`).toMatchCustomSnapshot({ length: 6 }) | ^ 71| }) 72| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯ FAIL basic.test.ts > properties 2 Error: [custom error] Snapshot properties mismatched - Expected + Received { - "length": toSatisfy<[Function lessThan10]>, + "length": 11, + "reversed": "tide-epepep", } ❯ basic.test.ts:74:25 72| 73| test('properties 2', () => { 74| expect(\`pepepe-edit\`).toMatchCustomSnapshot({ length: expect.toSatis… | ^ 75| }) 76| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯ FAIL basic.test.ts > raw Error: [custom error] Snapshot \`raw 1\` mismatched - Expected + Received { - "length": 6, + "length": 11, - "reversed": "ihihih", + "reversed": "tide-ihihih", } ❯ basic.test.ts:78:3 76| 77| test('raw', async () => { 78| await expect(\`hihihi-edit\`).toMatchCustomFileSnapshot('./__snapshots… | ^ 79| }) 80| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯ FAIL basic.test.ts > inline Error: [custom error] Snapshot \`inline 1\` mismatched - Expected + Received { - "length": 6, + "length": 11, - "reversed": "eheheh", + "reversed": "tide-eheheh", } ❯ basic.test.ts:82:25 80| 81| test('inline', () => { 82| expect(\`hehehe-edit\`).toMatchCustomInlineSnapshot(\` | ^ 83| { 84| "length": 6, ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯ FAIL basic.test.ts > async inline Error: [custom error] Snapshot \`async inline 1\` mismatched - Expected + Received { - "length": 6, + "length": 11, - "reversed": "uhuhuh", + "reversed": "tide-uhuhuh", } ❯ basic.test.ts:91:3 89| 90| test('async inline', async () => { 91| await expect(\`huhuhu-edit\`).toMatchCustomAsyncInlineSnapshot(\` | ^ 92| { 93| "length": 6, ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯ " `) expect(result.errorTree()).toMatchInlineSnapshot(` { "basic.test.ts": { "async inline": [ "[custom error] Snapshot \`async inline 1\` mismatched", ], "file": [ "[custom error] Snapshot \`file 1\` mismatched", ], "inline": [ "[custom error] Snapshot \`inline 1\` mismatched", ], "properties 1": [ "[custom error] Snapshot properties mismatched", ], "properties 2": [ "[custom error] Snapshot properties mismatched", ], "raw": [ "[custom error] Snapshot \`raw 1\` mismatched", ], }, } `) // run with update result = await runVitest({ root, update: 'all' }) expect(result.stderr).toMatchInlineSnapshot(` " ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯ FAIL basic.test.ts > properties 1 Error: [custom error] Snapshot properties mismatched - Expected + Received { - "length": 6, + "length": 11, + "reversed": "tide-opopop", } ❯ basic.test.ts:70:25 68| 69| test('properties 1', () => { 70| expect(\`popopo-edit\`).toMatchCustomSnapshot({ length: 6 }) | ^ 71| }) 72| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯ FAIL basic.test.ts > properties 2 Error: [custom error] Snapshot properties mismatched - Expected + Received { - "length": toSatisfy<[Function lessThan10]>, + "length": 11, + "reversed": "tide-epepep", } ❯ basic.test.ts:74:25 72| 73| test('properties 2', () => { 74| expect(\`pepepe-edit\`).toMatchCustomSnapshot({ length: expect.toSatis… | ^ 75| }) 76| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯ " `) expect(readFileSync(snapshotFile, 'utf-8')).toMatchInlineSnapshot(` "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[\`file 1\`] = \` { "length": 11, "reversed": "tide-ahahah", } \`; exports[\`properties 1 1\`] = \` { "length": 6, "reversed": "opopop", } \`; exports[\`properties 2 1\`] = \` { "length": toSatisfy<[Function lessThan10]>, "reversed": "epepep", } \`; " `) expect(readFileSync(rawSnapshotFile, 'utf-8')).toMatchInlineSnapshot(` "{ "length": 11, "reversed": "tide-ihihih", }" `) expect(extractInlineSnaphsots(readFileSync(testFile, 'utf-8'))).toMatchInlineSnapshot(` " expect(\`hehehe-edit\`).toMatchCustomInlineSnapshot(\` { "length": 11, "reversed": "tide-eheheh", } \`) expect(\`huhuhu-edit\`).toMatchCustomAsyncInlineSnapshot(\` { "length": 11, "reversed": "tide-uhuhuh", } \`) " `) expect(result.errorTree()).toMatchInlineSnapshot(` { "basic.test.ts": { "async inline": "passed", "file": "passed", "inline": "passed", "properties 1": [ "[custom error] Snapshot properties mismatched", ], "properties 2": [ "[custom error] Snapshot properties mismatched", ], "raw": "passed", }, } `) }) test('browser', async () => { const result = await runInlineTests({ 'basic.test.ts': ` import { test, expect, Snapshots } from 'vitest' const { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot, } = Snapshots expect.extend({ toMatchTrimmedSnapshot(received: string) { return toMatchSnapshot.call(this, received.slice(0, 10)) }, toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) { return toMatchInlineSnapshot.call(this, received.slice(0, 10), inlineSnapshot) }, async toMatchTrimmedFileSnapshot(received: string, filepath: string) { return toMatchFileSnapshot.call(this, received.slice(0, 10), filepath) }, }) test('file snapshot', () => { expect('extra long string oh my gerd').toMatchTrimmedSnapshot() }) test('inline snapshot', () => { expect('super long string oh my gerd').toMatchTrimmedInlineSnapshot() }) test('raw file snapshot', async () => { await expect('crazy long string oh my gerd').toMatchTrimmedFileSnapshot('./raw.txt') }) `, }, { update: 'all', browser: { enabled: true, headless: true, screenshotFailures: false, provider: playwright(), instances: [ { browser: 'chromium', }, ], }, }) expect(result.stderr).toMatchInlineSnapshot(`""`) expect(result.errorTree()).toMatchInlineSnapshot(` { "basic.test.ts": { "file snapshot": "passed", "inline snapshot": "passed", "raw file snapshot": "passed", }, } `) expect(result.fs.readFile('__snapshots__/basic.test.ts.snap')).toMatchInlineSnapshot(` "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[\`file snapshot 1\`] = \`"extra long"\`; " `) expect(extractInlineSnaphsots(result.fs.readFile('basic.test.ts'))).toMatchInlineSnapshot(` " expect('super long string oh my gerd').toMatchTrimmedInlineSnapshot(\`"super long"\`) " `) expect(result.fs.readFile('raw.txt')).toMatchInlineSnapshot(`"crazy long"`) }) test('outer expect message is prefixed by jest-extend for Snapshots wrappers', async () => { const result = await runInlineTests({ 'basic.test.ts': ` import { test, expect, Snapshots } from 'vitest' const { toMatchInlineSnapshot, } = Snapshots expect.extend({ toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) { return toMatchInlineSnapshot.call(this, received.slice(0, 5), inlineSnapshot) }, }) test('custom snapshot matcher', () => { expect('abcdefghij', 'outer message').toMatchTrimmedInlineSnapshot(\`"wrong"\`) }) test('builtin', () => { expect('abcdefghij', 'outer message').toMatchInlineSnapshot(\`"wrong"\`) }) test('builtin properties mismatch', () => { expect({ value: 1 }, 'outer message').toMatchSnapshot({ value: expect.any(String), }) }) `, }, { update: 'none', }) expect(result.stderr).toMatchInlineSnapshot(` " ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯ FAIL basic.test.ts > custom snapshot matcher Error: outer message: Snapshot \`custom snapshot matcher 1\` mismatched Expected: ""wrong"" Received: ""abcde"" ❯ basic.test.ts:15:41 13| 14| test('custom snapshot matcher', () => { 15| expect('abcdefghij', 'outer message').toMatchTrimmedInlineSnapshot(\`… | ^ 16| }) 17| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯ FAIL basic.test.ts > builtin Error: outer message: Snapshot \`builtin 1\` mismatched Expected: ""wrong"" Received: ""abcdefghij"" ❯ basic.test.ts:19:41 17| 18| test('builtin', () => { 19| expect('abcdefghij', 'outer message').toMatchInlineSnapshot(\`"wrong"… | ^ 20| }) 21| ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯ FAIL basic.test.ts > builtin properties mismatch Error: outer message: Snapshot properties mismatched - Expected + Received { - "value": Any, + "value": 1, } ❯ basic.test.ts:23:41 21| 22| test('builtin properties mismatch', () => { 23| expect({ value: 1 }, 'outer message').toMatchSnapshot({ | ^ 24| value: expect.any(String), 25| }) ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯ " `) expect(result.errorTree()).toMatchInlineSnapshot(` { "basic.test.ts": { "builtin": [ "outer message: Snapshot \`builtin 1\` mismatched", ], "builtin properties mismatch": [ "outer message: Snapshot properties mismatched", ], "custom snapshot matcher": [ "outer message: Snapshot \`custom snapshot matcher 1\` mismatched", ], }, } `) })