From 43cb4d7d99b630c2381415f1bef8e91c96bc9d65 Mon Sep 17 00:00:00 2001 From: Patrick Dewey
Date: Sun, 10 May 2026 18:26:17 -0400 Subject: [PATCH] fix(tui): support snapshots in subdirs --- internal/files/files.go | 40 +++++++++++++++++------------ internal/files/files_test.go | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/internal/files/files.go b/internal/files/files.go index 3c74d44..f07691b 100644 --- a/internal/files/files.go +++ b/internal/files/files.go @@ -231,23 +231,31 @@ func ListNewSnapshots() ([]SnapshotInfo, error) { var newSnapshots []SnapshotInfo for _, dir := range snapshotDirs { - entries, err := os.ReadDir(dir) - if err != nil { - // Skip directories we can't read - continue - } - - for _, entry := range entries { - if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".snap.new") { - // Remove .snap.new extension to get the title - name := strings.TrimSuffix(entry.Name(), ".snap.new") - fullPath := filepath.Join(dir, entry.Name()) - newSnapshots = append(newSnapshots, SnapshotInfo{ - Title: name, - Path: fullPath, - Dir: dir, - }) + walkErr := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err } + if info.IsDir() || !strings.HasSuffix(info.Name(), ".snap.new") { + return nil + } + rel, err := filepath.Rel(dir, path) + if err != nil { + return err + } + // Title is the path relative to the __snapshots__ dir, with the + // .snap.new extension removed. Snapshots saved with titles + // containing "/" live in nested subdirectories. + title := strings.TrimSuffix(filepath.ToSlash(rel), ".snap.new") + newSnapshots = append(newSnapshots, SnapshotInfo{ + Title: title, + Path: path, + Dir: dir, + }) + return nil + }) + if walkErr != nil { + // Skip directories we can't walk + continue } } diff --git a/internal/files/files_test.go b/internal/files/files_test.go index 9c68130..16bfc73 100644 --- a/internal/files/files_test.go +++ b/internal/files/files_test.go @@ -268,3 +268,52 @@ func TestRecursiveSnapshots(t *testing.T) { t.Errorf("Error listing snapshots: %v", err) } } + +func TestListNewSnapshotsNested(t *testing.T) { + // Run inside a fresh tempdir with its own go.mod so findProjectRoot + // scopes the scan to this test. + tmp := t.TempDir() + if err := os.WriteFile(filepath.Join(tmp, "go.mod"), []byte("module test\n"), 0644); err != nil { + t.Fatalf("write go.mod: %v", err) + } + + origCwd, err := os.Getwd() + if err != nil { + t.Fatalf("getwd: %v", err) + } + if err := os.Chdir(tmp); err != nil { + t.Fatalf("chdir: %v", err) + } + t.Cleanup(func() { _ = os.Chdir(origCwd) }) + + // Create a nested .snap.new under __snapshots__/sub/ + nestedDir := filepath.Join(tmp, "__snapshots__", "sub") + if err := os.MkdirAll(nestedDir, 0755); err != nil { + t.Fatalf("mkdirall: %v", err) + } + nestedPath := filepath.Join(nestedDir, "leaf.snap.new") + if err := os.WriteFile(nestedPath, []byte("---\ntitle: sub/leaf\n---\nbody"), 0644); err != nil { + t.Fatalf("write nested: %v", err) + } + // Also a flat one at the top level of __snapshots__/ + flatPath := filepath.Join(tmp, "__snapshots__", "flat.snap.new") + if err := os.WriteFile(flatPath, []byte("---\ntitle: flat\n---\nbody"), 0644); err != nil { + t.Fatalf("write flat: %v", err) + } + + snapshots, err := files.ListNewSnapshots() + if err != nil { + t.Fatalf("ListNewSnapshots: %v", err) + } + + titles := map[string]string{} + for _, s := range snapshots { + titles[s.Title] = s.Path + } + if got, ok := titles["sub/leaf"]; !ok || got != nestedPath { + t.Errorf("expected nested title 'sub/leaf' at %s, got titles=%v", nestedPath, titles) + } + if got, ok := titles["flat"]; !ok || got != flatPath { + t.Errorf("expected flat title 'flat' at %s, got titles=%v", flatPath, titles) + } +} -- 2.51.2