from cursor_flasher.config import ( Config, StyleConfig, ThemeStyles, load_config, ) class TestDefaultConfig: def test_dark_running_defaults(self): c = Config() assert c.dark.running.color == "#FF9500" assert c.dark.running.width == 4 assert c.dark.running.duration == 1.5 assert c.dark.running.opacity == 0.85 assert c.dark.running.pulse_speed == 1.5 assert c.dark.running.sound == "Glass" assert c.dark.running.volume == 0.5 def test_dark_completed_defaults(self): c = Config() assert c.dark.completed.color == "#FF9500" assert c.dark.completed.width == 4 assert c.dark.completed.sound == "" assert c.dark.completed.volume == 0.0 def test_light_running_defaults(self): c = Config() assert c.light.running.color == "#FF9500" assert c.light.running.sound == "Glass" def test_light_completed_defaults(self): c = Config() assert c.light.completed.sound == "" assert c.light.completed.volume == 0.0 def test_theme_defaults_to_auto(self): c = Config() assert c.theme == "auto" def test_has_approval_tools(self): c = Config() assert c.approval_tools == ["Shell", "Write", "Delete"] def test_has_cooldown(self): c = Config() assert c.cooldown == 2.0 def test_has_flash_mode(self): c = Config() assert c.flash_mode == "screen" class TestActiveStyles: def test_auto_returns_dark_when_system_dark(self): c = Config(theme="auto") dark_styles = ThemeStyles(running=StyleConfig(color="#111111")) light_styles = ThemeStyles(running=StyleConfig(color="#EEEEEE")) c.dark = dark_styles c.light = light_styles assert c.active_styles("dark").running.color == "#111111" def test_auto_returns_light_when_system_light(self): c = Config(theme="auto") dark_styles = ThemeStyles(running=StyleConfig(color="#111111")) light_styles = ThemeStyles(running=StyleConfig(color="#EEEEEE")) c.dark = dark_styles c.light = light_styles assert c.active_styles("light").running.color == "#EEEEEE" def test_explicit_dark_ignores_system(self): c = Config(theme="dark") c.dark = ThemeStyles(running=StyleConfig(color="#111111")) c.light = ThemeStyles(running=StyleConfig(color="#EEEEEE")) assert c.active_styles("light").running.color == "#111111" def test_explicit_light_ignores_system(self): c = Config(theme="light") c.dark = ThemeStyles(running=StyleConfig(color="#111111")) c.light = ThemeStyles(running=StyleConfig(color="#EEEEEE")) assert c.active_styles("dark").running.color == "#EEEEEE" def test_active_styles_includes_completed(self): c = Config(theme="dark") c.dark = ThemeStyles(completed=StyleConfig(color="#AA0000")) assert c.active_styles("light").completed.color == "#AA0000" class TestLoadConfig: def test_missing_file_returns_defaults(self, tmp_path): c = load_config(tmp_path / "nope.yaml") assert c == Config() def test_empty_file_returns_defaults(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("") c = load_config(p) assert c == Config() def test_loads_theme(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("theme: dark\n") c = load_config(p) assert c.theme == "dark" def test_invalid_theme_uses_default(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("theme: neon\n") c = load_config(p) assert c.theme == "auto" def test_loads_dark_running_overrides(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "dark:\n" " running:\n" " color: '#00FF00'\n" " duration: 2.0\n" " sound: Ping\n" ) c = load_config(p) assert c.dark.running.color == "#00FF00" assert c.dark.running.duration == 2.0 assert c.dark.running.sound == "Ping" assert c.dark.running.width == 4 def test_loads_dark_completed_overrides(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "dark:\n" " completed:\n" " color: '#0000FF'\n" " sound: Hero\n" " volume: 0.8\n" ) c = load_config(p) assert c.dark.completed.color == "#0000FF" assert c.dark.completed.sound == "Hero" assert c.dark.completed.volume == 0.8 def test_loads_light_running_overrides(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "light:\n" " running:\n" " color: '#3B82F6'\n" " opacity: 0.9\n" ) c = load_config(p) assert c.light.running.color == "#3B82F6" assert c.light.running.opacity == 0.9 assert c.light.running.width == 4 def test_missing_dark_section_uses_defaults(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "light:\n" " running:\n" " color: '#FFFFFF'\n" ) c = load_config(p) assert c.dark == ThemeStyles() def test_missing_running_within_theme_uses_defaults(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "dark:\n" " completed:\n" " color: '#FF0000'\n" ) c = load_config(p) assert c.dark.running == StyleConfig() assert c.dark.completed.color == "#FF0000" def test_loads_flash_mode(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("flash:\n mode: allscreens\n") c = load_config(p) assert c.flash_mode == "allscreens" def test_loads_general_overrides(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("general:\n cooldown: 5.0\n approval_delay: 3.0\n") c = load_config(p) assert c.cooldown == 5.0 assert c.approval_delay == 3.0 def test_loads_approval_tools(self, tmp_path): p = tmp_path / "config.yaml" p.write_text("approval_tools:\n - Shell\n - MCP\n") c = load_config(p) assert c.approval_tools == ["Shell", "MCP"] def test_full_config(self, tmp_path): p = tmp_path / "config.yaml" p.write_text( "theme: light\n" "dark:\n" " running:\n" " color: '#FF0000'\n" " width: 6\n" " opacity: 0.9\n" " pulse_speed: 2.0\n" " sound: Glass\n" " volume: 0.8\n" " completed:\n" " color: '#00FF00'\n" " sound: ''\n" "light:\n" " running:\n" " color: '#3B82F6'\n" " width: 3\n" " completed:\n" " color: '#22C55E'\n" " duration: 2.0\n" "flash:\n" " mode: window\n" "general:\n" " approval_delay: 1.0\n" " cooldown: 3.0\n" "approval_tools:\n" " - Shell\n" ) c = load_config(p) assert c.theme == "light" assert c.dark.running.color == "#FF0000" assert c.dark.running.width == 6 assert c.dark.running.opacity == 0.9 assert c.dark.running.pulse_speed == 2.0 assert c.dark.running.sound == "Glass" assert c.dark.running.volume == 0.8 assert c.dark.completed.color == "#00FF00" assert c.dark.completed.sound == "" assert c.light.running.color == "#3B82F6" assert c.light.running.width == 3 assert c.light.completed.color == "#22C55E" assert c.light.completed.duration == 2.0 assert c.flash_mode == "window" assert c.approval_delay == 1.0 assert c.cooldown == 3.0 assert c.approval_tools == ["Shell"]