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  }