example_tests.rs

  1//! Tests for the `ExampleEditor` entity.
  2//!
  3//! These use GPUI's test infrastructure which requires the `test-support` feature:
  4//!
  5//! ```sh
  6//! cargo test --example text_views -p gpui --features test-support
  7//! ```
  8
  9#[cfg(test)]
 10mod tests {
 11    use std::time::Duration;
 12
 13    use gpui::{Context, Entity, KeyBinding, TestAppContext, Window, prelude::*};
 14
 15    use crate::example_editor::ExampleEditor;
 16    use crate::example_input::ExampleInput;
 17    use crate::example_render_log::RenderLog;
 18    use crate::example_text_area::ExampleTextArea;
 19    use crate::{Backspace, Delete, End, Enter, Home, Left, Right};
 20
 21    struct InputWrapper {
 22        editor: Entity<ExampleEditor>,
 23        render_log: Entity<RenderLog>,
 24    }
 25
 26    impl Render for InputWrapper {
 27        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 28            ExampleInput::new(self.editor.clone(), self.render_log.clone())
 29        }
 30    }
 31
 32    struct TextAreaWrapper {
 33        editor: Entity<ExampleEditor>,
 34        render_log: Entity<RenderLog>,
 35    }
 36
 37    impl Render for TextAreaWrapper {
 38        fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 39            ExampleTextArea::new(self.editor.clone(), self.render_log.clone(), 5)
 40        }
 41    }
 42
 43    fn bind_keys(cx: &mut TestAppContext) {
 44        cx.update(|cx| {
 45            cx.bind_keys([
 46                KeyBinding::new("backspace", Backspace, None),
 47                KeyBinding::new("delete", Delete, None),
 48                KeyBinding::new("left", Left, None),
 49                KeyBinding::new("right", Right, None),
 50                KeyBinding::new("home", Home, None),
 51                KeyBinding::new("end", End, None),
 52                KeyBinding::new("enter", Enter, None),
 53            ]);
 54        });
 55    }
 56
 57    fn init_input(
 58        cx: &mut TestAppContext,
 59    ) -> (Entity<ExampleEditor>, &mut gpui::VisualTestContext) {
 60        bind_keys(cx);
 61
 62        let (wrapper, cx) = cx.add_window_view(|window, cx| {
 63            let editor = cx.new(|cx| ExampleEditor::new(window, cx));
 64            let render_log = cx.new(|cx| RenderLog::new(cx));
 65            InputWrapper { editor, render_log }
 66        });
 67
 68        let editor = cx.read_entity(&wrapper, |wrapper, _cx| wrapper.editor.clone());
 69
 70        cx.update(|window, cx| {
 71            let focus_handle = editor.read(cx).focus_handle.clone();
 72            window.focus(&focus_handle, cx);
 73        });
 74
 75        (editor, cx)
 76    }
 77
 78    fn init_textarea(
 79        cx: &mut TestAppContext,
 80    ) -> (Entity<ExampleEditor>, &mut gpui::VisualTestContext) {
 81        bind_keys(cx);
 82
 83        let (wrapper, cx) = cx.add_window_view(|window, cx| {
 84            let editor = cx.new(|cx| ExampleEditor::new(window, cx));
 85            let render_log = cx.new(|cx| RenderLog::new(cx));
 86            TextAreaWrapper { editor, render_log }
 87        });
 88
 89        let editor = cx.read_entity(&wrapper, |wrapper, _cx| wrapper.editor.clone());
 90
 91        cx.update(|window, cx| {
 92            let focus_handle = editor.read(cx).focus_handle.clone();
 93            window.focus(&focus_handle, cx);
 94        });
 95
 96        (editor, cx)
 97    }
 98
 99    #[gpui::test]
100    fn test_typing_and_cursor(cx: &mut TestAppContext) {
101        let (editor, cx) = init_input(cx);
102
103        cx.simulate_input("hello");
104
105        cx.read_entity(&editor, |editor, _cx| {
106            assert_eq!(editor.content, "hello");
107            assert_eq!(editor.cursor, 5);
108        });
109
110        cx.simulate_keystrokes("left left");
111
112        cx.read_entity(&editor, |editor, _cx| {
113            assert_eq!(editor.cursor, 3);
114        });
115
116        cx.simulate_input(" world");
117
118        cx.read_entity(&editor, |editor, _cx| {
119            assert_eq!(editor.content, "hel worldlo");
120            assert_eq!(editor.cursor, 9);
121        });
122    }
123
124    #[gpui::test]
125    fn test_backspace_and_delete(cx: &mut TestAppContext) {
126        let (editor, cx) = init_input(cx);
127
128        cx.simulate_input("abcde");
129
130        cx.simulate_keystrokes("backspace");
131        cx.read_entity(&editor, |editor, _cx| {
132            assert_eq!(editor.content, "abcd");
133            assert_eq!(editor.cursor, 4);
134        });
135
136        cx.simulate_keystrokes("home delete");
137        cx.read_entity(&editor, |editor, _cx| {
138            assert_eq!(editor.content, "bcd");
139            assert_eq!(editor.cursor, 0);
140        });
141
142        // Boundary no-ops
143        cx.simulate_keystrokes("backspace");
144        cx.read_entity(&editor, |editor, _cx| {
145            assert_eq!(editor.content, "bcd");
146        });
147
148        cx.simulate_keystrokes("end delete");
149        cx.read_entity(&editor, |editor, _cx| {
150            assert_eq!(editor.content, "bcd");
151        });
152    }
153
154    #[gpui::test]
155    fn test_cursor_blink(cx: &mut TestAppContext) {
156        let (editor, cx) = init_input(cx);
157
158        cx.read_entity(&editor, |editor, _cx| {
159            assert!(editor.cursor_visible);
160        });
161
162        cx.background_executor
163            .advance_clock(Duration::from_millis(500));
164        cx.run_until_parked();
165
166        cx.read_entity(&editor, |editor, _cx| {
167            assert!(!editor.cursor_visible);
168        });
169
170        // Typing resets the blink.
171        cx.simulate_input("a");
172
173        cx.read_entity(&editor, |editor, _cx| {
174            assert!(editor.cursor_visible);
175        });
176    }
177
178    #[gpui::test]
179    fn test_enter_does_not_insert_in_input(cx: &mut TestAppContext) {
180        let (editor, cx) = init_input(cx);
181
182        cx.simulate_input("hello");
183        cx.simulate_keystrokes("enter");
184
185        cx.read_entity(&editor, |editor, _cx| {
186            assert_eq!(
187                editor.content, "hello",
188                "Enter should not insert text in Input"
189            );
190            assert_eq!(editor.cursor, 5);
191        });
192    }
193
194    #[gpui::test]
195    fn test_enter_inserts_newline_in_textarea(cx: &mut TestAppContext) {
196        let (editor, cx) = init_textarea(cx);
197
198        cx.simulate_input("ab");
199        cx.simulate_keystrokes("enter");
200        cx.simulate_input("cd");
201
202        cx.read_entity(&editor, |editor, _cx| {
203            assert_eq!(editor.content, "ab\ncd");
204            assert_eq!(editor.cursor, 5);
205        });
206    }
207
208    #[gpui::test]
209    fn test_enter_at_start_of_textarea(cx: &mut TestAppContext) {
210        let (editor, cx) = init_textarea(cx);
211
212        cx.simulate_keystrokes("enter");
213        cx.simulate_input("hello");
214
215        cx.read_entity(&editor, |editor, _cx| {
216            assert_eq!(editor.content, "\nhello");
217            assert_eq!(editor.cursor, 6);
218        });
219    }
220}