Add tests for the dock

This commit is contained in:
K Simmons 2022-09-13 19:18:05 -07:00
parent ad77bb7b92
commit 3b9b2cd443
6 changed files with 276 additions and 88 deletions

View file

@ -7826,7 +7826,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.set_state("one «two threeˇ» four"); cx.set_state("one «two threeˇ» four");
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx); editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
@ -7974,7 +7974,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_newline_below(cx: &mut gpui::TestAppContext) { async fn test_newline_below(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.update(|cx| { cx.update(|cx| {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap()); settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
@ -8050,7 +8050,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_tab(cx: &mut gpui::TestAppContext) { async fn test_tab(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.update(|cx| { cx.update(|cx| {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap()); settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
@ -8081,7 +8081,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) { async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
let language = Arc::new( let language = Arc::new(
Language::new( Language::new(
LanguageConfig::default(), LanguageConfig::default(),
@ -8139,7 +8139,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.set_state(indoc! {" cx.set_state(indoc! {"
«oneˇ» «twoˇ» «oneˇ» «twoˇ»
@ -8208,7 +8208,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) { async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.update(|cx| { cx.update(|cx| {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.editor_overrides.hard_tabs = Some(true); settings.editor_overrides.hard_tabs = Some(true);
@ -8416,7 +8416,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_backspace(cx: &mut gpui::TestAppContext) { async fn test_backspace(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
// Basic backspace // Basic backspace
cx.set_state(indoc! {" cx.set_state(indoc! {"
@ -8463,7 +8463,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_delete(cx: &mut gpui::TestAppContext) { async fn test_delete(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.set_state(indoc! {" cx.set_state(indoc! {"
onˇe two three onˇe two three
@ -8800,7 +8800,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_clipboard(cx: &mut gpui::TestAppContext) { async fn test_clipboard(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six "); cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
cx.update_editor(|e, cx| e.cut(&Cut, cx)); cx.update_editor(|e, cx| e.cut(&Cut, cx));
@ -8876,7 +8876,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
let language = Arc::new(Language::new( let language = Arc::new(Language::new(
LanguageConfig::default(), LanguageConfig::default(),
Some(tree_sitter_rust::language()), Some(tree_sitter_rust::language()),
@ -9305,7 +9305,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_select_next(cx: &mut gpui::TestAppContext) { async fn test_select_next(cx: &mut gpui::TestAppContext) {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx);
cx.set_state("abc\nˇabc abc\ndefabc\nabc"); cx.set_state("abc\nˇabc abc\ndefabc\nabc");
cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx));

View file

@ -88,7 +88,7 @@ pub struct EditorTestContext<'a> {
} }
impl<'a> EditorTestContext<'a> { impl<'a> EditorTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
let (window_id, editor) = cx.update(|cx| { let (window_id, editor) = cx.update(|cx| {
cx.set_global(Settings::test(cx)); cx.set_global(Settings::test(cx));
crate::init(cx); crate::init(cx);

View file

@ -26,7 +26,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(Dock::move_dock); cx.add_action(Dock::move_dock);
} }
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DockPosition { pub enum DockPosition {
Shown(DockAnchor), Shown(DockAnchor),
Hidden(DockAnchor), Hidden(DockAnchor),
@ -372,12 +372,14 @@ impl StatusItemView for ToggleDockButton {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use std::ops::{Deref, DerefMut};
use gpui::{TestAppContext, ViewContext};
use gpui::{AppContext, TestAppContext, UpdateView, ViewContext};
use project::{FakeFs, Project}; use project::{FakeFs, Project};
use settings::Settings; use settings::Settings;
use crate::{tests::TestItem, ItemHandle, Workspace}; use super::*;
use crate::{sidebar::Sidebar, tests::TestItem, ItemHandle, Workspace};
pub fn default_item_factory( pub fn default_item_factory(
_workspace: &mut Workspace, _workspace: &mut Workspace,
@ -388,83 +390,263 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_dock_hides_when_pane_empty(cx: &mut TestAppContext) { async fn test_dock_hides_when_pane_empty(cx: &mut TestAppContext) {
cx.foreground().forbid_parking(); let mut cx = DockTestContext::new(cx).await;
Settings::test_async(cx); // Closing the last item in the dock hides the dock
let fs = FakeFs::new(cx.background()); cx.move_dock(DockAnchor::Right);
let old_items = cx.dock_items();
assert!(!old_items.is_empty());
cx.close_dock_items().await;
cx.assert_dock_position(DockPosition::Hidden(DockAnchor::Right));
let project = Project::test(fs, [], cx).await; // Reopening the dock adds a new item
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx)); cx.move_dock(DockAnchor::Right);
let new_items = cx.dock_items();
// Open dock assert!(!new_items.is_empty());
workspace.update(cx, |workspace, cx| { assert!(new_items
Dock::show(workspace, cx); .into_iter()
}); .all(|new_item| !old_items.contains(&new_item)));
// Ensure dock has an item in it
let dock_item_handle = workspace.read_with(cx, |workspace, cx| {
let dock = workspace.dock_pane().read(cx);
dock.items()
.next()
.expect("Dock should have an item in it")
.clone()
});
// Close item
let close_task = workspace.update(cx, |workspace, cx| {
Pane::close_item(
workspace,
workspace.dock_pane().clone(),
dock_item_handle.id(),
cx,
)
});
close_task.await.expect("Dock item closed successfully");
// Ensure dock closes
workspace.read_with(cx, |workspace, cx| {
assert!(workspace.dock.visible_pane().is_some())
});
// Open again
workspace.update(cx, |workspace, cx| {
Dock::show(workspace, cx);
});
// Ensure dock has item in it
workspace.read_with(cx, |workspace, cx| {
let dock = workspace.dock_pane().read(cx);
dock.items().next().expect("Dock should have an item in it");
});
} }
#[gpui::test] #[gpui::test]
async fn test_dock_panel_collisions(cx: &mut TestAppContext) { async fn test_dock_panel_collisions(cx: &mut TestAppContext) {
// Open dock expanded let mut cx = DockTestContext::new(cx).await;
// Open left panel
// Ensure dock closes // Dock closes when expanded for either panel
// Open dock to the right cx.move_dock(DockAnchor::Expanded);
// Open left panel cx.open_sidebar(SidebarSide::Left);
// Ensure dock is left open cx.assert_dock_position(DockPosition::Hidden(DockAnchor::Expanded));
// Open right panel cx.close_sidebar(SidebarSide::Left);
// Ensure dock closes cx.move_dock(DockAnchor::Expanded);
// Open dock bottom cx.open_sidebar(SidebarSide::Right);
// Open left panel cx.assert_dock_position(DockPosition::Hidden(DockAnchor::Expanded));
// Open right panel
// Ensure dock still open // Dock closes in the right position if the right sidebar is opened
cx.move_dock(DockAnchor::Right);
cx.open_sidebar(SidebarSide::Left);
cx.assert_dock_position(DockPosition::Shown(DockAnchor::Right));
cx.open_sidebar(SidebarSide::Right);
cx.assert_dock_position(DockPosition::Hidden(DockAnchor::Right));
cx.close_sidebar(SidebarSide::Right);
// Dock in bottom position ignores sidebars
cx.move_dock(DockAnchor::Bottom);
cx.open_sidebar(SidebarSide::Left);
cx.open_sidebar(SidebarSide::Right);
cx.assert_dock_position(DockPosition::Shown(DockAnchor::Bottom));
// Opening the dock in the right position closes the right sidebar
cx.move_dock(DockAnchor::Right);
cx.assert_sidebar_closed(SidebarSide::Right);
} }
#[gpui::test] #[gpui::test]
async fn test_focusing_panes_shows_and_hides_dock(cx: &mut TestAppContext) { async fn test_focusing_panes_shows_and_hides_dock(cx: &mut TestAppContext) {
// Open item in center pane let mut cx = DockTestContext::new(cx).await;
// Open dock expanded
// Focus new item // Focusing an item not in the dock when expanded hides the dock
// Ensure the dock gets hidden let center_item = cx.add_item_to_center_pane();
// Open dock to the right cx.move_dock(DockAnchor::Expanded);
// Focus new item let dock_item = cx
// Ensure dock stays shown but inactive .dock_items()
// Add item to dock and hide it .get(0)
// Focus the added item .cloned()
// Ensure the dock is open .expect("Dock should have an item at this point");
center_item.update(&mut cx, |_, cx| cx.focus_self());
cx.assert_dock_position(DockPosition::Hidden(DockAnchor::Expanded));
// Focusing an item not in the dock when not expanded, leaves the dock open but inactive
cx.move_dock(DockAnchor::Right);
center_item.update(&mut cx, |_, cx| cx.focus_self());
cx.assert_dock_position(DockPosition::Shown(DockAnchor::Right));
cx.assert_dock_pane_inactive();
// Focus dock item
dock_item.update(&mut cx, |_, cx| cx.focus_self());
cx.assert_dock_position(DockPosition::Shown(DockAnchor::Right));
cx.assert_dock_pane_active();
}
struct DockTestContext<'a> {
pub cx: &'a mut TestAppContext,
pub window_id: usize,
pub workspace: ViewHandle<Workspace>,
}
impl<'a> DockTestContext<'a> {
pub async fn new(cx: &'a mut TestAppContext) -> DockTestContext<'a> {
Settings::test_async(cx);
let fs = FakeFs::new(cx.background());
cx.update(|cx| init(cx));
let project = Project::test(fs, [], cx).await;
let (window_id, workspace) =
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
workspace.update(cx, |workspace, cx| {
let left_panel = cx.add_view(|_| TestItem::new());
workspace.left_sidebar().update(cx, |sidebar, cx| {
sidebar.add_item(
"icons/folder_tree_16.svg",
"Left Test Panel".to_string(),
left_panel.clone(),
cx,
);
});
let right_panel = cx.add_view(|_| TestItem::new());
workspace.right_sidebar().update(cx, |sidebar, cx| {
sidebar.add_item(
"icons/folder_tree_16.svg",
"Right Test Panel".to_string(),
right_panel.clone(),
cx,
);
});
});
Self {
cx,
window_id,
workspace,
}
}
pub fn workspace<F, T>(&self, read: F) -> T
where
F: FnOnce(&Workspace, &AppContext) -> T,
{
self.workspace.read_with(self.cx, read)
}
pub fn update_workspace<F, T>(&mut self, update: F) -> T
where
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
{
self.workspace.update(self.cx, update)
}
pub fn sidebar<F, T>(&self, sidebar_side: SidebarSide, read: F) -> T
where
F: FnOnce(&Sidebar, &AppContext) -> T,
{
self.workspace(|workspace, cx| {
let sidebar = match sidebar_side {
SidebarSide::Left => workspace.left_sidebar(),
SidebarSide::Right => workspace.right_sidebar(),
}
.read(cx);
read(sidebar, cx)
})
}
pub fn add_item_to_center_pane(&mut self) -> ViewHandle<TestItem> {
self.update_workspace(|workspace, cx| {
let item = cx.add_view(|_| TestItem::new());
let pane = workspace
.last_active_center_pane
.clone()
.unwrap_or_else(|| workspace.center.panes()[0].clone());
Pane::add_item(
workspace,
&pane,
Box::new(item.clone()),
true,
true,
None,
cx,
);
item
})
}
pub fn dock_pane<F, T>(&self, read: F) -> T
where
F: FnOnce(&Pane, &AppContext) -> T,
{
self.workspace(|workspace, cx| {
let dock_pane = workspace.dock_pane().read(cx);
read(dock_pane, cx)
})
}
pub fn move_dock(&self, anchor: DockAnchor) {
self.cx.dispatch_action(self.window_id, MoveDock(anchor));
}
pub fn open_sidebar(&mut self, sidebar_side: SidebarSide) {
if !self.sidebar(sidebar_side, |sidebar, _| sidebar.is_open()) {
self.update_workspace(|workspace, cx| workspace.toggle_sidebar(sidebar_side, cx));
}
}
pub fn close_sidebar(&mut self, sidebar_side: SidebarSide) {
if self.sidebar(sidebar_side, |sidebar, _| sidebar.is_open()) {
self.update_workspace(|workspace, cx| workspace.toggle_sidebar(sidebar_side, cx));
}
}
pub fn dock_items(&self) -> Vec<ViewHandle<TestItem>> {
self.dock_pane(|pane, cx| {
pane.items()
.map(|item| {
item.act_as::<TestItem>(cx)
.expect("Dock Test Context uses TestItems in the dock")
})
.collect()
})
}
pub async fn close_dock_items(&mut self) {
self.update_workspace(|workspace, cx| {
Pane::close_items(workspace, workspace.dock_pane().clone(), cx, |_| true)
})
.await
.expect("Could not close dock items")
}
pub fn assert_dock_position(&self, expected_position: DockPosition) {
self.workspace(|workspace, _| assert_eq!(workspace.dock.position, expected_position));
}
pub fn assert_sidebar_closed(&self, sidebar_side: SidebarSide) {
assert!(!self.sidebar(sidebar_side, |sidebar, _| sidebar.is_open()));
}
pub fn assert_dock_pane_active(&self) {
assert!(self.dock_pane(|pane, _| pane.is_active()))
}
pub fn assert_dock_pane_inactive(&self) {
assert!(!self.dock_pane(|pane, _| pane.is_active()))
}
}
impl<'a> Deref for DockTestContext<'a> {
type Target = gpui::TestAppContext;
fn deref(&self) -> &Self::Target {
self.cx
}
}
impl<'a> DerefMut for DockTestContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx
}
}
impl<'a> UpdateView for DockTestContext<'a> {
fn update_view<T, S>(
&mut self,
handle: &ViewHandle<T>,
update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
) -> S
where
T: View,
{
handle.update(self.cx, update)
}
} }
} }

View file

@ -706,7 +706,7 @@ impl Pane {
pane: ViewHandle<Pane>, pane: ViewHandle<Pane>,
item_id_to_close: usize, item_id_to_close: usize,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) -> Task<Result<bool>> { ) -> Task<Result<()>> {
Self::close_items(workspace, pane, cx, move |view_id| { Self::close_items(workspace, pane, cx, move |view_id| {
view_id == item_id_to_close view_id == item_id_to_close
}) })
@ -717,7 +717,7 @@ impl Pane {
pane: ViewHandle<Pane>, pane: ViewHandle<Pane>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
should_close: impl 'static + Fn(usize) -> bool, should_close: impl 'static + Fn(usize) -> bool,
) -> Task<Result<bool>> { ) -> Task<Result<()>> {
let project = workspace.project().clone(); let project = workspace.project().clone();
// Find the items to close. // Find the items to close.
@ -790,7 +790,7 @@ impl Pane {
} }
pane.update(&mut cx, |_, cx| cx.notify()); pane.update(&mut cx, |_, cx| cx.notify());
Ok(true) Ok(())
}) })
} }

View file

@ -11,7 +11,9 @@ pub trait SidebarItem: View {
fn should_activate_item_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { fn should_activate_item_on_event(&self, _: &Self::Event, _: &AppContext) -> bool {
false false
} }
fn should_show_badge(&self, cx: &AppContext) -> bool; fn should_show_badge(&self, _: &AppContext) -> bool {
false
}
fn contains_focused_view(&self, _: &AppContext) -> bool { fn contains_focused_view(&self, _: &AppContext) -> bool {
false false
} }

View file

@ -2934,6 +2934,8 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
mod tests { mod tests {
use std::cell::Cell; use std::cell::Cell;
use crate::sidebar::SidebarItem;
use super::*; use super::*;
use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext}; use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext};
use project::{FakeFs, Project, ProjectEntryId}; use project::{FakeFs, Project, ProjectEntryId};
@ -3724,4 +3726,6 @@ mod tests {
vec![ItemEvent::UpdateTab, ItemEvent::Edit] vec![ItemEvent::UpdateTab, ItemEvent::Edit]
} }
} }
impl SidebarItem for TestItem {}
} }