test.rs

  1mod neovim_backed_binding_test_context;
  2mod neovim_backed_test_context;
  3mod neovim_connection;
  4mod vim_binding_test_context;
  5mod vim_test_context;
  6
  7use std::sync::Arc;
  8
  9use command_palette::CommandPalette;
 10use editor::DisplayPoint;
 11pub use neovim_backed_binding_test_context::*;
 12pub use neovim_backed_test_context::*;
 13pub use vim_binding_test_context::*;
 14pub use vim_test_context::*;
 15
 16use indoc::indoc;
 17use search::BufferSearchBar;
 18
 19use crate::{state::Mode, ModeIndicator};
 20
 21#[gpui::test]
 22async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
 23    let mut cx = VimTestContext::new(cx, false).await;
 24    cx.simulate_keystrokes(["h", "j", "k", "l"]);
 25    cx.assert_editor_state("hjklˇ");
 26}
 27
 28#[gpui::test]
 29async fn test_neovim(cx: &mut gpui::TestAppContext) {
 30    let mut cx = NeovimBackedTestContext::new(cx).await;
 31
 32    cx.simulate_shared_keystroke("i").await;
 33    cx.assert_state_matches().await;
 34    cx.simulate_shared_keystrokes([
 35        "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
 36    ])
 37    .await;
 38    cx.assert_state_matches().await;
 39    cx.assert_editor_state("ˇtest");
 40}
 41
 42#[gpui::test]
 43async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
 44    let mut cx = VimTestContext::new(cx, true).await;
 45
 46    cx.simulate_keystroke("i");
 47    assert_eq!(cx.mode(), Mode::Insert);
 48
 49    // Editor acts as though vim is disabled
 50    cx.disable_vim();
 51    cx.simulate_keystrokes(["h", "j", "k", "l"]);
 52    cx.assert_editor_state("hjklˇ");
 53
 54    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
 55    cx.set_state("«hjklˇ»", Mode::Normal);
 56    cx.assert_editor_state("«hjklˇ»");
 57    cx.update_editor(|_, cx| cx.blur());
 58    cx.assert_editor_state("«hjklˇ»");
 59    cx.update_editor(|_, cx| cx.focus_self());
 60    cx.assert_editor_state("«hjklˇ»");
 61
 62    // Enabling dynamically sets vim mode again and restores normal mode
 63    cx.enable_vim();
 64    assert_eq!(cx.mode(), Mode::Normal);
 65    cx.simulate_keystrokes(["h", "h", "h", "l"]);
 66    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
 67    cx.assert_editor_state("hˇjkl");
 68    cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
 69    cx.assert_editor_state("hTestˇjkl");
 70
 71    // Disabling and enabling resets to normal mode
 72    assert_eq!(cx.mode(), Mode::Insert);
 73    cx.disable_vim();
 74    cx.enable_vim();
 75    assert_eq!(cx.mode(), Mode::Normal);
 76}
 77
 78#[gpui::test]
 79async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
 80    let mut cx = VimTestContext::new(cx, true).await;
 81
 82    cx.set_state(
 83        indoc! {"
 84            The quick brown
 85            fox juˇmps over
 86            the lazy dog"},
 87        Mode::Normal,
 88    );
 89    cx.simulate_keystroke("/");
 90
 91    let search_bar = cx.workspace(|workspace, cx| {
 92        workspace
 93            .active_pane()
 94            .read(cx)
 95            .toolbar()
 96            .read(cx)
 97            .item_of_type::<BufferSearchBar>()
 98            .expect("Buffer search bar should be deployed")
 99    });
100
101    search_bar.read_with(cx.cx, |bar, cx| {
102        assert_eq!(bar.query(cx), "");
103    })
104}
105
106#[gpui::test]
107async fn test_count_down(cx: &mut gpui::TestAppContext) {
108    let mut cx = VimTestContext::new(cx, true).await;
109
110    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
111    cx.simulate_keystrokes(["2", "down"]);
112    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
113    cx.simulate_keystrokes(["9", "down"]);
114    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
115}
116
117#[gpui::test]
118async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
119    let mut cx = VimTestContext::new(cx, true).await;
120
121    // goes to end by default
122    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
123    cx.simulate_keystrokes(["shift-g"]);
124    cx.assert_editor_state("aa\nbb\ncˇc");
125
126    // can go to line 1 (https://github.com/zed-industries/community/issues/710)
127    cx.simulate_keystrokes(["1", "shift-g"]);
128    cx.assert_editor_state("aˇa\nbb\ncc");
129}
130
131#[gpui::test]
132async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
133    let mut cx = VimTestContext::new(cx, true).await;
134
135    // works in normal mode
136    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
137    cx.simulate_keystrokes([">", ">"]);
138    cx.assert_editor_state("aa\n    bˇb\ncc");
139    cx.simulate_keystrokes(["<", "<"]);
140    cx.assert_editor_state("aa\nbˇb\ncc");
141
142    // works in visuial mode
143    cx.simulate_keystrokes(["shift-v", "down", ">"]);
144    cx.assert_editor_state("aa\n    b«b\n    ccˇ»");
145}
146
147#[gpui::test]
148async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
149    let mut cx = VimTestContext::new(cx, true).await;
150
151    cx.set_state("aˇbc\n", Mode::Normal);
152    cx.simulate_keystrokes(["i", "cmd-shift-p"]);
153
154    assert!(cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
155    cx.simulate_keystroke("escape");
156    assert!(!cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
157    cx.assert_state("aˇbc\n", Mode::Insert);
158}
159
160#[gpui::test]
161async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
162    let mut cx = VimTestContext::new(cx, true).await;
163
164    cx.set_state("aˇbˇc", Mode::Normal);
165    cx.simulate_keystrokes(["escape"]);
166
167    cx.assert_state("aˇbc", Mode::Normal);
168}
169
170#[gpui::test]
171async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
172    let mut cx = VimTestContext::new(cx, true).await;
173
174    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
175    cx.simulate_keystrokes(["/", "c", "c"]);
176
177    let search_bar = cx.workspace(|workspace, cx| {
178        workspace
179            .active_pane()
180            .read(cx)
181            .toolbar()
182            .read(cx)
183            .item_of_type::<BufferSearchBar>()
184            .expect("Buffer search bar should be deployed")
185    });
186
187    search_bar.read_with(cx.cx, |bar, cx| {
188        assert_eq!(bar.query(cx), "cc");
189    });
190
191    // wait for the query editor change event to fire.
192    search_bar.next_notification(&cx).await;
193
194    cx.update_editor(|editor, cx| {
195        let highlights = editor.all_background_highlights(cx);
196        assert_eq!(3, highlights.len());
197        assert_eq!(
198            DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
199            highlights[0].0
200        )
201    });
202    cx.simulate_keystrokes(["enter"]);
203
204    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
205    cx.simulate_keystrokes(["n"]);
206    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
207    cx.simulate_keystrokes(["shift-n"]);
208    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
209}
210
211#[gpui::test]
212async fn test_status_indicator(
213    cx: &mut gpui::TestAppContext,
214    deterministic: Arc<gpui::executor::Deterministic>,
215) {
216    let mut cx = VimTestContext::new(cx, true).await;
217    deterministic.run_until_parked();
218
219    let mode_indicator = cx.workspace(|workspace, cx| {
220        let status_bar = workspace.status_bar().read(cx);
221        let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
222        assert!(mode_indicator.is_some());
223        mode_indicator.unwrap()
224    });
225
226    assert_eq!(
227        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
228        Some(Mode::Normal)
229    );
230
231    // shows the correct mode
232    cx.simulate_keystrokes(["i"]);
233    deterministic.run_until_parked();
234    assert_eq!(
235        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
236        Some(Mode::Insert)
237    );
238
239    // shows even in search
240    cx.simulate_keystrokes(["escape", "v", "/"]);
241    deterministic.run_until_parked();
242    assert_eq!(
243        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
244        Some(Mode::Visual)
245    );
246
247    // hides if vim mode is disabled
248    cx.disable_vim();
249    deterministic.run_until_parked();
250    cx.workspace(|workspace, cx| {
251        let status_bar = workspace.status_bar().read(cx);
252        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
253        assert!(mode_indicator.read(cx).mode.is_none());
254    });
255
256    cx.enable_vim();
257    deterministic.run_until_parked();
258    cx.workspace(|workspace, cx| {
259        let status_bar = workspace.status_bar().read(cx);
260        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
261        assert!(mode_indicator.read(cx).mode.is_some());
262    });
263}
264
265#[gpui::test]
266async fn test_word_characters(cx: &mut gpui::TestAppContext) {
267    let mut cx = VimTestContext::new_typescript(cx).await;
268    cx.set_state(
269        indoc! { "
270        class A {
271            #ˇgoop = 99;
272            $ˇgoop () { return this.#gˇoop };
273        };
274        console.log(new A().$gooˇp())
275    "},
276        Mode::Normal,
277    );
278    cx.simulate_keystrokes(["v", "i", "w"]);
279    cx.assert_state(
280        indoc! {"
281        class A {
282            «#goopˇ» = 99;
283            «$goopˇ» () { return this.«#goopˇ» };
284        };
285        console.log(new A().«$goopˇ»())
286    "},
287        Mode::Visual,
288    )
289}