use saorsa_tui::Color;
use saorsa_tui::tcss::theme::{
Theme, ThemeManager, builtin_dark, builtin_light, register_all_themes,
};
use saorsa_tui::tcss::value::CssValue;
use saorsa_tui::tcss::variable::VariableMap;
#[test]
fn register_and_retrieve_all_builtin_themes() {
let manager = ThemeManager::with_defaults();
assert!(manager.has_theme("dark"));
assert!(manager.has_theme("light"));
assert!(manager.has_theme("catppuccin-mocha"));
assert!(manager.has_theme("catppuccin-macchiato"));
assert!(manager.has_theme("catppuccin-frappe"));
assert!(manager.has_theme("catppuccin-latte"));
assert!(manager.has_theme("dracula"));
assert!(manager.has_theme("dracula-light"));
assert!(manager.has_theme("nord"));
assert!(manager.has_theme("solarized-dark"));
assert!(manager.has_theme("solarized-light"));
assert!(manager.active_theme().is_none());
let names = manager.theme_names();
assert!(names.contains(&"dark"));
assert!(names.contains(&"catppuccin-mocha"));
}
#[test]
fn apply_theme_colors_to_variables() {
let mut manager = ThemeManager::new();
manager.register(builtin_dark());
let result = manager.set_active("dark");
assert!(result.is_ok());
let global = VariableMap::new();
let env = manager.build_environment(&global);
let fg = env.resolve("fg");
assert!(fg.is_some());
match fg {
Some(CssValue::Color(Color::Named(_))) => (),
_ => panic!("expected named color for fg"),
}
let bg = env.resolve("bg");
assert!(bg.is_some());
match bg {
Some(CssValue::Color(Color::Rgb { .. })) => (),
_ => panic!("expected RGB color for bg"),
}
let primary = env.resolve("primary");
assert!(primary.is_some());
match primary {
Some(CssValue::Color(Color::Rgb { .. })) => (),
_ => panic!("expected RGB color for primary"),
}
let error = env.resolve("error");
assert!(error.is_some());
match error {
Some(CssValue::Color(Color::Rgb { .. })) => (),
_ => panic!("expected RGB color for error"),
}
let border = env.resolve("border");
assert!(border.is_some());
match border {
Some(CssValue::Color(Color::Rgb { .. })) => (),
_ => panic!("expected RGB color for border"),
}
}
#[test]
fn theme_switching_at_runtime() {
let mut manager = ThemeManager::new();
manager.register(builtin_dark());
manager.register(builtin_light());
assert!(manager.active_theme().is_none());
let result = manager.set_active("dark");
assert!(result.is_ok());
assert_eq!(manager.active_name(), Some("dark"));
assert!(manager.active_theme().is_some());
let dark_theme = manager.active_theme();
assert_eq!(dark_theme.map(|t| t.name()), Some("dark"));
let result = manager.set_active("light");
assert!(result.is_ok());
assert_eq!(manager.active_name(), Some("light"));
let light_theme = manager.active_theme();
assert_eq!(light_theme.map(|t| t.name()), Some("light"));
let result = manager.set_active("nonexistent");
assert!(result.is_err());
assert_eq!(manager.active_name(), Some("light"));
}
#[test]
fn light_vs_dark_variant_detection() {
let mut manager = ThemeManager::with_defaults();
assert!(manager.has_theme("dark"));
let result = manager.set_active("dark");
assert!(result.is_ok());
let dark_theme = manager.active_theme();
assert!(dark_theme.is_some());
match dark_theme.map(|t| t.variables()) {
Some(dark_vars) => {
assert!(dark_vars.contains("fg"));
assert!(dark_vars.contains("bg"));
}
None => panic!("expected dark theme to have variables"),
}
assert!(manager.has_theme("light"));
let result = manager.set_active("light");
assert!(result.is_ok());
let light_theme = manager.active_theme();
assert!(light_theme.is_some());
match light_theme.map(|t| t.variables()) {
Some(light_vars) => {
assert!(light_vars.contains("fg"));
assert!(light_vars.contains("bg"));
}
None => panic!("expected light theme to have variables"),
}
let result = manager.set_active("catppuccin-latte");
assert!(result.is_ok());
let latte = manager.active_theme();
assert!(latte.is_some());
let latte_text = latte
.and_then(|t| t.variables().get("text"))
.and_then(|v| match v {
CssValue::Color(Color::Rgb { r, g, b }) => Some((*r, *g, *b)),
_ => None,
});
let latte_bg = latte
.and_then(|t| t.variables().get("base"))
.and_then(|v| match v {
CssValue::Color(Color::Rgb { r, g, b }) => Some((*r, *g, *b)),
_ => None,
});
match (latte_text, latte_bg) {
(Some((tr, tg, tb)), Some((br, bg, bb))) => {
assert!(
tr < br && tg < bg && tb < bb,
"light theme should have dark text on light background"
);
}
_ => panic!("expected RGB colors for latte theme"),
}
let result = manager.set_active("catppuccin-mocha");
assert!(result.is_ok());
let mocha = manager.active_theme();
assert!(mocha.is_some());
let mocha_text = mocha
.and_then(|t| t.variables().get("text"))
.and_then(|v| match v {
CssValue::Color(Color::Rgb { r, g, b }) => Some((*r, *g, *b)),
_ => None,
});
let mocha_bg = mocha
.and_then(|t| t.variables().get("base"))
.and_then(|v| match v {
CssValue::Color(Color::Rgb { r, g, b }) => Some((*r, *g, *b)),
_ => None,
});
match (mocha_text, mocha_bg) {
(Some((tr, tg, tb)), Some((br, bg, bb))) => {
assert!(
tr > br && tg > bg && tb > bb,
"dark theme should have light text on dark background"
);
}
_ => panic!("expected RGB colors for mocha theme"),
}
}
#[test]
fn custom_theme_registration() {
let mut manager = ThemeManager::new();
let mut vars = VariableMap::new();
vars.set(
"fg",
CssValue::Color(Color::Rgb {
r: 255,
g: 0,
b: 255,
}),
);
vars.set("bg", CssValue::Color(Color::Rgb { r: 0, g: 0, b: 0 }));
vars.set(
"primary",
CssValue::Color(Color::Rgb { r: 0, g: 255, b: 0 }),
);
let custom = Theme::with_variables("custom-theme", vars);
manager.register(custom);
assert!(manager.has_theme("custom-theme"));
let result = manager.set_active("custom-theme");
assert!(result.is_ok());
assert_eq!(manager.active_name(), Some("custom-theme"));
let global = VariableMap::new();
let env = manager.build_environment(&global);
let fg = env.resolve("fg");
match fg {
Some(CssValue::Color(Color::Rgb {
r: 255,
g: 0,
b: 255,
})) => (),
_ => panic!("expected custom fg color"),
}
let primary = env.resolve("primary");
match primary {
Some(CssValue::Color(Color::Rgb { r: 0, g: 255, b: 0 })) => (),
_ => panic!("expected custom primary color"),
}
}
#[test]
fn theme_color_slot_access() {
let mut manager = ThemeManager::with_defaults();
let theme_names = vec![
"dark",
"light",
"catppuccin-mocha",
"dracula",
"nord",
"solarized-dark",
];
for name in theme_names {
let result = manager.set_active(name);
assert!(result.is_ok(), "theme {name} should exist");
let theme = manager.active_theme();
assert!(theme.is_some(), "theme {name} should be active");
match theme.map(|t| t.variables()) {
Some(vars) => {
assert!(vars.contains("fg"), "{name} should have fg");
assert!(vars.contains("bg"), "{name} should have bg");
let common_slots = ["primary", "error", "border"];
for slot in &common_slots {
let has_slot = vars.contains(slot);
assert!(
has_slot,
"{name} should have {slot} (or equivalent semantic color)"
);
}
}
None => panic!("{name} should have variables"),
}
}
}
#[test]
fn theme_manager_list_operations() {
let manager = ThemeManager::with_defaults();
let names = manager.theme_names();
assert!(names.len() >= 11);
assert!(names.contains(&"dark"));
assert!(names.contains(&"light"));
assert!(names.contains(&"catppuccin-mocha"));
assert!(names.contains(&"dracula"));
assert!(names.contains(&"nord"));
assert!(names.contains(&"solarized-dark"));
for name in &names {
assert!(
manager.has_theme(name),
"has_theme should return true for {name}"
);
}
}
#[test]
fn theme_removal_and_active_management() {
let mut manager = ThemeManager::new();
manager.register(builtin_dark());
manager.register(builtin_light());
let result = manager.set_active("dark");
assert!(result.is_ok());
assert_eq!(manager.active_name(), Some("dark"));
assert!(manager.remove("light"));
assert!(!manager.has_theme("light"));
assert_eq!(manager.active_name(), Some("dark"));
assert!(manager.remove("dark"));
assert!(!manager.has_theme("dark"));
assert!(manager.active_name().is_none());
assert!(manager.active_theme().is_none());
assert!(!manager.remove("nonexistent"));
}
#[test]
fn variable_environment_layering_with_themes() {
let mut manager = ThemeManager::new();
manager.register(builtin_dark());
let mut global = VariableMap::new();
global.set(
"fg",
CssValue::Color(Color::Rgb {
r: 100,
g: 100,
b: 100,
}),
);
global.set("custom", CssValue::Integer(42));
let env_no_theme = manager.build_environment(&global);
assert_eq!(env_no_theme.resolve("custom"), Some(&CssValue::Integer(42)));
match env_no_theme.resolve("fg") {
Some(CssValue::Color(Color::Rgb {
r: 100,
g: 100,
b: 100,
})) => (),
_ => panic!("expected global fg color"),
}
let result = manager.set_active("dark");
assert!(result.is_ok());
let env_with_theme = manager.build_environment(&global);
let fg = env_with_theme.resolve("fg");
assert!(fg.is_some());
match fg {
Some(CssValue::Color(Color::Rgb {
r: 100,
g: 100,
b: 100,
})) => panic!("expected theme fg to override global"),
Some(CssValue::Color(_)) => (),
_ => panic!("expected color for fg"),
}
assert_eq!(
env_with_theme.resolve("custom"),
Some(&CssValue::Integer(42))
);
assert!(env_with_theme.resolve("primary").is_some());
assert!(env_with_theme.resolve("error").is_some());
}
#[test]
fn all_themes_have_minimum_variables() {
let mut manager = ThemeManager::with_defaults();
let theme_names: Vec<String> = manager
.theme_names()
.iter()
.map(|s| s.to_string())
.collect();
for name in theme_names {
let result = manager.set_active(&name);
assert!(result.is_ok(), "theme {name} should exist");
let theme = manager.active_theme();
assert!(theme.is_some(), "theme {name} should be active");
match theme.map(|t| t.variables()) {
Some(vars) => {
assert!(
vars.len() >= 3,
"{name} should have at least 3 variables (found {})",
vars.len()
);
assert!(
vars.contains("fg") || vars.contains("foreground"),
"{name} should have foreground color"
);
assert!(
vars.contains("bg") || vars.contains("background"),
"{name} should have background color"
);
}
None => panic!("{name} should have variables"),
}
}
}
#[test]
fn theme_registration_is_idempotent() {
let mut manager = ThemeManager::new();
manager.register(builtin_dark());
assert!(manager.has_theme("dark"));
let count_after_first = manager.theme_names().len();
assert_eq!(count_after_first, 1);
manager.register(builtin_dark());
assert!(manager.has_theme("dark"));
let count_after_second = manager.theme_names().len();
assert_eq!(count_after_second, 1);
let mut vars = VariableMap::new();
vars.set("custom", CssValue::Integer(999));
let custom_dark = Theme::with_variables("dark", vars);
manager.register(custom_dark);
assert_eq!(manager.theme_names().len(), 1);
let result = manager.set_active("dark");
assert!(result.is_ok());
let theme = manager.active_theme();
assert!(theme.is_some());
match theme {
Some(t) => assert!(t.variables().contains("custom")),
None => panic!("expected custom dark theme to be active"),
}
}
#[test]
fn default_theme_manager_has_all_builtins() {
let manager = ThemeManager::with_defaults();
let names = manager.theme_names();
assert_eq!(names.len(), 11);
assert!(manager.has_theme("dark"));
assert!(manager.has_theme("light"));
assert!(manager.has_theme("catppuccin-mocha"));
assert!(manager.has_theme("catppuccin-macchiato"));
assert!(manager.has_theme("catppuccin-frappe"));
assert!(manager.has_theme("catppuccin-latte"));
assert!(manager.has_theme("dracula"));
assert!(manager.has_theme("dracula-light"));
assert!(manager.has_theme("nord"));
assert!(manager.has_theme("solarized-dark"));
assert!(manager.has_theme("solarized-light"));
}
#[test]
fn register_all_themes_function() {
let mut manager = ThemeManager::new();
assert!(manager.theme_names().is_empty());
register_all_themes(&mut manager);
assert_eq!(manager.theme_names().len(), 11);
assert!(manager.has_theme("dark"));
assert!(manager.has_theme("catppuccin-mocha"));
assert!(manager.has_theme("dracula"));
assert!(manager.has_theme("nord"));
assert!(manager.has_theme("solarized-dark"));
}