719 lines
26 KiB
Rust
719 lines
26 KiB
Rust
//! 设置: the tab, 外观, 外部 CLI and the title bar's theme switch.
|
||
|
||
use crate::support::*;
|
||
|
||
/// The app's list hover is in effect, not the theme's own (a 0.6 alpha):
|
||
/// deepened, and dark halved to stay fainter than a selected row.
|
||
fn app_list_hover(theme: &gpui_kit::component::Theme) -> bool {
|
||
let alpha = theme.list_hover.a;
|
||
if theme.is_dark() {
|
||
(0.4..0.55).contains(&alpha)
|
||
} else {
|
||
alpha > 0.9
|
||
}
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn theme_toggle_flips_mode(cx: &mut TestAppContext) {
|
||
let (handle, _) = open_workspace(cx);
|
||
let before = cx.update(|cx| {
|
||
let theme = cx.theme();
|
||
assert_eq!(theme.list_hover, theme.tokens.list_hover.color);
|
||
assert!(app_list_hover(theme), "{:?}", theme.list_hover);
|
||
theme.is_dark()
|
||
});
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
window.click("theme-toggle", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
let after = cx.update(|cx| {
|
||
let theme = cx.theme();
|
||
assert_eq!(theme.list_hover, theme.tokens.list_hover.color);
|
||
assert!(app_list_hover(theme), "{:?}", theme.list_hover);
|
||
theme.is_dark()
|
||
});
|
||
assert_ne!(before, after);
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn settings_open_from_the_host_list_as_one_tab(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
|
||
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);
|
||
assert!(window.find("settings").visible());
|
||
assert_eq!(window.find("settings").focused(), Some(true));
|
||
assert_eq!(window.find("settings-tab").label(), Some("设置"));
|
||
// Two columns: the first category, 外观, is shown until another is
|
||
// picked. `Settings` names its category rows by position.
|
||
assert_eq!(appearance_dropdown(window, 0).as_deref(), Some("简体中文"));
|
||
assert!(window.try_find("terminal-font-preview").is_none());
|
||
window.within("settings").click("0-1", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
// 终端 is the second.
|
||
assert!(window.find("terminal-font-preview").visible());
|
||
window.click(("terminal-tab", INITIAL_WEB_TERMINAL), cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert!(window.find(("terminal", INITIAL_WEB_TERMINAL)).visible());
|
||
assert!(window.try_find("settings").is_none());
|
||
// Opening settings again brings the same tab forward.
|
||
#[cfg(target_os = "macos")]
|
||
window.press("cmd-,", cx);
|
||
#[cfg(not(target_os = "macos"))]
|
||
window.press("ctrl-,", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert!(window.find("settings").visible());
|
||
assert_eq!(window.find("settings").focused(), Some(true));
|
||
// Back on the category it showed, 终端, not the first one.
|
||
assert!(window.find("terminal-font-preview").visible());
|
||
})
|
||
.unwrap();
|
||
cx.update(|cx| {
|
||
let settings = workspace.read(cx).settings_tab().expect("settings open");
|
||
let group = settings.read(cx).tab_group().unwrap().upgrade().unwrap();
|
||
// The two host terminals and a single settings tab.
|
||
assert_eq!(group.read(cx).panels().len(), 3);
|
||
});
|
||
|
||
// Opening it while it is displayed changes nothing.
|
||
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);
|
||
assert!(window.find("settings").visible());
|
||
assert_eq!(window.find("settings").focused(), Some(true));
|
||
// The platform close shortcut closes it like any other tab.
|
||
window.press("secondary-w", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert!(window.try_find("settings-tab").is_none());
|
||
assert!(window.try_find("settings").is_none());
|
||
})
|
||
.unwrap();
|
||
cx.update(|cx| assert!(workspace.read(cx).settings_tab().is_none()));
|
||
}
|
||
|
||
/// The label of one dropdown in the 外观 page's 常规 group. `Settings` names
|
||
/// its groups, items and dropdown buttons by position.
|
||
/// Open the settings tab on 外部 CLI, the fifth category.
|
||
fn open_external_cli_settings(cx: &mut TestAppContext, handle: WindowHandle<Root>) {
|
||
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);
|
||
window.within("settings").click("0-4", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn the_external_cli_switch_is_off_until_turned_on(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let enabled = |cx: &mut TestAppContext| {
|
||
workspace.read_with(cx, |workspace, cx| {
|
||
workspace
|
||
.settings()
|
||
.read(cx)
|
||
.settings()
|
||
.external_cli
|
||
.enabled
|
||
})
|
||
};
|
||
open_external_cli_settings(cx, handle);
|
||
assert!(!enabled(cx));
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
// 访问控制 › 启用外部 CLI.
|
||
let switch = window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within("item-0")
|
||
.find("check");
|
||
assert_eq!(switch.checked(), Some(false));
|
||
window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within("item-0")
|
||
.click("check", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
assert!(enabled(cx));
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn without_a_home_to_install_into_the_external_cli_offers_nothing(cx: &mut TestAppContext) {
|
||
let (handle, _) = open_workspace(cx);
|
||
open_external_cli_settings(cx, handle);
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(
|
||
window.find("skill-status-codex").label(),
|
||
Some("此系统暂不支持安装")
|
||
);
|
||
assert!(window.try_find("skill-codex").is_none());
|
||
assert!(window.try_find("cli-binary").is_none());
|
||
// Copying the skill needs no home directory.
|
||
window.click("copy-agent-skill", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
assert_eq!(
|
||
cx.read_from_clipboard().and_then(|item| item.text()),
|
||
Some(shellrs::cli::SKILL.to_string())
|
||
);
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn agent_skills_install_where_each_agent_looks_and_come_off_again(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let root = tempfile::tempdir().unwrap();
|
||
let exe = root.path().join("app").join("shellrs");
|
||
std::fs::create_dir_all(exe.parent().unwrap()).unwrap();
|
||
std::fs::write(&exe, "binary").unwrap();
|
||
let paths = IntegrationPaths {
|
||
home: root.path().join("home"),
|
||
bin_link: root.path().join("bin").join("shellrs"),
|
||
exe,
|
||
user_path: None,
|
||
};
|
||
let codex = paths.skill_file(AgentKind::Codex);
|
||
workspace.update(cx, |workspace, cx| {
|
||
workspace.cli_integration().update(cx, |integration, cx| {
|
||
integration.set_paths(Some(paths.clone()), cx)
|
||
})
|
||
});
|
||
open_external_cli_settings(cx, handle);
|
||
|
||
let status = |cx: &mut TestAppContext, id: &'static str| {
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
window.find(id).label().map(str::to_string)
|
||
})
|
||
.unwrap()
|
||
};
|
||
assert_eq!(
|
||
status(cx, "skill-status-codex").as_deref(),
|
||
Some(format!("未安装({})", codex.display()).as_str())
|
||
);
|
||
|
||
// The row's button dispatches the install.
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
window.click("skill-codex", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
assert_eq!(
|
||
std::fs::read_to_string(&codex).unwrap(),
|
||
shellrs::cli::SKILL
|
||
);
|
||
assert_eq!(
|
||
status(cx, "skill-status-codex").as_deref(),
|
||
Some(format!("已安装于 {}", codex.display()).as_str())
|
||
);
|
||
// Only Codex's.
|
||
assert!(!paths.skill_file(AgentKind::ClaudeCode).exists());
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.dispatch_action(Box::new(RemoveAgentSkill(AgentKind::Codex)), cx);
|
||
window.dispatch_action(Box::new(InstallCliCommand), cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
assert!(!codex.exists());
|
||
assert_eq!(
|
||
status(cx, "cli-binary-status").as_deref(),
|
||
Some(format!("已安装于 {}", paths.bin_link.display()).as_str())
|
||
);
|
||
}
|
||
|
||
/// Menus are not driven here: a dropdown's item writes the settings store,
|
||
/// so the test writes it the same way and checks what follows.
|
||
#[gpui_kit::test]
|
||
fn the_appearance_setting_drives_the_theme_and_the_title_bar_switch(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let settings = cx.update(|cx| workspace.read(cx).settings().clone());
|
||
|
||
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 first category.
|
||
window.within("settings").click("0-0", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(appearance_dropdown(window, 0).as_deref(), Some("简体中文"));
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("跟随系统"));
|
||
})
|
||
.unwrap();
|
||
|
||
cx.update(|cx| {
|
||
settings.update(cx, |settings, cx| {
|
||
settings.update(|settings| settings.appearance = Appearance::Dark, cx)
|
||
})
|
||
});
|
||
cx.run_until_parked();
|
||
cx.update(|cx| {
|
||
let theme = cx.theme();
|
||
assert!(theme.is_dark());
|
||
// Applied like the title bar switch always did it.
|
||
assert_eq!(theme.list_hover, theme.tokens.list_hover.color);
|
||
assert!(app_list_hover(theme), "{:?}", theme.list_hover);
|
||
});
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("深色"));
|
||
// The title bar's switch picks the other appearance outright.
|
||
window.click("theme-toggle", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
|
||
cx.update(|cx| {
|
||
assert!(!cx.theme().is_dark());
|
||
let chosen = settings.read(cx).settings();
|
||
assert_eq!(chosen.appearance, Appearance::Light);
|
||
assert_eq!(chosen.language, InterfaceLanguage::SimplifiedChinese);
|
||
});
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("浅色"));
|
||
})
|
||
.unwrap();
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn terminal_themes_follow_the_appearance_and_a_card_chooses_one(cx: &mut TestAppContext) {
|
||
use gpui_kit::component::ThemeMode;
|
||
use shellrs::terminal::{TerminalColors, TerminalTheme};
|
||
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let settings = cx.update(|cx| workspace.read(cx).settings().clone());
|
||
let in_effect = |cx: &mut TestAppContext| cx.update(|cx| TerminalColors::current(cx).key());
|
||
let set_appearance = |appearance, cx: &mut TestAppContext| {
|
||
cx.update(|cx| {
|
||
settings.update(cx, |settings, cx| {
|
||
settings.update(|settings| settings.appearance = appearance, cx)
|
||
})
|
||
});
|
||
cx.run_until_parked();
|
||
};
|
||
let card = |theme: &TerminalTheme| {
|
||
gpui_kit::SharedString::from(format!("terminal-theme-{}", theme.key()))
|
||
};
|
||
let light = TerminalTheme::for_mode(ThemeMode::Light).nth(1).unwrap();
|
||
let dark = TerminalTheme::for_mode(ThemeMode::Dark).nth(1).unwrap();
|
||
|
||
set_appearance(Appearance::Light, cx);
|
||
assert_eq!(in_effect(cx), "shellrs-light");
|
||
in_frame(cx, handle, |window, cx| window.click("open-settings", cx));
|
||
// 外观 is the first category; its second group holds both columns.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.within("settings").click("0-0", cx)
|
||
});
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert_eq!(
|
||
window.find("terminal-theme-shellrs-light").checked(),
|
||
Some(true)
|
||
);
|
||
assert_eq!(
|
||
window.find("terminal-theme-shellrs-dark").checked(),
|
||
Some(true)
|
||
);
|
||
assert_eq!(window.find(card(light)).checked(), Some(false));
|
||
window.click(card(light), cx);
|
||
});
|
||
cx.update(|cx| {
|
||
assert_eq!(
|
||
settings.read(cx).settings().terminal_theme.light,
|
||
light.key()
|
||
)
|
||
});
|
||
// The light column is the one in effect, so the terminals change now.
|
||
// The app stays neutral until 界面跟随主题 is on.
|
||
assert_eq!(in_effect(cx), light.key());
|
||
let neutral = |cx: &mut TestAppContext| {
|
||
cx.update(|cx| {
|
||
let name = cx.theme().theme_name().clone();
|
||
name == "Default Light" || name == "ShellRS Dark"
|
||
})
|
||
};
|
||
assert!(neutral(cx));
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert_eq!(window.find(card(light)).checked(), Some(true));
|
||
assert_eq!(
|
||
window.find("terminal-theme-shellrs-light").checked(),
|
||
Some(false)
|
||
);
|
||
window.click(card(dark), cx);
|
||
});
|
||
// The dark one waits for the dark appearance.
|
||
cx.update(|cx| assert_eq!(settings.read(cx).settings().terminal_theme.dark, dark.key()));
|
||
assert_eq!(in_effect(cx), light.key());
|
||
|
||
// 主题 is the second group, the switch its first item.
|
||
let switch = |window: &mut gpui_kit::Window, cx: &mut App| {
|
||
window
|
||
.within("settings")
|
||
.within("group-1")
|
||
.within("item-0")
|
||
.click("check", cx)
|
||
};
|
||
in_frame(cx, handle, |window, cx| {
|
||
let on = window
|
||
.within("settings")
|
||
.within("group-1")
|
||
.within("item-0")
|
||
.find("check")
|
||
.checked();
|
||
assert_eq!(on, Some(false));
|
||
switch(window, cx);
|
||
});
|
||
assert!(cx.update(|cx| settings.read(cx).settings().terminal_theme.app_follows));
|
||
// On, the app takes the theme's colors, for either appearance.
|
||
let background = |cx: &mut TestAppContext| cx.update(|cx| cx.theme().background.to_rgb());
|
||
let near = |a: gpui_kit::Rgba, b: gpui_kit::Rgba| {
|
||
(a.r - b.r).abs() + (a.g - b.g).abs() + (a.b - b.b).abs() < 4. / 255.
|
||
};
|
||
assert!(near(background(cx), light.background().to_rgb()));
|
||
set_appearance(Appearance::Dark, cx);
|
||
assert_eq!(in_effect(cx), dark.key());
|
||
assert!(cx.update(|cx| cx.theme().is_dark()));
|
||
assert!(near(background(cx), dark.background().to_rgb()));
|
||
set_appearance(Appearance::Light, cx);
|
||
assert_eq!(in_effect(cx), light.key());
|
||
assert!(near(background(cx), light.background().to_rgb()));
|
||
|
||
// Off again, the app is neutral and the terminals keep the theme.
|
||
in_frame(cx, handle, |window, cx| switch(window, cx));
|
||
assert_eq!(in_effect(cx), light.key());
|
||
assert!(neutral(cx));
|
||
in_frame(cx, handle, |window, cx| switch(window, cx));
|
||
|
||
// Back on the default, the app is gpui-kit's own light theme again.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.click("terminal-theme-shellrs-light", cx)
|
||
});
|
||
assert_eq!(in_effect(cx), "shellrs-light");
|
||
cx.update(|cx| assert_eq!(cx.theme().theme_name().as_ref(), "Default Light"));
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
fn the_window_settings_are_on_until_turned_off(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let window = |cx: &mut TestAppContext| {
|
||
cx.update(|cx| workspace.read(cx).settings().read(cx).settings().window)
|
||
};
|
||
in_frame(cx, handle, |window, cx| window.click("open-settings", cx));
|
||
// 应用 comes right before 关于.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.within("settings").click("0-5", cx)
|
||
});
|
||
let switch = |item: usize| {
|
||
move |window: &mut gpui_kit::Window| {
|
||
window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within(format!("item-{item}"))
|
||
.find("check")
|
||
.checked()
|
||
}
|
||
};
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert_eq!(switch(0)(window), Some(true));
|
||
assert_eq!(switch(1)(window), Some(true));
|
||
window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within("item-1")
|
||
.click("check", cx);
|
||
});
|
||
let settings = window(cx);
|
||
assert!(settings.remember_size && !settings.remember_position);
|
||
}
|
||
|
||
/// The 关键字高亮 rules, as the settings hold them.
|
||
fn highlight_rules(
|
||
workspace: &Entity<Workspace>,
|
||
cx: &mut TestAppContext,
|
||
) -> Vec<shellrs::terminal::HighlightRule> {
|
||
cx.update(|cx| {
|
||
workspace
|
||
.read(cx)
|
||
.settings()
|
||
.read(cx)
|
||
.settings()
|
||
.terminal_highlight
|
||
.rules
|
||
})
|
||
}
|
||
|
||
fn patterns(rules: &[shellrs::terminal::HighlightRule]) -> Vec<&str> {
|
||
rules.iter().map(|rule| rule.pattern.as_str()).collect()
|
||
}
|
||
|
||
/// Scroll the settings page down to the rules, below the window's fold.
|
||
fn scroll_to_highlight_rules(cx: &mut TestAppContext, handle: WindowHandle<Root>) {
|
||
in_frame(cx, handle, |window, cx| {
|
||
let position = window.find("highlight-preview").bounds().center();
|
||
window.dispatch_event(
|
||
gpui_kit::ScrollWheelEvent {
|
||
position,
|
||
delta: gpui_kit::ScrollDelta::Pixels(point(px(0.), px(-600.))),
|
||
modifiers: gpui_kit::Modifiers::default(),
|
||
touch_phase: gpui_kit::TouchPhase::Moved,
|
||
}
|
||
.to_platform_input(),
|
||
cx,
|
||
);
|
||
});
|
||
}
|
||
|
||
#[gpui_kit::test]
|
||
async fn highlight_rules_are_edited_in_place_on_their_page(cx: &mut TestAppContext) {
|
||
use shellrs::terminal::{HighlightColor, TerminalHighlights};
|
||
|
||
let (handle, workspace) = open_workspace(cx);
|
||
in_frame(cx, handle, |window, cx| window.click("open-settings", cx));
|
||
// 关键字高亮 comes right after 终端, with the examples a new
|
||
// installation starts with: rows 0 to 2.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.within("settings").click("0-2", cx)
|
||
});
|
||
let ip = r"\b\d{1,3}(\.\d{1,3}){3}\b";
|
||
assert_eq!(
|
||
patterns(&highlight_rules(&workspace, cx)),
|
||
["ERROR", "WARN", ip]
|
||
);
|
||
in_frame(cx, handle, |window, _| {
|
||
assert!(window.find(("highlight-rule", 2usize)).visible());
|
||
assert!(window.find("highlight-preview").visible());
|
||
});
|
||
|
||
// Off until turned on: the examples color nothing before.
|
||
let colored = |cx: &mut TestAppContext| {
|
||
cx.update(|cx| !TerminalHighlights::current(cx).spans("ERROR").is_empty())
|
||
};
|
||
assert!(!colored(cx));
|
||
in_frame(cx, handle, |window, cx| {
|
||
// 常规 › 启用关键字高亮.
|
||
let switch = window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within("item-0")
|
||
.find("check");
|
||
assert_eq!(switch.checked(), Some(false));
|
||
window
|
||
.within("settings")
|
||
.within("group-0")
|
||
.within("item-0")
|
||
.click("check", cx);
|
||
});
|
||
cx.run_until_parked();
|
||
assert!(colored(cx));
|
||
|
||
// A new row takes the keyboard at once; what does not compile says so
|
||
// under it, and the settings have it as typed.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.click("add-highlight-rule", cx)
|
||
});
|
||
scroll_to_highlight_rules(cx, handle);
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert_eq!(
|
||
window.find(("highlight-rule-pattern", 3usize)).focused(),
|
||
Some(true)
|
||
);
|
||
window.input("OutOf(Memory", cx);
|
||
});
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert_eq!(
|
||
window.find(("highlight-rule-error", 3usize)).label(),
|
||
Some("正则表达式写法有误")
|
||
);
|
||
window.press("secondary-a", cx);
|
||
window.input(r"OutOfMemory\w*", cx);
|
||
});
|
||
in_frame(cx, handle, |window, cx| {
|
||
assert!(window.try_find(("highlight-rule-error", 3usize)).is_none());
|
||
window.click(("highlight-rule-note", 3usize), cx);
|
||
window.input("内存溢出", cx);
|
||
window.click(("highlight-rule-color", 3usize), cx);
|
||
window.press("secondary-a", cx);
|
||
window.input("#D946EF", cx);
|
||
window.click(("highlight-rule-notify", 3usize), cx);
|
||
window.click(("highlight-rule-enabled", 0usize), cx);
|
||
});
|
||
cx.run_until_parked();
|
||
let rules = highlight_rules(&workspace, cx);
|
||
assert_eq!(rules[3].pattern, r"OutOfMemory\w*");
|
||
assert_eq!(rules[3].note, "内存溢出");
|
||
assert_eq!(rules[3].color, HighlightColor::from_hex("#d946ef").unwrap());
|
||
assert!(rules[3].enabled && rules[3].notify);
|
||
assert!(!rules[0].enabled);
|
||
|
||
// The color picker opens on its palette. (Its swatches are keyed by
|
||
// color; the a11y tree that would catch two of one color is not built
|
||
// in tests, see FEATURED_COLORS.)
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.click(("highlight-rule-picker", 3usize), cx)
|
||
});
|
||
in_frame(cx, handle, |window, cx| window.press("escape", cx));
|
||
|
||
// Dragged by its handle, the new row goes first.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.drag_to(
|
||
("highlight-rule-handle", 3usize),
|
||
("highlight-rule", 0usize),
|
||
cx,
|
||
)
|
||
});
|
||
cx.run_until_parked();
|
||
assert_eq!(
|
||
patterns(&highlight_rules(&workspace, cx)),
|
||
[r"OutOfMemory\w*", "ERROR", "WARN", ip]
|
||
);
|
||
|
||
// Deleted at once, WARN goes; the terminals follow every change.
|
||
in_frame(cx, handle, |window, cx| {
|
||
window.click(("delete-highlight-rule", 1usize), cx)
|
||
});
|
||
cx.run_until_parked();
|
||
assert_eq!(
|
||
patterns(&highlight_rules(&workspace, cx)),
|
||
[r"OutOfMemory\w*", "ERROR", ip]
|
||
);
|
||
cx.update(|cx| {
|
||
let colored = TerminalHighlights::current(cx).spans("OutOfMemoryError ERROR 10.0.0.1");
|
||
// ERROR is off; the new rule and the address color.
|
||
assert_eq!(
|
||
colored
|
||
.iter()
|
||
.map(|(range, _)| range.clone())
|
||
.collect::<Vec<_>>(),
|
||
[0..16, 23..31]
|
||
);
|
||
});
|
||
|
||
// 启用关键字高亮 off, nothing colors.
|
||
let settings = cx.update(|cx| workspace.read(cx).settings().clone());
|
||
settings.update(cx, |settings, cx| {
|
||
settings.update(|settings| settings.terminal_highlight.enabled = false, cx)
|
||
});
|
||
cx.run_until_parked();
|
||
cx.update(|cx| {
|
||
assert!(
|
||
TerminalHighlights::current(cx)
|
||
.spans("OutOfMemoryError")
|
||
.is_empty()
|
||
);
|
||
});
|
||
}
|
||
|
||
/// 界面语言 changes the window at once, to the language just chosen and
|
||
/// back. The fixture keeps this test's language to itself.
|
||
#[gpui_kit::test]
|
||
fn the_interface_language_changes_the_window_at_once(cx: &mut TestAppContext) {
|
||
let (handle, workspace) = open_workspace(cx);
|
||
let settings = cx.update(|cx| workspace.read(cx).settings().clone());
|
||
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);
|
||
window.within("settings").click("0-0", cx);
|
||
})
|
||
.unwrap();
|
||
cx.run_until_parked();
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(window.find("settings-tab").label(), Some("设置"));
|
||
assert_eq!(window.find("show-hosts").label(), Some("主机"));
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("跟随系统"));
|
||
// Set once when the list was made, and kept by the input.
|
||
assert_eq!(window.find("host-search").label(), Some("搜索主机或分组"));
|
||
})
|
||
.unwrap();
|
||
|
||
let choose = |language, cx: &mut TestAppContext| {
|
||
cx.update(|cx| {
|
||
settings.update(cx, |settings, cx| {
|
||
settings.update(|settings| settings.language = language, cx)
|
||
})
|
||
});
|
||
cx.run_until_parked();
|
||
};
|
||
choose(InterfaceLanguage::English, cx);
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(window.find("settings-tab").label(), Some("Settings"));
|
||
assert_eq!(window.find("show-hosts").label(), Some("Hosts"));
|
||
assert_eq!(appearance_dropdown(window, 0).as_deref(), Some("English"));
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("System"));
|
||
assert_eq!(
|
||
window.find("host-search").label(),
|
||
Some("Search hosts or groups")
|
||
);
|
||
})
|
||
.unwrap();
|
||
|
||
choose(InterfaceLanguage::SimplifiedChinese, cx);
|
||
cx.update_window(handle.into(), |_, window, cx| {
|
||
window.render_frame(cx);
|
||
assert_eq!(window.find("settings-tab").label(), Some("设置"));
|
||
assert_eq!(appearance_dropdown(window, 1).as_deref(), Some("跟随系统"));
|
||
})
|
||
.unwrap();
|
||
}
|