GPUI testing framework: #[gpui::test], TestAppContext, VisualTestContext, simulating input, run_until_parked, assertions. Use when writing GPUI tests, simulating user interactions, or debugging test failures. Keywords: test, TestAppContext, VisualTestContext, run_until_parked, simulate, dispatch_action, simulate_keystrokes, simulate_input, gpui::test
#[gpui::test]
async fn test_counter(cx: &mut TestAppContext) {
let counter = cx.new(|_| Counter { count: 0 });
counter.update(cx, |c, cx| {
c.count += 1;
cx.notify();
});
assert_eq!(counter.read_with(cx, |c, _| c.count), 1);
}
#[gpui::test]
async fn test_input(cx: &mut TestAppContext) {
let (view, mut cx) = cx.add_window_view(|window, cx| {
MyView::new(window, cx)
});
// Simulate keyboard input
cx.simulate_keystrokes("cmd-a"); // auto-parks
cx.simulate_input("hello world"); // auto-parks
// Simulate mouse
cx.simulate_click(point(px(100.), px(50.)), Modifiers::default());
// Dispatch action
cx.dispatch_action(Save); // auto-parks
// Assert state
view.update(&mut cx, |view, _cx| {
assert_eq!(view.content, "hello world");
});
}
Pumps all pending foreground and background tasks until nothing is left to run.
// Required after detached tasks:
counter.update(cx, |c, cx| c.load_data(cx)); // spawns detached task
cx.run_until_parked(); // wait for task to complete
// NOT needed after simulate_* (auto-parks):
cx.simulate_keystrokes("cmd-s"); // already parked
#[gpui::test] // single run, random seed
#[gpui::test(iterations = 10)] // run 10 times with seeds 0..10
#[gpui::test(seed = 42)] // run once with seed 42
#[gpui::test(retries = 3)] // retry on failure
#[gpui::test]
async fn test(
cx: &mut TestAppContext, // main context
// cx_b: &mut TestAppContext, // second context (distributed testing)
// rng: StdRng, // deterministic RNG
// executor: BackgroundExecutor,
) { }
// Create
let entity = cx.new(|cx| MyStruct::new(cx));
// Read
entity.read_with(cx, |e, cx| e.value);
// Update
entity.update(cx, |e, cx| {
e.value = 42;
cx.notify();
});
// Create window with view
let (view, cx) = cx.add_window_view(|window, cx| MyView::new(window, cx));
// Empty window
let cx = cx.add_empty_window();
// Update existing window
cx.update(|window, cx| {
// access window + app
});
// Keystrokes (space-separated, auto-parks)
cx.simulate_keystrokes("cmd-p escape enter");
// Text input (auto-parks)
cx.simulate_input("typed text");
// Mouse (VisualTestContext)
cx.simulate_click(position, modifiers);
cx.simulate_mouse_down(position, MouseButton::Left, modifiers);
cx.simulate_mouse_up(position, MouseButton::Left, modifiers);
cx.simulate_mouse_move(position, None, modifiers);
// Wait for event
let event = entity.next_event(cx).await;
// Collect events
let events = cx.events(&entity);
// Wait for condition (timeout: 3s)
cx.condition(&entity, |e, cx| e.ready).await;
Always use GPUI executor timers:
// CORRECT:
cx.background_executor.timer(Duration::from_millis(100)).await;
// WRONG (may not be tracked by scheduler):