Files
shell-rs/tests/ui/terminal.rs
Xuz 388a1aa68a UI 测试按功能拆成模块:tests/ui.rs → tests/ui/
- 仍是一个测试程序(tests/ui/main.rs,cargo test --test ui),每个文件单独成测试程序会再链接一遍 GPUI
- 按功能分 11 个模块:host_dialog、host_tree、connection、terminal、workspace、settings、
  sftp、transfer、forward、credential、update
- 两个以上模块共用的夹具和假实现放进 support/:mod.rs 是公共的 use、种子 id 和通用助手,
  其余按终端、SFTP、端口转发、测试连接、凭据分文件;只有一个模块用的留在那个模块里
- 纯搬移,测试逻辑不变:147 个测试名称与搬移前一致,全部通过
- CLAUDE.md 写明模块划分和新测试该放哪里,AGENTS.md、README 改路径
2026-10-01 14:53:33 +08:00

762 lines
26 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Terminals: local terminals, keys and input, selection, find, clearing,
//! the font and the size.
use crate::support::*;
fn open_workspace_with_factory(
cx: &mut TestAppContext,
factory: Arc<FakeTerminalFactory>,
) -> (WindowHandle<Root>, Entity<Workspace>) {
cx.update(shellrs::init);
// Dialogs still, as in every fixture: see `open_workspace_with_tester`.
cx.update(|cx| cx.set_reduce_motion(true));
let mut workspace = None;
let handle = cx.open_window(size(px(1280.), px(800.)), |window, cx| {
let store = cx.new(|_| HostStore::seed());
let remote = Arc::new(FixedRemoteTerminalTransportProvider::new(Arc::new(
FakeTerminalFactory::default(),
)));
let view = cx.new(|cx| {
Workspace::new_with_services(
store,
cx.new(|_| SettingsStore::in_memory()),
remote,
factory.clone(),
Arc::new(FakeSftpProvider::default()),
Arc::new(FakeLocalDirectory::default()),
Arc::new(FakeConnectionTester::default()),
Arc::new(FakeForwardProvider::default()),
window,
cx,
)
});
workspace = Some(view.clone());
Root::new(view, window, cx)
});
(handle, workspace.expect("workspace created"))
}
#[gpui_kit::test]
async fn creates_independent_local_terminals_from_button_and_shortcut(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, workspace) = open_workspace_with_factory(cx, factory.clone());
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.run_until_parked();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.map(|element| element.label() == Some("运行中 本地终端"))
.unwrap_or(false)
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert!(window.find(("local-terminal", 1_u64)).visible());
assert_eq!(window.find(("local-terminal", 1_u64)).focused(), Some(true));
#[cfg(target_os = "macos")]
window.press("cmd-t", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-t", cx);
})
.unwrap();
cx.run_until_parked();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.map(|element| element.label() == Some("运行中 本地终端"))
.unwrap_or(false)
&& window.try_find(("local-terminal", 2_u64)).is_some()
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert!(window.find(("local-terminal", 2_u64)).visible());
assert_eq!(
window.find("status-connection").label(),
Some("运行中 本地终端")
);
window.click(("close-local-terminal", 2_u64), cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert!(window.try_find(("local-terminal", 2_u64)).is_none());
assert!(window.find(("local-terminal", 1_u64)).visible());
})
.unwrap();
cx.update(|cx| {
let workspace = workspace.read(cx);
assert!(workspace.local_terminal(LocalTerminalId(1)).is_some());
assert!(workspace.local_terminal(LocalTerminalId(2)).is_none());
assert!(workspace.store().read(cx).active().is_none());
assert_eq!(factory.starts.load(Ordering::SeqCst), 2);
});
}
#[gpui_kit::test]
async fn tab_keys_are_sent_to_the_focused_terminal(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, _) = open_workspace_with_factory(cx, factory.clone());
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.is_some_and(|element| element.label() == Some("运行中 本地终端"))
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert_eq!(window.find(("local-terminal", 1_u64)).focused(), Some(true));
window.press("tab", cx);
window.press("shift-tab", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, _| {
factory.written_text() == "\t\x1b[Z"
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert_eq!(window.find(("local-terminal", 1_u64)).focused(), Some(true));
})
.unwrap();
}
#[gpui_kit::test]
async fn exited_local_terminal_keeps_its_tab_and_restarts_fresh(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::exit_first());
let (handle, workspace) = open_workspace_with_factory(cx, factory.clone());
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window.try_find(("local-terminal-exit", 1_u64)).is_some()
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert!(window.find(("local-terminal", 1_u64)).visible());
assert!(window.find(("local-terminal-exit", 1_u64)).visible());
cx.write_to_clipboard(ClipboardItem::new_string("退出后不可粘贴".into()));
#[cfg(target_os = "macos")]
window.press("cmd-v", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-shift-v", cx);
assert!(factory.written_text().is_empty());
let bounds = window.find(("local-terminal", 1_u64)).bounds();
window.drag(
point(bounds.left() + px(1.), bounds.top() + px(8.)),
point(bounds.right() - px(1.), bounds.top() + px(8.)),
cx,
);
#[cfg(target_os = "macos")]
window.press("cmd-c", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-shift-c", cx);
assert_eq!(
cx.read_from_clipboard().and_then(|item| item.text()),
Some("run:1$ alpha.txt 会议纪要.md".into())
);
window.click(("restart-local-terminal", 1_u64), cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window.try_find(("local-terminal-exit", 1_u64)).is_none()
&& window
.try_find("status-connection")
.map(|element| element.label() == Some("运行中 本地终端"))
.unwrap_or(false)
})
.await;
cx.update(|cx| {
let panel = workspace
.read(cx)
.local_terminal(LocalTerminalId(1))
.unwrap()
.clone();
assert_eq!(
panel.read(cx).status(cx).lifecycle(),
&TerminalLifecycle::Running
);
assert_eq!(factory.starts.load(Ordering::SeqCst), 2);
});
}
#[gpui_kit::test]
async fn local_terminal_start_failure_is_kept_with_a_chinese_error(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::fail_first());
let (handle, workspace) = open_workspace_with_factory(cx, factory);
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window.try_find(("local-terminal-exit", 1_u64)).is_some()
})
.await;
cx.update(|cx| {
let panel = workspace
.read(cx)
.local_terminal(LocalTerminalId(1))
.unwrap()
.clone();
assert!(matches!(
panel.read(cx).status(cx).lifecycle(),
TerminalLifecycle::Failed(error) if error.contains("测试启动失败")
));
assert!(
panel
.read(cx)
.terminal()
.read(cx)
.screen_text(cx)
.contains("终端错误:测试启动失败")
);
});
}
#[gpui_kit::test]
async fn terminal_selection_copies_only_on_command_and_finishes_outside_view(
cx: &mut TestAppContext,
) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, _) = open_workspace_with_factory(cx, factory);
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.is_some_and(|element| element.label() == Some("运行中 本地终端"))
})
.await;
cx.update(|cx| cx.write_to_clipboard(ClipboardItem::new_string("原剪贴板".into())));
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.right_click(("local-terminal", 1_u64), cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
let mut popup = window.within("popup-menu");
assert_eq!(popup.find(0_usize).label(), Some("复制"));
popup.click(0_usize, cx);
assert_eq!(
cx.read_from_clipboard().and_then(|item| item.text()),
Some("原剪贴板".into())
);
window.click("host-search", cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
let bounds = window.find(("local-terminal", 1_u64)).bounds();
let from = point(bounds.left() + px(1.), bounds.top() + px(8.));
let to = point(bounds.right() - px(1.), bounds.top() + px(8.));
window.drag(from, to, cx);
assert_eq!(
cx.read_from_clipboard().and_then(|item| item.text()),
Some("原剪贴板".into())
);
#[cfg(target_os = "macos")]
window.press("cmd-c", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-shift-c", cx);
assert_eq!(
cx.read_from_clipboard().and_then(|item| item.text()),
Some("run:1$ alpha.txt 会议纪要.md".into())
);
window.right_click(("local-terminal", 1_u64), cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
let mut popup = window.within("popup-menu");
assert_eq!(popup.find(0_usize).label(), Some("复制"));
assert_eq!(popup.find(1_usize).label(), Some("粘贴"));
popup.click(0_usize, cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
let bounds = window.find(("local-terminal", 1_u64)).bounds();
let from = point(bounds.left() + px(100.), bounds.top() + px(8.));
let outside = point(px(20.), bounds.top() + px(8.));
let to = point(bounds.right() - px(1.), bounds.top() + px(8.));
window.drag(from, outside, cx);
#[cfg(target_os = "macos")]
window.press("cmd-c", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-shift-c", cx);
let selection_after_release = cx
.read_from_clipboard()
.and_then(|item| item.text())
.expect("outside release leaves a valid selection");
cx.write_to_clipboard(ClipboardItem::new_string("哨兵".into()));
window.dispatch_event(
MouseMoveEvent {
position: to,
pressed_button: Some(MouseButton::Left),
modifiers: Default::default(),
}
.to_platform_input(),
cx,
);
window.render_frame(cx);
#[cfg(target_os = "macos")]
window.press("cmd-c", cx);
#[cfg(not(target_os = "macos"))]
window.press("ctrl-shift-c", cx);
assert_eq!(
cx.read_from_clipboard().and_then(|item| item.text()),
Some(selection_after_release)
);
window.click(("close-local-terminal", 1_u64), cx);
})
.unwrap();
cx.run_until_parked();
}
#[gpui_kit::test]
async fn cjk_input_is_sent_once_and_resize_commands_are_deduplicated(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, workspace) = open_workspace_with_factory(cx, factory.clone());
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.is_some_and(|element| element.label() == Some("运行中 本地终端"))
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.input("输入一次", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, _| {
factory.written_text() == "输入一次"
})
.await;
cx.update(|cx| {
let terminal = workspace
.read(cx)
.local_terminal(LocalTerminalId(1))
.expect("local terminal exists")
.read(cx)
.terminal()
.clone();
let screen = terminal.read(cx).screen_text(cx);
assert_eq!(screen.matches("输入一次").count(), 1);
});
let resizes = factory
.resizes
.lock()
.unwrap_or_else(|error| error.into_inner());
assert!(!resizes.is_empty());
assert!(resizes.windows(2).all(|sizes| sizes[0] != sizes[1]));
}
/// Open a local terminal on `factory` and wait for the fake shell's banner.
async fn open_running_local_terminal(
cx: &mut TestAppContext,
factory: Arc<FakeTerminalFactory>,
) -> (WindowHandle<Root>, Entity<Workspace>) {
let (handle, workspace) = open_workspace_with_factory(cx, factory);
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("new-local-terminal", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find("status-connection")
.is_some_and(|element| element.label() == Some("运行中 本地终端"))
})
.await;
(handle, workspace)
}
/// The row height the fake shell was last told about.
fn last_cell_height(factory: &FakeTerminalFactory) -> Option<u16> {
factory
.resizes
.lock()
.unwrap_or_else(|error| error.into_inner())
.last()
.map(TerminalSize::cell_height)
}
/// Menus are not driven here: the settings page's fields write the settings
/// store, so the test writes it the same way.
#[gpui_kit::test]
async fn the_terminal_font_setting_reaches_open_terminals_and_the_preview(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, workspace) = open_running_local_terminal(cx, factory.clone()).await;
let settings = cx.update(|cx| workspace.read(cx).settings().clone());
// The defaults keep the 20 px rows terminals had before the setting.
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
window.render_frame(cx);
last_cell_height(&factory) == Some(20)
})
.await;
cx.update(|cx| {
settings.update(cx, |settings, cx| {
settings.update(
|settings| {
settings.terminal_font.size = 16.;
settings.terminal_font.line_height = 1.5;
},
cx,
)
})
});
// The open terminal takes the new rows at once.
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
window.render_frame(cx);
last_cell_height(&factory) == Some(24)
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.click("open-settings", cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
// 终端 is the second category.
window.within("settings").click("0-1", cx);
})
.unwrap();
cx.run_until_parked();
let default_family = cx.update(|cx| cx.theme().mono_font_family.to_string());
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
let field = |window: &mut gpui_kit::Window, item: usize| {
window
.within("settings")
.within("group-0")
.within(format!("item-{item}"))
.find("btn")
.label()
.map(str::to_string)
};
assert_eq!(field(window, 0), Some(format!("{default_family}(默认)")));
assert_eq!(field(window, 1).as_deref(), Some("16"));
assert_eq!(
window.find("terminal-font-preview").label(),
Some(format!("{default_family} 16 px,行高 1.5").as_str())
);
})
.unwrap();
// A family that is not installed, say from a file copied from another
// machine, gives way to the default instead of a wrong font.
cx.update(|cx| {
settings.update(cx, |settings, cx| {
settings.update(
|settings| settings.terminal_font.family = Some("没有这个字体".into()),
cx,
)
})
});
cx.run_until_parked();
cx.update(|cx| {
assert_eq!(cx.global::<TerminalFont>().family, None);
assert_eq!(
settings.read(cx).settings().terminal_font.family.as_deref(),
Some("没有这个字体")
);
});
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert_eq!(
window.find("terminal-font-preview").label(),
Some(format!("{default_family} 16 px,行高 1.5").as_str())
);
})
.unwrap();
}
fn local_screen(workspace: &Entity<Workspace>, cx: &App) -> String {
workspace
.read(cx)
.local_terminal(LocalTerminalId(1))
.expect("local terminal exists")
.read(cx)
.terminal()
.read(cx)
.screen_text(cx)
}
async fn wait_for_find_count(cx: &mut TestAppContext, handle: WindowHandle<Root>, label: &str) {
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
window.render_frame(cx);
window
.try_find("terminal-find-count")
.is_some_and(|element| element.label() == Some(label))
})
.await;
}
#[gpui_kit::test]
async fn find_highlights_matches_and_steps_between_them(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, workspace) = open_running_local_terminal(cx, factory.clone()).await;
// The banner holds one 「alpha」; the fake shell echoes two more.
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.input("alpha alpha", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, cx| {
local_screen(&workspace, cx).matches("alpha").count() == 3
})
.await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.dispatch_action(Box::new(FindInTerminal), cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert_eq!(window.find("terminal-find").focused(), Some(true));
// Nothing is counted before there is a query.
assert!(window.try_find("terminal-find-count").is_none());
// The bar floats over the terminal; clicking it must not hand the
// keyboard back to the terminal underneath.
window.click("terminal-find", cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert_eq!(window.find("terminal-find").focused(), Some(true));
window.input("alpha", cx);
})
.unwrap();
// The first match on screen is focused.
wait_for_find_count(cx, handle, "1/3").await;
// What is typed into the find bar never reaches the shell.
assert_eq!(factory.written_text(), "alpha alpha");
cx.update_window(handle.into(), |_, window, cx| {
window.dispatch_action(Box::new(FindNextInTerminal), cx);
})
.unwrap();
wait_for_find_count(cx, handle, "2/3").await;
cx.update_window(handle.into(), |_, window, cx| {
window.dispatch_action(Box::new(FindPreviousInTerminal), cx);
window.dispatch_action(Box::new(FindPreviousInTerminal), cx);
})
.unwrap();
// Stepping up from the first match wraps to the last.
wait_for_find_count(cx, handle, "3/3").await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.press("enter", cx);
})
.unwrap();
wait_for_find_count(cx, handle, "1/3").await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.press("backspace", cx);
window.input("z", cx);
})
.unwrap();
wait_for_find_count(cx, handle, "无结果").await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.press("escape", cx);
})
.unwrap();
cx.run_until_parked();
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
assert!(window.try_find("terminal-find").is_none());
assert_eq!(window.find(("local-terminal", 1_u64)).focused(), Some(true));
window.input("!", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, _| {
factory.written_text() == "alpha alpha!"
})
.await;
}
#[gpui_kit::test]
async fn clearing_a_terminal_keeps_only_the_prompt_line(cx: &mut TestAppContext) {
let factory = Arc::new(FakeTerminalFactory::default());
let (handle, workspace) = open_running_local_terminal(cx, factory.clone()).await;
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.input("root@localhost:~# ", cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, cx| {
local_screen(&workspace, cx).contains("root@localhost:~#")
})
.await;
assert!(
cx.update(|cx| local_screen(&workspace, cx))
.contains("alpha.txt")
);
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.dispatch_action(Box::new(ClearTerminal), cx);
})
.unwrap();
cx.run_until_parked();
assert_eq!(
cx.update(|cx| local_screen(&workspace, cx)).trim_end(),
"root@localhost:~#"
);
// Clearing happens on this side; the shell is not sent anything.
assert_eq!(factory.written_text(), "root@localhost:~# ");
}
#[gpui_kit::test]
async fn the_status_bar_shows_the_size_of_the_terminal_in_front(cx: &mut TestAppContext) {
let (handle, workspace) = open_workspace(cx);
let terminal = RemoteTerminalId(FIRST_NEW_TERMINAL);
cx.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
window.dispatch_action(Box::new(ConnectHost(HostId(WEB_01))), cx);
})
.unwrap();
cx.wait_for(handle.into(), Duration::from_secs(2), |_, cx| {
remote_lifecycle(&workspace, terminal, cx) == TerminalLifecycle::Running
})
.await;
let columns = |cx: &App| {
workspace
.read(cx)
.remote_terminal(terminal)
.expect("terminal exists")
.read(cx)
.status(cx)
.columns()
};
// Whether the status bar shows the terminal's screen as it is now.
let shows_its_size = |window: &mut gpui_kit::Window, cx: &mut App| {
let status = workspace
.read(cx)
.remote_terminal(terminal)
.expect("terminal exists")
.read(cx)
.status(cx);
let size = format!("{}×{}", status.columns(), status.rows());
window
.try_find("status-terminal-size")
.is_some_and(|element| element.label() == Some(size.as_str()))
};
// Laid out in the window, not the 80 columns it starts with.
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
columns(cx) > 80 && shows_its_size(window, cx)
})
.await;
let wide = cx.update(|cx| columns(cx));
// A smaller window, a smaller terminal, and the status bar follows.
cx.simulate_window_resize(handle.into(), size(px(900.), px(600.)));
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
columns(cx) < wide && shows_its_size(window, cx)
})
.await;
// An SFTP tab has no terminal, so no size either.
in_frame(cx, handle, |window, cx| {
window.dispatch_action(Box::new(OpenExplorer(HostId(WEB_01))), cx);
});
cx.wait_for(handle.into(), Duration::from_secs(2), |window, _| {
window
.try_find(("explorer", SFTP_TAB))
.is_some_and(|explorer| explorer.visible())
})
.await;
in_frame(cx, handle, |window, cx| {
assert!(window.find("status-connection").visible());
assert!(window.try_find("status-terminal-size").is_none());
window.click("new-local-terminal", cx);
});
// A local terminal shows its own.
cx.wait_for(handle.into(), Duration::from_secs(2), |window, cx| {
let Some(local) = workspace.read(cx).local_terminal(LocalTerminalId(1)) else {
return false;
};
let status = local.read(cx).status(cx);
let size = format!("{}×{}", status.columns(), status.rows());
// Laid out, so no longer the 80×24 it starts with.
size != "80×24"
&& window
.try_find("status-terminal-size")
.is_some_and(|element| element.label() == Some(size.as_str()))
})
.await;
}