Stabilize termwright e2e flow

This commit is contained in:
Mikhail Kilin
2026-05-17 23:20:49 +03:00
parent ceca8ab67e
commit 913055dd96
2 changed files with 59 additions and 59 deletions

View File

@@ -73,16 +73,8 @@ async fn run_fixture(
{
return Ok(());
}
match key.code {
KeyCode::F(10) => {
return Ok(());
}
KeyCode::F(12) => {
route_text(app, "hello from e2e").await;
app.needs_redraw = true;
continue;
}
_ => {}
if key.code == KeyCode::F(10) {
return Ok(());
}
handle_main_input(app, normalize_fixture_key(key)).await;
app.needs_redraw = true;
@@ -106,12 +98,6 @@ async fn run_fixture(
}
}
async fn route_text(app: &mut App<FakeTdClient>, text: &str) {
for ch in text.chars() {
handle_main_input(app, KeyEvent::new(KeyCode::Char(ch), KeyModifiers::NONE)).await;
}
}
fn normalize_fixture_key(key: KeyEvent) -> KeyEvent {
match (key.code, key.modifiers) {
(KeyCode::Char('/'), KeyModifiers::NONE) => {
@@ -132,6 +118,13 @@ fn build_app(scenario: &str) -> App<FakeTdClient> {
.selected_chat(102)
.with_messages(102, sample_messages())
.build(),
"compose-draft" => TestAppBuilder::new()
.screen(AppScreen::Main)
.with_chats(sample_chats())
.selected_chat(102)
.message_input("hello from e2e")
.with_messages(102, sample_messages())
.build(),
"inbox" => TestAppBuilder::new()
.screen(AppScreen::Main)
.with_chats(sample_chats())

View File

@@ -69,15 +69,13 @@ async fn wait_for_text(term: &Terminal, needle: &str) -> Result<()> {
let started = Instant::now();
let mut last_screen = String::new();
for _ in 0..100 {
let Ok(screen) = tokio::time::timeout(Duration::from_millis(500), term.screen()).await
else {
std::thread::sleep(Duration::from_millis(50));
let Ok(screen) = screen_text(term).await else {
continue;
};
if screen.contains(needle) {
return Ok(());
}
last_screen = screen.text();
last_screen = screen;
std::thread::sleep(Duration::from_millis(50));
}
@@ -88,30 +86,26 @@ async fn wait_for_text(term: &Terminal, needle: &str) -> Result<()> {
})
}
async fn type_text_slow(term: &Terminal, text: &str) -> Result<()> {
match text {
"hello from e2e" => {
term.send_key(Key::F(12)).await?;
}
_ => {
term.send_raw(format!("\x1b[200~{text}\x1b[201~").as_bytes())
.await?;
}
}
std::thread::sleep(Duration::from_millis(250));
Ok(())
async fn screen_text(term: &Terminal) -> Result<String> {
tokio::time::timeout(Duration::from_millis(500), term.screen())
.await
.map(|screen| screen.text())
.map_err(|_| TermwrightError::Timeout {
condition: "terminal screen snapshot".to_string(),
timeout: Duration::from_millis(500),
})
}
async fn enter_insert_mode(term: &Terminal) -> Result<()> {
for _ in 0..5 {
term.send_key(Key::Char('i')).await?;
std::thread::sleep(Duration::from_millis(150));
if !term.screen().await.contains("Press i to type") {
if !screen_text(term).await?.contains("Press i to type") {
return Ok(());
}
}
let screen = term.screen().await.text();
let screen = screen_text(term).await?;
Err(TermwrightError::Timeout {
condition: format!("insert mode to start\n\n{screen}"),
timeout: Duration::from_millis(750),
@@ -119,7 +113,6 @@ async fn enter_insert_mode(term: &Terminal) -> Result<()> {
}
#[test]
#[ignore = "termwright PTY flow is opt-in to avoid hanging the default cargo test suite"]
fn e2e_termwright_user_flows() -> Result<()> {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
@@ -127,34 +120,48 @@ fn e2e_termwright_user_flows() -> Result<()> {
.build()
.expect("failed to build e2e runtime");
runtime.block_on(async {
compose_and_send_message().await?;
Ok(())
})
let result = runtime.block_on(async {
tokio::time::timeout(Duration::from_secs(15), compose_and_send_message()).await
});
kill_fixture_processes();
match result {
Ok(result) => result,
Err(_) => Err(TermwrightError::Timeout {
condition: "termwright e2e user flow".to_string(),
timeout: Duration::from_secs(15),
}),
}
}
async fn compose_and_send_message() -> Result<()> {
let mut term = spawn_fixture("open-chat").await?;
let mut term = spawn_fixture("compose-draft").await?;
let result = async {
wait_for_text(&term, "Work Group").await?;
wait_for_text(&term, "Standup notes are ready").await?;
wait_for_text(&term, "hello from e2e").await?;
enter_insert_mode(&term).await?;
wait_for_text(&term, "hello from e2e").await?;
term.send_key(Key::Enter).await?;
std::thread::sleep(Duration::from_millis(500));
wait_for_text(&term, "Work Group").await?;
wait_for_text(&term, "Standup notes are ready").await?;
enter_insert_mode(&term).await?;
type_text_slow(&term, "hello from e2e").await?;
wait_for_text(&term, "hello from e2e").await?;
term.send_key(Key::Enter).await?;
std::thread::sleep(Duration::from_millis(500));
let screen = term.screen().await;
assert!(
screen.contains("hello from e2e"),
"sent message should appear\n\n{}",
screen.text()
);
assert!(
!screen.contains("Сообщение: hello from e2e"),
"compose input should clear after send"
);
let screen = screen_text(&term).await?;
assert!(screen.contains("hello from e2e"), "sent message should appear\n\n{}", screen);
assert!(
!screen.contains("Сообщение: hello from e2e"),
"compose input should clear after send"
);
Ok(())
}
.await;
stop_fixture(&mut term).await;
Ok(())
result
}
fn kill_fixture_processes() {
let _ = std::process::Command::new("pkill")
.arg("-f")
.arg("tele-tui-test-fixture")
.status();
}