diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-29 13:45:19 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-29 13:45:19 +0200 |
| commit | 6964ee56de01f9769a39ba480dc787339b642f7a (patch) | |
| tree | ce20ba33e56a414e96e81a3d9d438c373534027e /internal/tui/reader_test.go | |
| parent | 8ab0502569264478697bf3dbd060eadb82459647 (diff) | |
| download | lectio-6964ee56de01f9769a39ba480dc787339b642f7a.tar.gz lectio-6964ee56de01f9769a39ba480dc787339b642f7a.zip | |
tui(reader): filter bookmarks by tag, and show tags in the list
Bookmarks stored tags but the list neither showed them nor let you use them.
Now each row shows "Ref — note #tag1 #tag2", and `/` opens a live tag filter
(case-insensitive substring over a bookmark's tags): type to narrow, Enter keeps
the filter while j/k navigate the filtered list, Esc clears it. Navigation,
open and delete all act on the visible (filtered) list. Reuses the store's
existing tag data; help keybar (en/pl) updated; test covers filter/keys.
Diffstat (limited to 'internal/tui/reader_test.go')
| -rw-r--r-- | internal/tui/reader_test.go | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/internal/tui/reader_test.go b/internal/tui/reader_test.go index 7bce759..da8d4d4 100644 --- a/internal/tui/reader_test.go +++ b/internal/tui/reader_test.go @@ -49,6 +49,55 @@ func win(m ReaderModel, w, h int) ReaderModel { return nm.(ReaderModel) } +func TestReaderBookmarkTagFilter(t *testing.T) { + m := enReader(t) + m.marks = []bookmarks.Bookmark{ + {ID: "1", Book: "John", Chapter: 3, Verse: 16, Tags: []string{"faith"}}, + {ID: "2", Book: "Psalms", Chapter: 23, Verse: 1, Tags: []string{"work", "prayer"}}, + {ID: "3", Book: "Matthew", Chapter: 5, Verse: 3, Tags: []string{"prayer"}}, + } + m.mode = modeBookmarks + + if got := len(m.visMarks()); got != 3 { + t.Fatalf("unfiltered visible = %d, want 3", got) + } + + // "/" enters filter input; typing narrows to the two 'prayer' bookmarks. + m = key(m, runes("/")) + if !m.markFiltering { + t.Fatal(`"/" did not enter filter input`) + } + for _, c := range []string{"p", "r", "a", "y"} { + m = key(m, runes(c)) + } + if got := len(m.visMarks()); got != 2 { + t.Fatalf(`filter "pray" visible = %d, want 2 (Psalms, Matthew)`, got) + } + + // Enter keeps the filter and leaves input mode. + m = key(m, tea.KeyMsg{Type: tea.KeyEnter}) + if m.markFiltering || m.markFilter != "pray" { + t.Fatalf("after enter: filtering=%v filter=%q", m.markFiltering, m.markFilter) + } + + // Case-insensitive substring: "WORK" matches the 'work' tag (1 bookmark). + m.markFilter = "WORK" + if got := len(m.visMarks()); got != 1 { + t.Fatalf(`filter "WORK" visible = %d, want 1`, got) + } + + // Esc clears an active filter, back to all. + m.markFilter = "work" + m.markFiltering = false + m = key(m, tea.KeyMsg{Type: tea.KeyEsc}) + if m.markFilter != "" { + t.Fatalf("esc did not clear filter, got %q", m.markFilter) + } + if got := len(m.visMarks()); got != 3 { + t.Fatalf("after clearing filter, visible = %d, want 3", got) + } +} + func TestReaderInit(t *testing.T) { m := enReader(t) if m.mode != modePick { |
