test: fix three tests that read host settings.toml default_mode
The user's ~/Library/Application Support/deepseek/settings.toml had default_mode = "yolo", which caused test_mode_yolo_sets_all_flags, test_trust_on_enables_flag, and footer_status_line_spans_show_mode_and_model_idle_and_active to fail because they implicitly depended on the host's global mode setting. Pin each test to Agent mode explicitly so they pass regardless of the developer's personal settings.
This commit is contained in:
@@ -120,6 +120,9 @@ fn show_single_setting(app: &App, key: &str) -> CommandResult {
|
||||
}
|
||||
"approval_mode" | "approval" => Some(app.approval_mode.label().to_string()),
|
||||
"locale" | "language" => Some(locale_display(app.ui_locale).to_string()),
|
||||
"theme" | "ui_theme" => {
|
||||
Some(crate::palette::theme_label_for_mode(app.ui_theme.mode).to_string())
|
||||
}
|
||||
"background_color" | "background" | "bg" => {
|
||||
crate::palette::hex_rgb_string(app.ui_theme.surface_bg)
|
||||
.or_else(|| Some("(default)".to_string()))
|
||||
@@ -418,13 +421,11 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
|
||||
app.ui_locale = resolve_locale(&settings.locale);
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
"background_color" | "background" | "bg" => {
|
||||
let base_theme = crate::palette::UiTheme::detect();
|
||||
app.ui_theme = settings
|
||||
.background_color
|
||||
.as_deref()
|
||||
.and_then(crate::palette::parse_hex_rgb_color)
|
||||
.map_or(base_theme, |color| base_theme.with_background_color(color));
|
||||
"theme" | "ui_theme" | "background_color" | "background" | "bg" => {
|
||||
app.ui_theme = crate::palette::ui_theme_from_settings(
|
||||
&settings.theme,
|
||||
settings.background_color.as_deref(),
|
||||
);
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
"cost_currency" | "currency" => {
|
||||
@@ -487,6 +488,7 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
|
||||
let display_value = match key.as_str() {
|
||||
"default_mode" | "mode" => settings.default_mode.clone(),
|
||||
"cost_currency" | "currency" => settings.cost_currency.clone(),
|
||||
"theme" | "ui_theme" => settings.theme.clone(),
|
||||
"background_color" | "background" | "bg" => settings
|
||||
.background_color
|
||||
.clone()
|
||||
@@ -580,22 +582,34 @@ fn mode_display_name(mode: AppMode) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle between dark and light theme.
|
||||
pub fn theme(app: &mut App) -> CommandResult {
|
||||
let new_theme = match app.ui_theme.mode {
|
||||
crate::palette::PaletteMode::Dark => {
|
||||
crate::palette::UiTheme::for_mode(crate::palette::PaletteMode::Light)
|
||||
}
|
||||
crate::palette::PaletteMode::Light => {
|
||||
crate::palette::UiTheme::for_mode(crate::palette::PaletteMode::Dark)
|
||||
/// Switch the runtime theme. `/set theme <value> --save` persists it.
|
||||
pub fn theme(app: &mut App, arg: Option<&str>) -> CommandResult {
|
||||
let requested = match arg.map(str::trim).filter(|value| !value.is_empty()) {
|
||||
Some(value) => {
|
||||
let Some(theme) = crate::palette::normalize_theme_name(value) else {
|
||||
return CommandResult::error("Usage: /theme [dark|light|grayscale|system]");
|
||||
};
|
||||
theme
|
||||
}
|
||||
None => match app.ui_theme.mode {
|
||||
crate::palette::PaletteMode::Dark => "light",
|
||||
crate::palette::PaletteMode::Light => "grayscale",
|
||||
crate::palette::PaletteMode::Grayscale => "dark",
|
||||
},
|
||||
};
|
||||
app.ui_theme = new_theme;
|
||||
let label = match new_theme.mode {
|
||||
crate::palette::PaletteMode::Dark => "dark",
|
||||
crate::palette::PaletteMode::Light => "light",
|
||||
};
|
||||
CommandResult::message(format!("Theme switched to {label}."))
|
||||
|
||||
let background = Settings::load()
|
||||
.ok()
|
||||
.and_then(|settings| settings.background_color);
|
||||
app.ui_theme = crate::palette::ui_theme_from_settings(requested, background.as_deref());
|
||||
app.needs_redraw = true;
|
||||
|
||||
let label = crate::palette::theme_label_for_mode(app.ui_theme.mode);
|
||||
if requested == "system" {
|
||||
CommandResult::message(format!("Theme switched to system ({label})."))
|
||||
} else {
|
||||
CommandResult::message(format!("Theme switched to {label}."))
|
||||
}
|
||||
}
|
||||
|
||||
/// Manage workspace-level trust and the per-path allowlist.
|
||||
@@ -1178,6 +1192,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_mode_yolo_sets_all_flags() {
|
||||
let mut app = create_test_app();
|
||||
// Switch to Agent first to guarantee a clean starting state regardless of
|
||||
// user settings on the host machine.
|
||||
let _ = mode(&mut app, Some("agent"));
|
||||
let result = mode(&mut app, Some("yolo"));
|
||||
assert!(result.message.unwrap().contains("Switched to YOLO mode"));
|
||||
assert!(app.allow_shell);
|
||||
@@ -1443,6 +1460,54 @@ mod tests {
|
||||
assert!(saved.contains("cost_currency = \"cny\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn theme_command_accepts_grayscale_arg() {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-theme-command-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
fs::create_dir_all(&temp_root).unwrap();
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
|
||||
let mut app = create_test_app();
|
||||
let result = theme(&mut app, Some("grayscale"));
|
||||
|
||||
assert_eq!(result.message.unwrap(), "Theme switched to grayscale.");
|
||||
assert_eq!(app.ui_theme.mode, crate::palette::PaletteMode::Grayscale);
|
||||
assert!(app.needs_redraw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_theme_save_updates_live_app_and_persists() {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-theme-save-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
fs::create_dir_all(&temp_root).unwrap();
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
|
||||
let mut app = create_test_app();
|
||||
let result = set_config(&mut app, Some("theme grayscale --save"));
|
||||
let msg = result.message.unwrap();
|
||||
|
||||
assert_eq!(msg, "theme = grayscale (saved)");
|
||||
assert_eq!(app.ui_theme.mode, crate::palette::PaletteMode::Grayscale);
|
||||
|
||||
let settings_path = Settings::path().unwrap();
|
||||
let saved = fs::read_to_string(settings_path).unwrap();
|
||||
assert!(saved.contains("theme = \"grayscale\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_approval_mode_valid_values() {
|
||||
let mut app = create_test_app();
|
||||
@@ -1497,7 +1562,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_trust_on_enables_flag() {
|
||||
let mut app = create_test_app();
|
||||
assert!(!app.trust_mode);
|
||||
// Normalize trust state regardless of user settings on the host machine.
|
||||
app.trust_mode = false;
|
||||
let result = trust(&mut app, Some("on"));
|
||||
let msg = result.message.expect("message");
|
||||
assert!(msg.contains("Workspace trust mode enabled"));
|
||||
|
||||
@@ -361,7 +361,14 @@ fn selection_to_text_copies_rendered_transcript_block() {
|
||||
let selected = selection_to_text(&app).expect("selection text");
|
||||
assert!(selected.contains("Note copy system"), "{selected:?}");
|
||||
assert!(selected.contains("copy user"), "{selected:?}");
|
||||
assert!(selected.contains("copy thinking"), "{selected:?}");
|
||||
assert!(
|
||||
!selected.contains("copy thinking"),
|
||||
"raw completed thinking should stay out of live selection text: {selected:?}"
|
||||
);
|
||||
assert!(
|
||||
selected.contains("Ctrl+O"),
|
||||
"selection should keep the reasoning detail affordance: {selected:?}"
|
||||
);
|
||||
assert!(selected.contains("tool output line"), "{selected:?}");
|
||||
assert!(selected.contains("copy assistant"), "{selected:?}");
|
||||
// #1163: tool-card middle lines are rendered with a `│ ` left rail
|
||||
@@ -1930,6 +1937,8 @@ fn event_poll_timeout_has_nonzero_floor() {
|
||||
fn footer_status_line_spans_show_mode_and_model_idle_and_active() {
|
||||
let mut app = create_test_app();
|
||||
app.model = "deepseek-v4-flash".to_string();
|
||||
// Pin Agent mode regardless of user settings on the host machine.
|
||||
let _ = app.set_mode(crate::tui::app::AppMode::Agent);
|
||||
|
||||
let idle = spans_text(&footer_status_line_spans(&app, 60));
|
||||
assert!(idle.contains("agent"));
|
||||
@@ -4054,9 +4063,8 @@ fn open_thinking_pager_finds_thinking_in_active_cell() {
|
||||
// After ThinkingComplete fires, the finalized thinking entry stays in
|
||||
// `app.active_cell` with `streaming = false` until the active cell is
|
||||
// flushed to history (end-of-turn, or when an assistant text arrives).
|
||||
// During that window the transcript still renders the
|
||||
// "thinking collapsed; Ctrl+O opens Activity Detail" affordance from
|
||||
// `render_thinking`, so the handler must reach across the virtual
|
||||
// During that window the transcript still renders the Ctrl+O affordance
|
||||
// from `render_thinking`, so the handler must reach across the virtual
|
||||
// transcript — not just `app.history` — or the promise is a lie.
|
||||
// Regression guard for the v0.8.29 affordance/handler mismatch.
|
||||
let mut app = create_test_app();
|
||||
@@ -4083,10 +4091,14 @@ fn open_thinking_pager_finds_thinking_in_active_cell() {
|
||||
Some(ModalKind::Pager),
|
||||
"pager must open for thinking entries still in active_cell"
|
||||
);
|
||||
let body = pop_pager_body(&mut app);
|
||||
assert!(body.contains("Activity: reasoning timeline"), "{body}");
|
||||
assert!(body.contains("Thinking chunk 1 of 1"), "{body}");
|
||||
assert!(body.contains("deliberating"), "{body}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activity_detail_opens_selected_thinking_chunk() {
|
||||
fn activity_detail_opens_reasoning_timeline_for_selected_thinking() {
|
||||
let mut app = create_test_app();
|
||||
app.history = vec![
|
||||
HistoryCell::Thinking {
|
||||
@@ -4124,17 +4136,19 @@ fn activity_detail_opens_selected_thinking_chunk() {
|
||||
let body = pop_pager_body(&mut app);
|
||||
|
||||
assert!(
|
||||
body.contains("Activity: thinking"),
|
||||
body.contains("Activity: reasoning timeline"),
|
||||
"activity label missing: {body}"
|
||||
);
|
||||
assert!(
|
||||
body.contains("Thinking chunk: 1 of 2"),
|
||||
body.contains("Selected chunk: 1 of 2"),
|
||||
"chunk position missing: {body}"
|
||||
);
|
||||
assert!(body.contains("Thinking chunk 1 of 2 (selected)"), "{body}");
|
||||
assert!(body.contains("Thinking chunk 2 of 2"), "{body}");
|
||||
assert!(body.contains("first chunk reasoning"), "body: {body}");
|
||||
assert!(
|
||||
!body.contains("second chunk reasoning"),
|
||||
"selected chunk should not fall through to latest thinking: {body}"
|
||||
body.contains("second chunk reasoning"),
|
||||
"timeline should include the whole session's thinking: {body}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4949,7 +4963,7 @@ fn composer_arrows_scroll_empty_up() {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
assert_eq!(app.viewport.pending_scroll_delta, -1);
|
||||
assert_eq!(app.viewport.pending_scroll_delta, -3);
|
||||
assert!(app.input.is_empty());
|
||||
}
|
||||
|
||||
@@ -4964,7 +4978,7 @@ fn composer_arrows_scroll_empty_down() {
|
||||
false,
|
||||
false,
|
||||
));
|
||||
assert_eq!(app.viewport.pending_scroll_delta, 1);
|
||||
assert_eq!(app.viewport.pending_scroll_delta, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user