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

Configure Feed

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

refactor: internal packages

+425 -277
+4
README.md
··· 12 12 13 13 TODO: 14 14 15 + ## Disclaimer 16 + 17 + - This package was largely vibe coded, your mileage may vary (but this library provides more of what I want than the ones below). 18 + 15 19 ## Other Libraries 16 20 17 21 - [go-snaps](https://github.com/gkampitakis/go-snaps)
__snapshots__/test_map.new __snapshots__/test_map.accepted
__snapshots__/test_snap_custom_type.new __snapshots__/test_snap_custom_type.accepted
+13
__snapshots__/test_snap_multiple.accepted
··· 1 + --- 2 + version: 0.1.0 3 + test_name: TestSnapMultiple 4 + --- 5 + value1 6 + value2 7 + 42 8 + foo 9 + bar 10 + baz 11 + wibble 12 + wobble 13 + tick
+1 -1
__snapshots__/test_snap_multiple.new
··· 10 10 baz 11 11 wibble 12 12 wobble 13 - tick 13 + tock
__snapshots__/test_snap_string.new __snapshots__/test_snap_string.accepted
+82
api.go
··· 1 + package freeze 2 + 3 + import ( 4 + "github.com/ptdewey/freeze/internal/diff" 5 + "github.com/ptdewey/freeze/internal/files" 6 + "github.com/ptdewey/freeze/internal/pretty" 7 + ) 8 + 9 + type Snapshot = files.Snapshot 10 + 11 + type DiffLine = diff.DiffLine 12 + 13 + const ( 14 + DiffShared = diff.DiffShared 15 + DiffOld = diff.DiffOld 16 + DiffNew = diff.DiffNew 17 + ) 18 + 19 + func Deserialize(raw string) (*Snapshot, error) { 20 + return files.Deserialize(raw) 21 + } 22 + 23 + func SaveSnapshot(snap *Snapshot, state string) error { 24 + return files.SaveSnapshot(snap, state) 25 + } 26 + 27 + func ReadSnapshot(testName string, state string) (*Snapshot, error) { 28 + return files.ReadSnapshot(testName, state) 29 + } 30 + 31 + func SnapshotFileName(testName string) string { 32 + return files.SnapshotFileName(testName) 33 + } 34 + 35 + func Histogram(old, new string) []DiffLine { 36 + return diff.Histogram(old, new) 37 + } 38 + 39 + func NewSnapshotBox(snap *Snapshot) string { 40 + return pretty.NewSnapshotBox(snap) 41 + } 42 + 43 + func DiffSnapshotBox(old, new *Snapshot) string { 44 + diffLines := convertDiffLines(diff.Histogram(old.Content, new.Content)) 45 + return pretty.DiffSnapshotBox(old, new, diffLines) 46 + } 47 + 48 + func Red(s string) string { 49 + return pretty.Red(s) 50 + } 51 + 52 + func Green(s string) string { 53 + return pretty.Green(s) 54 + } 55 + 56 + func Yellow(s string) string { 57 + return pretty.Yellow(s) 58 + } 59 + 60 + func Blue(s string) string { 61 + return pretty.Blue(s) 62 + } 63 + 64 + func Gray(s string) string { 65 + return pretty.Gray(s) 66 + } 67 + 68 + func Bold(s string) string { 69 + return pretty.Bold(s) 70 + } 71 + 72 + func TerminalWidth() int { 73 + return pretty.TerminalWidth() 74 + } 75 + 76 + func ClearScreen() { 77 + pretty.ClearScreen() 78 + } 79 + 80 + func ClearLine() { 81 + pretty.ClearLine() 82 + }
+1 -1
diff.go internal/diff/diff.go
··· 1 - package freeze 1 + package diff 2 2 3 3 import ( 4 4 "strings"
+50 -6
files.go internal/files/files.go
··· 1 - package freeze 1 + package files 2 2 3 3 import ( 4 4 "fmt" ··· 8 8 "strings" 9 9 ) 10 10 11 + type Snapshot struct { 12 + Version string 13 + TestName string 14 + Content string 15 + } 16 + 17 + func (s *Snapshot) Serialize() string { 18 + header := fmt.Sprintf("---\nversion: %s\ntest_name: %s\n---\n", s.Version, s.TestName) 19 + return header + s.Content 20 + } 21 + 22 + func Deserialize(raw string) (*Snapshot, error) { 23 + parts := strings.SplitN(raw, "---\n", 3) 24 + if len(parts) < 3 { 25 + return nil, fmt.Errorf("invalid snapshot format") 26 + } 27 + 28 + header := parts[1] 29 + content := parts[2] 30 + 31 + snap := &Snapshot{ 32 + Content: content, 33 + } 34 + 35 + for _, line := range strings.Split(header, "\n") { 36 + line = strings.TrimSpace(line) 37 + if line == "" { 38 + continue 39 + } 40 + 41 + kv := strings.SplitN(line, ": ", 2) 42 + if len(kv) != 2 { 43 + continue 44 + } 45 + 46 + key, value := kv[0], kv[1] 47 + switch key { 48 + case "version": 49 + snap.Version = value 50 + case "test_name": 51 + snap.TestName = value 52 + } 53 + } 54 + 55 + return snap, nil 56 + } 57 + 11 58 func findProjectRoot() (string, error) { 12 59 cwd, err := os.Getwd() 13 60 if err != nil { ··· 34 81 return "", err 35 82 } 36 83 37 - // TODO: pull this from config. 38 - // config should allow having snapshot dir at project root (with or w/o subdirs) 39 - // or in a __snapshots__ dir inside of each package dir 40 84 snapshotDir := filepath.Join(root, "__snapshots__") 41 85 if err := os.MkdirAll(snapshotDir, 0755); err != nil { 42 86 return "", err ··· 89 133 return Deserialize(string(data)) 90 134 } 91 135 92 - func readAccepted(testName string) (*Snapshot, error) { 136 + func ReadAccepted(testName string) (*Snapshot, error) { 93 137 return ReadSnapshot(testName, "accepted") 94 138 } 95 139 96 - func readNew(testName string) (*Snapshot, error) { 140 + func ReadNew(testName string) (*Snapshot, error) { 97 141 return ReadSnapshot(testName, "new") 98 142 } 99 143
-168
format.go
··· 1 - package freeze 2 - 3 - import ( 4 - "fmt" 5 - "os" 6 - "strconv" 7 - "strings" 8 - ) 9 - 10 - const ( 11 - colorRed = "\033[31m" 12 - colorGreen = "\033[32m" 13 - colorYellow = "\033[33m" 14 - colorBlue = "\033[34m" 15 - colorGray = "\033[90m" 16 - colorReset = "\033[0m" 17 - colorBold = "\033[1m" 18 - ) 19 - 20 - func TerminalWidth() int { 21 - width := os.Getenv("COLUMNS") 22 - if w, err := strconv.Atoi(width); err == nil && w > 0 { 23 - return w 24 - } 25 - return 80 26 - } 27 - 28 - func ClearScreen() { 29 - fmt.Print("\033[2J") 30 - fmt.Print("\033[H") 31 - } 32 - 33 - func ClearLine() { 34 - fmt.Print("\033[K") 35 - } 36 - 37 - func Red(s string) string { 38 - if !hasColor() { 39 - return s 40 - } 41 - return colorRed + s + colorReset 42 - } 43 - 44 - func Green(s string) string { 45 - if !hasColor() { 46 - return s 47 - } 48 - return colorGreen + s + colorReset 49 - } 50 - 51 - func Yellow(s string) string { 52 - if !hasColor() { 53 - return s 54 - } 55 - return colorYellow + s + colorReset 56 - } 57 - 58 - func Blue(s string) string { 59 - if !hasColor() { 60 - return s 61 - } 62 - return colorBlue + s + colorReset 63 - } 64 - 65 - func Gray(s string) string { 66 - if !hasColor() { 67 - return s 68 - } 69 - return colorGray + s + colorReset 70 - } 71 - 72 - func Bold(s string) string { 73 - if !hasColor() { 74 - return s 75 - } 76 - return colorBold + s + colorReset 77 - } 78 - 79 - func hasColor() bool { 80 - return os.Getenv("NO_COLOR") == "" 81 - } 82 - 83 - func NewSnapshotBox(snap *Snapshot) string { 84 - width := TerminalWidth() 85 - 86 - var sb strings.Builder 87 - sb.WriteString(strings.Repeat("─", width+2) + "\n") 88 - // TODO: add file path to a new line below this 89 - sb.WriteString(fmt.Sprintf(" %s \n", Blue("New Snapshot -- \""+snap.TestName+"\""))) 90 - 91 - lines := strings.Split(snap.Content, "\n") 92 - numLines := len(lines) 93 - lineNumWidth := len(strconv.Itoa(numLines)) 94 - 95 - topBar := strings.Repeat("─", lineNumWidth+3) + "┬" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 96 - sb.WriteString(topBar) 97 - 98 - for i, line := range lines { 99 - lineNum := fmt.Sprintf("%*d", lineNumWidth, i+1) 100 - prefix := fmt.Sprintf("%s %s", Green(lineNum), Green("+")) 101 - 102 - if len(line) > width-len(prefix)-4 { 103 - line = line[:width-len(prefix)-7] + "..." 104 - } 105 - 106 - display := fmt.Sprintf("%s %s", prefix, Green(line)) 107 - sb.WriteString(fmt.Sprintf(" %s\n", display)) 108 - } 109 - 110 - bottomBar := strings.Repeat("─", lineNumWidth+3) + "┴" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 111 - sb.WriteString(bottomBar) 112 - 113 - return sb.String() 114 - } 115 - 116 - // TODO: this probably needs the styling overhaul from above 117 - func DiffSnapshotBox(old, new *Snapshot) string { 118 - width := TerminalWidth() 119 - 120 - diffLines := Histogram(old.Content, new.Content) 121 - 122 - var sb strings.Builder 123 - sb.WriteString(strings.Repeat("─", width) + "\n") 124 - sb.WriteString(fmt.Sprintf(" %s\n", Blue("Snapshot Diff"))) 125 - sb.WriteString(strings.Repeat("─", width) + "\n") 126 - 127 - for _, dl := range diffLines { 128 - var prefix string 129 - var formatted string 130 - 131 - switch dl.Kind { 132 - case DiffOld: 133 - prefix = Red("−") 134 - formatted = Red(dl.Line) 135 - case DiffNew: 136 - prefix = Green("+") 137 - formatted = Green(dl.Line) 138 - case DiffShared: 139 - prefix = " " 140 - formatted = dl.Line 141 - } 142 - 143 - display := fmt.Sprintf("%s %s", prefix, formatted) 144 - if len(display) > width-4 { 145 - display = display[:width-7] + "..." 146 - } 147 - sb.WriteString(fmt.Sprintf(" %s\n", display)) 148 - } 149 - 150 - sb.WriteString(strings.Repeat("─", width) + "\n") 151 - return sb.String() 152 - } 153 - 154 - func FormatHeader(text string) string { 155 - return Bold(Blue(text)) 156 - } 157 - 158 - func FormatSuccess(text string) string { 159 - return Green(text) 160 - } 161 - 162 - func FormatError(text string) string { 163 - return Red(text) 164 - } 165 - 166 - func FormatWarning(text string) string { 167 - return Yellow(text) 168 - }
+24 -7
freeze.go
··· 3 3 import ( 4 4 "fmt" 5 5 "reflect" 6 + 7 + "github.com/ptdewey/freeze/internal/diff" 8 + "github.com/ptdewey/freeze/internal/files" 9 + "github.com/ptdewey/freeze/internal/pretty" 6 10 ) 7 11 8 12 const version = "0.1.0" ··· 33 37 func snapWithTitle(t testingT, title string, content string) { 34 38 t.Helper() 35 39 36 - snapshot := &Snapshot{ 40 + snapshot := &files.Snapshot{ 37 41 Version: version, 38 42 TestName: title, 39 43 Content: content, 40 44 } 41 45 42 - accepted, err := readAccepted(title) 46 + accepted, err := files.ReadAccepted(title) 43 47 if err == nil { 44 48 if accepted.Content == content { 45 49 return 46 50 } 47 51 48 - if err := SaveSnapshot(snapshot, "new"); err != nil { 52 + if err := files.SaveSnapshot(snapshot, "new"); err != nil { 49 53 t.Error("failed to save snapshot:", err) 50 54 return 51 55 } 52 56 53 - fmt.Println(DiffSnapshotBox(accepted, snapshot)) 57 + diffLines := convertDiffLines(diff.Histogram(accepted.Content, snapshot.Content)) 58 + fmt.Println(pretty.DiffSnapshotBox(accepted, snapshot, diffLines)) 54 59 t.Error("snapshot mismatch - run 'freeze review' to update") 55 60 return 56 61 } 57 62 58 - if err := SaveSnapshot(snapshot, "new"); err != nil { 63 + if err := files.SaveSnapshot(snapshot, "new"); err != nil { 59 64 t.Error("failed to save snapshot:", err) 60 65 return 61 66 } 62 67 63 - fmt.Println(NewSnapshotBox(snapshot)) 68 + fmt.Println(pretty.NewSnapshotBox(snapshot)) 64 69 t.Error("new snapshot created - run 'freeze review' to accept") 70 + } 71 + 72 + func convertDiffLines(diffLines []diff.DiffLine) []pretty.DiffLine { 73 + result := make([]pretty.DiffLine, len(diffLines)) 74 + for i, dl := range diffLines { 75 + result[i] = pretty.DiffLine{ 76 + Number: dl.Number, 77 + Line: dl.Line, 78 + Kind: pretty.DiffKind(dl.Kind), 79 + } 80 + } 81 + return result 65 82 } 66 83 67 84 func formatValues(values ...any) string { ··· 102 119 case reflect.String: 103 120 return v.(string) 104 121 case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: 105 - // TODO: make this better probably 122 + // TODO: make this better probably (utter?) 106 123 return fmt.Sprintf("%#v", v) 107 124 default: 108 125 return fmt.Sprint(v)
+1 -1
freeze_test.go
··· 14 14 } 15 15 16 16 func TestSnapMultiple(t *testing.T) { 17 - freeze.Snap(t, "value1", "value2", 42, "foo", "bar", "baz", "wibble", "wobble", "tick") 17 + freeze.Snap(t, "value1", "value2", 42, "foo", "bar", "baz", "wibble", "wobble", "tock") 18 18 } 19 19 20 20 type CustomStruct struct {
+95
internal/pretty/boxes.go
··· 1 + package pretty 2 + 3 + import ( 4 + "fmt" 5 + "strconv" 6 + "strings" 7 + 8 + "github.com/ptdewey/freeze/internal/files" 9 + ) 10 + 11 + type DiffLine struct { 12 + Number int 13 + Line string 14 + Kind DiffKind 15 + } 16 + 17 + type DiffKind int 18 + 19 + const ( 20 + DiffShared DiffKind = iota 21 + DiffOld 22 + DiffNew 23 + ) 24 + 25 + func NewSnapshotBox(snap *files.Snapshot) string { 26 + width := TerminalWidth() 27 + 28 + var sb strings.Builder 29 + sb.WriteString(strings.Repeat("─", width+2) + "\n") 30 + // TODO: "New Snapshot" should be above this line, in default color. 31 + // - color should be on test name and path 32 + sb.WriteString(fmt.Sprintf(" %s \n", Blue("New Snapshot -- \""+snap.TestName+"\""))) 33 + 34 + lines := strings.Split(snap.Content, "\n") 35 + numLines := len(lines) 36 + lineNumWidth := len(strconv.Itoa(numLines)) 37 + 38 + topBar := strings.Repeat("─", lineNumWidth+3) + "┬" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 39 + sb.WriteString(topBar) 40 + 41 + for i, line := range lines { 42 + lineNum := fmt.Sprintf("%*d", lineNumWidth, i+1) 43 + prefix := fmt.Sprintf("%s %s", Green(lineNum), Green("+")) 44 + 45 + if len(line) > width-len(prefix)-4 { 46 + line = line[:width-len(prefix)-7] + "..." 47 + } 48 + 49 + display := fmt.Sprintf("%s %s", prefix, Green(line)) 50 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 51 + } 52 + 53 + bottomBar := strings.Repeat("─", lineNumWidth+3) + "┴" + strings.Repeat("─", width-lineNumWidth-2) + "\n" 54 + sb.WriteString(bottomBar) 55 + 56 + return sb.String() 57 + } 58 + 59 + // TODO: needs to get overhauled with styling like above 60 + // - should show line numbers, line numbers with diffs should be the same 61 + // - should show test name and path in the header section 62 + func DiffSnapshotBox(old, new *files.Snapshot, diffLines []DiffLine) string { 63 + width := TerminalWidth() 64 + 65 + var sb strings.Builder 66 + sb.WriteString(strings.Repeat("─", width) + "\n") 67 + sb.WriteString(fmt.Sprintf(" %s\n", Blue("Snapshot Diff"))) 68 + sb.WriteString(strings.Repeat("─", width) + "\n") 69 + 70 + for _, dl := range diffLines { 71 + var prefix string 72 + var formatted string 73 + 74 + switch dl.Kind { 75 + case DiffOld: 76 + prefix = Red("−") 77 + formatted = Red(dl.Line) 78 + case DiffNew: 79 + prefix = Green("+") 80 + formatted = Green(dl.Line) 81 + case DiffShared: 82 + prefix = " " 83 + formatted = dl.Line 84 + } 85 + 86 + display := fmt.Sprintf("%s %s", prefix, formatted) 87 + if len(display) > width-4 { 88 + display = display[:width-7] + "..." 89 + } 90 + sb.WriteString(fmt.Sprintf(" %s\n", display)) 91 + } 92 + 93 + sb.WriteString(strings.Repeat("─", width) + "\n") 94 + return sb.String() 95 + }
+96
internal/pretty/pretty.go
··· 1 + package pretty 2 + 3 + import ( 4 + "fmt" 5 + "os" 6 + "strconv" 7 + ) 8 + 9 + const ( 10 + colorRed = "\033[31m" 11 + colorGreen = "\033[32m" 12 + colorYellow = "\033[33m" 13 + colorBlue = "\033[34m" 14 + colorGray = "\033[90m" 15 + colorReset = "\033[0m" 16 + colorBold = "\033[1m" 17 + ) 18 + 19 + func TerminalWidth() int { 20 + width := os.Getenv("COLUMNS") 21 + if w, err := strconv.Atoi(width); err == nil && w > 0 { 22 + return w 23 + } 24 + return 80 25 + } 26 + 27 + func ClearScreen() { 28 + fmt.Print("\033[2J") 29 + fmt.Print("\033[H") 30 + } 31 + 32 + func ClearLine() { 33 + fmt.Print("\033[K") 34 + } 35 + 36 + func Red(s string) string { 37 + if !hasColor() { 38 + return s 39 + } 40 + return colorRed + s + colorReset 41 + } 42 + 43 + func Green(s string) string { 44 + if !hasColor() { 45 + return s 46 + } 47 + return colorGreen + s + colorReset 48 + } 49 + 50 + func Yellow(s string) string { 51 + if !hasColor() { 52 + return s 53 + } 54 + return colorYellow + s + colorReset 55 + } 56 + 57 + func Blue(s string) string { 58 + if !hasColor() { 59 + return s 60 + } 61 + return colorBlue + s + colorReset 62 + } 63 + 64 + func Gray(s string) string { 65 + if !hasColor() { 66 + return s 67 + } 68 + return colorGray + s + colorReset 69 + } 70 + 71 + func Bold(s string) string { 72 + if !hasColor() { 73 + return s 74 + } 75 + return colorBold + s + colorReset 76 + } 77 + 78 + func hasColor() bool { 79 + return os.Getenv("NO_COLOR") == "" 80 + } 81 + 82 + func Header(text string) string { 83 + return Bold(Blue(text)) 84 + } 85 + 86 + func Success(text string) string { 87 + return Green(text) 88 + } 89 + 90 + func Error(text string) string { 91 + return Red(text) 92 + } 93 + 94 + func Warning(text string) string { 95 + return Yellow(text) 96 + }
+58 -40
review.go
··· 5 5 "fmt" 6 6 "os" 7 7 "strings" 8 + 9 + "github.com/ptdewey/freeze/internal/diff" 10 + "github.com/ptdewey/freeze/internal/files" 11 + "github.com/ptdewey/freeze/internal/pretty" 8 12 ) 9 13 10 14 type ReviewChoice int ··· 13 17 Accept ReviewChoice = iota 14 18 Reject 15 19 Skip 16 - ToggleDiff 20 + // ToggleDiff 17 21 Quit 18 22 ) 19 23 24 + func computeDiffLines(old, new *files.Snapshot) []pretty.DiffLine { 25 + diffLines := diff.Histogram(old.Content, new.Content) 26 + result := make([]pretty.DiffLine, len(diffLines)) 27 + for i, dl := range diffLines { 28 + result[i] = pretty.DiffLine{ 29 + Number: dl.Number, 30 + Line: dl.Line, 31 + Kind: pretty.DiffKind(dl.Kind), 32 + } 33 + } 34 + return result 35 + } 36 + 20 37 func Review() error { 21 - snapshots, err := ListNewSnapshots() 38 + snapshots, err := files.ListNewSnapshots() 22 39 if err != nil { 23 40 return err 24 41 } 25 42 26 43 if len(snapshots) == 0 { 27 - fmt.Println(FormatSuccess("✓ No new snapshots to review")) 44 + fmt.Println(pretty.Success("✓ No new snapshots to review")) 28 45 return nil 29 46 } 30 47 31 - fmt.Println(FormatHeader("🐦 Freeze - Snapshot Review")) 48 + fmt.Println(pretty.Header("🐦 Freeze - Snapshot Review")) 32 49 fmt.Printf("Found %d new snapshot(s) to review\n\n", len(snapshots)) 33 50 34 51 return reviewLoop(snapshots) ··· 39 56 showDiff := false 40 57 41 58 for i, testName := range snapshots { 42 - fmt.Printf("\n[%d/%d] %s\n", i+1, len(snapshots), FormatHeader(testName)) 59 + fmt.Printf("\n[%d/%d] %s\n", i+1, len(snapshots), pretty.Header(testName)) 43 60 44 - newSnap, err := ReadSnapshot(testName, "new") 61 + newSnap, err := files.ReadSnapshot(testName, "new") 45 62 if err != nil { 46 - fmt.Println(FormatError("✗ Failed to read new snapshot: " + err.Error())) 63 + fmt.Println(pretty.Error("✗ Failed to read new snapshot: " + err.Error())) 47 64 continue 48 65 } 49 66 50 - accepted, acceptErr := ReadSnapshot(testName, "accepted") 67 + accepted, acceptErr := files.ReadSnapshot(testName, "accepted") 51 68 52 69 if acceptErr == nil && showDiff { 53 - fmt.Println(DiffSnapshotBox(accepted, newSnap)) 70 + diffLines := computeDiffLines(accepted, newSnap) 71 + fmt.Println(pretty.DiffSnapshotBox(accepted, newSnap, diffLines)) 54 72 } else if acceptErr == nil { 55 - fmt.Println(DiffSnapshotBox(accepted, newSnap)) 73 + diffLines := computeDiffLines(accepted, newSnap) 74 + fmt.Println(pretty.DiffSnapshotBox(accepted, newSnap, diffLines)) 56 75 } else { 57 - fmt.Println(NewSnapshotBox(newSnap)) 76 + fmt.Println(pretty.NewSnapshotBox(newSnap)) 58 77 } 59 78 60 79 for { ··· 65 84 66 85 switch choice { 67 86 case Accept: 68 - if err := AcceptSnapshot(testName); err != nil { 69 - fmt.Println(FormatError("✗ Failed to accept snapshot: " + err.Error())) 87 + if err := files.AcceptSnapshot(testName); err != nil { 88 + fmt.Println(pretty.Error("✗ Failed to accept snapshot: " + err.Error())) 70 89 } else { 71 - fmt.Println(FormatSuccess("✓ Snapshot accepted")) 90 + fmt.Println(pretty.Success("✓ Snapshot accepted")) 72 91 } 73 - break 74 92 case Reject: 75 - if err := RejectSnapshot(testName); err != nil { 76 - fmt.Println(FormatError("✗ Failed to reject snapshot: " + err.Error())) 93 + if err := files.RejectSnapshot(testName); err != nil { 94 + fmt.Println(pretty.Error("✗ Failed to reject snapshot: " + err.Error())) 77 95 } else { 78 - fmt.Println(FormatWarning("⊘ Snapshot rejected")) 96 + fmt.Println(pretty.Warning("⊘ Snapshot rejected")) 79 97 } 80 - break 81 98 case Skip: 82 - fmt.Println(FormatWarning("⊘ Snapshot skipped")) 83 - break 84 - case ToggleDiff: 85 - showDiff = !showDiff 86 - if acceptErr == nil { 87 - fmt.Println(DiffSnapshotBox(accepted, newSnap)) 88 - } else { 89 - fmt.Println(NewSnapshotBox(newSnap)) 90 - } 91 - continue 99 + fmt.Println(pretty.Warning("⊘ Snapshot skipped")) 100 + // case ToggleDiff: 101 + // showDiff = !showDiff 102 + // if acceptErr == nil { 103 + // diffLines := computeDiffLines(accepted, newSnap) 104 + // fmt.Println(pretty.DiffSnapshotBox(accepted, newSnap, diffLines)) 105 + // } else { 106 + // fmt.Println(pretty.NewSnapshotBox(newSnap)) 107 + // } 108 + // continue 92 109 case Quit: 93 110 fmt.Println("\nReview interrupted") 94 111 return nil ··· 97 114 } 98 115 } 99 116 100 - fmt.Println("\n" + FormatSuccess("✓ Review complete")) 117 + fmt.Println("\n" + pretty.Success("✓ Review complete")) 101 118 return nil 102 119 } 103 120 104 121 func askChoice(reader *bufio.Reader, current, total int) (ReviewChoice, error) { 105 - fmt.Printf("\nOptions: [a]ccept [r]eject [s]kip [d]iff [q]uit: ") 122 + // fmt.Printf("\nOptions: [a]ccept [r]eject [s]kip [d]iff [q]uit: ") 123 + fmt.Printf("\nOptions: [a]ccept [r]eject [s]kip [q]uit: ") 106 124 107 125 input, err := reader.ReadString('\n') 108 126 if err != nil { ··· 118 136 return Reject, nil 119 137 case "s", "skip": 120 138 return Skip, nil 121 - case "d", "diff": 122 - return ToggleDiff, nil 139 + // case "d", "diff": 140 + // return ToggleDiff, nil 123 141 case "q", "quit": 124 142 return Quit, nil 125 143 default: 126 - fmt.Println(FormatWarning("Invalid option, please try again")) 144 + fmt.Println(pretty.Warning("Invalid option, please try again")) 127 145 return askChoice(reader, current, total) 128 146 } 129 147 } 130 148 131 149 func AcceptAll() error { 132 - snapshots, err := ListNewSnapshots() 150 + snapshots, err := files.ListNewSnapshots() 133 151 if err != nil { 134 152 return err 135 153 } 136 154 137 155 for _, testName := range snapshots { 138 - if err := AcceptSnapshot(testName); err != nil { 156 + if err := files.AcceptSnapshot(testName); err != nil { 139 157 return err 140 158 } 141 159 } 142 160 143 - fmt.Printf(FormatSuccess("✓ Accepted %d snapshot(s)\n"), len(snapshots)) 161 + fmt.Printf(pretty.Success("✓ Accepted %d snapshot(s)\n"), len(snapshots)) 144 162 return nil 145 163 } 146 164 147 165 func RejectAll() error { 148 - snapshots, err := ListNewSnapshots() 166 + snapshots, err := files.ListNewSnapshots() 149 167 if err != nil { 150 168 return err 151 169 } 152 170 153 171 for _, testName := range snapshots { 154 - if err := RejectSnapshot(testName); err != nil { 172 + if err := files.RejectSnapshot(testName); err != nil { 155 173 return err 156 174 } 157 175 } 158 176 159 - fmt.Printf(FormatWarning("⊘ Rejected %d snapshot(s)\n"), len(snapshots)) 177 + fmt.Printf(pretty.Warning("⊘ Rejected %d snapshot(s)\n"), len(snapshots)) 160 178 return nil 161 179 }
-53
snapshot.go
··· 1 - package freeze 2 - 3 - import ( 4 - "fmt" 5 - "strings" 6 - ) 7 - 8 - type Snapshot struct { 9 - Version string 10 - TestName string 11 - Content string 12 - } 13 - 14 - func (s *Snapshot) Serialize() string { 15 - header := fmt.Sprintf("---\nversion: %s\ntest_name: %s\n---\n", s.Version, s.TestName) 16 - return header + s.Content 17 - } 18 - 19 - func Deserialize(raw string) (*Snapshot, error) { 20 - parts := strings.SplitN(raw, "---\n", 3) 21 - if len(parts) < 3 { 22 - return nil, fmt.Errorf("invalid snapshot format") 23 - } 24 - 25 - header := parts[1] 26 - content := parts[2] 27 - 28 - snap := &Snapshot{ 29 - Content: content, 30 - } 31 - 32 - for _, line := range strings.Split(header, "\n") { 33 - line = strings.TrimSpace(line) 34 - if line == "" { 35 - continue 36 - } 37 - 38 - kv := strings.SplitN(line, ": ", 2) 39 - if len(kv) != 2 { 40 - continue 41 - } 42 - 43 - key, value := kv[0], kv[1] 44 - switch key { 45 - case "version": 46 - snap.Version = value 47 - case "test_name": 48 - snap.TestName = value 49 - } 50 - } 51 - 52 - return snap, nil 53 - }