Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/blue-pugs-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'vitest-plugin-vis': patch
---

Show snapshot paths in failure messages relative to the project root (`./__vis__/...`) instead of absolute.

Editors and terminals resolve the relative path just as well, and the output no longer leaks the local absolute path when a failure is pasted into a bug report. Paths outside the project root stay absolute. Filesystem operations are unaffected and still resolve to absolute paths.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ it('fails when the image is different', async ({ expect }) => {
},
(error) => {
expect(error.message).toMatch(/Expected image to match but was differ by \d+ pixels./)
expect(error.message).toMatch(/Expected:\s+\.\/__vis__\/.*\.png/)
expect(error.message).toMatch(/Actual:\s+\.\/__vis__\/.*\.png/)
expect(error.message).toMatch(/Difference:\s+\.\/__vis__\/.*\.png/)
},
)
})
Expand Down Expand Up @@ -298,6 +301,8 @@ it('should fail with additional info when it does not fail with expectToFail', a
expect(error.message).toMatch(/Snapshot .* matched but expected to fail/)
expect(error.message).toMatch(/Options:\s+failureThreshold: \d+ pixels/)
expect(error.message).toMatch(/Diff:\s+\d+ pixels/)
expect(error.message).toMatch(/Expected:\s+\.\/__vis__\/.*\.png/)
expect(error.message).toMatch(/Actual:\s+\.\/__vis__\/.*\.png/)
},
)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isAbsolute, relative, resolve } from 'pathe'
import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'

export function prettifyOptions(options: ToMatchImageSnapshotOptions<any> | undefined) {
Expand All @@ -13,3 +14,19 @@ export function prettifyOptions(options: ToMatchImageSnapshotOptions<any> | unde
.filter(Boolean)
.join('\n ')
}

/**
* Formats a snapshot path for display in a failure message.
*
* Paths within the project root are shown relative to it so that the failure output can be shared
* verbatim without leaking the local absolute path, while remaining clickable in editors and terminals.
* Paths outside the project root stay absolute, as a `../../..` chain is neither shorter nor clearer.
*/
export function formatSnapshotPath(projectRoot: string, path: string) {
const absolutePath = resolve(projectRoot, path)
const relativePath = relative(projectRoot, absolutePath)
if (!relativePath || relativePath === '..' || relativePath.startsWith('../') || isAbsolute(relativePath)) {
return absolutePath
}
return `./${relativePath}`
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { ImageSnapshotComparisonInfo, ToMatchImageSnapshotOptions } from '.
import { toDataURL, toImageData } from '../external/browser/image_data.ts'
import { server } from '../external/vitest/vitest_browser_context_proxy.ts'
import { alignImagesToSameSize } from '../image/align_images.ts'
import { prettifyOptions } from './compare_image_snapshot.logic.ts'
import { formatSnapshotPath, prettifyOptions } from './compare_image_snapshot.logic.ts'

export async function compareImageSnapshot(
commands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,
Expand All @@ -31,7 +31,7 @@ export async function compareImageSnapshot(
dedent`Snapshot \`${taskId}\` has no baseline image. Please review the new snapshot and update the baseline image.

Options: ${prettifyOptions(options)}
Actual: ${resolve(info.projectRoot, info.resultPath)}`,
Actual: ${formatSnapshotPath(info.projectRoot, info.resultPath)}`,
)
}

Expand All @@ -55,8 +55,8 @@ export async function compareImageSnapshot(
Options: ${prettifyOptions(options)}
Diff: ${options.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}

Expected: ${resolve(info.projectRoot, info.baselinePath)}
Actual: ${resolve(info.projectRoot, info.resultPath)}`,
Expected: ${formatSnapshotPath(info.projectRoot, info.baselinePath)}
Actual: ${formatSnapshotPath(info.projectRoot, info.resultPath)}`,
)
}
return
Expand Down Expand Up @@ -85,9 +85,9 @@ export async function compareImageSnapshot(

Options: ${prettifyOptions(options)}

Expected: ${resolve(info.projectRoot, info.baselinePath)}
Actual: ${resolve(info.projectRoot, info.resultPath)}
Difference: ${resolve(info.projectRoot, info.diffPath)}`,
Expected: ${formatSnapshotPath(info.projectRoot, info.baselinePath)}
Actual: ${formatSnapshotPath(info.projectRoot, info.resultPath)}
Difference: ${formatSnapshotPath(info.projectRoot, info.diffPath)}`,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it } from 'vitest'
import { prettifyOptions } from './compare_image_snapshot.logic.ts'
import { describe, it } from 'vitest'
import { formatSnapshotPath, prettifyOptions } from './compare_image_snapshot.logic.ts'

it('returns none when no options', ({ expect }) => {
expect(prettifyOptions(undefined)).toBe('none')
Expand All @@ -23,3 +23,31 @@ it('stringify diffOptions', ({ expect }) => {
/failureThreshold: 0 pixels\s{17}comparisonMethod: pixel\s{17}diffOptions: {"threshold":0.1}/,
)
})

describe('formatSnapshotPath', () => {
it('formats a path within the project root as a relative path', ({ expect }) => {
expect(formatSnapshotPath('/home/me/project', '__vis__/local/__baselines__/some.spec.tsx/case-1.png')).toBe(
'./__vis__/local/__baselines__/some.spec.tsx/case-1.png',
)
})

it('does not leak the project root of an absolute path within the project', ({ expect }) => {
expect(formatSnapshotPath('/home/me/project', '/home/me/project/__vis__/local/case-1.png')).toBe(
'./__vis__/local/case-1.png',
)
})

it('keeps a path outside the project root absolute', ({ expect }) => {
expect(formatSnapshotPath('/home/me/project/packages/app', '../../__vis__/local/case-1.png')).toBe(
'/home/me/project/__vis__/local/case-1.png',
)
})

it('keeps the project root itself absolute', ({ expect }) => {
expect(formatSnapshotPath('/home/me/project', '.')).toBe('/home/me/project')
})

it('does not mistake a leading dot-dot in a filename for an escape', ({ expect }) => {
expect(formatSnapshotPath('/home/me/project', '..vis/case-1.png')).toBe('./..vis/case-1.png')
})
})