Unsaved-changes guard: - _closeSettingsPanel() intercepts all three close paths (X button, overlay click, Escape key) and checks _settingsDirty before closing - If dirty: shows inline 'Unsaved changes' bar with Save & Close / Discard - Discard reverts the live theme preview to what it was when panel opened - _markSettingsDirty() wired to all inputs via addEventListener in loadSettingsPanel() - saveSettings() now resets dirty flag and hides the bar on successful save Theme improvements: - Add 'Slate' theme: warm charcoal (#2b2d30 bg), a softer/lighter dark option that sits between Dark and the full light themes - Rework 'Light' theme: replace pure white (#f5f5f7) with warm off-white (#f0ede8) -- warmer, lower contrast, less harsh on most displays - Update /theme command to include 'slate' in valid list - Add test_settings_set_theme_slate() to test_sprint26.py
120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
"""
|
|
Sprint 26 Tests: pluggable UI themes — settings persistence, theme default,
|
|
custom theme names accepted.
|
|
"""
|
|
import json, urllib.error, urllib.request
|
|
|
|
BASE = "http://127.0.0.1:8788"
|
|
|
|
|
|
def get(path):
|
|
with urllib.request.urlopen(BASE + path, timeout=10) as r:
|
|
return json.loads(r.read()), r.status
|
|
|
|
|
|
def post(path, body=None):
|
|
data = json.dumps(body or {}).encode()
|
|
req = urllib.request.Request(BASE + path, data=data,
|
|
headers={"Content-Type": "application/json"})
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as r:
|
|
return json.loads(r.read()), r.status
|
|
except urllib.error.HTTPError as e:
|
|
return json.loads(e.read()), e.code
|
|
|
|
|
|
# ── Theme settings ───────────────────────────────────────────────────────
|
|
|
|
def test_settings_default_theme():
|
|
"""Default theme should be 'dark'."""
|
|
d, status = get("/api/settings")
|
|
assert status == 200
|
|
assert d.get("theme") == "dark"
|
|
|
|
|
|
def test_settings_set_theme_light():
|
|
"""Setting theme to 'light' should persist and round-trip."""
|
|
try:
|
|
d, status = post("/api/settings", {"theme": "light"})
|
|
assert status == 200
|
|
d2, _ = get("/api/settings")
|
|
assert d2.get("theme") == "light"
|
|
finally:
|
|
# Reset to dark
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_settings_set_theme_solarized():
|
|
"""Setting theme to 'solarized' should persist."""
|
|
try:
|
|
post("/api/settings", {"theme": "solarized"})
|
|
d, _ = get("/api/settings")
|
|
assert d.get("theme") == "solarized"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_settings_set_theme_monokai():
|
|
"""Setting theme to 'monokai' should persist."""
|
|
try:
|
|
post("/api/settings", {"theme": "monokai"})
|
|
d, _ = get("/api/settings")
|
|
assert d.get("theme") == "monokai"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_settings_set_theme_nord():
|
|
"""Setting theme to 'nord' should persist."""
|
|
try:
|
|
post("/api/settings", {"theme": "nord"})
|
|
d, _ = get("/api/settings")
|
|
assert d.get("theme") == "nord"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_settings_set_theme_slate():
|
|
"""Setting theme to 'slate' should persist."""
|
|
try:
|
|
post("/api/settings", {"theme": "slate"})
|
|
d, _ = get("/api/settings")
|
|
assert d.get("theme") == "slate"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_settings_custom_theme_accepted():
|
|
"""Custom theme names should be accepted (no enum gate)."""
|
|
try:
|
|
d, status = post("/api/settings", {"theme": "my-custom-theme"})
|
|
assert status == 200
|
|
d2, _ = get("/api/settings")
|
|
assert d2.get("theme") == "my-custom-theme"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_theme_does_not_break_other_settings():
|
|
"""Setting theme should not disturb other settings."""
|
|
d_before, _ = get("/api/settings")
|
|
send_key_before = d_before.get("send_key")
|
|
try:
|
|
post("/api/settings", {"theme": "nord"})
|
|
d_after, _ = get("/api/settings")
|
|
assert d_after.get("send_key") == send_key_before
|
|
assert d_after.get("theme") == "nord"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|
|
|
|
|
|
def test_theme_survives_round_trip():
|
|
"""Theme set via POST should appear in subsequent GET."""
|
|
try:
|
|
post("/api/settings", {"theme": "monokai"})
|
|
d, status = get("/api/settings")
|
|
assert status == 200
|
|
assert d["theme"] == "monokai"
|
|
finally:
|
|
post("/api/settings", {"theme": "dark"})
|