返回 CodeWhale
search_text_pty.rs
根目录 / crates / tui / tests / cucumber / search_text_pty.rs
1 //! Search owns printable keys from the first character, through the real
2 //! terminal decoder and modal host. No provider or saved user config is used.
3
4 use std::time::Duration;
5
6 use super::qa_harness::{
7 harness::{Harness, make_sealed_workspace},
8 keys,
9 };
10
11 #[test]
12 fn search_text_stays_in_modal_and_out_of_composer() {
13 let workspace = make_sealed_workspace().expect("sealed workspace");
14 let mut tui = Harness::builder(Harness::cargo_bin("codewhale-tui"))
15 .cwd(workspace.workspace())
16 .clear_env()
17 .seal_home(workspace.home())
18 .env("CODEWHALE_DISABLE_MODELS_DEV_FETCH", "1")
19 .env("CODEWHALE_NO_UPDATE_CHECK", "1")
20 .env("CODEWHALE_TELEMETRY", "0")
21 .env("NO_ANIMATIONS", "1")
22 .args([
23 "--workspace",
24 workspace.workspace().to_str().unwrap(),
25 "--no-project-config",
26 "--fresh",
27 ])
28 .size(24, 80)
29 .spawn()
30 .expect("start TUI");
31 let timeout = Duration::from_secs(10);
32 tui.wait_for_text("Type a message", timeout).unwrap();
33 // Enter the live shell with a local command, without making a model call.
34 tui.paste("/help").unwrap();
35 tui.wait_for_text("❯ /help", timeout).unwrap();
36 tui.send(keys::key::enter()).unwrap();
37 tui.wait_for_text("Help —", timeout).unwrap();
38 tui.send(keys::key::esc()).unwrap();
39 tui.wait_for_text("Type a message", timeout).unwrap();
40 let draft = "draft_no_leak";
41 tui.paste(draft).unwrap();
42 tui.wait_for_text(draft, timeout).unwrap();
43
44 for (open, title, prefix, query, escapes) in [
45 (keys::key::f1(), "Help —", "Filter: ", "queue", 1),
46 (keys::key::f1(), "Help —", "Filter: ", "Queue", 1),
47 (keys::key::f2(), "Config", "Search: ", "quiet", 2),
48 (keys::key::f2(), "Config", "Search: ", "effort", 2),
49 (keys::key::f2(), "Config", "Search: ", "json", 2),
50 (keys::key::f2(), "Config", "Search: ", "key", 2),
51 (keys::key::f2(), "Config", "Search: ", " 队列é", 2),
52 (keys::key::ctrl('k'), "Command —", "Filter: ", "json", 1),
53 (keys::key::ctrl('k'), "Command —", "Filter: ", "key", 1),
54 ] {
55 tui.send(open).unwrap();
56 tui.wait_for_text(title, timeout).unwrap();
57 let mut typed = String::new();
58 for ch in query.chars() {
59 tui.send(ch.to_string()).unwrap();
60 typed.push(ch);
61 tui.wait_for_text(&format!("{prefix}{typed}"), timeout)
62 .unwrap_or_else(|error| panic!("{title} query {typed:?}: {error}"));
63 }
64 for _ in 0..escapes {
65 tui.send(keys::key::esc()).unwrap();
66 tui.wait_for_idle(Duration::from_millis(150), timeout)
67 .unwrap();
68 }
69 tui.wait_for_text(draft, timeout).unwrap();
70 let frame = tui.frame();
71 let composer = (0..frame.rows())
72 .map(|row| frame.row(row))
73 .find(|row| row.contains('❯'))
74 .expect("composer is visible after closing search");
75 let content = composer
76 .split_once('❯')
77 .unwrap()
78 .1
79 .split("[↵]")
80 .next()
81 .unwrap()
82 .trim_end();
83 let content = content.strip_suffix('│').unwrap_or(content).trim();
84 assert_eq!(content, draft, "{title} leaked query {query:?}");
85 }
86 tui.shutdown();
87 }
88
88 lines RUST