mirror of
https://github.com/zed-industries/zed.git
synced 2026-08-06 23:53:59 +00:00
This allows us to build powerful and flexible Input and TextArea components Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
131 lines
4.3 KiB
Rust
131 lines
4.3 KiB
Rust
//! Tests for the input composition. Require the `test-support` feature:
|
|
//!
|
|
//! ```sh
|
|
//! cargo test -p gpui --example view_example --features test-support
|
|
//! ```
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use gpui::{Context, Entity, KeyBinding, TestAppContext, Window, prelude::*};
|
|
|
|
use crate::example_editor::Editor;
|
|
use crate::example_input::Input;
|
|
use crate::{Backspace, Delete, End, Home, Left, Right};
|
|
|
|
/// Two inputs, each backed by an editor we own (so the test can focus and
|
|
/// read them). Proves data flows through the shared `String` and that
|
|
/// sibling inputs stay isolated.
|
|
struct Harness {
|
|
a: Entity<Editor>,
|
|
b: Entity<Editor>,
|
|
}
|
|
|
|
impl Render for Harness {
|
|
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
|
gpui::div()
|
|
.child(Input::editor(self.a.clone()))
|
|
.child(Input::editor(self.b.clone()))
|
|
}
|
|
}
|
|
|
|
fn bind_keys(cx: &mut TestAppContext) {
|
|
cx.update(|cx| {
|
|
cx.bind_keys([
|
|
KeyBinding::new("backspace", Backspace, None),
|
|
KeyBinding::new("delete", Delete, None),
|
|
KeyBinding::new("left", Left, None),
|
|
KeyBinding::new("right", Right, None),
|
|
KeyBinding::new("home", Home, None),
|
|
KeyBinding::new("end", End, None),
|
|
]);
|
|
});
|
|
}
|
|
|
|
fn setup(
|
|
cx: &mut TestAppContext,
|
|
) -> (
|
|
Entity<Editor>,
|
|
Entity<String>,
|
|
Entity<String>,
|
|
&mut gpui::VisualTestContext,
|
|
) {
|
|
bind_keys(cx);
|
|
|
|
let (harness, cx) = cx.add_window_view(|window, cx| {
|
|
let a_value = cx.new(|_| String::new());
|
|
let b_value = cx.new(|_| String::new());
|
|
let a = cx.new(|cx| Editor::over(a_value, window, cx));
|
|
let b = cx.new(|cx| Editor::over(b_value, window, cx));
|
|
Harness { a, b }
|
|
});
|
|
|
|
let a = cx.read_entity(&harness, |h, _| h.a.clone());
|
|
let b = cx.read_entity(&harness, |h, _| h.b.clone());
|
|
let a_value = cx.read_entity(&a, |e, _| e.value.clone());
|
|
let b_value = cx.read_entity(&b, |e, _| e.value.clone());
|
|
|
|
// Focus the first input's editor.
|
|
cx.update(|window, cx| {
|
|
let focus_handle = a.read(cx).focus_handle.clone();
|
|
window.focus(&focus_handle, cx);
|
|
});
|
|
|
|
(a, a_value, b_value, cx)
|
|
}
|
|
|
|
#[gpui::test]
|
|
fn typing_updates_the_shared_string(cx: &mut TestAppContext) {
|
|
let (editor, a_value, _b_value, cx) = setup(cx);
|
|
|
|
cx.simulate_input("hello");
|
|
|
|
cx.read_entity(&a_value, |value, _| assert_eq!(value, "hello"));
|
|
cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 5));
|
|
}
|
|
|
|
#[gpui::test]
|
|
fn sibling_inputs_are_isolated(cx: &mut TestAppContext) {
|
|
let (_editor, a_value, b_value, cx) = setup(cx);
|
|
|
|
cx.simulate_input("x");
|
|
|
|
cx.read_entity(&a_value, |value, _| assert_eq!(value, "x"));
|
|
cx.read_entity(&b_value, |value, _| {
|
|
assert_eq!(value, "", "typing in input A must not touch input B")
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
fn external_writes_clamp_the_cursor(cx: &mut TestAppContext) {
|
|
let (editor, a_value, _b_value, cx) = setup(cx);
|
|
|
|
cx.simulate_input("hello");
|
|
cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 5));
|
|
|
|
// Write the shared value from outside the editor. The old cursor (5)
|
|
// now points into the middle of a multi-byte character; the editor's
|
|
// observation must clamp it back onto a boundary.
|
|
cx.update(|_, cx| {
|
|
a_value.update(cx, |value, cx| {
|
|
*value = "日本".to_string();
|
|
cx.notify();
|
|
})
|
|
});
|
|
|
|
cx.read_entity(&a_value, |value, _| assert_eq!(value, "日本"));
|
|
cx.read_entity(&editor, |editor, _| {
|
|
assert_eq!(editor.cursor, 3, "cursor must clamp to a char boundary");
|
|
});
|
|
}
|
|
|
|
#[gpui::test]
|
|
fn arrows_move_the_cursor(cx: &mut TestAppContext) {
|
|
let (editor, _a_value, _b_value, cx) = setup(cx);
|
|
|
|
cx.simulate_input("abc");
|
|
cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 3));
|
|
|
|
cx.simulate_keystrokes("left left");
|
|
cx.read_entity(&editor, |editor, _| assert_eq!(editor.cursor, 1));
|
|
}
|
|
}
|