From 721d7615c39f39f0ce039ce384710e112cb56b2d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 12 Dec 2023 13:32:57 +0200 Subject: [PATCH] Add tests --- crates/project_panel/src/project_panel.rs | 459 +++++++++++++++++++++- 1 file changed, 457 insertions(+), 2 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 75e64e4926..30d750cf46 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1532,7 +1532,6 @@ impl ProjectPanel { .into_any_named("project panel entry") } - // TODO kb tests fn reveal_entry( &mut self, project: ModelHandle, @@ -2982,6 +2981,445 @@ mod tests { ); } + #[gpui::test] + async fn test_autoreveal_and_gitignored_files(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + cx.update(|cx| { + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |project_settings| { + project_settings.file_scan_exclusions = Some(Vec::new()); + }); + store.update_user_settings::(cx, |project_panel_settings| { + project_panel_settings.auto_reveal_entries = Some(false) + }); + }) + }); + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/project_root", + json!({ + ".git": {}, + ".gitignore": "**/gitignored_dir", + "dir_1": { + "file_1.py": "# File 1_1 contents", + "file_2.py": "# File 1_2 contents", + "file_3.py": "# File 1_3 contents", + "gitignored_dir": { + "file_a.py": "# File contents", + "file_b.py": "# File contents", + "file_c.py": "# File contents", + }, + }, + "dir_2": { + "file_1.py": "# File 2_1 contents", + "file_2.py": "# File 2_2 contents", + "file_3.py": "# File 2_3 contents", + } + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .root(cx); + let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); + + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1", + " > dir_2", + " .gitignore", + ] + ); + + let dir_1_file = find_project_entry(&panel, "project_root/dir_1/file_1.py", cx) + .expect("dir 1 file is not ignored and should have an entry"); + let dir_2_file = find_project_entry(&panel, "project_root/dir_2/file_1.py", cx) + .expect("dir 2 file is not ignored and should have an entry"); + let gitignored_dir_file = + find_project_entry(&panel, "project_root/dir_1/gitignored_dir/file_a.py", cx); + assert_eq!( + gitignored_dir_file, None, + "File in the gitignored dir should not have an entry before its dir is toggled" + ); + + toggle_expand_dir(&panel, "project_root/dir_1", cx); + toggle_expand_dir(&panel, "project_root/dir_1/gitignored_dir", cx); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " v gitignored_dir <== selected", + " file_a.py", + " file_b.py", + " file_c.py", + " file_1.py", + " file_2.py", + " file_3.py", + " > dir_2", + " .gitignore", + ], + "Should show gitignored dir file list in the project panel" + ); + let gitignored_dir_file = + find_project_entry(&panel, "project_root/dir_1/gitignored_dir/file_a.py", cx) + .expect("after gitignored dir got opened, a file entry should be present"); + + toggle_expand_dir(&panel, "project_root/dir_1/gitignored_dir", cx); + toggle_expand_dir(&panel, "project_root/dir_1", cx); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1 <== selected", + " > dir_2", + " .gitignore", + ], + "Should hide all dir contents again and prepare for the auto reveal test" + ); + + for file_entry in [dir_1_file, dir_2_file, gitignored_dir_file] { + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::ActiveEntryChanged(Some(file_entry))) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1 <== selected", + " > dir_2", + " .gitignore", + ], + "When no auto reveal is enabled, the selected entry should not be revealed in the project panel" + ); + } + + cx.update(|cx| { + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |project_panel_settings| { + project_panel_settings.auto_reveal_entries = Some(true) + }); + }) + }); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::ActiveEntryChanged(Some(dir_1_file))) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " > gitignored_dir", + " file_1.py <== selected", + " file_2.py", + " file_3.py", + " > dir_2", + " .gitignore", + ], + "When auto reveal is enabled, not ignored dir_1 entry should be revealed" + ); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::ActiveEntryChanged(Some(dir_2_file))) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " > gitignored_dir", + " file_1.py", + " file_2.py", + " file_3.py", + " v dir_2", + " file_1.py <== selected", + " file_2.py", + " file_3.py", + " .gitignore", + ], + "When auto reveal is enabled, not ignored dir_2 entry should be revealed" + ); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::ActiveEntryChanged(Some( + gitignored_dir_file, + ))) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " > gitignored_dir", + " file_1.py", + " file_2.py", + " file_3.py", + " v dir_2", + " file_1.py <== selected", + " file_2.py", + " file_3.py", + " .gitignore", + ], + "When auto reveal is enabled, a gitignored selected entry should not be revealed in the project panel" + ); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel(gitignored_dir_file)) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " v gitignored_dir", + " file_a.py <== selected", + " file_b.py", + " file_c.py", + " file_1.py", + " file_2.py", + " file_3.py", + " v dir_2", + " file_1.py", + " file_2.py", + " file_3.py", + " .gitignore", + ], + "When a gitignored entry is explicitly revealed, it should be shown in the project tree" + ); + } + + #[gpui::test] + async fn test_explicit_reveal(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + cx.update(|cx| { + cx.update_global::(|store, cx| { + store.update_user_settings::(cx, |project_settings| { + project_settings.file_scan_exclusions = Some(Vec::new()); + }); + store.update_user_settings::(cx, |project_panel_settings| { + project_panel_settings.auto_reveal_entries = Some(false) + }); + }) + }); + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/project_root", + json!({ + ".git": {}, + ".gitignore": "**/gitignored_dir", + "dir_1": { + "file_1.py": "# File 1_1 contents", + "file_2.py": "# File 1_2 contents", + "file_3.py": "# File 1_3 contents", + "gitignored_dir": { + "file_a.py": "# File contents", + "file_b.py": "# File contents", + "file_c.py": "# File contents", + }, + }, + "dir_2": { + "file_1.py": "# File 2_1 contents", + "file_2.py": "# File 2_2 contents", + "file_3.py": "# File 2_3 contents", + } + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; + let workspace = cx + .add_window(|cx| Workspace::test_new(project.clone(), cx)) + .root(cx); + let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); + + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1", + " > dir_2", + " .gitignore", + ] + ); + + let dir_1_file = find_project_entry(&panel, "project_root/dir_1/file_1.py", cx) + .expect("dir 1 file is not ignored and should have an entry"); + let dir_2_file = find_project_entry(&panel, "project_root/dir_2/file_1.py", cx) + .expect("dir 2 file is not ignored and should have an entry"); + let gitignored_dir_file = + find_project_entry(&panel, "project_root/dir_1/gitignored_dir/file_a.py", cx); + assert_eq!( + gitignored_dir_file, None, + "File in the gitignored dir should not have an entry before its dir is toggled" + ); + + toggle_expand_dir(&panel, "project_root/dir_1", cx); + toggle_expand_dir(&panel, "project_root/dir_1/gitignored_dir", cx); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " v gitignored_dir <== selected", + " file_a.py", + " file_b.py", + " file_c.py", + " file_1.py", + " file_2.py", + " file_3.py", + " > dir_2", + " .gitignore", + ], + "Should show gitignored dir file list in the project panel" + ); + let gitignored_dir_file = + find_project_entry(&panel, "project_root/dir_1/gitignored_dir/file_a.py", cx) + .expect("after gitignored dir got opened, a file entry should be present"); + + toggle_expand_dir(&panel, "project_root/dir_1/gitignored_dir", cx); + toggle_expand_dir(&panel, "project_root/dir_1", cx); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1 <== selected", + " > dir_2", + " .gitignore", + ], + "Should hide all dir contents again and prepare for the explicit reveal test" + ); + + for file_entry in [dir_1_file, dir_2_file, gitignored_dir_file] { + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::ActiveEntryChanged(Some(file_entry))) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " > dir_1 <== selected", + " > dir_2", + " .gitignore", + ], + "When no auto reveal is enabled, the selected entry should not be revealed in the project panel" + ); + } + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel(dir_1_file)) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " > gitignored_dir", + " file_1.py <== selected", + " file_2.py", + " file_3.py", + " > dir_2", + " .gitignore", + ], + "With no auto reveal, explicit reveal should show the dir_1 entry in the project panel" + ); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel(dir_2_file)) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " > gitignored_dir", + " file_1.py", + " file_2.py", + " file_3.py", + " v dir_2", + " file_1.py <== selected", + " file_2.py", + " file_3.py", + " .gitignore", + ], + "With no auto reveal, explicit reveal should show the dir_2 entry in the project panel" + ); + + panel.update(cx, |panel, cx| { + panel.project.update(cx, |_, cx| { + cx.emit(project::Event::RevealInProjectPanel(gitignored_dir_file)) + }) + }); + cx.foreground().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..20, cx), + &[ + "v project_root", + " > .git", + " v dir_1", + " v gitignored_dir", + " file_a.py <== selected", + " file_b.py", + " file_c.py", + " file_1.py", + " file_2.py", + " file_3.py", + " v dir_2", + " file_1.py", + " file_2.py", + " file_3.py", + " .gitignore", + ], + "With no auto reveal, explicit reveal should show the gitignored entry in the project panel" + ); + } + fn toggle_expand_dir( panel: &ViewHandle, path: impl AsRef, @@ -3019,10 +3457,27 @@ mod tests { return; } } - panic!("no worktree for path {:?}", path); + panic!("no worktree for path {path:?}"); }); } + fn find_project_entry( + panel: &ViewHandle, + path: impl AsRef, + cx: &mut TestAppContext, + ) -> Option { + let path = path.as_ref(); + panel.update(cx, |panel, cx| { + for worktree in panel.project.read(cx).worktrees(cx).collect::>() { + let worktree = worktree.read(cx); + if let Ok(relative_path) = path.strip_prefix(worktree.root_name()) { + return worktree.entry_for_path(relative_path).map(|entry| entry.id); + } + } + panic!("no worktree for path {path:?}"); + }) + } + fn visible_entries_as_strings( panel: &ViewHandle, range: Range,