Tag autocomplete shows suggestions when typing #partial in capture bar. Tab/enter accepts, up/down navigates, esc dismisses. Query composition extends ? search with date filters (@today, @week, @month, <7d, >30d), card type filters (^snippet), all composable with existing text and tag filters.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/lerko/nib/internal/db"
|
||||
)
|
||||
|
||||
func TestTagTokenAtCursor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
val string
|
||||
cursor int
|
||||
wantStart int
|
||||
wantEnd int
|
||||
wantPfx string
|
||||
wantOk bool
|
||||
}{
|
||||
{"mid tag cursor after a", "hello #par world", 9, 6, 10, "pa", true},
|
||||
{"end of tag", "hello #par world", 10, 6, 10, "par", true},
|
||||
{"end of input", "hello #parenting", 16, 6, 16, "parenting", true},
|
||||
{"start of tag just hash", "hello # world", 7, 6, 7, "", true},
|
||||
{"not in tag", "hello world", 5, 0, 0, "", false},
|
||||
{"tag at start", "#ops stuff", 4, 0, 4, "ops", true},
|
||||
{"cursor at hash", "#ops", 1, 0, 4, "", true},
|
||||
{"multiple tags second", "hello #ops #inf", 15, 11, 15, "inf", true},
|
||||
{"empty string", "", 0, 0, 0, "", false},
|
||||
{"cursor past end", "#ops", 10, 0, 4, "ops", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
start, end, pfx, ok := tagTokenAtCursor(tt.val, tt.cursor)
|
||||
if ok != tt.wantOk {
|
||||
t.Fatalf("ok = %v, want %v", ok, tt.wantOk)
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if start != tt.wantStart || end != tt.wantEnd {
|
||||
t.Fatalf("range = [%d,%d), want [%d,%d)", start, end, tt.wantStart, tt.wantEnd)
|
||||
}
|
||||
if pfx != tt.wantPfx {
|
||||
t.Fatalf("prefix = %q, want %q", pfx, tt.wantPfx)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterTagSuggestions(t *testing.T) {
|
||||
tags := []db.TagCount{
|
||||
{Tag: "ops", Count: 5},
|
||||
{Tag: "ops-deploy", Count: 3},
|
||||
{Tag: "infra", Count: 2},
|
||||
{Tag: "ops-team", Count: 1},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
want []string
|
||||
}{
|
||||
{"empty prefix", "", nil},
|
||||
{"exact match excluded", "ops", []string{"ops-deploy", "ops-team"}},
|
||||
{"partial match", "op", []string{"ops", "ops-deploy", "ops-team"}},
|
||||
{"no match", "zzz", nil},
|
||||
{"case insensitive", "OP", []string{"ops", "ops-deploy", "ops-team"}},
|
||||
{"single match", "inf", []string{"infra"}},
|
||||
{"full match excluded", "infra", nil},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := filterTagSuggestions(tags, tt.prefix)
|
||||
if len(got) != len(tt.want) {
|
||||
t.Fatalf("got %v, want %v", got, tt.want)
|
||||
}
|
||||
for i := range got {
|
||||
if got[i] != tt.want[i] {
|
||||
t.Fatalf("got %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user