package app import ( "os" "path/filepath" "strings" "testing" "github.com/gdamore/tcell/v2" ) // newTreeApp returns an editor whose working directory is a small project. func newTreeApp(t *testing.T, names ...string) (*App, tcell.SimulationScreen, string) { t.Helper() root := testDirectory(t, names...) t.Chdir(root) a, screen := newTestApp(t) return a, screen, root } func TestTheProjectTreeOpensAWindowNamedAfterTheProject(t *testing.T) { a, _, root := newTreeApp(t, "main.go") a.ProjectTree() if a.Desktop().Count() != 1 { t.Fatalf("Count() = %d, want the tree window", a.Desktop().Count()) } if got := a.Desktop().Active().Title(); got != filepath.Base(root) { t.Errorf("the window is called %q, want %q", got, filepath.Base(root)) } if a.activeView() != nil { t.Error("activeView() returned an editor for the tree window") } } func TestTheTreeIsRootedWhereTheEditorWasStarted(t *testing.T) { // The same rule .turbo-go follows, so "the project" means one thing. a, screen, _ := newTreeApp(t, "internal/", "main.go") a.ProjectTree() lines := render(t, a, screen) for _, want := range []string{"internal", "main.go"} { if !containsAnyLine(lines, want) { t.Errorf("the tree does not show %q", want) } } } func TestOpeningTheTreeTwiceRaisesTheSameWindow(t *testing.T) { a, _, _ := newTreeApp(t, "main.go") a.ProjectTree() first := a.Desktop().Active() a.NewFile() a.ProjectTree() if a.Desktop().Count() != 2 { t.Fatalf("Count() = %d, want the tree and the file", a.Desktop().Count()) } if a.Desktop().Active() != first { t.Error("a second tree window was opened instead of raising the first") } } func TestF9OpensTheProjectTree(t *testing.T) { a, _, root := newTreeApp(t, "main.go") press(a, tcell.KeyF9, 0, tcell.ModNone) if a.Desktop().Count() != 1 { t.Fatalf("Count() = %d, want F9 to have opened the tree", a.Desktop().Count()) } if got := a.Desktop().Active().Title(); got != filepath.Base(root) { t.Errorf("the window is called %q", got) } } func TestChoosingAFileInTheTreeOpensIt(t *testing.T) { a, _, root := newTreeApp(t, "main.go") writeTestFile(t, filepath.Join(root, "main.go"), "package main\n") a.ProjectTree() press(a, tcell.KeyEnter, 0, tcell.ModNone) // the highlight starts on main.go if a.Desktop().Count() != 2 { t.Fatalf("Count() = %d, want the tree and the opened file", a.Desktop().Count()) } if got := a.Desktop().Active().Title(); got != "main.go" { t.Errorf("the front window is %q, want the file the tree opened", got) } if got := activeBuffer(t, a).Text(); got != "package main\n" { t.Errorf("the buffer holds %q", got) } } func TestChoosingADirectoryInTheTreeOpensNoWindow(t *testing.T) { a, _, _ := newTreeApp(t, "internal/", "main.go") a.ProjectTree() press(a, tcell.KeyEnter, 0, tcell.ModNone) // the highlight starts on internal/ if a.Desktop().Count() != 1 { t.Errorf("Count() = %d; choosing a directory opened a window", a.Desktop().Count()) } } func TestClosingTheTreeLetsANewOneOpen(t *testing.T) { a, _, _ := newTreeApp(t, "main.go") a.ProjectTree() a.CloseFile() if a.Desktop().Count() != 0 { t.Fatalf("Count() = %d, want the tree closed", a.Desktop().Count()) } if a.Modals() != 0 { t.Error("closing the tree asked about unsaved changes") } a.ProjectTree() if a.Desktop().Count() != 1 { t.Error("the tree could not be opened again after being closed") } } func TestTheFileActionsLeaveTheTreeAlone(t *testing.T) { a, _, _ := newTreeApp(t, "main.go") a.ProjectTree() a.SaveFile() a.SaveFileAs() a.ToggleLineNumbers() a.RequestCompletion() if a.Modals() != 0 { t.Errorf("%d dialogs opened over the tree window", a.Modals()) } if a.Desktop().Count() != 1 { t.Errorf("Count() = %d, want the tree still there", a.Desktop().Count()) } } func TestSavingAFileRefreshesTheTree(t *testing.T) { a, screen, root := newTreeApp(t, "main.go") writeTestFile(t, filepath.Join(root, "main.go"), "package main\n") a.ProjectTree() a.Open(filepath.Join(root, "main.go")) // A file that appeared after the tree was read — as a build in a terminal // window would leave behind. if err := os.WriteFile(filepath.Join(root, "made-later.go"), nil, 0o644); err != nil { t.Fatalf("creating the file: %v", err) } if containsAnyLine(render(t, a, screen), "made-later.go") { t.Fatal("the tree showed the new file before anything refreshed it") } a.SaveFile() if !containsAnyLine(render(t, a, screen), "made-later.go") { t.Error("saving did not refresh the tree") } } func TestLeavingTheEditorWithATreeOpenAsksNothing(t *testing.T) { a, _, _ := newTreeApp(t, "main.go") a.ProjectTree() a.Quit() if !a.Quitting() { t.Error("Quit() did not leave with only a tree open") } if a.Modals() != 0 { t.Errorf("Quit() opened %d dialogs for a window with no unsaved work", a.Modals()) } } // containsAnyLine reports whether any drawn row holds a piece of text. func containsAnyLine(lines []string, want string) bool { for _, line := range lines { if strings.Contains(line, want) { return true } } return false }