diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/package.json b/packages/cli/snap-tests/test-inline-snapshot-indent/package.json new file mode 100644 index 0000000000..d007f670b3 --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-inline-snapshot-indent", + "private": true +} diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt b/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt new file mode 100644 index 0000000000..2fb66bcc52 --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/snap.txt @@ -0,0 +1,23 @@ +> vp test run -u src/inline-snapshot.test.ts # write inline snapshot via --update (regression test for #1553) + RUN + + ✓ src/inline-snapshot.test.ts (1 test) ms + + Snapshots 1 written + Test Files 1 passed (1) + Tests 1 passed (1) + Start at + Duration ms (transform ms, setup ms, import ms, tests ms, environment ms) + + +> cat src/inline-snapshot.test.ts # snapshot must use 2-space indentation, not tabs +import { describe, expect, it } from 'vite-plus/test'; + +describe('inline snapshot indentation', () => { + it('writes multiline snapshots using the surrounding file indentation style', () => { + expect('alpha\nbeta').toMatchInlineSnapshot(` + "alpha + beta" + `); + }); +}); diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts b/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts new file mode 100644 index 0000000000..1e6d7eb2bb --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/src/inline-snapshot.test.ts @@ -0,0 +1,7 @@ +import { describe, expect, it } from 'vite-plus/test'; + +describe('inline snapshot indentation', () => { + it('writes multiline snapshots using the surrounding file indentation style', () => { + expect('alpha\nbeta').toMatchInlineSnapshot(); + }); +}); diff --git a/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json new file mode 100644 index 0000000000..8e3ce00a6c --- /dev/null +++ b/packages/cli/snap-tests/test-inline-snapshot-indent/steps.json @@ -0,0 +1,7 @@ +{ + "ignoredPlatforms": ["win32"], + "commands": [ + "vp test run -u src/inline-snapshot.test.ts # write inline snapshot via --update (regression test for #1553)", + "cat src/inline-snapshot.test.ts # snapshot must use 2-space indentation, not tabs" + ] +} diff --git a/packages/test/__tests__/build-artifacts.spec.ts b/packages/test/__tests__/build-artifacts.spec.ts index 0767207323..641b98260c 100644 --- a/packages/test/__tests__/build-artifacts.spec.ts +++ b/packages/test/__tests__/build-artifacts.spec.ts @@ -65,6 +65,27 @@ describe('build artifacts', () => { }); }); + /** + * `convertTabsToSpaces()` in build.ts must not touch tabs inside string + * literals. Upstream `@vitest/snapshot` decides multi-line snapshot + * indentation via `indent.includes("\t")` — where `"\t"` is a literal + * tab byte in the bundled source. A blanket tab→spaces rewrite turned + * this into `indent.includes(" ")`, so every 2-space indent matched + * and the tab-appending branch always ran, producing tab-indented + * snapshots in 2-space files. + * + * See: https://github.com/voidzero-dev/vite-plus/issues/1553 + */ + describe('snapshot indent check (regression test for #1553)', () => { + const snapshotIndexPath = path.join(distDir, '@vitest/snapshot/index.js'); + + it('preserves the literal tab byte inside the indent.includes string', () => { + const content = fs.readFileSync(snapshotIndexPath, 'utf-8'); + expect(content).toContain('indent.includes("\t")'); + expect(content).not.toMatch(/indent\.includes\(" "\)/); + }); + }); + /** * Third-party packages that call `expect.extend()` internally * (e.g., @testing-library/jest-dom) break under npm override because diff --git a/packages/test/build.ts b/packages/test/build.ts index 444963eec7..dd313749cf 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -1365,18 +1365,26 @@ async function patchVitestCoreResolver() { } /** - * Convert tabs to spaces in all JS files in dist/ for consistent formatting. - * This allows our patching code to use space-based patterns instead of tabs. + * Convert leading tabs to spaces in all JS files in dist/ for consistent + * formatting. This allows our patching code to use space-based patterns + * instead of tabs. + * + * Only leading whitespace is rewritten — tabs inside string or template + * literals are semantically meaningful (e.g. `indent.includes("\t")` in + * @vitest/snapshot picks the snapshot indent style by checking for a + * literal tab byte) and must be preserved. + * + * See: https://github.com/voidzero-dev/vite-plus/issues/1553 */ async function convertTabsToSpaces() { - console.log('\nConverting tabs to spaces in dist/...'); + console.log('\nConverting leading tabs to spaces in dist/...'); let convertedCount = 0; for await (const file of fsGlob(resolve(distDir, '**/*.js'))) { const content = await readFile(file, 'utf-8'); - if (content.includes('\t')) { - const converted = content.replace(/\t/g, ' '); + const converted = content.replace(/^\t+/gm, (match) => ' '.repeat(match.length)); + if (converted !== content) { await writeFile(file, converted); convertedCount++; }