1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
package grid
import (
"strings"
"testing"
tea "github.com/charmbracelet/bubbletea"
"ticktock/internal/store"
)
func loggedFixture() *fakeStore {
return &fakeStore{entries: []store.Entry{{
Key: "2026-07-07-01", Start: at(8, 0), End: at(8, 30),
Project: "ARCHER", Description: "standup", Duration: 30 * 60 * 1e9,
}}}
}
// place cursor on the labeled row for the fixture (08:00 with default window).
func onLoggedRow(m Model) Model {
row := m.grid.RowOf(8 * 60)
m.cursor = row
m.col = ColLogged
return m
}
func TestEnterOnLoggedOpensEditThenSafeReplace(t *testing.T) {
f := loggedFixture()
m := onLoggedRow(loaded(f))
nm, _ := m.Update(special(tea.KeyEnter))
mm := nm.(Model)
if !mm.editing || mm.adding || mm.form == nil {
t.Fatalf("enter on logged should open edit (not add) form")
}
if mm.editKey != "2026-07-07-01" || mm.fv.Desc != "standup" {
t.Errorf("edit form not prefilled from session: key=%q desc=%q", mm.editKey, mm.fv.Desc)
}
mm.fv.Desc = "standup (edited)"
nm, _ = mm.Update(special(tea.KeyCtrlS))
if len(f.repl) != 1 || f.repl[0].Description != "standup (edited)" {
t.Fatalf("SafeReplace not called with edit: %+v", f.repl)
}
}
func TestEnterOnEmptyLoggedRowDoesNothing(t *testing.T) {
f := loggedFixture()
m := loaded(f)
m.cursor = 0 // 07:00, empty
m.col = ColLogged
nm, _ := m.Update(special(tea.KeyEnter))
if nm.(Model).editing {
t.Errorf("enter on empty logged row should not open a form")
}
}
func TestDeleteConfirmThenRemove(t *testing.T) {
f := loggedFixture()
m := onLoggedRow(loaded(f))
nm, _ := m.Update(key("d"))
mm := nm.(Model)
if !mm.confirmDelete || mm.confirmKey != "2026-07-07-01" {
t.Fatalf("d should arm delete confirm for the session")
}
nm, _ = mm.Update(key("y"))
if len(f.removed) != 1 || f.removed[0] != "2026-07-07-01" {
t.Fatalf("y should confirm remove: %v", f.removed)
}
}
func TestConfirmLabelDescribesSession(t *testing.T) {
f := loggedFixture()
m := onLoggedRow(loaded(f))
nm, _ := m.Update(key("d"))
label := nm.(Model).confirmLabel()
if !strings.Contains(label, "standup") || !strings.Contains(label, "08:00") {
t.Errorf("confirm label should name the session and range, got %q", label)
}
}
func TestDeleteConfirmCancel(t *testing.T) {
f := loggedFixture()
m := onLoggedRow(loaded(f))
nm, _ := m.Update(key("d"))
nm, _ = nm.(Model).Update(key("n"))
if nm.(Model).confirmDelete {
t.Errorf("n should cancel the delete confirm")
}
if len(f.removed) != 0 {
t.Errorf("cancel should not remove")
}
}
|