github.com/clysto/awgo@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  }