use gwm::tui::theme::{parse_color, preset_names, Theme};
use ratatui::style::Color;
#[test]
fn default_theme_matches_pre_issue_33_scheme() {
let t = Theme::default();
assert_eq!(t.focus, Color::Cyan);
assert_eq!(t.accent, Color::Cyan);
assert_eq!(t.branch, Color::Green);
assert_eq!(t.clean, Color::Green);
assert_eq!(t.dirty, Color::Yellow);
assert_eq!(t.main, Color::Yellow);
assert_eq!(t.locked, Color::Magenta);
assert_eq!(t.prunable, Color::Red);
assert_eq!(t.muted, Color::DarkGray);
assert_eq!(t.name, Color::White, "name role default → White");
assert_eq!(t.path, Color::Gray, "path role default → Gray");
assert_eq!(t.staged, Color::Cyan, "staged role default → Cyan");
assert_eq!(t.modified, Color::Yellow, "modified role default → Yellow");
assert_eq!(t.untracked, Color::Green, "untracked role default → Green");
}
#[test]
fn apply_override_replaces_git_status_roles() {
let mut t = Theme::default();
t.apply_override("staged", "magenta").unwrap();
t.apply_override("modified", "#123456").unwrap();
t.apply_override("untracked", "200").unwrap();
assert_eq!(t.staged, Color::Magenta, "staged override wins");
assert_eq!(t.modified, Color::Rgb(0x12, 0x34, 0x56), "modified override wins");
assert_eq!(t.untracked, Color::Indexed(200), "untracked override wins");
assert_eq!(t.accent, Color::Cyan, "accent untouched by staged override");
assert_eq!(t.dirty, Color::Yellow, "dirty untouched by modified override");
assert_eq!(t.clean, Color::Green, "clean untouched by untracked override");
}
#[test]
fn apply_override_replaces_name_and_path_roles() {
let mut t = Theme::default();
t.apply_override("name", "#abcdef").unwrap();
t.apply_override("path", "240").unwrap();
assert_eq!(t.name, Color::Rgb(0xab, 0xcd, 0xef), "name override wins");
assert_eq!(t.path, Color::Indexed(240), "path override wins");
assert_eq!(t.focus, Color::Cyan);
assert_eq!(t.muted, Color::DarkGray);
}
#[test]
fn claude_dark_preset_ports_the_palette() {
let t = Theme::preset("claude-dark").expect("claude-dark preset must resolve");
assert_eq!(t.accent, Color::Rgb(0xD4, 0x82, 0x5D), "primary orange");
assert_eq!(t.focus, Color::Rgb(0xC1, 0x5F, 0x3C), "orange-dark / focused borders");
assert_eq!(t.branch, Color::Rgb(0x86, 0xE8, 0x9A), "success green");
assert_eq!(t.clean, Color::Rgb(0x86, 0xE8, 0x9A), "success green");
assert_eq!(t.dirty, Color::Rgb(0xFF, 0xDF, 0x61), "warning yellow");
assert_eq!(t.main, Color::Rgb(0xFF, 0xDF, 0x61), "warning yellow");
assert_eq!(t.locked, Color::Rgb(0xC7, 0x9B, 0xFF), "special purple");
assert_eq!(t.prunable, Color::Rgb(0xFF, 0x7A, 0x7A), "error red");
assert_eq!(t.name, Color::Rgb(0xE0, 0xE0, 0xE0), "--text (primary text)");
assert_eq!(t.path, Color::Rgb(0xB0, 0xB0, 0xB0), "--text-dim (secondary text)");
assert_eq!(Theme::preset("claude"), Some(t), "`claude` aliases `claude-dark`");
}
#[test]
fn claude_dark_is_listed_among_presets() {
assert!(
preset_names().contains(&"claude-dark"),
"claude-dark must be discoverable via `gwm theme list`"
);
}
#[test]
fn preset_names_lists_at_least_one_builtin() {
let names = preset_names();
assert!(!names.is_empty(), "at least one built-in preset must be shipped");
for name in names {
assert!(
Theme::preset(name).is_some(),
"preset {:?} listed in preset_names() must resolve",
name
);
}
}
#[test]
fn unknown_preset_returns_none() {
assert!(Theme::preset("does-not-exist").is_none());
}
#[test]
fn preset_produces_a_theme_different_from_default() {
let names = preset_names();
let any_differs = names.iter().any(|&name| {
let preset = Theme::preset(name).unwrap();
preset != Theme::default()
});
assert!(
any_differs,
"at least one preset must differ from the default (otherwise the framework ships no actual themes)"
);
}
#[test]
fn apply_override_replaces_a_single_role() {
let mut t = Theme::default();
t.apply_override("focus", "red").unwrap();
assert_eq!(t.focus, Color::Red, "override must win for the targeted role");
assert_eq!(t.accent, Color::Cyan);
assert_eq!(t.branch, Color::Green);
}
#[test]
fn apply_override_rejects_unknown_role() {
let mut t = Theme::default();
let err = t.apply_override("phantom", "red").unwrap_err();
assert!(
err.to_string().to_lowercase().contains("phantom"),
"expected message to name the bad role, got: {err}"
);
}
#[test]
fn apply_override_rejects_unparsable_color() {
let mut t = Theme::default();
let err = t.apply_override("focus", "not_a_color").unwrap_err();
assert!(
err.to_string().to_lowercase().contains("not_a_color"),
"expected message to name the bad color, got: {err}"
);
}
#[test]
fn parse_named_color() {
assert_eq!(parse_color("cyan").unwrap(), Color::Cyan);
assert_eq!(parse_color("Cyan").unwrap(), Color::Cyan, "case-insensitive");
assert_eq!(parse_color("red").unwrap(), Color::Red);
assert_eq!(parse_color("dark_gray").unwrap(), Color::DarkGray);
}
#[test]
fn parse_indexed_color() {
let c = parse_color("220").unwrap();
assert_eq!(c, Color::Indexed(220));
}
#[test]
fn parse_hex_color() {
let c = parse_color("#89b4fa").unwrap();
assert_eq!(c, Color::Rgb(0x89, 0xb4, 0xfa));
}
#[test]
fn parse_rejects_garbage() {
assert!(parse_color("").is_err());
assert!(parse_color("not_a_color").is_err());
assert!(parse_color("#zzz").is_err());
assert!(parse_color("256").is_err()); }