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}