From 0321ea57f5c561215a8dea111ed7f3ea7b05594e Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 24 Jul 2026 16:07:14 +0200 Subject: tui reader: bookmarks (m=mark w/ note, b=list/jump/delete, per-verse via top-visible verse) + remember last place; v0.18.0 --- internal/tui/reader_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) (limited to 'internal/tui/reader_test.go') diff --git a/internal/tui/reader_test.go b/internal/tui/reader_test.go index 167e546..514f892 100644 --- a/internal/tui/reader_test.go +++ b/internal/tui/reader_test.go @@ -7,11 +7,13 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/lukaszkasprzak/lectio/internal/bible" + "github.com/lukaszkasprzak/lectio/internal/bookmarks" "github.com/lukaszkasprzak/lectio/internal/config" ) func enReader(t *testing.T) ReaderModel { t.Helper() + t.Setenv("XDG_DATA_HOME", t.TempDir()) // isolate bookmarks + last-place files tbl, err := bible.LoadBookTable(nil) if err != nil { t.Fatal(err) @@ -20,7 +22,7 @@ func enReader(t *testing.T) ReaderModel { cfg.UILanguage = "en" cfg.SiglaStyle = "english" cfg.Versions = []string{"bt", "wuj", "vul", "grb", "drb"} - return NewReader(cfg, tbl) + return NewReader(cfg, tbl, bookmarks.Open()) } func key(m ReaderModel, k tea.KeyMsg) ReaderModel { @@ -30,6 +32,11 @@ func key(m ReaderModel, k tea.KeyMsg) ReaderModel { func runes(s string) tea.KeyMsg { return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(s)} } +func win(m ReaderModel, w, h int) ReaderModel { + nm, _ := m.Update(tea.WindowSizeMsg{Width: w, Height: h}) + return nm.(ReaderModel) +} + func TestReaderInit(t *testing.T) { m := enReader(t) if m.mode != modePick { @@ -98,3 +105,72 @@ func TestReaderFuzzyScore(t *testing.T) { t.Error("gss should subsequence-match") } } + +func TestReaderBookmarkFlow(t *testing.T) { + m := enReader(t) + m = win(m, 80, 24) + m = key(m, runes("jn")) + m = key(m, tea.KeyMsg{Type: tea.KeyEnter}) // open John 1 + if m.mode != modeRead { + t.Fatal("did not enter reading") + } + // mark: m -> type note -> enter + m = key(m, runes("m")) + if m.mode != modeMark { + t.Fatal("m did not enter modeMark") + } + m = key(m, runes("hi")) + m = key(m, tea.KeyMsg{Type: tea.KeyEnter}) + if m.mode != modeRead { + t.Fatal("enter did not save + return to reading") + } + // list: b + m = key(m, runes("b")) + if m.mode != modeBookmarks || len(m.marks) != 1 { + t.Fatalf("bookmarks mode=%v n=%d", m.mode, len(m.marks)) + } + if m.marks[0].Book != "John" || m.marks[0].Note != "hi" { + t.Errorf("mark = %+v", m.marks[0]) + } + if !strings.Contains(m.View(), "John") { + t.Errorf("bookmarks view missing John:\n%s", m.View()) + } + // open the bookmark + m = key(m, tea.KeyMsg{Type: tea.KeyEnter}) + if m.mode != modeRead || m.books[m.bookIdx].Canonical != "John" { + t.Errorf("jump failed: mode=%v book=%q", m.mode, m.books[m.bookIdx].Canonical) + } + // delete via list + m = key(m, runes("b")) + m = key(m, runes("d")) + if len(m.marks) != 0 { + t.Errorf("delete failed, %d remaining", len(m.marks)) + } +} + +func TestReaderRemembersPlace(t *testing.T) { + dir := t.TempDir() + t.Setenv("XDG_DATA_HOME", dir) + tbl, _ := bible.LoadBookTable(nil) + cfg := config.Default() + cfg.UILanguage = "en" + cfg.SiglaStyle = "english" + cfg.Versions = []string{"wuj", "vul", "grb", "drb"} + + m := NewReader(cfg, tbl, bookmarks.Open()) + m = win(m, 80, 24) + m = key(m, runes("jn")) + m = key(m, tea.KeyMsg{Type: tea.KeyEnter}) // John 1 (openBook saves place) + m = key(m, runes("n")) // -> John 2 (chapterStep saves place) + + m2 := NewReader(cfg, tbl, bookmarks.Open()) + if m2.mode != modeRead { + t.Fatalf("did not restore reading mode") + } + if m2.books[m2.bookIdx].Canonical != "John" { + t.Errorf("restored book = %q want John", m2.books[m2.bookIdx].Canonical) + } + if m2.currentChapter() != 2 { + t.Errorf("restored chapter = %d want 2", m2.currentChapter()) + } +} -- cgit v1.3