git.deanishe.net/deanishe/awgo.git@v0.15.0/feedback_test.go (about) 1 // 2 // Copyright (c) 2016 Dean Jackson <deanishe@deanishe.net> 3 // 4 // MIT Licence. See http://opensource.org/licenses/MIT 5 // 6 7 package aw 8 9 import ( 10 "encoding/json" 11 "os" 12 "path/filepath" 13 "strings" 14 "testing" 15 ) 16 17 func TestNewFileItem(t *testing.T) { 18 19 var ( 20 wf = New() 21 ipPath = filepath.Join(wf.Dir(), "info.plist") 22 ipShort = strings.Replace(ipPath, os.Getenv("HOME"), "~", -1) 23 24 fb = Feedback{} 25 it = fb.NewFileItem(ipPath) 26 ) 27 28 if it.title != "info.plist" { 29 t.Fatalf("Incorrect title: %v", it.title) 30 } 31 32 if *it.subtitle != ipShort { 33 t.Fatalf("Incorrect subtitle: %v", *it.subtitle) 34 } 35 36 if *it.uid != ipPath { 37 t.Fatalf("Incorrect UID: %v", *it.uid) 38 } 39 40 if it.file != true { 41 t.Fatalf("Incorrect file: %v", it.file) 42 } 43 44 if it.icon.Type != "fileicon" { 45 t.Fatalf("Incorrect type: %v", it.icon.Type) 46 } 47 48 if it.icon.Value != ipPath { 49 t.Fatalf("Incorrect Value: %v", it.icon.Value) 50 } 51 } 52 53 func TestSetIcon(t *testing.T) { 54 it := Item{} 55 it.Icon(&Icon{"first", "fileicon"}) 56 if it.icon.Value != "first" { 57 t.Fatalf("Incorrect icon value: %v", it.icon.Value) 58 } 59 60 if it.icon.Type != "fileicon" { 61 t.Fatalf("Incorrect type: %v", it.icon.Type) 62 } 63 } 64 65 func p(s string) *string { 66 var v *string 67 v = &s 68 return v 69 } 70 71 var marshalItemTests = []struct { 72 Item *Item 73 ExpectedJSON string 74 }{ 75 // Minimal item 76 {Item: &Item{title: "title"}, 77 ExpectedJSON: `{"title":"title","valid":false}`}, 78 // With UID 79 {Item: &Item{title: "title", uid: p("xxx-yyy")}, 80 ExpectedJSON: `{"title":"title","uid":"xxx-yyy","valid":false}`}, 81 // With autocomplete 82 {Item: &Item{title: "title", autocomplete: p("xxx-yyy")}, 83 ExpectedJSON: `{"title":"title","autocomplete":"xxx-yyy","valid":false}`}, 84 // With empty autocomplete 85 {Item: &Item{title: "title", autocomplete: p("")}, 86 ExpectedJSON: `{"title":"title","autocomplete":"","valid":false}`}, 87 // With subtitle 88 {Item: &Item{title: "title", subtitle: p("subtitle")}, 89 ExpectedJSON: `{"title":"title","subtitle":"subtitle","valid":false}`}, 90 // Alternate subtitle 91 {Item: &Item{title: "title", subtitle: p("subtitle"), 92 mods: map[ModKey]*Modifier{ 93 "cmd": &Modifier{ 94 Key: "cmd", 95 subtitle: p("command sub")}}}, 96 ExpectedJSON: `{"title":"title","subtitle":"subtitle",` + 97 `"valid":false,"mods":{"cmd":{"subtitle":"command sub"}}}`}, 98 // Valid item 99 {Item: &Item{title: "title", valid: true}, 100 ExpectedJSON: `{"title":"title","valid":true}`}, 101 // With arg 102 {Item: &Item{title: "title", arg: p("arg1")}, 103 ExpectedJSON: `{"title":"title","arg":"arg1","valid":false}`}, 104 // Empty arg 105 {Item: &Item{title: "title", arg: p("")}, 106 ExpectedJSON: `{"title":"title","arg":"","valid":false}`}, 107 // Arg contains escapes 108 {Item: &Item{title: "title", arg: p("\x00arg\x00")}, 109 ExpectedJSON: `{"title":"title","arg":"\u0000arg\u0000","valid":false}`}, 110 // Valid with arg 111 {Item: &Item{title: "title", arg: p("arg1"), valid: true}, 112 ExpectedJSON: `{"title":"title","arg":"arg1","valid":true}`}, 113 // With icon 114 {Item: &Item{title: "title", 115 icon: &Icon{Value: "icon.png", Type: ""}}, 116 ExpectedJSON: `{"title":"title","valid":false,"icon":{"path":"icon.png"}}`}, 117 // With file icon 118 {Item: &Item{title: "title", 119 icon: &Icon{Value: "icon.png", Type: "fileicon"}}, 120 ExpectedJSON: `{"title":"title","valid":false,"icon":{"path":"icon.png","type":"fileicon"}}`}, 121 // With filetype icon 122 {Item: &Item{title: "title", 123 icon: &Icon{Value: "public.folder", Type: "filetype"}}, 124 ExpectedJSON: `{"title":"title","valid":false,"icon":{"path":"public.folder","type":"filetype"}}`}, 125 // With type = file 126 {Item: &Item{title: "title", file: true}, 127 ExpectedJSON: `{"title":"title","valid":false,"type":"file"}`}, 128 // With copy text 129 {Item: &Item{title: "title", copytext: p("copy")}, 130 ExpectedJSON: `{"title":"title","valid":false,"text":{"copy":"copy"}}`}, 131 // With large text 132 {Item: &Item{title: "title", largetype: p("large")}, 133 ExpectedJSON: `{"title":"title","valid":false,"text":{"largetype":"large"}}`}, 134 // With copy and large text 135 {Item: &Item{title: "title", copytext: p("copy"), largetype: p("large")}, 136 ExpectedJSON: `{"title":"title","valid":false,"text":{"copy":"copy","largetype":"large"}}`}, 137 // With arg and variable 138 {Item: &Item{title: "title", arg: p("value"), vars: map[string]string{"foo": "bar"}}, 139 ExpectedJSON: `{"title":"title","arg":"value","valid":false,"variables":{"foo":"bar"}}`}, 140 // With match 141 {Item: &Item{title: "title", match: p("one two three")}, 142 ExpectedJSON: `{"title":"title","match":"one two three","valid":false}`}, 143 } 144 145 var marshalModifierTests = []struct { 146 Mod *Modifier 147 ExpectedJSON string 148 }{ 149 // Empty item 150 {Mod: &Modifier{}, 151 ExpectedJSON: `{}`}, 152 // With arg 153 {Mod: &Modifier{arg: p("title")}, 154 ExpectedJSON: `{"arg":"title"}`}, 155 // Empty arg 156 {Mod: &Modifier{arg: p("")}, 157 ExpectedJSON: `{"arg":""}`}, 158 // With subtitle 159 {Mod: &Modifier{subtitle: p("sub here")}, 160 ExpectedJSON: `{"subtitle":"sub here"}`}, 161 // valid 162 {Mod: &Modifier{valid: true}, 163 ExpectedJSON: `{"valid":true}`}, 164 // icon 165 {Mod: &Modifier{icon: &Icon{"icon.png", ""}}, 166 ExpectedJSON: `{"icon":{"path":"icon.png"}}`}, 167 // With all 168 {Mod: &Modifier{ 169 arg: p("title"), 170 subtitle: p("sub here"), 171 valid: true, 172 }, 173 ExpectedJSON: `{"arg":"title","subtitle":"sub here","valid":true}`}, 174 // With variable 175 {Mod: &Modifier{ 176 arg: p("title"), 177 subtitle: p("sub here"), 178 valid: true, 179 vars: map[string]string{"foo": "bar"}, 180 }, 181 ExpectedJSON: `{"arg":"title","subtitle":"sub here","valid":true,"variables":{"foo":"bar"}}`}, 182 } 183 184 var marshalArgTests = []struct { 185 Arg *ArgVars 186 ExpectedJSON string 187 }{ 188 // Empty 189 {Arg: &ArgVars{}, 190 ExpectedJSON: `""`}, 191 // With arg 192 {Arg: &ArgVars{arg: p("title")}, 193 ExpectedJSON: `"title"`}, 194 // With non-ASCII arg 195 {Arg: &ArgVars{arg: p("fübär")}, 196 ExpectedJSON: `"fübär"`}, 197 // With escapes 198 {Arg: &ArgVars{arg: p("\x00")}, 199 ExpectedJSON: `"\u0000"`}, 200 // With variable 201 {Arg: &ArgVars{vars: map[string]string{"foo": "bar"}}, 202 ExpectedJSON: `{"alfredworkflow":{"variables":{"foo":"bar"}}}`}, 203 // Multiple variables 204 {Arg: &ArgVars{vars: map[string]string{"foo": "bar", "ducky": "fuzz"}}, 205 ExpectedJSON: `{"alfredworkflow":{"variables":{"ducky":"fuzz","foo":"bar"}}}`}, 206 // Multiple variables and arg 207 {Arg: &ArgVars{arg: p("title"), vars: map[string]string{"foo": "bar", "ducky": "fuzz"}}, 208 ExpectedJSON: `{"alfredworkflow":{"arg":"title","variables":{"ducky":"fuzz","foo":"bar"}}}`}, 209 } 210 211 var stringifyArgTests = []struct { 212 Arg *ArgVars 213 ExpectedString string 214 }{ 215 // Empty 216 {Arg: &ArgVars{}, 217 ExpectedString: ""}, 218 // With arg 219 {Arg: &ArgVars{arg: p("title")}, 220 ExpectedString: "title"}, 221 // With non-ASCII 222 {Arg: &ArgVars{arg: p("fübär")}, 223 ExpectedString: "fübär"}, 224 // With escapes 225 {Arg: &ArgVars{arg: p("\x00")}, 226 ExpectedString: "\x00"}, 227 } 228 229 // TestEmpty asserts feedback is empty. 230 func TestEmpty(t *testing.T) { 231 fb := NewFeedback() 232 if !fb.IsEmpty() { 233 t.Errorf("Feedback not empty.") 234 } 235 fb.NewItem("test") 236 if fb.IsEmpty() { 237 t.Errorf("Feedback empty.") 238 } 239 } 240 241 func TestMarshalItem(t *testing.T) { 242 for i, test := range marshalItemTests { 243 // log.Printf("#%d: %v", i, test.Item) 244 data, err := json.Marshal(test.Item) 245 if err != nil { 246 t.Errorf("#%d: marshal(%v): %v", i, test.Item, err) 247 continue 248 } 249 250 if got, want := string(data), test.ExpectedJSON; got != want { 251 t.Fatalf("#%d: got: %v wanted: %v", i, got, want) 252 } 253 } 254 } 255 256 func TestMarshalModifier(t *testing.T) { 257 for i, test := range marshalModifierTests { 258 data, err := json.Marshal(test.Mod) 259 if err != nil { 260 t.Errorf("#%d: marshal(%v): %v", i, test.Mod, err) 261 continue 262 } 263 264 if got, want := string(data), test.ExpectedJSON; got != want { 265 t.Fatalf("#%d: got: %v wanted: %v", i, got, want) 266 } 267 } 268 } 269 270 func TestMarshalArg(t *testing.T) { 271 for i, test := range marshalArgTests { 272 data, err := json.Marshal(test.Arg) 273 if err != nil { 274 t.Errorf("#%d: marshal(%v): %v", i, test.Arg, err) 275 continue 276 } 277 278 if got, want := string(data), test.ExpectedJSON; got != want { 279 t.Errorf("#%d: got: %v wanted: %v", i, got, want) 280 } 281 } 282 } 283 284 func TestStringifyArg(t *testing.T) { 285 for i, test := range stringifyArgTests { 286 s, err := test.Arg.String() 287 if err != nil { 288 t.Errorf("#%d: string(%v): %v", i, test.Arg, err) 289 continue 290 } 291 if got, want := s, test.ExpectedString; got != want { 292 t.Errorf("#%d: got: %v wanted: %v", i, got, want) 293 } 294 } 295 } 296 297 func TestMarshalFeedback(t *testing.T) { 298 // Empty feedback 299 fb := NewFeedback() 300 want := `{"items":[]}` 301 got, err := json.Marshal(fb) 302 if err != nil { 303 t.Fatalf("Error marshalling feedback: got: %s want: %s: %v", 304 got, want, err) 305 } 306 if string(got) != want { 307 t.Fatalf("Incorrect feedback: got: %s, wanted: %s", got, want) 308 } 309 310 // Feedback with item 311 // want = `<items><item valid="no"><title>item 1</title></item></items>` 312 want = `{"items":[{"title":"item 1","valid":false}]}` 313 fb.NewItem("item 1") 314 315 got, err = json.Marshal(fb) 316 if err != nil { 317 t.Fatalf("Error marshalling feedback: got: %s want: %s: %v", 318 got, want, err) 319 } 320 if string(got) != want { 321 t.Fatalf("Wrong feedback JSON. Expected=%s, got=%s", want, got) 322 } 323 324 } 325 326 // TestModifiersInheritVars tests that Modifiers inherit variables from their 327 // parent Item 328 func TestModifiersInheritVars(t *testing.T) { 329 fb := NewFeedback() 330 it := fb.NewItem("title") 331 it.Var("foo", "bar") 332 m := it.NewModifier("cmd") 333 334 if m.Vars()["foo"] != "bar" { 335 t.Fatalf("Modifier var has wrong value. Expected=bar, Received=%v", m.Vars()["foo"]) 336 } 337 } 338 339 // TestFeedbackRerun verifies that rerun is properly set. 340 func TestFeedbackRerun(t *testing.T) { 341 fb := NewFeedback() 342 343 fb.Rerun(1.5) 344 345 want := `{"rerun":1.5,"items":[]}` 346 got, err := json.Marshal(fb) 347 if err != nil { 348 t.Fatalf("Error serializing feedback: got: %s want: %s: %s", got, want, err) 349 } 350 if string(got) != want { 351 t.Fatalf("Wrong feedback JSON. Expected=%s, got=%s", want, got) 352 } 353 } 354 355 // TestFeedbackVars tests if vars are properly inherited by Items and Modifiers 356 func TestFeedbackVars(t *testing.T) { 357 fb := NewFeedback() 358 359 fb.Var("foo", "bar") 360 if fb.Vars()["foo"] != "bar" { 361 t.Fatalf("Feedback var has wrong value. Expected=bar, Received=%v", fb.Vars()["foo"]) 362 } 363 364 want := `{"variables":{"foo":"bar"},"items":[]}` 365 got, err := json.Marshal(fb) 366 if err != nil { 367 t.Fatalf("Error serializing feedback: got: %s want: %s: %s", got, want, err) 368 } 369 if string(got) != want { 370 t.Fatalf("Wrong feedback JSON. Expected=%s, got=%s", want, got) 371 } 372 373 // Top-level vars are inherited 374 it := fb.NewItem("title") 375 if it.Vars()["foo"] != "bar" { 376 t.Fatalf("Item var has wrong value. Expected=bar, Received=%v", it.Vars()["foo"]) 377 } 378 379 // Modifier inherits Item and top-level vars 380 it.Var("baz", "qux") 381 m := it.NewModifier("cmd") 382 if m.Vars()["baz"] != "qux" { 383 t.Fatalf("Modifier var has wrong value. Expected=qux, Received=%v", m.Vars()["baz"]) 384 } 385 if m.Vars()["foo"] != "bar" { 386 t.Fatalf("Modifier var has wrong value. Expected=bar, Received=%v", m.Vars()["foo"]) 387 } 388 } 389 390 // TestSortFeedback sorts Feedback.Items 391 func TestSortFeedback(t *testing.T) { 392 for _, td := range feedbackTitles { 393 fb := NewFeedback() 394 for _, s := range td.in { 395 fb.NewItem(s) 396 } 397 r := fb.Sort(td.q) 398 for i, it := range fb.Items { 399 if it.title != td.out[i] { 400 t.Errorf("query=%#v, pos=%d, expected=%s, got=%s", td.q, i+1, td.out[i], it.title) 401 } 402 if r[i].Match != td.m[i] { 403 t.Errorf("query=%#v, keywords=%#v, expected=%v, got=%v", td.q, it.title, td.m[i], r[i].Match) 404 } 405 } 406 } 407 } 408 409 var feedbackTitles = []struct { 410 q string 411 in []string 412 out []string 413 m []bool 414 }{ 415 { 416 q: "got", 417 in: []string{"game of thrones", "no match", "got milk?", "got"}, 418 out: []string{"got", "game of thrones", "got milk?", "no match"}, 419 m: []bool{true, true, true, false}, 420 }, 421 { 422 q: "of", 423 in: []string{"out of time", "spelunking", "OmniFocus", "game of thrones"}, 424 out: []string{"OmniFocus", "out of time", "game of thrones", "spelunking"}, 425 m: []bool{true, true, true, false}, 426 }, 427 { 428 q: "safa", 429 in: []string{"see all fellows' armpits", "Safari", "french canada", "spanish harlem"}, 430 out: []string{"Safari", "see all fellows' armpits", "spanish harlem", "french canada"}, 431 m: []bool{true, true, false, false}, 432 }, 433 } 434 435 var filterTitles = []struct { 436 q string 437 in []string 438 out []string 439 }{ 440 { 441 q: "got", 442 in: []string{"game of thrones", "no match", "got milk?", "got"}, 443 out: []string{"got", "game of thrones", "got milk?"}, 444 }, 445 { 446 q: "of", 447 in: []string{"out of time", "spelunking", "OmniFocus", "game of thrones"}, 448 out: []string{"OmniFocus", "out of time", "game of thrones"}, 449 }, 450 { 451 q: "safa", 452 in: []string{"see all fellows' armpits", "Safari", "french canada", "spanish harlem"}, 453 out: []string{"Safari", "see all fellows' armpits"}, 454 }, 455 } 456 457 // TestFilterFeedback filters Feedback.Items 458 func TestFilterFeedback(t *testing.T) { 459 for _, td := range filterTitles { 460 fb := NewFeedback() 461 for _, s := range td.in { 462 fb.NewItem(s) 463 } 464 fb.Filter(td.q) 465 if len(fb.Items) != len(td.out) { 466 t.Errorf("query=%#v, expected %d results, got %d", td.q, len(td.out), len(fb.Items)) 467 } 468 for i, it := range fb.Items { 469 if it.title != td.out[i] { 470 t.Errorf("query=%#v, pos=%d, expected=%s, got=%s", td.q, i+1, td.out[i], it.title) 471 } 472 } 473 } 474 }