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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
package app
import (
"os"
"path/filepath"
"strings"
"testing"
tea "github.com/charmbracelet/bubbletea"
)
func typeInto(a *App, s string) {
for _, r := range s {
a.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{r}})
}
}
func TestCtrlNOpensNamePrompt(t *testing.T) {
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
if a.mode != ModeNamePrompt {
t.Fatalf("mode = %d, want ModeNamePrompt", a.mode)
}
if a.dialog.Title() != "New note" {
t.Errorf("dialog title = %q, want 'New note'", a.dialog.Title())
}
}
func TestNamePromptShowsTargetDir(t *testing.T) {
dir := t.TempDir()
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.openNamePrompt(namingNew, dir, "")
if !strings.Contains(a.View(), filepath.Base(dir)) {
t.Errorf("View() does not show the target dir %q:\n%s", dir, a.View())
}
}
func TestEscCancelsBackToPreviousMode(t *testing.T) {
dir := t.TempDir()
p := filepath.Join(dir, "open.md")
os.WriteFile(p, []byte("body"), 0o644)
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.Load(p)
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
a.Update(tea.KeyMsg{Type: tea.KeyEsc})
if a.mode != ModeEditor {
t.Errorf("mode = %d after Esc, want ModeEditor", a.mode)
}
if a.path != p {
t.Errorf("path = %q after Esc, want the file we were editing (%q)", a.path, p)
}
entries, _ := os.ReadDir(dir)
if len(entries) != 1 {
t.Errorf("Esc wrote %d entries to disk, want 1 (the pre-existing file)", len(entries))
}
}
func TestDirtyBufferConfirmsDiscardBeforePrompt(t *testing.T) {
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.editor.SetContent([]byte("x"))
a.editor.Dirty = true
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
if a.mode == ModeNamePrompt {
t.Fatal("first Ctrl+N on a dirty buffer opened the prompt; want a discard confirmation")
}
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
if a.mode != ModeNamePrompt {
t.Errorf("second Ctrl+N: mode = %d, want ModeNamePrompt", a.mode)
}
}
func TestPickerCtrlNPrefillsQuery(t *testing.T) {
dir := t.TempDir()
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
if err := a.StartPickerIn(dir); err != nil {
t.Fatal(err)
}
typeInto(a, "draft")
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
if a.mode != ModeNamePrompt {
t.Fatalf("mode = %d, want ModeNamePrompt", a.mode)
}
if a.dialog.Value() != "draft" {
t.Errorf("dialog prefill = %q, want draft", a.dialog.Value())
}
}
func TestF2OpensRenamePrefilled(t *testing.T) {
dir := t.TempDir()
p := filepath.Join(dir, "before.md")
os.WriteFile(p, []byte("body"), 0o644)
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.Load(p)
a.Update(tea.KeyMsg{Type: tea.KeyF2})
if a.mode != ModeNamePrompt {
t.Fatalf("mode = %d, want ModeNamePrompt", a.mode)
}
if a.dialog.Title() != "Rename" {
t.Errorf("dialog title = %q, want Rename", a.dialog.Title())
}
if a.dialog.Value() != "before" {
t.Errorf("dialog prefill = %q, want before", a.dialog.Value())
}
}
func TestReentrantCtrlNDoesNotTrapTheDialog(t *testing.T) {
dir := t.TempDir()
a := newApp()
a.Update(tea.WindowSizeMsg{Width: 100, Height: 20})
a.saveDir = dir
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN})
a.Update(tea.KeyMsg{Type: tea.KeyCtrlN}) // re-press while the prompt is open
if a.prevMode == ModeNamePrompt {
t.Fatal("prevMode became ModeNamePrompt; Esc would be a no-op")
}
a.Update(tea.KeyMsg{Type: tea.KeyEsc})
if a.mode == ModeNamePrompt {
t.Error("Esc left the app in ModeNamePrompt; the dialog is inescapable")
}
}
func TestDirLabelDoesNotCollapseSiblingSharingHomePrefix(t *testing.T) {
home, err := os.UserHomeDir()
if err != nil || home == "" {
t.Skip("no $HOME to test against")
}
a := newApp()
sibling := home + "bar" // shares home's string prefix but is not under it
if got := a.dirLabel(sibling); strings.HasPrefix(got, "~") {
t.Errorf("dirLabel(%q) = %q, want no ~ collapse for a sibling directory", sibling, got)
}
}
|