src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/cli/modes/navigation_test.go (about) 1 package modes 2 3 import ( 4 "errors" 5 "testing" 6 7 "src.elv.sh/pkg/cli" 8 . "src.elv.sh/pkg/cli/clitest" 9 "src.elv.sh/pkg/cli/lscolors" 10 "src.elv.sh/pkg/cli/term" 11 "src.elv.sh/pkg/cli/tk" 12 "src.elv.sh/pkg/must" 13 "src.elv.sh/pkg/testutil" 14 "src.elv.sh/pkg/ui" 15 ) 16 17 var testDir = testutil.Dir{ 18 "a": "", 19 "d": testutil.Dir{ 20 "d1": "content\td1\nline 2", 21 "d2": testutil.Dir{ 22 "d21": "content d21", 23 "d22": "content d22", 24 "other.png": "", 25 }, 26 "d3": testutil.Dir{}, 27 ".dh": "hidden", 28 }, 29 "f": "", 30 } 31 32 func TestErrorInAscend(t *testing.T) { 33 f := Setup() 34 defer f.Stop() 35 36 c := getTestCursor() 37 c.ascendErr = errors.New("cannot ascend") 38 startNavigation(f.App, NavigationSpec{Cursor: c}) 39 40 f.TTY.Inject(term.K(ui.Left)) 41 f.TestTTYNotes(t, 42 "error: cannot ascend", Styles, 43 "!!!!!!") 44 } 45 46 func TestErrorInDescend(t *testing.T) { 47 f := Setup() 48 defer f.Stop() 49 50 c := getTestCursor() 51 c.descendErr = errors.New("cannot descend") 52 startNavigation(f.App, NavigationSpec{Cursor: c}) 53 54 f.TTY.Inject(term.K(ui.Down)) 55 f.TTY.Inject(term.K(ui.Right)) 56 f.TestTTYNotes(t, 57 "error: cannot descend", Styles, 58 "!!!!!!") 59 } 60 61 func TestErrorInCurrent(t *testing.T) { 62 f := setupNav(t) 63 defer f.Stop() 64 65 c := getTestCursor() 66 c.currentErr = errors.New("ERR") 67 startNavigation(f.App, NavigationSpec{Cursor: c}) 68 69 f.TestTTY(t, 70 "", term.DotHere, "\n", 71 " NAVIGATING \n", Styles, 72 "************ ", 73 " a ERR \n", Styles, 74 " !!!", 75 " d \n", Styles, 76 "////", 77 " f ", 78 ) 79 80 // Test that Right does nothing. 81 f.TTY.Inject(term.K(ui.Right)) 82 // We can't just test that the buffer hasn't changed, because that might 83 // capture the state of the buffer before the Right key is handled. Instead 84 // we inject a key and test the result of that instead, to ensure that the 85 // Right key had no effect. 86 f.TTY.Inject(term.K('x')) 87 f.TestTTY(t, 88 "x", term.DotHere, "\n", 89 " NAVIGATING \n", Styles, 90 "************ ", 91 " a ERR \n", Styles, 92 " !!!", 93 " d \n", Styles, 94 "////", 95 " f ", 96 ) 97 } 98 99 func TestErrorInParent(t *testing.T) { 100 f := setupNav(t) 101 defer f.Stop() 102 103 c := getTestCursor() 104 c.parentErr = errors.New("ERR") 105 startNavigation(f.App, NavigationSpec{Cursor: c}) 106 107 f.TestTTY(t, 108 "", term.DotHere, "\n", 109 " NAVIGATING \n", Styles, 110 "************ ", 111 "ERR d1 content d1\n", Styles, 112 "!!! ++++++++++++++", 113 " d2 line 2\n", Styles, 114 " //////////////", 115 " d3 ", Styles, 116 " //////////////", 117 ) 118 } 119 120 func TestWidthRatio(t *testing.T) { 121 f := setupNav(t) 122 defer f.Stop() 123 124 c := getTestCursor() 125 startNavigation(f.App, NavigationSpec{ 126 Cursor: c, 127 WidthRatio: func() [3]int { return [3]int{1, 1, 1} }, 128 }) 129 130 f.TestTTY(t, 131 "", term.DotHere, "\n", 132 " NAVIGATING \n", Styles, 133 "************ ", 134 " a d1 content d1\n", Styles, 135 " +++++++++++++", 136 " d d2 line 2\n", Styles, 137 "############ /////////////", 138 " f d3 ", Styles, 139 " /////////////", 140 ) 141 } 142 143 func TestNavigation_SelectedName(t *testing.T) { 144 f := Setup() 145 defer f.Stop() 146 147 w := startNavigation(f.App, NavigationSpec{Cursor: getTestCursor()}) 148 149 wantName := "d1" 150 if name := w.SelectedName(); name != wantName { 151 t.Errorf("Got name %q, want %q", name, wantName) 152 } 153 } 154 155 func TestNavigation_SelectedName_EmptyDirectory(t *testing.T) { 156 f := Setup() 157 defer f.Stop() 158 159 cursor := &testCursor{ 160 root: testutil.Dir{"d": testutil.Dir{}}, 161 pwd: []string{"d"}} 162 w := startNavigation(f.App, NavigationSpec{Cursor: cursor}) 163 164 wantName := "" 165 if name := w.SelectedName(); name != wantName { 166 t.Errorf("Got name %q, want %q", name, wantName) 167 } 168 } 169 170 func TestNavigation_FakeFS(t *testing.T) { 171 cursor := getTestCursor() 172 testNavigation(t, cursor) 173 } 174 175 func TestNavigation_RealFS(t *testing.T) { 176 testutil.InTempDir(t) 177 testutil.ApplyDir(testDir) 178 179 must.Chdir("d") 180 testNavigation(t, nil) 181 } 182 183 func testNavigation(t *testing.T, c NavigationCursor) { 184 f := setupNav(t) 185 defer f.Stop() 186 187 w := startNavigation(f.App, NavigationSpec{Cursor: c}) 188 189 // Test initial UI and file preview. 190 // NOTE: Buffers are named after the file that is now being selected. 191 d1Buf := f.MakeBuffer( 192 "", term.DotHere, "\n", 193 " NAVIGATING \n", Styles, 194 "************ ", 195 " a d1 content d1\n", Styles, 196 " ++++++++++++++", 197 " d d2 line 2\n", Styles, 198 "#### //////////////", 199 " f d3 ", Styles, 200 " //////////////", 201 ) 202 f.TTY.TestBuffer(t, d1Buf) 203 204 // Test scrolling of preview. 205 w.ScrollPreview(1) 206 f.App.Redraw() 207 d1Buf2 := f.MakeBuffer( 208 "", term.DotHere, "\n", 209 " NAVIGATING \n", Styles, 210 "************ ", 211 " a d1 line 2 │\n", Styles, 212 " ++++++++++++++ -", 213 " d d2 │\n", Styles, 214 "#### ////////////// -", 215 " f d3 ", Styles, 216 " ////////////// X", 217 ) 218 f.TTY.TestBuffer(t, d1Buf2) 219 220 // Test handling of selection change and directory preview. Also test 221 // LS_COLORS. 222 w.Select(tk.Next) 223 f.App.Redraw() 224 d2Buf := f.MakeBuffer( 225 "", term.DotHere, "\n", 226 " NAVIGATING \n", Styles, 227 "************ ", 228 " a d1 d21 \n", Styles, 229 " ++++++++++++++++++++", 230 " d d2 d22 \n", Styles, 231 "#### ##############", 232 " f d3 other.png ", Styles, 233 " ////////////// !!!!!!!!!!!!!!!!!!!!", 234 ) 235 f.TTY.TestBuffer(t, d2Buf) 236 237 // Test handling of Descend. 238 w.Descend() 239 f.App.Redraw() 240 d21Buf := f.MakeBuffer( 241 "", term.DotHere, "\n", 242 " NAVIGATING \n", Styles, 243 "************ ", 244 " d1 d21 content d21\n", Styles, 245 " ++++++++++++++", 246 " d2 d22 \n", Styles, 247 "####", 248 " d3 other.png ", Styles, 249 "//// !!!!!!!!!!!!!!", 250 ) 251 f.TTY.TestBuffer(t, d21Buf) 252 253 // Test handling of Ascend, and that the current column selects the 254 // directory we just ascended from, thus reverting to wantBuf1. 255 w.Ascend() 256 f.App.Redraw() 257 f.TTY.TestBuffer(t, d2Buf) 258 259 // Test handling of Descend on a regular file, i.e. do nothing. First move 260 // the cursor to d1, which is a regular file. 261 w.Select(tk.Prev) 262 f.App.Redraw() 263 f.TTY.TestBuffer(t, d1Buf) 264 // Now descend, and verify that the buffer has not changed. 265 w.Descend() 266 f.App.Redraw() 267 f.TTY.TestBuffer(t, d1Buf) 268 269 // Test showing hidden. 270 w.MutateShowHidden(func(bool) bool { return true }) 271 f.App.Redraw() 272 f.TestTTY(t, 273 "", term.DotHere, "\n", 274 " NAVIGATING (show hidden) \n", Styles, 275 "************************** ", 276 " a .dh content d1\n", 277 " d d1 line 2\n", Styles, 278 "#### ++++++++++++++", 279 " f d2 \n", Styles, 280 " //////////////", 281 " d3 ", Styles, 282 " //////////////", 283 ) 284 w.MutateShowHidden(func(bool) bool { return false }) 285 286 // Test filtering; current column shows d1, d2, d3 before filtering, and 287 // only shows d2 after filtering. 288 w.MutateFiltering(func(bool) bool { return true }) 289 f.TTY.Inject(term.K('2')) 290 dFilter2Buf := f.MakeBuffer( 291 "\n", 292 " NAVIGATING 2", Styles, 293 "************ ", term.DotHere, "\n", 294 " a d2 d21 \n", Styles, 295 " ############## ++++++++++++++++++++", 296 " d d22 \n", Styles, 297 "####", 298 " f other.png ", Styles, 299 " !!!!!!!!!!!!!!!!!!!!", 300 ) 301 f.TTY.TestBuffer(t, dFilter2Buf) 302 303 // Unbound key while filtering is ignored. 304 f.TTY.Inject(term.K('a', ui.Alt)) 305 f.TTY.TestBuffer(t, dFilter2Buf) 306 w.MutateFiltering(func(bool) bool { return false }) 307 308 // Now move into d2, and test that the filter has been cleared when 309 // descending. 310 w.Descend() 311 f.App.Redraw() 312 f.TTY.TestBuffer(t, d21Buf) 313 314 // Apply a filter within d2. 315 w.MutateFiltering(func(bool) bool { return true }) 316 f.TTY.Inject(term.K('2')) 317 f.TestTTY(t, 318 "\n", 319 " NAVIGATING 2", Styles, 320 "************ ", term.DotHere, "\n", 321 " d1 d21 content d21\n", Styles, 322 " ++++++++++++++", 323 " d2 d22 \n", Styles, 324 "####", 325 " d3 ", Styles, 326 "////", 327 ) 328 w.MutateFiltering(func(bool) bool { return false }) 329 330 // Ascend, and test that the filter has been cleared again when ascending. 331 w.Ascend() 332 f.App.Redraw() 333 f.TTY.TestBuffer(t, d2Buf) 334 335 // Now move into d3, an empty directory. 336 w.Select(tk.Next) 337 w.Descend() 338 f.App.Redraw() 339 d3NoneBuf := f.MakeBuffer( 340 "", term.DotHere, "\n", 341 " NAVIGATING \n", Styles, 342 "************ ", 343 " d1 \n", 344 " d2 \n", Styles, 345 "////", 346 " d3 ", Styles, 347 "####", 348 ) 349 f.TTY.TestBuffer(t, d3NoneBuf) 350 // Test that selecting the previous does nothing in an empty directory. 351 w.Select(tk.Prev) 352 f.App.Redraw() 353 f.TTY.TestBuffer(t, d3NoneBuf) 354 // Test that selecting the next does nothing in an empty directory. 355 w.Select(tk.Next) 356 f.App.Redraw() 357 f.TTY.TestBuffer(t, d3NoneBuf) 358 // Test that Descend does nothing in an empty directory. 359 w.Descend() 360 f.App.Redraw() 361 f.TTY.TestBuffer(t, d3NoneBuf) 362 } 363 364 func TestNewNavigation_FocusedWidgetNotCodeArea(t *testing.T) { 365 testFocusedWidgetNotCodeArea(t, func(app cli.App) error { 366 _, err := NewNavigation(app, NavigationSpec{}) 367 return err 368 }) 369 } 370 371 func setupNav(c testutil.Cleanuper) *Fixture { 372 lscolors.SetTestLsColors(c) 373 // Use a small TTY size to make the test buffer easier to build. 374 return Setup(WithTTY(func(tty TTYCtrl) { tty.SetSize(6, 40) })) 375 } 376 377 func startNavigation(app cli.App, spec NavigationSpec) Navigation { 378 w, _ := NewNavigation(app, spec) 379 startMode(app, w, nil) 380 return w 381 } 382 383 func getTestCursor() *testCursor { 384 return &testCursor{root: testDir, pwd: []string{"d"}} 385 }