-
-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Problem
MatchStandaloneSnapshot passes input through pretty.Sprint (github.com/kr/pretty) before writing and comparing snapshots. pretty.Sprint wraps all output through a tabwriter (formatter.go#L56) which silently expands tab bytes (0x09) into spaces.
This means snapshot files don't contain the exact bytes the program under test produces. For our use case — a Dockerfile linter/formatter that outputs tab-indented continuation lines — the .snap.Dockerfile files show 4 spaces where the actual output has tabs.
The comparison still passes because pretty.Sprint is applied symmetrically (both to the new input and when the snapshot was written), but the snapshot files on disk are not faithful representations of the actual output.
Why the existing API can't work around this
- All internal helpers (
snapshotPath,prettyDiff,upsertStandaloneSnapshot,standaloneTestsRegistry) are unexported, so callers can't reuse them selectively. pretty.Sprintwraps output throughtabwriterunconditionally whenforce: true(whichSprintalways sets). Implementingfmt.GoStringerorfmt.Formatteron the input type doesn't help — the output still flows through the tabwriter.- Post-processing the written file doesn't work either: on the next run,
getPrevStandaloneSnapshotreads raw bytes from disk whilepretty.Sprint(input)produces the mangled version, causing an asymmetric mismatch.
Proposed solution
A new config option (e.g. snaps.Raw()) that tells MatchStandaloneSnapshot to use fmt.Sprint (or just a string conversion) instead of pretty.Sprint:
snaps.WithConfig(snaps.Raw(), snaps.Ext(".Dockerfile")).MatchStandaloneSnapshot(t, content)The implementation change in matchStandaloneSnapshot would be minimal:
// current
snapshot := pretty.Sprint(input)
// with Raw() option
var snapshot string
if c.raw {
snapshot = fmt.Sprint(input)
} else {
snapshot = pretty.Sprint(input)
}Current workaround
We maintain a small helper that reimplements the standalone snapshot write/read/compare cycle without pretty.Sprint: https://github.com/wharflab/tally/blob/main/internal/testutil/snapshot.go
This works but duplicates path computation and snapshot management logic that go-snaps already handles well.
References
kr/prettytabwriter setup: https://github.com/kr/pretty/blob/v0.3.1/formatter.go#L56matchStandaloneSnapshotpretty.Sprint call:go-snaps/snaps/matchStandaloneSnapshot.go
Line 50 in 1c47e5c
snapshot := pretty.Sprint(input)