diff --git a/__snapshots__/test_accept.snap b/__snapshots__/test_accept.snap index 3b601ee..7daa3fa 100644 --- a/__snapshots__/test_accept.snap +++ b/__snapshots__/test_accept.snap @@ -1,5 +1,6 @@ --- -version: 0.1.0 test_name: TestAccept +file_path: +func_name: --- new content to accept \ No newline at end of file diff --git a/__snapshots__/test_map.snap.new b/__snapshots__/test_map.snap similarity index 51% rename from __snapshots__/test_map.snap.new rename to __snapshots__/test_map.snap index d7b719b..94ec841 100644 --- a/__snapshots__/test_map.snap.new +++ b/__snapshots__/test_map.snap @@ -1,6 +1,7 @@ --- -version: 0.1.0 test_name: TestMap +file_path: /home/patrick/projects/freeze/freeze.go +func_name: --- map[string]interface{}{ "foo": "bar", diff --git a/__snapshots__/test_snap_custom_type.snap.new b/__snapshots__/test_snap_custom_type.snap similarity index 59% rename from __snapshots__/test_snap_custom_type.snap.new rename to __snapshots__/test_snap_custom_type.snap index 4e167a3..cdbca53 100644 --- a/__snapshots__/test_snap_custom_type.snap.new +++ b/__snapshots__/test_snap_custom_type.snap @@ -1,6 +1,7 @@ --- -version: 0.1.0 test_name: TestSnapCustomType +file_path: /home/patrick/projects/freeze/freeze.go +func_name: --- freeze_test.CustomStruct{ Name: "Alice", diff --git a/__snapshots__/test_snap_func.snap b/__snapshots__/test_snap_func.snap new file mode 100644 index 0000000..2dc7b3a --- /dev/null +++ b/__snapshots__/test_snap_func.snap @@ -0,0 +1,6 @@ +--- +test_name: TestSnapFunc +file_path: /home/patrick/projects/freeze/freeze_test.go +func_name: testHelperFunction +--- +"helper result" diff --git a/__snapshots__/test_snap_func_another_helper.snap b/__snapshots__/test_snap_func_another_helper.snap new file mode 100644 index 0000000..22cc026 --- /dev/null +++ b/__snapshots__/test_snap_func_another_helper.snap @@ -0,0 +1,6 @@ +--- +test_name: TestSnapFuncAnotherHelper +file_path: /home/patrick/projects/freeze/freeze_test.go +func_name: calculateSomething +--- +10 diff --git a/__snapshots__/test_snap_multiple.snap.new b/__snapshots__/test_snap_multiple.snap similarity index 65% rename from __snapshots__/test_snap_multiple.snap.new rename to __snapshots__/test_snap_multiple.snap index 15b579e..ad8a9ff 100644 --- a/__snapshots__/test_snap_multiple.snap.new +++ b/__snapshots__/test_snap_multiple.snap @@ -1,6 +1,7 @@ --- -version: 0.1.0 test_name: TestSnapMultiple +file_path: /home/patrick/projects/freeze/freeze.go +func_name: --- "value1" "value2" diff --git a/__snapshots__/test_snap_string.snap b/__snapshots__/test_snap_string.snap new file mode 100644 index 0000000..1f12b23 --- /dev/null +++ b/__snapshots__/test_snap_string.snap @@ -0,0 +1,6 @@ +--- +test_name: TestSnapString +file_path: /home/patrick/projects/freeze/freeze.go +func_name: +--- +hello world \ No newline at end of file diff --git a/__snapshots__/test_snap_string.snap.new b/__snapshots__/test_snap_string.snap.new deleted file mode 100644 index 36b244e..0000000 --- a/__snapshots__/test_snap_string.snap.new +++ /dev/null @@ -1,5 +0,0 @@ ---- -version: 0.1.0 -test_name: TestSnapString ---- -hello world \ No newline at end of file diff --git a/api.go b/api.go index 25d749b..ad58b13 100644 --- a/api.go +++ b/api.go @@ -38,42 +38,10 @@ func NewSnapshotBox(snap *Snapshot) string { return api.NewSnapshotBox(snap) } -func DiffSnapshotBox(old, new *Snapshot) string { - return api.DiffSnapshotBox(old, new) -} - -func Red(s string) string { - return api.Red(s) -} - -func Green(s string) string { - return api.Green(s) -} - -func Yellow(s string) string { - return api.Yellow(s) -} - -func Blue(s string) string { - return api.Blue(s) +func NewSnapshotBoxFunc(snap *Snapshot) string { + return api.NewSnapshotBoxFunc(snap) } -func Gray(s string) string { - return api.Gray(s) -} - -func Bold(s string) string { - return api.Bold(s) -} - -func TerminalWidth() int { - return api.TerminalWidth() -} - -func ClearScreen() { - api.ClearScreen() -} - -func ClearLine() { - api.ClearLine() +func DiffSnapshotBox(old, new *Snapshot) string { + return api.DiffSnapshotBox(old, new) } diff --git a/freeze.go b/freeze.go index b1bc82c..747e157 100644 --- a/freeze.go +++ b/freeze.go @@ -2,6 +2,7 @@ package freeze import ( "fmt" + "runtime" "github.com/kortschak/utter" "github.com/ptdewey/freeze/internal/diff" @@ -10,8 +11,6 @@ import ( "github.com/ptdewey/freeze/internal/review" ) -const version = "0.1.0" - // TODO: probably make this (and other things) configurable func init() { utter.Config.ElideType = true @@ -34,19 +33,37 @@ func SnapWithTitle(t testingT, title string, values ...any) { snapWithTitle(t, title, content) } +func SnapFunc(t testingT, values ...any) { + t.Helper() + content := formatValues(values...) + snapWithTitle(t, t.Name(), content, t.Name()) +} + +func SnapFuncWithName(t testingT, funcName string, values ...any) { + t.Helper() + content := formatValues(values...) + snapWithTitle(t, t.Name(), content, funcName) +} + func snap(t testingT, content string) { t.Helper() testName := t.Name() snapWithTitle(t, testName, content) } -func snapWithTitle(t testingT, title string, content string) { +func snapWithTitle(t testingT, title string, content string, funcName ...string) { t.Helper() + _, filePath, _, _ := runtime.Caller(2) + snapshot := &files.Snapshot{ - Version: version, - Name: title, - Content: content, + Name: title, + FilePath: filePath, + Content: content, + } + + if len(funcName) > 0 && funcName[0] != "" { + snapshot.FuncName = funcName[0] } accepted, err := files.ReadAccepted(title) @@ -71,7 +88,11 @@ func snapWithTitle(t testingT, title string, content string) { return } - fmt.Println(pretty.NewSnapshotBox(snapshot)) + if len(funcName) > 0 && funcName[0] != "" { + fmt.Println(pretty.NewSnapshotBoxFunc(snapshot)) + } else { + fmt.Println(pretty.NewSnapshotBox(snapshot)) + } t.Error("new snapshot created - run 'freeze review' to accept") } @@ -96,13 +117,10 @@ func formatValues(values ...any) string { } func formatValue(v any) string { - // if v == nil { - // return "" - // } - return utter.Sdump(v) } +// DOCS: func Review() error { return review.Review() } diff --git a/freeze_test.go b/freeze_test.go index 7fdc03d..cc485c8 100644 --- a/freeze_test.go +++ b/freeze_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/ptdewey/freeze" + "github.com/ptdewey/freeze/internal/api" ) func TestSnapString(t *testing.T) { @@ -40,15 +41,30 @@ func TestMap(t *testing.T) { }) } +func testHelperFunction() string { + return "helper result" +} + +func TestSnapFunc(t *testing.T) { + freeze.SnapFuncWithName(t, "testHelperFunction", testHelperFunction()) +} + +func TestSnapFuncAnotherHelper(t *testing.T) { + freeze.SnapFuncWithName(t, "calculateSomething", calculateSomething(5)) +} + +func calculateSomething(n int) int { + return n * 2 +} + func TestSerializeDeserialize(t *testing.T) { snap := &freeze.Snapshot{ - Version: "1.0.0", Name: "TestExample", Content: "test content\nmultiline", } serialized := snap.Serialize() - expected := "---\nversion: 1.0.0\ntest_name: TestExample\n---\ntest content\nmultiline" + expected := "---\ntest_name: TestExample\nfile_path: \nfunc_name: \n---\ntest content\nmultiline" if serialized != expected { t.Errorf("expected:\n%s\ngot:\n%s", expected, serialized) } @@ -58,9 +74,6 @@ func TestSerializeDeserialize(t *testing.T) { t.Fatalf("failed to deserialize: %v", err) } - if deserialized.Version != snap.Version { - t.Errorf("version mismatch: %s != %s", deserialized.Version, snap.Version) - } if deserialized.Name != snap.Name { t.Errorf("test name mismatch: %s != %s", deserialized.Name, snap.Name) } @@ -71,12 +84,11 @@ func TestSerializeDeserialize(t *testing.T) { func TestFileOperations(t *testing.T) { snap := &freeze.Snapshot{ - Version: "0.1.0", Name: "TestFileOps", Content: "file test content", } - if err := freeze.SaveSnapshot(snap, "test"); err != nil { + if err := api.SaveSnapshot(snap, "test"); err != nil { t.Fatalf("failed to save snapshot: %v", err) } @@ -151,13 +163,11 @@ func TestHistogramDiff(t *testing.T) { func TestDiffSnapshotBox(t *testing.T) { old := &freeze.Snapshot{ - Version: "0.1.0", Name: "TestDiff", Content: "old content", } new := &freeze.Snapshot{ - Version: "0.1.0", Name: "TestDiff", Content: "new content", } @@ -174,7 +184,6 @@ func TestDiffSnapshotBox(t *testing.T) { func TestNewSnapshotBox(t *testing.T) { snap := &freeze.Snapshot{ - Version: "0.1.0", Name: "TestNew", Content: "test content", } @@ -189,26 +198,6 @@ func TestNewSnapshotBox(t *testing.T) { } } -func TestFormatFunctions(t *testing.T) { - tests := []struct { - name string - fn func(string) string - text string - }{ - {"Red", freeze.Red, "error"}, - {"Green", freeze.Green, "success"}, - {"Yellow", freeze.Yellow, "warning"}, - {"Blue", freeze.Blue, "info"}, - } - - for _, tt := range tests { - result := tt.fn(tt.text) - if result == "" { - t.Errorf("%s returned empty string", tt.name) - } - } -} - func contains(s, substr string) bool { return strings.Contains(s, substr) } diff --git a/go.mod b/go.mod index ea2f607..f54093e 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/ptdewey/freeze go 1.25.2 -require github.com/kortschak/utter v1.7.0 // indirect +require github.com/kortschak/utter v1.7.0 diff --git a/internal/api/api.go b/internal/api/api.go index d8b2c44..2cdaa0b 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -40,6 +40,10 @@ func NewSnapshotBox(snap *Snapshot) string { return pretty.NewSnapshotBox(snap) } +func NewSnapshotBoxFunc(snap *Snapshot) string { + return pretty.NewSnapshotBoxFunc(snap) +} + func DiffSnapshotBox(old, new *Snapshot) string { diffLines := convertDiffLines(diff.Histogram(old.Content, new.Content)) return pretty.DiffSnapshotBox(old, new, diffLines) diff --git a/internal/files/files.go b/internal/files/files.go index 2a456eb..4d2c9e9 100644 --- a/internal/files/files.go +++ b/internal/files/files.go @@ -9,13 +9,14 @@ import ( ) type Snapshot struct { - Version string - Name string - Content string + Name string + FilePath string + FuncName string + Content string } func (s *Snapshot) Serialize() string { - header := fmt.Sprintf("---\nversion: %s\ntest_name: %s\n---\n", s.Version, s.Name) + header := fmt.Sprintf("---\ntest_name: %s\nfile_path: %s\nfunc_name: %s\n---\n", s.Name, s.FilePath, s.FuncName) return header + s.Content } @@ -32,7 +33,7 @@ func Deserialize(raw string) (*Snapshot, error) { Content: content, } - for _, line := range strings.Split(header, "\n") { + for line := range strings.SplitSeq(header, "\n") { line = strings.TrimSpace(line) if line == "" { continue @@ -45,10 +46,12 @@ func Deserialize(raw string) (*Snapshot, error) { key, value := kv[0], kv[1] switch key { - case "version": - snap.Version = value case "test_name": snap.Name = value + case "file_path": + snap.FilePath = value + case "func_name": + snap.FuncName = value } } diff --git a/internal/files/files_test.go b/internal/files/files_test.go index bb7398e..3275361 100644 --- a/internal/files/files_test.go +++ b/internal/files/files_test.go @@ -34,13 +34,13 @@ func TestSnapshotFileName(t *testing.T) { func TestSerializeDeserialize(t *testing.T) { snap := &files.Snapshot{ - Version: "1.0.0", - Name: "TestExample", - Content: "test content\nmultiline", + Name: "TestExample", + FilePath: "/path/to/test.go", + Content: "test content\nmultiline", } serialized := snap.Serialize() - expected := "---\nversion: 1.0.0\ntest_name: TestExample\n---\ntest content\nmultiline" + expected := "---\ntest_name: TestExample\nfile_path: /path/to/test.go\nfunc_name: \n---\ntest content\nmultiline" if serialized != expected { t.Errorf("Serialize():\nexpected:\n%s\n\ngot:\n%s", expected, serialized) } @@ -50,12 +50,12 @@ func TestSerializeDeserialize(t *testing.T) { t.Fatalf("Deserialize failed: %v", err) } - if deserialized.Version != snap.Version { - t.Errorf("Version mismatch: %s != %s", deserialized.Version, snap.Version) - } if deserialized.Name != snap.Name { t.Errorf("Name mismatch: %s != %s", deserialized.Name, snap.Name) } + if deserialized.FilePath != snap.FilePath { + t.Errorf("FilePath mismatch: %s != %s", deserialized.FilePath, snap.FilePath) + } if deserialized.Content != snap.Content { t.Errorf("Content mismatch: %s != %s", deserialized.Content, snap.Content) } @@ -85,28 +85,24 @@ func TestDeserializeValidFormats(t *testing.T) { tests := []struct { name string input string - wantVer string wantTest string wantContent string }{ { "simple", - "---\nversion: 1.0\ntest_name: Test\n---\ncontent", - "1.0", + "---\ntest_name: Test\nfile_path: /path\nfunc_name: \n---\ncontent", "Test", "content", }, { "multiline content", - "---\nversion: 0.1\ntest_name: MyTest\n---\nline1\nline2\nline3", - "0.1", + "---\ntest_name: MyTest\nfile_path: /path\nfunc_name: \n---\nline1\nline2\nline3", "MyTest", "line1\nline2\nline3", }, { "with extra fields", - "---\nversion: 1.0\ntest_name: Test\nextra: ignored\n---\ncontent", - "1.0", + "---\ntest_name: Test\nfile_path: /path\nfunc_name: \nextra: ignored\n---\ncontent", "Test", "content", }, @@ -118,9 +114,6 @@ func TestDeserializeValidFormats(t *testing.T) { if err != nil { t.Fatalf("Deserialize failed: %v", err) } - if snap.Version != tt.wantVer { - t.Errorf("Version = %s, want %s", snap.Version, tt.wantVer) - } if snap.Name != tt.wantTest { t.Errorf("Name = %s, want %s", snap.Name, tt.wantTest) } @@ -133,7 +126,6 @@ func TestDeserializeValidFormats(t *testing.T) { func TestSaveAndReadSnapshot(t *testing.T) { snap := &files.Snapshot{ - Version: "0.1.0", Name: "TestSaveRead", Content: "saved content", } @@ -150,9 +142,6 @@ func TestSaveAndReadSnapshot(t *testing.T) { if read.Content != snap.Content { t.Errorf("Content mismatch: %s != %s", read.Content, snap.Content) } - if read.Version != snap.Version { - t.Errorf("Version mismatch: %s != %s", read.Version, snap.Version) - } cleanupSnapshot(t, "TestSaveRead", "test") } @@ -166,7 +155,6 @@ func TestReadSnapshotNotFound(t *testing.T) { func TestAcceptSnapshot(t *testing.T) { newSnap := &files.Snapshot{ - Version: "0.1.0", Name: "TestAccept", Content: "new content to accept", } @@ -198,7 +186,6 @@ func TestAcceptSnapshot(t *testing.T) { func TestRejectSnapshot(t *testing.T) { snap := &files.Snapshot{ - Version: "0.1.0", Name: "TestReject", Content: "content to reject", } diff --git a/internal/pretty/boxes.go b/internal/pretty/boxes.go index e8647f8..dc27e4d 100644 --- a/internal/pretty/boxes.go +++ b/internal/pretty/boxes.go @@ -22,13 +22,24 @@ const ( DiffNew ) -func NewSnapshotBox(snap *files.Snapshot) string { +func newSnapshotBoxInternal(snap *files.Snapshot, isFuncSnapshot bool) string { width := TerminalWidth() var sb strings.Builder sb.WriteString("─── " + "New Snapshot " + strings.Repeat("─", width-15) + "\n\n") - sb.WriteString(fmt.Sprintf(" test: %s\n", Blue("\""+snap.Name+"\""))) - sb.WriteString(fmt.Sprintf(" snapshot: %s\n\n", Gray(files.SnapshotFileName(snap.Name)))) + + if isFuncSnapshot && snap.FuncName != "" { + sb.WriteString(fmt.Sprintf(" func: %s\n", Blue("\""+snap.FuncName+"\""))) + sb.WriteString(fmt.Sprintf(" test: %s\n", Blue("\""+snap.Name+"\""))) + } else { + sb.WriteString(fmt.Sprintf(" test: %s\n", Blue("\""+snap.Name+"\""))) + } + + sb.WriteString(fmt.Sprintf(" snapshot: %s\n", Gray(files.SnapshotFileName(snap.Name)+".snap.new"))) + if snap.FilePath != "" { + sb.WriteString(fmt.Sprintf(" file: %s\n", Gray(snap.FilePath))) + } + sb.WriteString("\n") lines := strings.Split(snap.Content, "\n") numLines := len(lines) @@ -55,17 +66,24 @@ func NewSnapshotBox(snap *files.Snapshot) string { return sb.String() } -// TODO: needs to get overhauled with styling like above -// - should show line numbers, line numbers with diffs should be the same -// - should show test name and path in the header section -// TODO: additional styling -// show helper text to say + is new results, - is old snapshot +func NewSnapshotBox(snap *files.Snapshot) string { + return newSnapshotBoxInternal(snap, false) +} + +func NewSnapshotBoxFunc(snap *files.Snapshot) string { + return newSnapshotBoxInternal(snap, true) +} + +// TODO: diff should show old and new line numbers func DiffSnapshotBox(old, new *files.Snapshot, diffLines []DiffLine) string { width := TerminalWidth() var sb strings.Builder sb.WriteString(strings.Repeat("─", width) + "\n") sb.WriteString(fmt.Sprintf(" %s\n", Blue("Snapshot Diff"))) + if new.FilePath != "" { + sb.WriteString(fmt.Sprintf(" file: %s\n", Gray(new.FilePath))) + } sb.WriteString(strings.Repeat("─", width) + "\n") for _, dl := range diffLines { diff --git a/internal/review/review.go b/internal/review/review.go index 9d3b4dd..6dd0674 100644 --- a/internal/review/review.go +++ b/internal/review/review.go @@ -75,7 +75,11 @@ func reviewLoop(snapshots []string) error { diffLines := computeDiffLines(accepted, newSnap) fmt.Println(pretty.DiffSnapshotBox(accepted, newSnap, diffLines)) } else { - fmt.Println(pretty.NewSnapshotBox(newSnap)) + if newSnap.FuncName != "" { + fmt.Println(pretty.NewSnapshotBoxFunc(newSnap)) + } else { + fmt.Println(pretty.NewSnapshotBox(newSnap)) + } } for {