Files
XuzandClaude Opus 5.5 7b0dad9b41 Docker 的标签改用可横向滚动的下划线标签,与系统服务一致
均分宽度放不下英文的 "Containers 12"。不管什么语言,标签都按文字和数量的宽度排,
放不下时横向滚动。去掉只给 Docker 用的 count_tabs 和分段控件的计数。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:42:23 +08:00

215 lines
7.5 KiB
Rust

//! What the tests share: the imports, the seeded ids, opening a workspace
//! and driving it frame by frame.
// Every test module starts with `use crate::support::*;`, which brings in
// these along with the fixtures and fakes.
pub use gpui_kit::component::{ActiveTheme as _, Root, WindowExt as _, notification::Notification};
pub use gpui_kit::test::{TestAppContextExt, TestWindowExt};
pub use gpui_kit::{
App, AppContext as _, ClipboardItem, ElementId, Entity, InputEvent as _, MouseButton,
MouseMoveEvent, TestAppContext, WindowHandle, point, px, size,
};
pub use semver::Version;
pub use shellrs::app::{
CenterTab, CheckForUpdates, ClearTerminal, CloseScope, CloseTabs, CollapseAllGroups,
ConnectGroup, ConnectHost, CopyCredentialPublicKey, CopyHostAddress, CopyHostId,
DeleteCredential, DeleteForward, DeleteGroup, DeleteHost, DisconnectHost, DisconnectTerminal,
EditCredential, EditForward, EditHost, ExpandAllGroups, FindInTerminal, FindNextInTerminal,
FindPreviousInTerminal, FocusSearch, InstallCliCommand, NewHostInGroup, NewLocalTerminal,
OpenExplorer, OpenSettings, ReconnectTerminal, RemoveAgentSkill, RenameGroup, RenameTerminal,
StartForward, StopForward, ToggleHostPanel, ToggleToolSidebar,
};
pub use shellrs::cli::{AgentKind, IntegrationPaths};
pub use shellrs::connection::{
ConnectionPrompt, ConnectionPromptField, ConnectionPromptKind, ConnectionPromptReply,
ConnectionTester, Latency, LoginTest, TrustCallback,
};
pub use shellrs::explorer::ExplorerId;
pub use shellrs::forward::{
ForwardCommand, ForwardEvent, ForwardStatus, ForwardTransport, ForwardTransportProvider,
};
pub use shellrs::host::{
AuthKind, ConnectionState, CredentialDraft, CredentialId, CredentialKind, ForwardDraft,
ForwardEndpoint, ForwardId, ForwardKind, ForwardRule, GeneratedKey, GroupDraft, GroupId,
HostDatabase, HostDraft, HostId, HostLogin, HostOs, HostStore, JumpLogin, KeyAlgorithm,
LoginMethod, LoginRoute, PastedKey, ProxyKind, ProxySettings, Route, read_public_key,
};
pub use shellrs::secrets::{InMemorySecretStore, SecretRef, SecretStore as _};
pub use shellrs::settings::{Appearance, InterfaceLanguage, SettingsStore};
pub use shellrs::sftp::{
DirectoryEntry, DirectoryListing, EDIT_LIMIT, EntryKind, FileBytes, FileMetadata, FileStamp,
LocalDirectoryProvider, ReadFailure, RemotePath, SaveFailure, SftpCommand, SftpEvent,
SftpTransport, SftpTransportProvider, TextFile, UploadRequest,
};
pub use shellrs::terminal::{
FixedRemoteTerminalTransportProvider, LocalTerminalId, RemoteTerminalId,
RemoteTerminalTransportProvider, SharedTerminalTransportFactory, TerminalFont,
TerminalLifecycle, TerminalSize, TerminalTransport, TerminalTransportCommand,
TerminalTransportEvent, TerminalTransportFactory,
};
pub use shellrs::update::{
Channel, InstallKind, Installer, Relaunch, Release, Staged, TrustedKeys, Unsupported,
UpdateError, UpdateFeed, UpdateServices,
};
pub use shellrs::workspace::Workspace;
pub use std::sync::atomic::{AtomicUsize, Ordering};
pub use std::sync::{Arc, Mutex, mpsc};
pub use std::time::Duration;
mod connection;
mod credential;
mod forward;
mod sftp;
mod terminal;
pub use connection::*;
pub use credential::*;
pub use forward::*;
pub use sftp::*;
pub use terminal::*;
/// Seeded host ids, in insertion order (see `HostStore::seed`).
pub const WEB_01: u64 = 1;
pub const WEB_02: u64 = 2;
pub const DB_01: u64 = 3;
/// The first SFTP tab a test opens; tab ids count up from 1.
pub const SFTP_TAB: u64 = 1;
pub const STAGING_API: u64 = 4;
pub const DEV_BOX: u64 = 6;
pub const INITIAL_WEB_TERMINAL: u64 = 1;
pub const INITIAL_STAGING_TERMINAL: u64 = 2;
pub const FIRST_NEW_TERMINAL: u64 = 3;
/// Seeded group ids, in insertion order: 生产, 测试, 开发.
pub const PRODUCTION: u64 = 1;
pub const DEVELOPMENT: u64 = 3;
pub fn open_workspace(cx: &mut TestAppContext) -> (WindowHandle<Root>, Entity<Workspace>) {
open_workspace_with_store(cx, HostStore::seed())
}
/// Production loads the store from the database; the tests hand one in
/// directly so they get the fixed shape `HostStore::seed` describes.
pub fn open_workspace_with_store(
cx: &mut TestAppContext,
store: HostStore,
) -> (WindowHandle<Root>, Entity<Workspace>) {
open_workspace_with_tester(cx, store, Arc::new(FakeConnectionTester::default()))
}
pub fn one_host_store(auth: AuthKind) -> (HostStore, HostId) {
let mut store = HostStore::empty();
let id = store.insert_unnotified(HostDraft::new(
"prompt-host",
"example.test",
22,
"tester",
auth,
None,
));
(store, id)
}
/// What every fixture starts with: ShellRS's globals, in Chinese, the
/// language the assertions are written in. The test's interface language is
/// its own, so one that switches to English switches no other test.
pub fn init_app(cx: &mut TestAppContext) {
shellrs::i18n::isolate_thread();
shellrs::i18n::set_locale("zh-CN");
cx.update(shellrs::init);
}
/// Settings kept in memory, in Chinese: by default the interface follows the
/// system, and CI's is English.
pub fn settings_store() -> SettingsStore {
let mut store = SettingsStore::in_memory();
store.update_unnotified(|settings| settings.language = InterfaceLanguage::SimplifiedChinese);
store
}
pub fn appearance_dropdown(window: &mut gpui_kit::Window, item: usize) -> Option<String> {
window
.within("settings")
.within("group-0")
.within(format!("item-{item}"))
.find("btn")
.label()
.map(str::to_string)
}
/// `TestWindowExt::click` sends no modifiers, and file lists need ⌘ and
/// Shift clicks.
pub fn modified_click(
window: &mut gpui_kit::Window,
pane: (&'static str, u64),
row: &str,
modifiers: gpui_kit::Modifiers,
cx: &mut App,
) {
let row = ElementId::Name(row.to_string().into());
let position = window.within(pane).find(row).bounds().center();
click_at(window, position, modifiers, cx);
}
/// A left click at a point, for places without an element of their own.
pub fn click_at(
window: &mut gpui_kit::Window,
position: gpui_kit::Point<gpui_kit::Pixels>,
modifiers: gpui_kit::Modifiers,
cx: &mut App,
) {
use gpui_kit::{MouseDownEvent, MouseUpEvent};
window.dispatch_event(
MouseMoveEvent {
position,
pressed_button: None,
modifiers,
}
.to_platform_input(),
cx,
);
window.render_frame(cx);
window.dispatch_event(
MouseDownEvent {
button: MouseButton::Left,
position,
modifiers,
click_count: 1,
first_mouse: false,
}
.to_platform_input(),
cx,
);
window.dispatch_event(
MouseUpEvent {
button: MouseButton::Left,
position,
modifiers,
click_count: 1,
}
.to_platform_input(),
cx,
);
}
/// Whether a button accepts input. gpui-base does not report `disabled` for
/// buttons, but a disabled one also leaves the focus order.
pub fn enabled(button: &gpui_kit::test::ElementSnapshot) -> bool {
button.focused().is_some()
}
/// Draw a frame, run `body` against it, and let what it started settle.
pub fn in_frame<R>(
cx: &mut TestAppContext,
handle: WindowHandle<Root>,
body: impl FnOnce(&mut gpui_kit::Window, &mut App) -> R,
) -> R {
let result = cx
.update_window(handle.into(), |_, window, cx| {
window.render_frame(cx);
body(window, cx)
})
.unwrap();
cx.run_until_parked();
result
}