|
|
|
@@ -0,0 +1,184 @@
|
|
|
|
|
"""routers/api_widgets.py's POST .../font-scale endpoint (models.Widget.
|
|
|
|
|
font_scale) -- a Widget-level property, not a per-type config field, same
|
|
|
|
|
reasoning/shape as test_widget_border.py's border coverage. The second
|
|
|
|
|
half confirms the actual render threading (widgets/calendar.py and
|
|
|
|
|
widgets/tasks.py pass widget.font_scale into both the classic and modern
|
|
|
|
|
builders), the same "spy on the resolve call" approach test_widgets_tasks.
|
|
|
|
|
py/test_widgets_calendar.py already use for frame.theme threading."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
from app.models import CalendarWidgetConfig, Frame, TaskWidgetConfig, Widget
|
|
|
|
|
|
|
|
|
|
from .conftest import csrf_headers, link_user, login, make_user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _widget_id(db_session, widget_type="photos") -> int:
|
|
|
|
|
return db_session.query(Widget).filter_by(frame_id=1, widget_type=widget_type).one().id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_new_widget_defaults_to_normal_font_scale(db_session):
|
|
|
|
|
widget = db_session.query(Widget).filter_by(frame_id=1).one()
|
|
|
|
|
assert widget.font_scale == 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_font_scale_persists(client, db_session):
|
|
|
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
|
|
|
widget_id = _widget_id(db_session)
|
|
|
|
|
resp = client.post(f"/api/frames/1/widgets/{widget_id}/font-scale",
|
|
|
|
|
json={"font_scale": 1.25}, headers=csrf_headers(client))
|
|
|
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
|
assert resp.json()["font_scale"] == 1.25
|
|
|
|
|
widget = db_session.get(Widget, widget_id)
|
|
|
|
|
assert widget.font_scale == 1.25
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_font_scale_rejects_a_value_outside_the_fixed_choices(client, db_session):
|
|
|
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
|
|
|
widget_id = _widget_id(db_session)
|
|
|
|
|
resp = client.post(f"/api/frames/1/widgets/{widget_id}/font-scale",
|
|
|
|
|
json={"font_scale": 3.0}, headers=csrf_headers(client))
|
|
|
|
|
assert resp.status_code == 400
|
|
|
|
|
assert "font_scale" in resp.json()["detail"]
|
|
|
|
|
assert db_session.get(Widget, widget_id).font_scale == 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_font_scale_404s_for_unknown_widget(client, db_session):
|
|
|
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
|
|
|
resp = client.post("/api/frames/1/widgets/999999/font-scale",
|
|
|
|
|
json={"font_scale": 1.25}, headers=csrf_headers(client))
|
|
|
|
|
assert resp.status_code == 404
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_font_scale_unrelated_user_404s(client, db_session):
|
|
|
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
|
|
|
make_user(db_session, "mallory")
|
|
|
|
|
widget_id = _widget_id(db_session)
|
|
|
|
|
|
|
|
|
|
client.cookies.clear()
|
|
|
|
|
login(client, "mallory")
|
|
|
|
|
resp = client.post(f"/api/frames/1/widgets/{widget_id}/font-scale",
|
|
|
|
|
json={"font_scale": 1.25}, headers=csrf_headers(client))
|
|
|
|
|
assert resp.status_code == 404
|
|
|
|
|
assert db_session.get(Widget, widget_id).font_scale == 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_font_scale_linked_but_not_controlling_user_409s(client, db_session):
|
|
|
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
|
|
|
bob = make_user(db_session, "bob")
|
|
|
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
|
link_user(db_session, bob, frame)
|
|
|
|
|
widget_id = _widget_id(db_session)
|
|
|
|
|
|
|
|
|
|
client.cookies.clear()
|
|
|
|
|
login(client, "bob")
|
|
|
|
|
resp = client.post(f"/api/frames/1/widgets/{widget_id}/font-scale",
|
|
|
|
|
json={"font_scale": 1.25}, headers=csrf_headers(client))
|
|
|
|
|
assert resp.status_code == 409
|
|
|
|
|
assert resp.json()["detail"]["error"] == "not_controller"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- actually threads through to the renderers -----------------------------
|
|
|
|
|
|
|
|
|
|
def test_calendar_classic_render_threads_font_scale_through(db_session, monkeypatch):
|
|
|
|
|
"""Confirms widgets/calendar.py's classic branch passes widget.
|
|
|
|
|
font_scale into calendar_render._build, by spying on panel_style.
|
|
|
|
|
scaled_size (every classic builder's one shared scale point -- see
|
|
|
|
|
its own docstring)."""
|
|
|
|
|
from app import panel_style, widgets
|
|
|
|
|
|
|
|
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
|
widget = Widget(frame_id=frame.id, widget_type="calendar", x=0, y=0, w=8, h=5,
|
|
|
|
|
sort_order=0, created_at=time.time(), font_scale=1.5)
|
|
|
|
|
db_session.add(widget)
|
|
|
|
|
db_session.flush()
|
|
|
|
|
db_session.add(CalendarWidgetConfig(widget_id=widget.id, view="agenda"))
|
|
|
|
|
db_session.commit()
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(widgets.calendar, "get_or_refresh_calendar_events_for_widget",
|
|
|
|
|
lambda db, frame, widget: ([], ""))
|
|
|
|
|
monkeypatch.setattr(widgets.calendar, "get_or_refresh_weather_for_widget",
|
|
|
|
|
lambda db, frame, widget: [])
|
|
|
|
|
|
|
|
|
|
seen_scales = []
|
|
|
|
|
real_scaled_size = panel_style.scaled_size
|
|
|
|
|
monkeypatch.setattr(panel_style, "scaled_size", lambda v, s: (seen_scales.append(s), real_scaled_size(v, s))[1])
|
|
|
|
|
|
|
|
|
|
widgets.calendar.render(db_session, frame, widget, 300, 200)
|
|
|
|
|
assert seen_scales and all(s == 1.5 for s in seen_scales)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_calendar_modern_render_threads_font_scale_through(db_session, monkeypatch):
|
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
from app import html_render, widgets
|
|
|
|
|
|
|
|
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
|
widget = Widget(frame_id=frame.id, widget_type="calendar", x=0, y=0, w=8, h=5,
|
|
|
|
|
sort_order=0, created_at=time.time(), font_scale=1.25)
|
|
|
|
|
db_session.add(widget)
|
|
|
|
|
db_session.flush()
|
|
|
|
|
db_session.add(CalendarWidgetConfig(widget_id=widget.id, view="agenda", render_style="modern"))
|
|
|
|
|
db_session.commit()
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(widgets.calendar, "get_or_refresh_calendar_events_for_widget",
|
|
|
|
|
lambda db, frame, widget: ([], ""))
|
|
|
|
|
monkeypatch.setattr(widgets.calendar, "get_or_refresh_weather_for_widget",
|
|
|
|
|
lambda db, frame, widget: [])
|
|
|
|
|
monkeypatch.setattr(html_render, "render_html_to_image",
|
|
|
|
|
lambda html, target_w, target_h: Image.new("RGB", (target_w, target_h), (255, 255, 255)))
|
|
|
|
|
|
|
|
|
|
from app import panel_style
|
|
|
|
|
|
|
|
|
|
seen_scales = []
|
|
|
|
|
real_scaled_size = panel_style.scaled_size
|
|
|
|
|
monkeypatch.setattr(panel_style, "scaled_size", lambda v, s: (seen_scales.append(s), real_scaled_size(v, s))[1])
|
|
|
|
|
|
|
|
|
|
widgets.calendar.render(db_session, frame, widget, 300, 200)
|
|
|
|
|
assert seen_scales and all(s == 1.25 for s in seen_scales)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_tasks_classic_render_threads_font_scale_through(db_session, monkeypatch):
|
|
|
|
|
from app import panel_style, widgets
|
|
|
|
|
|
|
|
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
|
widget = Widget(frame_id=frame.id, widget_type="tasks", x=0, y=0, w=2, h=2,
|
|
|
|
|
sort_order=0, created_at=time.time(), font_scale=1.5)
|
|
|
|
|
db_session.add(widget)
|
|
|
|
|
db_session.flush()
|
|
|
|
|
db_session.add(TaskWidgetConfig(widget_id=widget.id))
|
|
|
|
|
db_session.commit()
|
|
|
|
|
monkeypatch.setattr(widgets.tasks, "get_or_refresh_tasks_for_widget", lambda db, frame, widget: [])
|
|
|
|
|
|
|
|
|
|
seen_scales = []
|
|
|
|
|
real_scaled_size = panel_style.scaled_size
|
|
|
|
|
monkeypatch.setattr(panel_style, "scaled_size", lambda v, s: (seen_scales.append(s), real_scaled_size(v, s))[1])
|
|
|
|
|
|
|
|
|
|
widgets.tasks.render(db_session, frame, widget, 300, 200)
|
|
|
|
|
assert seen_scales and all(s == 1.5 for s in seen_scales)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_tasks_modern_render_threads_font_scale_through(db_session, monkeypatch):
|
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
from app import html_render, panel_style, widgets
|
|
|
|
|
|
|
|
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
|
widget = Widget(frame_id=frame.id, widget_type="tasks", x=0, y=0, w=2, h=2,
|
|
|
|
|
sort_order=0, created_at=time.time(), font_scale=1.25)
|
|
|
|
|
db_session.add(widget)
|
|
|
|
|
db_session.flush()
|
|
|
|
|
db_session.add(TaskWidgetConfig(widget_id=widget.id, render_style="modern"))
|
|
|
|
|
db_session.commit()
|
|
|
|
|
monkeypatch.setattr(widgets.tasks, "get_or_refresh_tasks_for_widget", lambda db, frame, widget: [])
|
|
|
|
|
monkeypatch.setattr(html_render, "render_html_to_image",
|
|
|
|
|
lambda html, target_w, target_h: Image.new("RGB", (target_w, target_h), (255, 255, 255)))
|
|
|
|
|
|
|
|
|
|
seen_scales = []
|
|
|
|
|
real_scaled_size = panel_style.scaled_size
|
|
|
|
|
monkeypatch.setattr(panel_style, "scaled_size", lambda v, s: (seen_scales.append(s), real_scaled_size(v, s))[1])
|
|
|
|
|
|
|
|
|
|
widgets.tasks.render(db_session, frame, widget, 300, 200)
|
|
|
|
|
assert seen_scales and all(s == 1.25 for s in seen_scales)
|