Approval-based snapshot testing library for Go (mirror)
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: don't truncate snapshot lines in tui view

+93 -23
-2
.gitignore
··· 5 5 *.dylib 6 6 *.test 7 7 *.out 8 - go.work 9 - go.work.sum 10 8 .env 11 9 cmd/tui/shutter 12 10 cmd/shutter/shutter
+1 -1
LICENSE
··· 1 1 MIT License 2 2 3 - Copyright (c) 2025 Patrick Dewey 3 + Copyright (c) 2025 - 2026 Patrick Dewey 4 4 5 5 Permission is hereby granted, free of charge, to any person obtaining a copy 6 6 of this software and associated documentation files (the "Software"), to deal
-1
cmd/shutter/go.sum
··· 33 33 github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= 34 34 github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 35 35 github.com/ptdewey/shutter v0.1.4 h1:tMTNMTxCpA1F0REyi+taztoHVe9EpB5sSKhaIBzYu1c= 36 - github.com/ptdewey/shutter v0.1.4/go.mod h1:teeIXF4LdgsE9E4kjHk9nGzDxl2cjdbVb1qbdzAHSR4= 37 36 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 38 37 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 39 38 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+2 -2
cmd/shutter/main.go
··· 258 258 259 259 // Show diff or new snapshot 260 260 if m.accepted != nil && m.diffLines != nil { 261 - b.WriteString(pretty.DiffSnapshotBox(m.accepted, m.newSnap, m.diffLines)) 261 + b.WriteString(pretty.DiffSnapshotBox(m.accepted, m.newSnap, m.diffLines, m.width)) 262 262 } else { 263 263 if m.newSnap != nil { 264 - b.WriteString(pretty.NewSnapshotBox(m.newSnap)) 264 + b.WriteString(pretty.NewSnapshotBox(m.newSnap, m.width)) 265 265 } 266 266 } 267 267
+6
go.work
··· 1 + go 1.25.2 2 + 3 + use ( 4 + . 5 + ./cmd/shutter 6 + )
+13
go.work.sum
··· 1 + github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= 2 + github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 3 + github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= 4 + github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 5 + github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= 6 + github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= 7 + github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 8 + github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 9 + github.com/ptdewey/shutter v0.1.4/go.mod h1:teeIXF4LdgsE9E4kjHk9nGzDxl2cjdbVb1qbdzAHSR4= 10 + github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= 11 + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 12 + golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 13 + golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+2 -2
internal/files/files_test.go
··· 256 256 if err != nil { 257 257 t.Fatalf("ListNewSnapshots failed: %v", err) 258 258 } 259 - 259 + 260 260 t.Logf("Found %d snapshots", len(snapshots)) 261 261 for _, snap := range snapshots { 262 262 t.Logf(" - Title: %s, Path: %s", snap.Title, snap.Path) 263 263 } 264 - 264 + 265 265 // Just verify it doesn't error - we can't make assumptions about which 266 266 // snapshots exist since this depends on the test environment 267 267 if err != nil {
+69 -15
internal/pretty/boxes.go
··· 8 8 "github.com/ptdewey/shutter/internal/files" 9 9 ) 10 10 11 - func NewSnapshotBox(snap *files.Snapshot) string { 12 - return newSnapshotBoxInternal(snap) 11 + func NewSnapshotBox(snap *files.Snapshot, width ...int) string { 12 + w := TerminalWidth() 13 + if len(width) > 0 && width[0] > 0 { 14 + w = width[0] 15 + } 16 + return newSnapshotBoxInternal(snap, w) 13 17 } 14 18 15 19 // calculateLineNumWidth returns the width needed to display line numbers ··· 31 35 } 32 36 } 33 37 34 - func DiffSnapshotBox(old, newSnapshot *files.Snapshot, diffLines []diff.DiffLine) string { 38 + func DiffSnapshotBox(old, newSnapshot *files.Snapshot, diffLines []diff.DiffLine, widthOpt ...int) string { 35 39 width := TerminalWidth() 40 + if len(widthOpt) > 0 && widthOpt[0] > 0 { 41 + width = widthOpt[0] 42 + } 36 43 snapshotFileName := files.SnapshotFileName(newSnapshot.Title) + ".snap" 37 44 38 45 var sb strings.Builder ··· 99 106 formatted = dl.Line 100 107 } 101 108 102 - // Adjust for actual display length considering ANSI codes 109 + // Wrap long lines instead of truncating 103 110 // Account for: 2 spaces padding + 2 line number columns + 2 spaces between + prefix + space 104 111 maxContentWidth := width - (lineNumWidth * 2) - 8 105 - if len(dl.Line) > maxContentWidth { 106 - truncated := dl.Line[:maxContentWidth-3] + "..." 107 - formatted = formatColoredLine(truncated, dl.Kind) 112 + if maxContentWidth < 20 { 113 + maxContentWidth = 20 108 114 } 109 115 110 - display := fmt.Sprintf("%s %s %s %s", leftNum, rightNum, prefix, formatted) 111 - sb.WriteString(fmt.Sprintf(" %s\n", display)) 116 + if len(dl.Line) > maxContentWidth { 117 + // Emit wrapped chunks with proper gutter alignment 118 + line := dl.Line 119 + first := true 120 + for len(line) > 0 { 121 + chunk := line 122 + if len(chunk) > maxContentWidth { 123 + chunk = line[:maxContentWidth] 124 + line = line[maxContentWidth:] 125 + } else { 126 + line = "" 127 + } 128 + coloredChunk := formatColoredLine(chunk, dl.Kind) 129 + if first { 130 + display := fmt.Sprintf("%s %s %s %s", leftNum, rightNum, prefix, coloredChunk) 131 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 132 + first = false 133 + } else { 134 + pad := strings.Repeat(" ", lineNumWidth) 135 + display := fmt.Sprintf("%s %s %s %s", pad, pad, "│", coloredChunk) 136 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 137 + } 138 + } 139 + } else { 140 + display := fmt.Sprintf("%s %s %s %s", leftNum, rightNum, prefix, formatted) 141 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 142 + } 112 143 } 113 144 114 145 // Bottom bar with corner (account for both line number columns) ··· 119 150 return sb.String() 120 151 } 121 152 122 - func newSnapshotBoxInternal(snap *files.Snapshot) string { 123 - width := TerminalWidth() 153 + func newSnapshotBoxInternal(snap *files.Snapshot, width int) string { 124 154 125 155 var sb strings.Builder 126 156 sb.WriteString("─── " + "New Snapshot " + strings.Repeat("─", width-15) + "\n\n") ··· 148 178 lineNum := fmt.Sprintf("%*d", lineNumWidth, i+1) 149 179 prefix := fmt.Sprintf("%s %s", Green(lineNum), Green("+")) 150 180 151 - if len(line) > width-len(prefix)-4 { 152 - line = line[:width-len(prefix)-7] + "..." 181 + maxContentWidth := width - lineNumWidth - 6 182 + if maxContentWidth < 20 { 183 + maxContentWidth = 20 153 184 } 154 185 155 - display := fmt.Sprintf("%s %s", prefix, Green(line)) 156 - sb.WriteString(fmt.Sprintf(" %s\n", display)) 186 + if len(line) > maxContentWidth { 187 + remaining := line 188 + first := true 189 + for len(remaining) > 0 { 190 + chunk := remaining 191 + if len(chunk) > maxContentWidth { 192 + chunk = remaining[:maxContentWidth] 193 + remaining = remaining[maxContentWidth:] 194 + } else { 195 + remaining = "" 196 + } 197 + if first { 198 + display := fmt.Sprintf("%s %s", prefix, Green(chunk)) 199 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 200 + first = false 201 + } else { 202 + pad := strings.Repeat(" ", lineNumWidth) 203 + display := fmt.Sprintf("%s %s %s", pad, "│", Green(chunk)) 204 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 205 + } 206 + } 207 + } else { 208 + display := fmt.Sprintf("%s %s", prefix, Green(line)) 209 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 210 + } 157 211 } 158 212 159 213 bottomBar := strings.Repeat("─", lineNumWidth+3) + "┴" +