diff --git a/packages/vitest/src/integrations/snapshot/chai.ts b/packages/vitest/src/integrations/snapshot/chai.ts index 27bd424e4fef..4e2d50e28f96 100644 --- a/packages/vitest/src/integrations/snapshot/chai.ts +++ b/packages/vitest/src/integrations/snapshot/chai.ts @@ -143,12 +143,6 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { throw new Error('toMatchInlineSnapshot cannot be used with "not"') } const test = getTest('toMatchInlineSnapshot', this) - const isInsideEach = test.each || test.suite?.each - if (isInsideEach) { - throw new Error( - 'InlineSnapshot cannot be used inside of test.each or describe.each', - ) - } const expected = utils.flag(this, 'object') const error = utils.flag(this, 'error') if (typeof properties === 'string') { @@ -211,12 +205,6 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { ) } const test = getTest('toThrowErrorMatchingInlineSnapshot', this) - const isInsideEach = test.each || test.suite?.each - if (isInsideEach) { - throw new Error( - 'InlineSnapshot cannot be used inside of test.each or describe.each', - ) - } const expected = utils.flag(this, 'object') const error = utils.flag(this, 'error') const promise = utils.flag(this, 'promise') as string | undefined diff --git a/test/cli/fixtures/fails/inline-snapshop-inside-each.test.ts b/test/cli/fixtures/fails/inline-snapshop-inside-each.test.ts deleted file mode 100644 index 8a66298bc215..000000000000 --- a/test/cli/fixtures/fails/inline-snapshop-inside-each.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, test } from 'vitest' - -test.each([1])('', () => { - expect('').toMatchInlineSnapshot() -}) - -describe.each([1])('', () => { - test('', () => { - expect('').toMatchInlineSnapshot() - }) - - test.each([1])('', () => { - expect('').toMatchInlineSnapshot() - }) - - test('', () => { - expect(() => { - throw new Error('1') - }).toThrowErrorMatchingInlineSnapshot() - }) - - test.each([1])('', () => { - expect(() => { - throw new Error('1') - }).toThrowErrorMatchingInlineSnapshot() - }) -}) diff --git a/test/cli/test/__snapshots__/fails.test.ts.snap b/test/cli/test/__snapshots__/fails.test.ts.snap index 6afab023cb1f..80a8c0ff3a45 100644 --- a/test/cli/test/__snapshots__/fails.test.ts.snap +++ b/test/cli/test/__snapshots__/fails.test.ts.snap @@ -49,14 +49,6 @@ exports[`should fail hooks-timeout-before-hook-cleanup-callback.test.ts 1`] = ` Error: Hook timed out in 102ms." `; -exports[`should fail inline-snapshop-inside-each.test.ts 1`] = ` -"Error: InlineSnapshot cannot be used inside of test.each or describe.each -Error: InlineSnapshot cannot be used inside of test.each or describe.each -Error: InlineSnapshot cannot be used inside of test.each or describe.each -Error: InlineSnapshot cannot be used inside of test.each or describe.each -Error: InlineSnapshot cannot be used inside of test.each or describe.each" -`; - exports[`should fail mock-import-proxy-module.test.ts 1`] = `"Error: There are some problems in resolving the mocks API."`; exports[`should fail nested-suite.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`; diff --git a/test/snapshots/test/fixtures/inline-multiple-calls/each.test.ts b/test/snapshots/test/fixtures/inline-multiple-calls/each.test.ts new file mode 100644 index 000000000000..28c683717586 --- /dev/null +++ b/test/snapshots/test/fixtures/inline-multiple-calls/each.test.ts @@ -0,0 +1,17 @@ +import { expect, test, describe } from "vitest"; + +test.for(["hello", "world"])("test %s", (arg) => { + expect(arg.length).toMatchInlineSnapshot(`5`); +}); + +describe.for(["hello", "world"])("suite %s", (arg) => { + test("length", () => { + expect(arg.length).toMatchInlineSnapshot(`5`); + }); +}); + +test.for(["hello", "world"])("toThrowErrorMatchingInlineSnapshot %s", (arg) => { + expect(() => { + throw new Error(`length = ${arg.length}`); + }).toThrowErrorMatchingInlineSnapshot(`[Error: length = 5]`) +}); diff --git a/test/snapshots/test/inline-multiple-calls.test.ts b/test/snapshots/test/inline-multiple-calls.test.ts index 0bddd9b8d0bc..cfca232c688e 100644 --- a/test/snapshots/test/inline-multiple-calls.test.ts +++ b/test/snapshots/test/inline-multiple-calls.test.ts @@ -1,4 +1,4 @@ -import fs from 'node:fs' +import fs, { readFileSync } from 'node:fs' import { join } from 'pathe' import { expect, test } from 'vitest' import { editFile, runVitest } from '../../test-utils' @@ -384,3 +384,342 @@ Received: ""test1"" `) expect(fs.readFileSync(testFile, 'utf-8')).toContain('expect(value).toMatchInlineSnapshot(`"test2"`)') }) + +test('test.each/for', async () => { + const root = join(import.meta.dirname, 'fixtures/inline-multiple-calls') + const testFile = join(root, 'each.test.ts') + + // remove inline snapshots + editFile(testFile, s => s + .replace(/toMatchInlineSnapshot\(`[^`]*`\)/g, 'toMatchInlineSnapshot()') + .replace(/toThrowErrorMatchingInlineSnapshot\(`[^`]*`\)/g, 'toThrowErrorMatchingInlineSnapshot()')) + + // create snapshots from scratch + let result = await runVitest({ root, include: [testFile], update: 'new' }) + expect(result.stderr).toMatchInlineSnapshot(`""`) + expect(readFileSync(testFile, 'utf-8')).toMatchInlineSnapshot(` + "import { expect, test, describe } from "vitest"; + + test.for(["hello", "world"])("test %s", (arg) => { + expect(arg.length).toMatchInlineSnapshot(\`5\`); + }); + + describe.for(["hello", "world"])("suite %s", (arg) => { + test("length", () => { + expect(arg.length).toMatchInlineSnapshot(\`5\`); + }); + }); + + test.for(["hello", "world"])("toThrowErrorMatchingInlineSnapshot %s", (arg) => { + expect(() => { + throw new Error(\`length = \${arg.length}\`); + }).toThrowErrorMatchingInlineSnapshot(\`[Error: length = 5]\`) + }); + " + `) + expect(result.errorTree()).toMatchInlineSnapshot(` + Object { + "each.test.ts": Object { + "suite hello": Object { + "length": "passed", + }, + "suite world": Object { + "length": "passed", + }, + "test hello": "passed", + "test world": "passed", + "toThrowErrorMatchingInlineSnapshot hello": "passed", + "toThrowErrorMatchingInlineSnapshot world": "passed", + }, + } + `) + expect(result.ctx?.snapshot.summary).toMatchInlineSnapshot(` + Object { + "added": 6, + "didUpdate": false, + "failure": false, + "filesAdded": 1, + "filesRemoved": 0, + "filesRemovedList": Array [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 6, + "unchecked": 0, + "uncheckedKeysByFile": Array [], + "unmatched": 0, + "updated": 0, + } + `) + + // edit tests to introduce errors + editFile(testFile, s => s.replaceAll(`"hello"`, `"hey"`)) + + // fails with update=false + result = await runVitest({ root, include: [testFile], update: false }) + expect(result.stderr).toMatchInlineSnapshot(` + " + ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL each.test.ts > test hey + Error: Snapshot \`test hey 1\` mismatched + + Expected: "5" + Received: "3" + + ❯ each.test.ts:4:22 + 2| + 3| test.for(["hey", "world"])("test %s", (arg) => { + 4| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 5| }); + 6| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯ + + FAIL each.test.ts > test world + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "3" + Received: "5" + + ❯ each.test.ts:4:22 + 2| + 3| test.for(["hey", "world"])("test %s", (arg) => { + 4| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 5| }); + 6| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯ + + FAIL each.test.ts > suite hey > length + Error: Snapshot \`suite hey > length 1\` mismatched + + Expected: "5" + Received: "3" + + ❯ each.test.ts:9:24 + 7| describe.for(["hey", "world"])("suite %s", (arg) => { + 8| test("length", () => { + 9| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 10| }); + 11| }); + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯ + + FAIL each.test.ts > suite world > length + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "3" + Received: "5" + + ❯ each.test.ts:9:24 + 7| describe.for(["hey", "world"])("suite %s", (arg) => { + 8| test("length", () => { + 9| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 10| }); + 11| }); + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯ + + FAIL each.test.ts > toThrowErrorMatchingInlineSnapshot hey + Error: Snapshot \`toThrowErrorMatchingInlineSnapshot hey 1\` mismatched + + Expected: "[Error: length = 5]" + Received: "[Error: length = 3]" + + ❯ each.test.ts:16:6 + 14| expect(() => { + 15| throw new Error(\`length = \${arg.length}\`); + 16| }).toThrowErrorMatchingInlineSnapshot(\`[Error: length = 5]\`) + | ^ + 17| }); + 18| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯ + + FAIL each.test.ts > toThrowErrorMatchingInlineSnapshot world + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "[Error: length = 3]" + Received: "[Error: length = 5]" + + ❯ each.test.ts:16:6 + 14| expect(() => { + 15| throw new Error(\`length = \${arg.length}\`); + 16| }).toThrowErrorMatchingInlineSnapshot(\`[Error: length = 5]\`) + | ^ + 17| }); + 18| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯ + + " + `) + expect(result.errorTree()).toMatchInlineSnapshot(` + Object { + "each.test.ts": Object { + "suite hey": Object { + "length": Array [ + "Snapshot \`suite hey > length 1\` mismatched", + ], + }, + "suite world": Object { + "length": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + }, + "test hey": Array [ + "Snapshot \`test hey 1\` mismatched", + ], + "test world": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + "toThrowErrorMatchingInlineSnapshot hey": Array [ + "Snapshot \`toThrowErrorMatchingInlineSnapshot hey 1\` mismatched", + ], + "toThrowErrorMatchingInlineSnapshot world": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + }, + } + `) + expect(result.ctx?.snapshot.summary).toMatchInlineSnapshot(` + Object { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": Array [], + "filesUnmatched": 1, + "filesUpdated": 0, + "matched": 0, + "total": 3, + "unchecked": 0, + "uncheckedKeysByFile": Array [], + "unmatched": 3, + "updated": 0, + } + `) + + // fails with update=all + result = await runVitest({ root, include: [testFile], update: 'all' }) + expect(result.stderr).toMatchInlineSnapshot(` + " + ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯ + + FAIL each.test.ts > test world + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "3" + Received: "5" + + ❯ each.test.ts:4:22 + 2| + 3| test.for(["hey", "world"])("test %s", (arg) => { + 4| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 5| }); + 6| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯ + + FAIL each.test.ts > suite world > length + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "3" + Received: "5" + + ❯ each.test.ts:9:24 + 7| describe.for(["hey", "world"])("suite %s", (arg) => { + 8| test("length", () => { + 9| expect(arg.length).toMatchInlineSnapshot(\`5\`); + | ^ + 10| }); + 11| }); + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯ + + FAIL each.test.ts > toThrowErrorMatchingInlineSnapshot world + Error: toMatchInlineSnapshot with different snapshots cannot be called at the same location + + Expected: "[Error: length = 3]" + Received: "[Error: length = 5]" + + ❯ each.test.ts:16:6 + 14| expect(() => { + 15| throw new Error(\`length = \${arg.length}\`); + 16| }).toThrowErrorMatchingInlineSnapshot(\`[Error: length = 5]\`) + | ^ + 17| }); + 18| + + ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯ + + " + `) + expect(result.errorTree()).toMatchInlineSnapshot(` + Object { + "each.test.ts": Object { + "suite hey": Object { + "length": "passed", + }, + "suite world": Object { + "length": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + }, + "test hey": "passed", + "test world": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + "toThrowErrorMatchingInlineSnapshot hey": "passed", + "toThrowErrorMatchingInlineSnapshot world": Array [ + "toMatchInlineSnapshot with different snapshots cannot be called at the same location", + ], + }, + } + `) + expect(readFileSync(testFile, 'utf-8')).toMatchInlineSnapshot(` + "import { expect, test, describe } from "vitest"; + + test.for(["hey", "world"])("test %s", (arg) => { + expect(arg.length).toMatchInlineSnapshot(\`5\`); + }); + + describe.for(["hey", "world"])("suite %s", (arg) => { + test("length", () => { + expect(arg.length).toMatchInlineSnapshot(\`5\`); + }); + }); + + test.for(["hey", "world"])("toThrowErrorMatchingInlineSnapshot %s", (arg) => { + expect(() => { + throw new Error(\`length = \${arg.length}\`); + }).toThrowErrorMatchingInlineSnapshot(\`[Error: length = 5]\`) + }); + " + `) + expect(result.ctx?.snapshot.summary).toMatchInlineSnapshot(` + Object { + "added": 0, + "didUpdate": true, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": Array [], + "filesUnmatched": 0, + "filesUpdated": 1, + "matched": 0, + "total": 3, + "unchecked": 0, + "uncheckedKeysByFile": Array [], + "unmatched": 0, + "updated": 3, + } + `) +})