github.com/clusterize-io/tusk@v0.6.3-0.20211001020217-cfe8a8cd0d4a/appcli/install_completion_test.go (about) 1 package appcli 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "regexp" 9 "testing" 10 11 "github.com/clusterize-io/tusk/runner" 12 "github.com/clusterize-io/tusk/ui" 13 "gotest.tools/v3/assert" 14 "gotest.tools/v3/assert/cmp" 15 "gotest.tools/v3/env" 16 "gotest.tools/v3/fs" 17 ) 18 19 func TestInstallCompletionUnsupported(t *testing.T) { 20 err := InstallCompletion( 21 &runner.Metadata{ 22 InstallCompletion: "fake", 23 }, 24 ) 25 assert.ErrorContains(t, err, `tab completion for "fake" is not supported`) 26 } 27 28 func TestUninstallCompletionUnsupported(t *testing.T) { 29 err := UninstallCompletion( 30 &runner.Metadata{ 31 UninstallCompletion: "fake", 32 }, 33 ) 34 assert.ErrorContains(t, err, `tab completion for "fake" is not supported`) 35 } 36 37 func TestInstallBashCompletion(t *testing.T) { 38 homedir := fs.NewDir(t, "home") 39 defer homedir.Remove() 40 41 datadir := fs.NewDir(t, "data", fs.WithDir("tusk")) 42 defer datadir.Remove() 43 44 defer env.PatchAll(t, map[string]string{ 45 "HOME": homedir.Path(), 46 "USERPROFILE": homedir.Path(), 47 "XDG_DATA_HOME": datadir.Path(), 48 })() 49 50 err := installBashCompletion(ui.Noop()) 51 assert.NilError(t, err) 52 53 completionFile := filepath.Join(datadir.Path(), "tusk", "tusk-completion.bash") 54 contents, err := ioutil.ReadFile(completionFile) 55 assert.NilError(t, err) 56 57 assert.Check(t, cmp.Equal(string(contents), rawBashCompletion)) 58 59 rcfile := filepath.Join(homedir.Path(), ".bashrc") 60 rcContents, err := ioutil.ReadFile(rcfile) 61 assert.NilError(t, err) 62 63 command := fmt.Sprintf("source %q", filepath.ToSlash(completionFile)) 64 assert.Check(t, cmp.Contains(string(rcContents), command)) 65 } 66 67 func TestGetBashRCFile(t *testing.T) { 68 tests := []struct { 69 name string 70 ops []fs.PathOp 71 expect string 72 }{ 73 { 74 "no-files", 75 []fs.PathOp{}, 76 ".bashrc", 77 }, 78 { 79 "all-files", 80 []fs.PathOp{ 81 fs.WithFile(".bashrc", ""), 82 fs.WithFile(".bash_profile", ""), 83 fs.WithFile(".profile", ""), 84 }, 85 ".bashrc", 86 }, 87 { 88 "bash-profile-only", 89 []fs.PathOp{ 90 fs.WithFile(".bash_profile", ""), 91 }, 92 ".bash_profile", 93 }, 94 { 95 "profile-only", 96 []fs.PathOp{ 97 fs.WithFile(".profile", ""), 98 }, 99 ".profile", 100 }, 101 } 102 103 for _, tt := range tests { 104 t.Run(tt.name, func(t *testing.T) { 105 homedir := fs.NewDir(t, "home", tt.ops...) 106 defer homedir.Remove() 107 108 defer env.PatchAll(t, map[string]string{ 109 "HOME": homedir.Path(), 110 "USERPROFILE": homedir.Path(), 111 })() 112 113 rcfile, err := getBashRCFile() 114 assert.NilError(t, err) 115 116 want := filepath.Join(homedir.Path(), tt.expect) 117 assert.Check(t, cmp.Equal(want, rcfile)) 118 }) 119 } 120 } 121 122 func TestAppendIfAbsent_trailing_newlines(t *testing.T) { 123 existing := "# First Line\n\n" 124 f := fs.NewFile(t, "bashrc", fs.WithContent(existing)) 125 defer f.Remove() 126 127 text := "# Second Line" 128 err := appendIfAbsent(f.Path(), text) 129 assert.NilError(t, err) 130 131 want := existing + text + "\n" 132 got, err := ioutil.ReadFile(f.Path()) 133 assert.NilError(t, err) 134 135 assert.Check(t, cmp.Equal(want, string(got))) 136 } 137 138 func TestAppendIfAbsent_no_trailing_newline(t *testing.T) { 139 existing := "# First Line" 140 f := fs.NewFile(t, "bashrc", fs.WithContent(existing)) 141 defer f.Remove() 142 143 text := "# Second Line" 144 err := appendIfAbsent(f.Path(), text) 145 assert.NilError(t, err) 146 147 want := existing + "\n" + text + "\n" 148 got, err := ioutil.ReadFile(f.Path()) 149 assert.NilError(t, err) 150 151 assert.Check(t, cmp.Equal(want, string(got))) 152 } 153 154 func TestAppendIfAbsent_exists(t *testing.T) { 155 text := "# Existing Line" 156 f := fs.NewFile(t, "bashrc", fs.WithContent(text)) 157 defer f.Remove() 158 159 err := appendIfAbsent(f.Path(), text) 160 assert.NilError(t, err) 161 162 got, err := ioutil.ReadFile(f.Path()) 163 assert.NilError(t, err) 164 165 assert.Check(t, cmp.Equal(text, string(got))) 166 } 167 168 func TestAppendIfAbsent_no_file(t *testing.T) { 169 f := fs.NewFile(t, "bashrc") 170 defer f.Remove() // Will be recreated 171 172 f.Remove() 173 174 text := "# Target Line" 175 err := appendIfAbsent(f.Path(), text) 176 assert.NilError(t, err) 177 178 got, err := ioutil.ReadFile(f.Path()) 179 assert.NilError(t, err) 180 181 assert.Check(t, cmp.Equal(text+"\n", string(got))) 182 } 183 184 func TestUninstallBashCompletion(t *testing.T) { 185 datadir := fs.NewDir( 186 t, 187 "data", 188 fs.WithDir("tusk", 189 fs.WithFile("tusk-completion.bash", rawBashCompletion), 190 ), 191 ) 192 defer datadir.Remove() 193 194 rcfile := filepath.Join(datadir.Path(), "tusk", "tusk-completion.bash") 195 196 contents := fmt.Sprintf("# Preamble\nsource %q", filepath.ToSlash(rcfile)) 197 homedir := fs.NewDir(t, "home", fs.WithFile(".bashrc", contents)) 198 defer homedir.Remove() 199 200 defer env.PatchAll(t, map[string]string{ 201 "HOME": homedir.Path(), 202 "USERPROFILE": homedir.Path(), 203 "XDG_DATA_HOME": datadir.Path(), 204 })() 205 206 err := uninstallBashCompletion() 207 assert.NilError(t, err) 208 209 _, err = os.Stat(rcfile) 210 assert.Check(t, os.IsNotExist(err)) 211 212 got, err := ioutil.ReadFile(filepath.Join(homedir.Path(), ".bashrc")) 213 assert.NilError(t, err) 214 215 assert.Check(t, cmp.Equal("# Preamble\n", string(got))) 216 } 217 218 func TestRemoveLineInFile(t *testing.T) { 219 content := `# First 220 match 221 222 # Second 223 224 match` 225 want := `# First 226 227 # Second 228 ` 229 230 file := fs.NewFile(t, "file", fs.WithContent(content)) 231 defer file.Remove() 232 233 err := removeLineInFile(file.Path(), regexp.MustCompile("match")) 234 assert.NilError(t, err) 235 236 got, err := ioutil.ReadFile(file.Path()) 237 assert.NilError(t, err) 238 239 assert.Check(t, cmp.Equal(want, string(got))) 240 } 241 242 func TestInstallFishCompletion(t *testing.T) { 243 cfgdir := fs.NewDir(t, "data") 244 defer cfgdir.Remove() 245 246 defer env.PatchAll(t, map[string]string{ 247 "XDG_CONFIG_HOME": cfgdir.Path(), 248 })() 249 250 err := installFishCompletion(ui.Noop()) 251 assert.NilError(t, err) 252 253 completionFile := filepath.Join(cfgdir.Path(), "fish", "completions", "tusk.fish") 254 contents, err := ioutil.ReadFile(completionFile) 255 assert.NilError(t, err) 256 257 assert.Check(t, cmp.Equal(string(contents), rawFishCompletion)) 258 } 259 260 func TestUninstallFishCompletion(t *testing.T) { 261 cfgdir := fs.NewDir( 262 t, 263 "data", 264 fs.WithDir( 265 "fish", 266 fs.WithDir( 267 "completions", 268 fs.WithFile("tusk.fish", rawFishCompletion), 269 ), 270 ), 271 ) 272 defer cfgdir.Remove() 273 274 completionFile := filepath.Join(cfgdir.Path(), "fish", "completions", "tusk.fish") 275 _, err := os.Stat(completionFile) 276 assert.NilError(t, err) 277 278 defer env.PatchAll(t, map[string]string{ 279 "XDG_CONFIG_HOME": cfgdir.Path(), 280 })() 281 282 err = uninstallFishCompletion() 283 assert.NilError(t, err) 284 285 _, err = os.Stat(completionFile) 286 assert.Check(t, os.IsNotExist(err)) 287 } 288 289 func TestGetDataDir_xdg(t *testing.T) { 290 xdgDataHome := "/foo/bar/baz" 291 defer env.Patch(t, "XDG_DATA_HOME", xdgDataHome)() 292 293 want := filepath.Join(xdgDataHome, "tusk") 294 295 got, err := getDataDir() 296 assert.NilError(t, err) 297 298 assert.Equal(t, want, got) 299 } 300 301 func TestGetDataDir_default(t *testing.T) { 302 home, err := os.UserHomeDir() 303 assert.NilError(t, err) 304 305 want := filepath.Join(home, ".local", "share", "tusk") 306 307 got, err := getDataDir() 308 assert.NilError(t, err) 309 310 assert.Equal(t, want, got) 311 } 312 313 func TestGetFishCompletionsDir_xdg(t *testing.T) { 314 cfgHome := "/foo/bar/baz" 315 defer env.Patch(t, "XDG_CONFIG_HOME", cfgHome)() 316 317 want := filepath.Join(cfgHome, "fish", "completions") 318 319 got, err := getFishCompletionsDir() 320 assert.NilError(t, err) 321 322 assert.Equal(t, want, got) 323 } 324 325 func TestGetFishCompletionsDir_default(t *testing.T) { 326 home, err := os.UserHomeDir() 327 assert.NilError(t, err) 328 329 want := filepath.Join(home, ".config", "fish", "completions") 330 331 got, err := getFishCompletionsDir() 332 assert.NilError(t, err) 333 334 assert.Equal(t, want, got) 335 } 336 337 func TestInstallZshCompletion(t *testing.T) { 338 dir := fs.NewDir(t, "project-dir") 339 defer dir.Remove() 340 341 err := installZshCompletion(ui.Noop(), dir.Path()) 342 assert.NilError(t, err) 343 344 contents, err := ioutil.ReadFile(filepath.Join(dir.Path(), "_tusk")) 345 assert.NilError(t, err) 346 347 assert.Check(t, cmp.Equal(string(contents), rawZshCompletion)) 348 } 349 350 func TestUninstallZshCompletion(t *testing.T) { 351 dir := fs.NewDir(t, "project-dir", fs.WithFile("_tusk", rawZshCompletion)) 352 defer dir.Remove() 353 354 err := uninstallZshCompletion(dir.Path()) 355 assert.NilError(t, err) 356 357 _, err = os.Stat(filepath.Join(dir.Path(), "_tusk")) 358 assert.Assert(t, os.IsNotExist(err)) 359 } 360 361 func TestUninstallZshCompletion_empty(t *testing.T) { 362 dir := fs.NewDir(t, "project-dir") 363 defer dir.Remove() 364 365 err := uninstallZshCompletion(dir.Path()) 366 assert.NilError(t, err) 367 368 _, err = os.Stat(filepath.Join(dir.Path(), "_tusk")) 369 assert.Assert(t, os.IsNotExist(err)) 370 }