use std::fs;
use std::path::PathBuf;
use rio_theme::{emit, resolve_theme, Color, ThemeInput};
fn golden_path(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("golden")
.join(format!("{name}.css"))
}
fn check(name: &str, input: ThemeInput) {
let tokens = resolve_theme(input);
let actual = emit::emit(&tokens);
let path = golden_path(name);
if std::env::var("RIO_THEME_BLESS").is_ok() {
fs::write(&path, &actual)
.unwrap_or_else(|e| panic!("could not bless {}: {e}", path.display()));
return;
}
let expected = fs::read_to_string(&path).unwrap_or_else(|e| {
panic!(
"could not read golden {}: {e}\n\n\
If this is a new fixture, bless it with:\n\
RIO_THEME_BLESS=1 cargo test -p rio-theme --test engine\n\n\
--- engine output ---\n{actual}",
path.display()
)
});
assert_eq!(
actual, expected,
"\ngolden {} drifted.\n\nIf this change is intentional, re-bless with:\n RIO_THEME_BLESS=1 cargo test -p rio-theme --test engine\n\n--- expected ---\n{expected}\n--- actual ---\n{actual}",
path.display()
);
}
fn hex(s: &str) -> Color {
Color::from_hex(s).expect("test fixture should parse")
}
#[test]
fn empty_input_uses_default_brand() {
check("empty", ThemeInput::empty());
}
#[test]
fn single_calm_brand() {
check(
"calm_single",
ThemeInput {
brand_colors: vec![hex("#0d9488")],
},
);
}
#[test]
fn single_neon_brand_is_split_and_tamed() {
check(
"neon_single",
ThemeInput {
brand_colors: vec![hex("#39ff14")],
},
);
}
#[test]
fn two_near_identical_darks() {
check(
"two_dark",
ThemeInput {
brand_colors: vec![hex("#0a1a2e"), hex("#101a2c")],
},
);
}
#[test]
fn five_color_palette() {
check(
"five_palette",
ThemeInput {
brand_colors: vec![
hex("#3f6089"),
hex("#c9572e"),
hex("#2e7d5b"),
hex("#8a4cb4"),
hex("#d4a017"),
],
},
);
}
#[test]
fn pipeline_is_deterministic() {
let input = ThemeInput {
brand_colors: vec![hex("#3f6089"), hex("#c9572e")],
};
let a = emit::emit(&resolve_theme(input.clone()));
let b = emit::emit(&resolve_theme(input));
assert_eq!(a, b);
}