Add msg_gradient() helper function to render.py that applies message (ntfy) gradient using the active theme's message_gradient property. This replaces hardcoded magenta gradient in scroll.py with a theme-aware approach that uses complementary colors from the active theme. - Add msg_gradient(rows, offset) helper in render.py with fallback to default magenta gradient when no theme is active - Update scroll.py imports to use msg_gradient instead of lr_gradient_opposite - Replace lr_gradient_opposite() call with msg_gradient() in message overlay rendering - Add 6 comprehensive tests for msg_gradient covering theme usage, fallback behavior, and edge cases All tests pass (121 passed), no regressions detected. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
302 lines
9.8 KiB
Python
302 lines
9.8 KiB
Python
"""
|
|
Tests for engine.render module.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from engine import config, render
|
|
|
|
|
|
class TestDefaultGradients:
|
|
"""Tests for default gradient fallback functions."""
|
|
|
|
def test_default_green_gradient_length(self):
|
|
"""_default_green_gradient returns 12 colors."""
|
|
gradient = render._default_green_gradient()
|
|
assert len(gradient) == 12
|
|
|
|
def test_default_green_gradient_is_list(self):
|
|
"""_default_green_gradient returns a list."""
|
|
gradient = render._default_green_gradient()
|
|
assert isinstance(gradient, list)
|
|
|
|
def test_default_green_gradient_all_strings(self):
|
|
"""_default_green_gradient returns list of ANSI code strings."""
|
|
gradient = render._default_green_gradient()
|
|
assert all(isinstance(code, str) for code in gradient)
|
|
|
|
def test_default_magenta_gradient_length(self):
|
|
"""_default_magenta_gradient returns 12 colors."""
|
|
gradient = render._default_magenta_gradient()
|
|
assert len(gradient) == 12
|
|
|
|
def test_default_magenta_gradient_is_list(self):
|
|
"""_default_magenta_gradient returns a list."""
|
|
gradient = render._default_magenta_gradient()
|
|
assert isinstance(gradient, list)
|
|
|
|
def test_default_magenta_gradient_all_strings(self):
|
|
"""_default_magenta_gradient returns list of ANSI code strings."""
|
|
gradient = render._default_magenta_gradient()
|
|
assert all(isinstance(code, str) for code in gradient)
|
|
|
|
|
|
class TestLrGradientUsesActiveTheme:
|
|
"""Tests for lr_gradient using active theme."""
|
|
|
|
def test_lr_gradient_uses_active_theme_when_cols_none(self):
|
|
"""lr_gradient uses ACTIVE_THEME.main_gradient when cols=None."""
|
|
# Save original state
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
# Set a theme
|
|
config.set_active_theme("green")
|
|
|
|
# Create simple test data
|
|
rows = ["text"]
|
|
|
|
# Call without cols parameter (cols=None)
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
# Should not raise and should return colored output
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
# Should have ANSI codes (no plain "text")
|
|
assert result[0] != "text"
|
|
finally:
|
|
# Restore original state
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_lr_gradient_fallback_when_no_theme(self):
|
|
"""lr_gradient uses fallback green when ACTIVE_THEME is None."""
|
|
# Save original state
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
# Clear the theme
|
|
config.ACTIVE_THEME = None
|
|
|
|
# Create simple test data
|
|
rows = ["text"]
|
|
|
|
# Call without cols parameter (should use fallback)
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
# Should not raise and should return colored output
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
# Should have ANSI codes (no plain "text")
|
|
assert result[0] != "text"
|
|
finally:
|
|
# Restore original state
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_lr_gradient_explicit_cols_parameter_still_works(self):
|
|
"""lr_gradient with explicit cols parameter overrides theme."""
|
|
# Custom gradient
|
|
custom_cols = ["\033[38;5;1m", "\033[38;5;2m"] * 6
|
|
|
|
rows = ["xy"]
|
|
result = render.lr_gradient(rows, offset=0.0, cols=custom_cols)
|
|
|
|
# Should use the provided cols
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
|
|
def test_lr_gradient_respects_cols_parameter_name(self):
|
|
"""lr_gradient accepts cols as keyword argument."""
|
|
custom_cols = ["\033[38;5;1m", "\033[38;5;2m"] * 6
|
|
|
|
rows = ["xy"]
|
|
# Call with cols as keyword
|
|
result = render.lr_gradient(rows, offset=0.0, cols=custom_cols)
|
|
|
|
assert isinstance(result, list)
|
|
|
|
|
|
class TestLrGradientBasicFunctionality:
|
|
"""Tests to ensure lr_gradient basic functionality still works."""
|
|
|
|
def test_lr_gradient_colors_non_space_chars(self):
|
|
"""lr_gradient colors non-space characters."""
|
|
rows = ["hello"]
|
|
|
|
# Set a theme for the test
|
|
original_theme = config.ACTIVE_THEME
|
|
try:
|
|
config.set_active_theme("green")
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
# Result should have ANSI codes
|
|
assert any("\033[" in r for r in result), "Expected ANSI codes in result"
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_lr_gradient_preserves_spaces(self):
|
|
"""lr_gradient preserves spaces in output."""
|
|
rows = ["a b c"]
|
|
|
|
original_theme = config.ACTIVE_THEME
|
|
try:
|
|
config.set_active_theme("green")
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
# Spaces should be preserved (not colored)
|
|
assert " " in result[0]
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_lr_gradient_empty_rows(self):
|
|
"""lr_gradient handles empty rows correctly."""
|
|
rows = [""]
|
|
|
|
original_theme = config.ACTIVE_THEME
|
|
try:
|
|
config.set_active_theme("green")
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
assert result == [""]
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_lr_gradient_multiple_rows(self):
|
|
"""lr_gradient handles multiple rows."""
|
|
rows = ["row1", "row2", "row3"]
|
|
|
|
original_theme = config.ACTIVE_THEME
|
|
try:
|
|
config.set_active_theme("green")
|
|
result = render.lr_gradient(rows, offset=0.0)
|
|
|
|
assert len(result) == 3
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
|
|
class TestMsgGradient:
|
|
"""Tests for msg_gradient function (message/ntfy overlay coloring)."""
|
|
|
|
def test_msg_gradient_uses_active_theme(self):
|
|
"""msg_gradient uses ACTIVE_THEME.message_gradient when theme is set."""
|
|
# Save original state
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
# Set a theme
|
|
config.set_active_theme("green")
|
|
|
|
# Create simple test data
|
|
rows = ["MESSAGE"]
|
|
|
|
# Call msg_gradient
|
|
result = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Should return colored output using theme's message_gradient
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
# Should have ANSI codes from the message gradient
|
|
assert result[0] != "MESSAGE"
|
|
assert "\033[" in result[0]
|
|
finally:
|
|
# Restore original state
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_msg_gradient_fallback_when_no_theme(self):
|
|
"""msg_gradient uses fallback magenta when ACTIVE_THEME is None."""
|
|
# Save original state
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
# Clear the theme
|
|
config.ACTIVE_THEME = None
|
|
|
|
# Create simple test data
|
|
rows = ["MESSAGE"]
|
|
|
|
# Call msg_gradient
|
|
result = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Should return colored output using default magenta
|
|
assert isinstance(result, list)
|
|
assert len(result) == 1
|
|
# Should have ANSI codes
|
|
assert result[0] != "MESSAGE"
|
|
assert "\033[" in result[0]
|
|
finally:
|
|
# Restore original state
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_msg_gradient_returns_colored_rows(self):
|
|
"""msg_gradient returns properly colored rows with animation offset."""
|
|
# Save original state
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
# Set a theme
|
|
config.set_active_theme("orange")
|
|
|
|
rows = ["NTFY", "ALERT"]
|
|
|
|
# Call with offset
|
|
result = render.msg_gradient(rows, offset=0.5)
|
|
|
|
# Should return same number of rows
|
|
assert len(result) == 2
|
|
# Both should be colored
|
|
assert all("\033[" in r for r in result)
|
|
# Should not be the original text
|
|
assert result != rows
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_msg_gradient_different_themes_produce_different_results(self):
|
|
"""msg_gradient produces different colors for different themes."""
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
rows = ["TEST"]
|
|
|
|
# Get result with green theme
|
|
config.set_active_theme("green")
|
|
result_green = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Get result with orange theme
|
|
config.set_active_theme("orange")
|
|
result_orange = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Results should be different (different message gradients)
|
|
assert result_green != result_orange
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_msg_gradient_preserves_spacing(self):
|
|
"""msg_gradient preserves spaces in rows."""
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
config.set_active_theme("purple")
|
|
rows = ["M E S S A G E"]
|
|
|
|
result = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Spaces should be preserved
|
|
assert " " in result[0]
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|
|
|
|
def test_msg_gradient_empty_rows(self):
|
|
"""msg_gradient handles empty rows correctly."""
|
|
original_theme = config.ACTIVE_THEME
|
|
|
|
try:
|
|
config.set_active_theme("green")
|
|
rows = [""]
|
|
|
|
result = render.msg_gradient(rows, offset=0.0)
|
|
|
|
# Empty row should stay empty
|
|
assert result == [""]
|
|
finally:
|
|
config.ACTIVE_THEME = original_theme
|