···66 "github.com/ptdewey/freeze/internal/pretty"
77)
8899+// Snapshot represents a captured test snapshot with metadata.
910type Snapshot = files.Snapshot
10111212+// DiffLine represents a line in a diff comparison.
1113type DiffLine = diff.DiffLine
12141315const (
1616+ // DiffShared indicates a line that is unchanged in both versions.
1417 DiffShared = diff.DiffShared
1515- DiffOld = diff.DiffOld
1616- DiffNew = diff.DiffNew
1818+ // DiffOld indicates a line that was removed.
1919+ DiffOld = diff.DiffOld
2020+ // DiffNew indicates a line that was added.
2121+ DiffNew = diff.DiffNew
1722)
18232424+// Deserialize parses a raw snapshot file string into a Snapshot struct.
1925func Deserialize(raw string) (*Snapshot, error) {
2026 return files.Deserialize(raw)
2127}
22282929+// SaveSnapshot writes a snapshot to disk with the specified state ("new" or "accepted").
2330func SaveSnapshot(snap *Snapshot, state string) error {
2431 return files.SaveSnapshot(snap, state)
2532}
26333434+// ReadSnapshot reads a snapshot from disk for the given test name and state.
2735func ReadSnapshot(testName string, state string) (*Snapshot, error) {
2836 return files.ReadSnapshot(testName, state)
2937}
30383939+// SnapshotFileName returns the snapshot file name for a given test name.
3140func SnapshotFileName(testName string) string {
3241 return files.SnapshotFileName(testName)
3342}
34434444+// Histogram computes a line-by-line diff between two strings using the histogram algorithm.
3545func Histogram(old, new string) []DiffLine {
3646 return diff.Histogram(old, new)
3747}
38484949+// NewSnapshotBox formats a new snapshot as a pretty-printed box for display.
3950func NewSnapshotBox(snap *Snapshot) string {
4051 return pretty.NewSnapshotBox(snap)
4152}
42535454+// DiffSnapshotBox formats a diff between old and new snapshots as a pretty-printed box.
4355func DiffSnapshotBox(oldSnap, newSnap *Snapshot) string {
4444- diffLines := convertDiffLines(diff.Histogram(oldSnap.Content, newSnap.Content))
5656+ diffLines := diff.Histogram(oldSnap.Content, newSnap.Content)
4557 return pretty.DiffSnapshotBox(oldSnap, newSnap, diffLines)
4658}
···103103 J2 int
104104}
105105106106-func min(a, b int) int {
107107- if a < b {
108108- return a
109109- }
110110- return b
111111-}
112112-113113-func max(a, b int) int {
114114- if a > b {
115115- return a
116116- }
117117- return b
118118-}
119119-120106// sequenceMatcher compares sequence of strings. The basic
121107// algorithm predates, and is a little fancier than, an algorithm
122108// published in the late 1980's by Ratcliff and Obershelp under the
···170156171157// Set the first sequence to be compared.
172158func (m *sequenceMatcher) setSeq1(a []string) {
173173- if &a == &m.a {
174174- return
175175- }
176159 m.a = a
177160 m.matchingBlocks, m.opCodes = nil, nil
178161}
179162180163// Set the second sequence to be compared.
181164func (m *sequenceMatcher) setSeq2(b []string) {
182182- if &b == &m.b {
183183- return
184184- }
185165 m.b = b
186166 m.matchingBlocks, m.opCodes, m.fullBCount = nil, nil, nil
187167 m.chainB()
+15-44
internal/files/files.go
···6464 return snap, nil
6565}
66666767-// TODO: make snapshots in root vs package dirs a configurable option?
6868-func findProjectRoot() (string, error) {
6969- cwd, err := os.Getwd()
7070- if err != nil {
7171- return "", err
7272- }
7373-7474- current := cwd
7575- for {
7676- if _, err := os.Stat(filepath.Join(current, "go.mod")); err == nil {
7777- return current, nil
7878- }
7979-8080- parent := filepath.Dir(current)
8181- if parent == current {
8282- return "", fmt.Errorf("go.mod not found")
8383- }
8484- current = parent
8585- }
8686-}
8787-8867func getSnapshotDir() (string, error) {
8968 // NOTE: maybe this could be configurable?
9069 // Storing snapshots in root may be desirable in some cases
9191- // root, err := findProjectRoot()
9292- // if err != nil {
9393- // return "", err
9494- // }
9595- // snapshotDir := filepath.Join(root, "__snapshots__")
9670 snapshotDir := "__snapshots__"
9771 if err := os.MkdirAll(snapshotDir, 0755); err != nil {
9872 return "", err
···11690 return s
11791}
118929393+// getSnapshotFileName returns the filename for a snapshot based on test name and state
9494+func getSnapshotFileName(testName string, state string) string {
9595+ baseName := SnapshotFileName(testName)
9696+ switch state {
9797+ case "accepted":
9898+ return baseName + ".snap"
9999+ case "new":
100100+ return baseName + ".snap.new"
101101+ default:
102102+ return baseName + "." + state
103103+ }
104104+}
105105+119106func SaveSnapshot(snap *Snapshot, state string) error {
120107 snapshotDir, err := getSnapshotDir()
121108 if err != nil {
122109 return err
123110 }
124111125125- var fileName string
126126- switch state {
127127- case "accepted":
128128- fileName = SnapshotFileName(snap.Test) + ".snap"
129129- case "new":
130130- fileName = SnapshotFileName(snap.Test) + ".snap.new"
131131- default:
132132- fileName = SnapshotFileName(snap.Test) + "." + state
133133- }
112112+ fileName := getSnapshotFileName(snap.Test, state)
134113 filePath := filepath.Join(snapshotDir, fileName)
135114136115 return os.WriteFile(filePath, []byte(snap.Serialize()), 0644)
···142121 return nil, err
143122 }
144123145145- var fileName string
146146- switch state {
147147- case "accepted":
148148- fileName = SnapshotFileName(testName) + ".snap"
149149- case "new":
150150- fileName = SnapshotFileName(testName) + ".snap.new"
151151- default:
152152- fileName = SnapshotFileName(testName) + "." + state
153153- }
124124+ fileName := getSnapshotFileName(testName, state)
154125 filePath := filepath.Join(snapshotDir, fileName)
155126156127 data, err := os.ReadFile(filePath)
+13-26
internal/pretty/boxes.go
···55 "strconv"
66 "strings"
7788+ "github.com/ptdewey/freeze/internal/diff"
89 "github.com/ptdewey/freeze/internal/files"
910)
10111111-type DiffLine struct {
1212- OldNumber int
1313- NewNumber int
1414- Line string
1515- Kind DiffKind
1616-}
1717-1818-type DiffKind int
1919-2020-const (
2121- DiffShared DiffKind = iota
2222- DiffOld
2323- DiffNew
2424-)
2525-2612func NewSnapshotBox(snap *files.Snapshot) string {
2713 return newSnapshotBoxInternal(snap)
2814}
29153030-func DiffSnapshotBox(old, new *files.Snapshot, diffLines []DiffLine) string {
1616+func DiffSnapshotBox(old, newSnapshot *files.Snapshot, diffLines []diff.DiffLine) string {
3117 width := TerminalWidth()
3232- snapshotFileName := files.SnapshotFileName(new.Test) + ".snap"
1818+ snapshotFileName := files.SnapshotFileName(newSnapshot.Test) + ".snap"
33193420 var sb strings.Builder
3535- sb.WriteString(strings.Repeat("─", width) + "\n")
2121+ sb.WriteString("─── " + "Review Snapshot " + strings.Repeat("─", width-20) + "\n\n")
2222+3623 // TODO: maybe make helper functions for this, swap coloring between the key and the value
3724 // TODO: maybe show the snapshot file name in gray next to the "a/r/s" options
3825 // (i.e. "a accept -> snap_file_name.snap", "reject" w/strikethrough?, skip, keeps "*snap.new")
3939- sb.WriteString(fmt.Sprintf(" file: %s\n", Gray(snapshotFileName)))
4040- sb.WriteString(fmt.Sprintf(" %s\n", Blue("Snapshot Diff")))
4141- if new.Title != "" {
4242- sb.WriteString(fmt.Sprintf(" title: %s\n", Blue("\""+new.Title+"\"")))
2626+ if newSnapshot.Title != "" {
2727+ sb.WriteString(Blue(" title: ") + newSnapshot.Title + "\n")
4328 }
4444- sb.WriteString(fmt.Sprintf(" test: %s\n", Blue("\""+new.Test+"\"")))
2929+ sb.WriteString(Blue(" test: ") + newSnapshot.Test + "\n")
3030+ sb.WriteString(Blue(" file: ") + snapshotFileName + "\n")
3131+ sb.WriteString("\n")
4532 sb.WriteString(strings.Repeat("─", width) + "\n")
46334734 // Calculate max line numbers for proper spacing
···6350 var formatted string
64516552 switch dl.Kind {
6666- case DiffOld:
5353+ case diff.DiffOld:
6754 oldNumStr = fmt.Sprintf("%*d", oldWidth, dl.OldNumber)
6855 newNumStr = strings.Repeat(" ", newWidth)
6956 prefix = Red("−")
7057 formatted = Red(dl.Line)
7171- case DiffNew:
5858+ case diff.DiffNew:
7259 oldNumStr = strings.Repeat(" ", oldWidth)
7360 newNumStr = fmt.Sprintf("%*d", newWidth, dl.NewNumber)
7461 prefix = Green("+")
7562 formatted = Green(dl.Line)
7676- case DiffShared:
6363+ case diff.DiffShared:
7764 oldNumStr = fmt.Sprintf("%*d", oldWidth, dl.OldNumber)
7865 newNumStr = fmt.Sprintf("%*d", newWidth, dl.NewNumber)
7966 prefix = " "