use super::*;
fn all_presets() -> Vec<Theme> {
vec![
Theme::windows_light(),
Theme::windows_dark(),
Theme::macos_light(),
Theme::macos_dark(),
Theme::device(),
Theme::skade_vinter(),
Theme::neutral(),
]
}
#[test]
fn wcag_gate_passes_for_every_preset_light_and_dark() {
for t in all_presets() {
let p = &t.palette;
let body = p.body_text_contrast();
assert!(body >= 4.5, "{}: body text contrast {body:.2} < 4.5", t.name);
let dim = p.dim_text_contrast();
assert!(dim >= 3.0, "{}: dim text contrast {dim:.2} < 3.0", t.name);
let ui = p.ui_boundary_contrast();
assert!(ui >= 3.0, "{}: UI boundary contrast {ui:.2} < 3.0", t.name);
let status = p.status_contrast();
assert!(status >= 4.5, "{}: status on-colour contrast {status:.2} < 4.5", t.name);
}
}
#[test]
fn every_preset_fully_populates_and_is_distinct() {
let names = Theme::preset_names();
let mut sorted = names.clone();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), names.len(), "preset names must be unique");
for n in &names {
assert_eq!(Theme::by_name(n).map(|t| t.name), Some(n.clone()));
}
assert_eq!(Theme::by_name("Windows Dark").map(|t| t.name), Some("windows-dark".into()));
}
#[test]
fn theme_serde_round_trips_with_a_remapped_key() {
let mut t = Theme::macos_light();
t.keymap.set(Action::Find, egui::KeyboardShortcut::new(egui::Modifiers::COMMAND | egui::Modifiers::SHIFT, egui::Key::F));
let json = serde_json::to_string_pretty(&t).unwrap();
let back: Theme = serde_json::from_str(&json).unwrap();
assert_eq!(t, back, "theme must serde round-trip exactly");
assert_eq!(
back.keymap.shortcut(Action::Find),
Some(egui::KeyboardShortcut::new(egui::Modifiers::COMMAND | egui::Modifiers::SHIFT, egui::Key::F))
);
}
#[test]
fn apply_installs_style_and_publishes_legacy_palette() {
let theme = Theme::windows_dark();
let ctx = egui::Context::default();
theme.apply(&ctx);
let style = ctx.style();
assert_eq!(style.spacing.item_spacing, theme.metrics.item_spacing_vec());
assert_eq!(style.spacing.scroll.bar_width, theme.scroll.bar_width);
assert!(style.visuals.override_text_color.is_none(), "§27: no global override_text_color");
let mut got = "";
let _ = ctx.run(egui::RawInput::default(), |ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
got = crate::theme(ui).name;
});
});
assert_eq!(got, "windows-dark");
}
#[test]
fn from_os_falls_back_to_windows_on_linux() {
use egui::os::OperatingSystem as Os;
assert_eq!(Theme::from_os(Os::Mac).name, "macos-dark");
assert_eq!(Theme::from_os(Os::Windows).name, "windows-dark");
assert_eq!(Theme::from_os(Os::Nix).name, "windows-dark", "Linux → documented default");
}
#[test]
fn device_has_effects_off() {
let d = Theme::device();
assert_eq!(d.effects, EffectsPolicy::None);
assert!(!d.effects.allows_transparency());
assert_eq!(d.surface, SurfaceSpec::Opaque);
assert_eq!(d.motion.duration, 0.0, "no decorative motion on Device");
}
#[test]
fn full_effects_presets_carry_a_non_default_glass_surface() {
for t in [Theme::windows_dark(), Theme::macos_dark(), Theme::skade_vinter()] {
assert_eq!(t.effects, EffectsPolicy::Full, "{}: showcase preset is full-effects", t.name);
assert_ne!(t.surface, SurfaceSpec::Opaque, "{}: showcase declares a non-default glass surface", t.name);
assert!(t.surface.tint_color().is_some(), "{}: glass surface paints a tint", t.name);
assert_ne!(t.surface.resolve(EffectsPolicy::Full), SurfaceSpec::Opaque, "{}: glass under Full", t.name);
assert_eq!(
t.surface.resolve(EffectsPolicy::None),
SurfaceSpec::Opaque,
"{}: glass degrades to opaque under Device",
t.name
);
}
assert_ne!(Theme::windows_light().surface, SurfaceSpec::Opaque, "windows-light inherits glass");
assert_ne!(Theme::macos_light().surface, SurfaceSpec::Opaque, "macos-light inherits glass");
}
#[test]
fn win_mac_parity_same_semantic_surface_different_chrome() {
let w = Theme::windows_dark();
let m = Theme::macos_dark();
assert!(w.palette.body_text_contrast() >= 4.5 && m.palette.body_text_contrast() >= 4.5);
for a in Action::ALL {
assert!(w.keymap.shortcut(*a).is_some() && m.keymap.shortcut(*a).is_some());
}
assert!(!w.scroll.floating && m.scroll.floating, "Windows solid vs macOS floating scrollbars");
assert_ne!(w.keymap.shortcut(Action::LineStart), m.keymap.shortcut(Action::LineStart), "line nav differs");
assert!(m.metrics.corner_radius >= w.metrics.corner_radius, "macOS radii softer");
assert_eq!(w.keymap.shortcut(Action::Copy), m.keymap.shortcut(Action::Copy));
}
#[test]
fn mac_and_windows_native_feel_differ_as_data() {
let w = Theme::windows_dark();
let m = Theme::macos_dark();
assert!(m.metrics.corner_radius > w.metrics.corner_radius, "macOS radii softer");
assert!(m.metrics.window_corner_radius > w.metrics.window_corner_radius, "macOS window radius softer");
let tint_alpha = |t: &Theme| t.surface.tint_color().map(|c| c.a()).unwrap_or(255);
let (mac_a, win_a) = (tint_alpha(&m), tint_alpha(&w));
assert!(mac_a < win_a, "vibrancy (mac α {mac_a}) is more translucent than Mica (win α {win_a})");
assert!(m.motion.duration > w.motion.duration, "macOS motion is longer/gentler");
assert_ne!(m.motion.curve, w.motion.curve, "motion curve differs per platform");
assert_eq!(m.motion.curve, crate::effects::Curve::EaseOutBack, "macOS = spring overshoot");
assert_eq!(w.motion.curve, crate::effects::Curve::EaseInOutCubic, "Windows = connected ease");
assert_ne!(m.typography.ui_font, w.typography.ui_font, "SF vs Segoe UI");
assert_eq!(m.typography.ui_font, UiFont::SanFrancisco);
assert_eq!(w.typography.ui_font, UiFont::SegoeUi);
assert!(w.native.reveal_highlight && !m.native.reveal_highlight, "reveal is Windows-only");
assert!(m.native.rubber_band && !w.native.rubber_band, "rubber-band is macOS-only");
assert!(m.native.window_controls.on_left() && !w.native.window_controls.on_left());
assert!(m.native.focus_ring.glow && !w.native.focus_ring.glow, "mac ring glows; win rect is crisp");
assert!(m.native.accent_tint > w.native.accent_tint, "macOS leans on accent");
assert!(w.native.elevation > m.native.elevation, "Windows elevation shadow heavier");
}
#[test]
fn mac_frosted_and_windows_reveal_are_live_under_their_presets() {
use crate::effects::RevealHighlight;
let m = Theme::macos_dark();
assert!(matches!(m.surface, SurfaceSpec::Frosted { .. }), "macOS preset is frosted glass");
assert_eq!(m.surface.resolve(EffectsPolicy::Full), m.surface, "frosted under Full = real blur");
assert!(!m.native.reveal_highlight);
let w = Theme::windows_dark();
assert!(w.native.reveal_highlight, "Windows preset arms the reveal highlight");
let reveal = RevealHighlight::new(w.palette.accent.to_color32());
let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(120.0, 32.0));
assert!(reveal.proximity(rect, Some(egui::pos2(60.0, 16.0))) > 0.0, "reveal lights up on hover");
assert_eq!(reveal.proximity(rect, None), 0.0, "reveal dark with no pointer");
}
#[test]
fn for_platform_selects_matching_preset() {
use crate::look::Platform;
assert_eq!(Theme::for_platform(Platform::Mac).name, "macos-dark");
assert_eq!(Theme::for_platform(Platform::Windows).name, "windows-dark");
assert_eq!(Theme::for_platform(Platform::Neutral).name, "neutral-dark");
assert_eq!(Theme::for_platform(Platform::Mac).native.platform, Platform::Mac);
assert_eq!(Theme::for_platform(Platform::Windows).native.platform, Platform::Windows);
assert_eq!(Theme::for_platform(Platform::Neutral).native.platform, Platform::Neutral);
let n = Theme::neutral();
assert_eq!(n.effects, EffectsPolicy::Full, "neutral is premium full-effects");
assert!(!n.native.reveal_highlight && !n.native.rubber_band, "no OS-specific chrome");
assert!(matches!(n.native.window_controls, WindowControls::None));
}
#[test]
fn legacy_palette_bridge_carries_semantic_roles() {
let t = Theme::windows_dark();
let legacy = t.to_legacy_palette();
assert_eq!(legacy.bg, t.palette.surface.to_color32());
assert_eq!(legacy.accent, t.palette.accent.to_color32());
assert_eq!(legacy.text, t.palette.on_surface.to_color32());
assert_eq!(legacy.glow, t.palette.glow.to_color32());
}