Add command palette tests and simulate_keystrokes
This commit is contained in:
parent
e37d7f5b0e
commit
91b54b352b
6 changed files with 180 additions and 119 deletions
|
@ -354,129 +354,117 @@ impl std::fmt::Debug for Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod tests {
|
mod tests {
|
||||||
// use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// use super::*;
|
use super::*;
|
||||||
// use editor::Editor;
|
use editor::Editor;
|
||||||
// use gpui::{executor::Deterministic, TestAppContext};
|
use gpui::TestAppContext;
|
||||||
// use project::Project;
|
use project::Project;
|
||||||
// use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_humanize_action_name() {
|
fn test_humanize_action_name() {
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// humanize_action_name("editor::GoToDefinition"),
|
humanize_action_name("editor::GoToDefinition"),
|
||||||
// "editor: go to definition"
|
"editor: go to definition"
|
||||||
// );
|
);
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// humanize_action_name("editor::Backspace"),
|
humanize_action_name("editor::Backspace"),
|
||||||
// "editor: backspace"
|
"editor: backspace"
|
||||||
// );
|
);
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// humanize_action_name("go_to_line::Deploy"),
|
humanize_action_name("go_to_line::Deploy"),
|
||||||
// "go to line: deploy"
|
"go to line: deploy"
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[gpui::test]
|
#[gpui::test]
|
||||||
// async fn test_command_palette(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
|
async fn test_command_palette(cx: &mut TestAppContext) {
|
||||||
// let app_state = init_test(cx);
|
let app_state = init_test(cx);
|
||||||
|
|
||||||
// let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
// let workspace = window.root(cx);
|
let cx = &mut cx;
|
||||||
// let editor = window.add_view(cx, |cx| {
|
|
||||||
// let mut editor = Editor::single_line(None, cx);
|
|
||||||
// editor.set_text("abc", cx);
|
|
||||||
// editor
|
|
||||||
// });
|
|
||||||
|
|
||||||
// workspace.update(cx, |workspace, cx| {
|
let editor = cx.build_view(|cx| {
|
||||||
// cx.focus(&editor);
|
let mut editor = Editor::single_line(cx);
|
||||||
// workspace.add_item(Box::new(editor.clone()), cx)
|
editor.set_text("abc", cx);
|
||||||
// });
|
editor
|
||||||
|
});
|
||||||
|
|
||||||
// workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
// toggle_command_palette(workspace, &Toggle, cx);
|
workspace.add_item(Box::new(editor.clone()), cx);
|
||||||
// });
|
editor.update(cx, |editor, cx| editor.focus(cx))
|
||||||
|
});
|
||||||
|
|
||||||
// let palette = workspace.read_with(cx, |workspace, _| {
|
cx.simulate_keystrokes("cmd-shift-p");
|
||||||
// workspace.modal::<CommandPalette>().unwrap()
|
|
||||||
// });
|
|
||||||
|
|
||||||
// palette
|
let palette = workspace.update(cx, |workspace, cx| {
|
||||||
// .update(cx, |palette, cx| {
|
workspace
|
||||||
// // Fill up palette's command list by running an empty query;
|
.current_modal::<CommandPalette>(cx)
|
||||||
// // we only need it to subsequently assert that the palette is initially
|
.unwrap()
|
||||||
// // sorted by command's name.
|
.read(cx)
|
||||||
// palette.delegate_mut().update_matches("".to_string(), cx)
|
.picker
|
||||||
// })
|
.clone()
|
||||||
// .await;
|
});
|
||||||
|
|
||||||
// palette.update(cx, |palette, _| {
|
palette.update(cx, |palette, _| {
|
||||||
// let is_sorted =
|
assert!(palette.delegate.commands.len() > 5);
|
||||||
// |actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name);
|
let is_sorted =
|
||||||
// assert!(is_sorted(&palette.delegate().actions));
|
|actions: &[Command]| actions.windows(2).all(|pair| pair[0].name <= pair[1].name);
|
||||||
// });
|
assert!(is_sorted(&palette.delegate.commands));
|
||||||
|
});
|
||||||
|
|
||||||
// palette
|
cx.simulate_keystrokes("b c k s p");
|
||||||
// .update(cx, |palette, cx| {
|
|
||||||
// palette
|
|
||||||
// .delegate_mut()
|
|
||||||
// .update_matches("bcksp".to_string(), cx)
|
|
||||||
// })
|
|
||||||
// .await;
|
|
||||||
|
|
||||||
// palette.update(cx, |palette, cx| {
|
palette.update(cx, |palette, _| {
|
||||||
// assert_eq!(palette.delegate().matches[0].string, "editor: backspace");
|
assert_eq!(palette.delegate.matches[0].string, "editor: backspace");
|
||||||
// palette.confirm(&Default::default(), cx);
|
});
|
||||||
// });
|
|
||||||
// deterministic.run_until_parked();
|
|
||||||
// editor.read_with(cx, |editor, cx| {
|
|
||||||
// assert_eq!(editor.text(cx), "ab");
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Add namespace filter, and redeploy the palette
|
cx.simulate_keystrokes("enter");
|
||||||
// cx.update(|cx| {
|
|
||||||
// cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
|
|
||||||
// filter.filtered_namespaces.insert("editor");
|
|
||||||
// })
|
|
||||||
// });
|
|
||||||
|
|
||||||
// workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
// toggle_command_palette(workspace, &Toggle, cx);
|
assert!(workspace.current_modal::<CommandPalette>(cx).is_none());
|
||||||
// });
|
assert_eq!(editor.read(cx).text(cx), "ab")
|
||||||
|
});
|
||||||
|
|
||||||
// // Assert editor command not present
|
// Add namespace filter, and redeploy the palette
|
||||||
// let palette = workspace.read_with(cx, |workspace, _| {
|
cx.update(|cx| {
|
||||||
// workspace.modal::<CommandPalette>().unwrap()
|
cx.set_global(CommandPaletteFilter::default());
|
||||||
// });
|
cx.update_global::<CommandPaletteFilter, _>(|filter, _| {
|
||||||
|
filter.filtered_namespaces.insert("editor");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
// palette
|
cx.simulate_keystrokes("cmd-shift-p");
|
||||||
// .update(cx, |palette, cx| {
|
cx.simulate_keystrokes("b c k s p");
|
||||||
// palette
|
|
||||||
// .delegate_mut()
|
|
||||||
// .update_matches("bcksp".to_string(), cx)
|
|
||||||
// })
|
|
||||||
// .await;
|
|
||||||
|
|
||||||
// palette.update(cx, |palette, _| {
|
let palette = workspace.update(cx, |workspace, cx| {
|
||||||
// assert!(palette.delegate().matches.is_empty())
|
workspace
|
||||||
// });
|
.current_modal::<CommandPalette>(cx)
|
||||||
// }
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.picker
|
||||||
|
.clone()
|
||||||
|
});
|
||||||
|
palette.update(cx, |palette, _| {
|
||||||
|
assert!(palette.delegate.matches.is_empty())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||||
// cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
// let app_state = AppState::test(cx);
|
let app_state = AppState::test(cx);
|
||||||
// theme::init(cx);
|
theme::init(cx);
|
||||||
// language::init(cx);
|
language::init(cx);
|
||||||
// editor::init(cx);
|
editor::init(cx);
|
||||||
// workspace::init(app_state.clone(), cx);
|
workspace::init(app_state.clone(), cx);
|
||||||
// init(cx);
|
init(cx);
|
||||||
// Project::init_settings(cx);
|
Project::init_settings(cx);
|
||||||
// app_state
|
settings::load_default_keymap(cx);
|
||||||
// })
|
app_state
|
||||||
// }
|
})
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,6 +54,9 @@ pub trait Action: std::fmt::Debug + 'static {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn build(value: Option<serde_json::Value>) -> Result<Box<dyn Action>>
|
fn build(value: Option<serde_json::Value>) -> Result<Box<dyn Action>>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
fn is_registered() -> bool
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
@ -88,6 +91,14 @@ where
|
||||||
Ok(Box::new(action))
|
Ok(Box::new(action))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_registered() -> bool {
|
||||||
|
ACTION_REGISTRY
|
||||||
|
.read()
|
||||||
|
.names_by_type_id
|
||||||
|
.get(&TypeId::of::<A>())
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
fn partial_eq(&self, action: &dyn Action) -> bool {
|
fn partial_eq(&self, action: &dyn Action) -> bool {
|
||||||
action
|
action
|
||||||
.as_any()
|
.as_any()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||||
BackgroundExecutor, Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent,
|
BackgroundExecutor, Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent,
|
||||||
Keystroke, Model, ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View,
|
Keystroke, Model, ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, TestWindow,
|
||||||
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
|
View, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
|
@ -220,7 +220,21 @@ impl TestAppContext {
|
||||||
{
|
{
|
||||||
window
|
window
|
||||||
.update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
|
.update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
|
||||||
.unwrap()
|
.unwrap();
|
||||||
|
|
||||||
|
self.background_executor.run_until_parked()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simulate_keystrokes(&mut self, window: AnyWindowHandle, keystrokes: &str) {
|
||||||
|
for keystroke in keystrokes
|
||||||
|
.split(" ")
|
||||||
|
.map(Keystroke::parse)
|
||||||
|
.map(Result::unwrap)
|
||||||
|
{
|
||||||
|
self.dispatch_keystroke(window, keystroke.into(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.background_executor.run_until_parked()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_keystroke(
|
pub fn dispatch_keystroke(
|
||||||
|
@ -229,15 +243,41 @@ impl TestAppContext {
|
||||||
keystroke: Keystroke,
|
keystroke: Keystroke,
|
||||||
is_held: bool,
|
is_held: bool,
|
||||||
) {
|
) {
|
||||||
|
let keystroke2 = keystroke.clone();
|
||||||
let handled = window
|
let handled = window
|
||||||
.update(self, |_, cx| {
|
.update(self, |_, cx| {
|
||||||
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
|
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
|
||||||
})
|
})
|
||||||
.is_ok_and(|handled| handled);
|
.is_ok_and(|handled| handled);
|
||||||
|
if handled {
|
||||||
if !handled {
|
return;
|
||||||
// todo!() simluate input here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let input_handler = self.update_test_window(window, |window| window.input_handler.clone());
|
||||||
|
let Some(input_handler) = input_handler else {
|
||||||
|
panic!(
|
||||||
|
"dispatch_keystroke {:?} failed to dispatch action or input",
|
||||||
|
&keystroke2
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let text = keystroke2.ime_key.unwrap_or(keystroke2.key);
|
||||||
|
input_handler.lock().replace_text_in_range(None, &text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_test_window<R>(
|
||||||
|
&mut self,
|
||||||
|
window: AnyWindowHandle,
|
||||||
|
f: impl FnOnce(&mut TestWindow) -> R,
|
||||||
|
) -> R {
|
||||||
|
window
|
||||||
|
.update(self, |_, cx| {
|
||||||
|
f(cx.window
|
||||||
|
.platform_window
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<TestWindow>()
|
||||||
|
.unwrap())
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notifications<T: 'static>(&mut self, entity: &Model<T>) -> impl Stream<Item = ()> {
|
pub fn notifications<T: 'static>(&mut self, entity: &Model<T>) -> impl Stream<Item = ()> {
|
||||||
|
@ -401,12 +441,20 @@ impl<'a> VisualTestContext<'a> {
|
||||||
Self { cx, window }
|
Self { cx, window }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_until_parked(&self) {
|
||||||
|
self.cx.background_executor.run_until_parked();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dispatch_action<A>(&mut self, action: A)
|
pub fn dispatch_action<A>(&mut self, action: A)
|
||||||
where
|
where
|
||||||
A: Action,
|
A: Action,
|
||||||
{
|
{
|
||||||
self.cx.dispatch_action(self.window, action)
|
self.cx.dispatch_action(self.window, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
|
||||||
|
self.cx.simulate_keystrokes(self.window, keystrokes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context for VisualTestContext<'a> {
|
impl<'a> Context for VisualTestContext<'a> {
|
||||||
|
|
|
@ -229,6 +229,20 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
|
||||||
mut self,
|
mut self,
|
||||||
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
|
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
// NOTE: this debug assert has the side-effect of working around
|
||||||
|
// a bug where a crate consisting only of action definitions does
|
||||||
|
// not register the actions in debug builds:
|
||||||
|
//
|
||||||
|
// https://github.com/rust-lang/rust/issues/47384
|
||||||
|
// https://github.com/mmastrac/rust-ctor/issues/280
|
||||||
|
//
|
||||||
|
// if we are relying on this side-effect still, removing the debug_assert!
|
||||||
|
// likely breaks the command_palette tests.
|
||||||
|
debug_assert!(
|
||||||
|
A::is_registered(),
|
||||||
|
"{:?} is not registered as an action",
|
||||||
|
A::qualified_name()
|
||||||
|
);
|
||||||
self.interactivity().action_listeners.push((
|
self.interactivity().action_listeners.push((
|
||||||
TypeId::of::<A>(),
|
TypeId::of::<A>(),
|
||||||
Box::new(move |view, action, phase, cx| {
|
Box::new(move |view, action, phase, cx| {
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub struct TestWindow {
|
||||||
bounds: WindowBounds,
|
bounds: WindowBounds,
|
||||||
current_scene: Mutex<Option<Scene>>,
|
current_scene: Mutex<Option<Scene>>,
|
||||||
display: Rc<dyn PlatformDisplay>,
|
display: Rc<dyn PlatformDisplay>,
|
||||||
input_handler: Option<Box<dyn PlatformInputHandler>>,
|
pub(crate) input_handler: Option<Arc<Mutex<Box<dyn PlatformInputHandler>>>>,
|
||||||
handlers: Mutex<Handlers>,
|
handlers: Mutex<Handlers>,
|
||||||
platform: Weak<TestPlatform>,
|
platform: Weak<TestPlatform>,
|
||||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||||
|
@ -80,11 +80,11 @@ impl PlatformWindow for TestWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
todo!()
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
|
fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
|
||||||
self.input_handler = Some(input_handler);
|
self.input_handler = Some(Arc::new(Mutex::new(input_handler)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt(
|
fn prompt(
|
||||||
|
|
|
@ -189,7 +189,7 @@ impl Drop for FocusHandle {
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub(crate) handle: AnyWindowHandle,
|
pub(crate) handle: AnyWindowHandle,
|
||||||
pub(crate) removed: bool,
|
pub(crate) removed: bool,
|
||||||
platform_window: Box<dyn PlatformWindow>,
|
pub(crate) platform_window: Box<dyn PlatformWindow>,
|
||||||
display_id: DisplayId,
|
display_id: DisplayId,
|
||||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||||
rem_size: Pixels,
|
rem_size: Pixels,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue