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
141
142
143
144
|
package askcli
import (
"strings"
"testing"
)
func TestFishCompletion_IncludesCommandsAndExcludesExport(t *testing.T) {
script := FishCompletion()
for _, name := range []string{"na", "no-agent"} {
if !strings.Contains(script, " -a '"+name+"' ") {
t.Fatalf("script missing scope completion for %q", name)
}
}
for _, name := range []string{"add", "list", "all", "ready", "info", "annotate", "start", "stop", "done", "priority", "tag", "dep", "urgency", "modify", "denotate", "delete", "fish", "help"} {
if !strings.Contains(script, " -a '"+name+"' ") {
t.Fatalf("script missing root completion for %q", name)
}
}
for _, line := range []string{
"# Source with: ask fish | source",
"complete -c ask -n '__ask_in_dep_context' -a 'add' -d 'Add a dependency'",
"complete -c ask -n '__ask_in_dep_context' -a 'rm' -d 'Remove a dependency'",
"complete -c ask -n '__ask_in_dep_context' -a 'list' -d 'List dependencies'",
"function __ask_command_positionals",
"function __ask_scope_prefix",
"function __ask_task_selectors",
"function __ask_add_dependency_modifiers",
`set -l ask_bin "ask"`,
"set -l selectors",
"set selectors (command $ask_bin complete-uuids 2>/dev/null)",
"set selectors (command $ask_bin $scope_prefix complete-uuids 2>/dev/null)",
"complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_selectors)' -d 'Task selector'",
"complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_selectors)' -d 'Task selector'",
"complete -c ask -n '__ask_in_add_dep_modifier_context' -a '(__ask_add_dependency_modifiers)' -d 'Task dependency'",
} {
if !strings.Contains(script, line) {
t.Fatalf("script missing dep completion line %q", line)
}
}
if strings.Contains(script, "ask export") {
t.Fatalf("script should not advertise non-existent export command")
}
if strings.Contains(script, "assets/ask.fish") {
t.Fatalf("script should not reference a static asset")
}
for _, name := range []string{"info", "annotate", "start", "stop", "done", "priority", "tag", "modify", "denotate", "delete"} {
if strings.Contains(script, "complete -c ask -n '__ask_in_uuid_context' -a '"+name+"'") {
t.Fatalf("script should not hard-code UUID completion item %q", name)
}
}
}
func TestFishSingleSelectorCompletionContext(t *testing.T) {
testCases := []struct {
name string
positional []string
want bool
}{
{name: "info expects selector", positional: []string{"info"}, want: true},
{name: "info expects selector with no-agent prefix", positional: []string{"na", "info"}, want: true},
{name: "annotate expects selector", positional: []string{"annotate"}, want: true},
{name: "priority expects selector", positional: []string{"priority"}, want: true},
{name: "delete expects selector", positional: []string{"delete"}, want: true},
{name: "delete expects selector with alias prefix", positional: []string{"no-agent", "delete"}, want: true},
{name: "annotate stops after selector", positional: []string{"annotate", "0"}, want: false},
{name: "priority stops after selector", positional: []string{"priority", "0"}, want: false},
{name: "modify stops after selector", positional: []string{"modify", "0"}, want: false},
{name: "dep is not a single selector command", positional: []string{"dep"}, want: false},
{name: "empty positional", positional: nil, want: false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := fishSingleSelectorCompletionContext(tc.positional); got != tc.want {
t.Fatalf("fishSingleSelectorCompletionContext(%v) = %t, want %t", tc.positional, got, tc.want)
}
})
}
}
func TestFishDepSelectorCompletionContext(t *testing.T) {
testCases := []struct {
name string
positional []string
want bool
}{
{name: "dep add first selector", positional: []string{"dep", "add"}, want: true},
{name: "dep add first selector with no-agent prefix", positional: []string{"na", "dep", "add"}, want: true},
{name: "dep add second selector", positional: []string{"dep", "add", "0"}, want: true},
{name: "dep add stops after second selector", positional: []string{"dep", "add", "0", "1"}, want: false},
{name: "dep rm first selector", positional: []string{"dep", "rm"}, want: true},
{name: "dep rm second selector", positional: []string{"dep", "rm", "0"}, want: true},
{name: "dep rm stops after second selector", positional: []string{"dep", "rm", "0", "1"}, want: false},
{name: "dep list selector", positional: []string{"dep", "list"}, want: true},
{name: "dep list stops after selector", positional: []string{"dep", "list", "0"}, want: false},
{name: "dep unknown operation", positional: []string{"dep", "noop"}, want: false},
{name: "non dep command", positional: []string{"info"}, want: false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := fishDepSelectorCompletionContext(tc.positional); got != tc.want {
t.Fatalf("fishDepSelectorCompletionContext(%v) = %t, want %t", tc.positional, got, tc.want)
}
})
}
}
func TestFishAddDependencyModifierCompletionContext(t *testing.T) {
testCases := []struct {
name string
positional []string
current string
want bool
}{
{name: "add without depends modifier", positional: []string{"add", "task"}, current: "task", want: false},
{name: "add with depends keyword prefix", positional: []string{"add"}, current: "depends", want: true},
{name: "add with depends modifier", positional: []string{"add", "+cli"}, current: "depends:0", want: true},
{name: "add with depends modifier and no-agent prefix", positional: []string{"na", "add", "+cli"}, current: "depends:0", want: true},
{name: "add with comma continuation", positional: []string{"add", "+cli"}, current: "depends:0,", want: true},
{name: "non add command", positional: []string{"dep", "add"}, current: "depends:0", want: false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := fishAddDependencyModifierCompletionContext(tc.positional, tc.current); got != tc.want {
t.Fatalf("fishAddDependencyModifierCompletionContext(%v, %q) = %t, want %t", tc.positional, tc.current, got, tc.want)
}
})
}
}
func TestFishCompletionFor_EmbedsBinaryPath(t *testing.T) {
script := FishCompletionFor(`/tmp/ask "$HOME"`)
for _, line := range []string{
`set -l ask_bin "/tmp/ask \"\$HOME\""`,
"set selectors (command $ask_bin complete-uuids 2>/dev/null)",
} {
if !strings.Contains(script, line) {
t.Fatalf("script missing %q", line)
}
}
}
|