feat: separate webapp favicon configuration in appearance admin panel section
This commit is contained in:
@@ -39,6 +39,9 @@ class SettingsTests(unittest.TestCase):
|
||||
WEBAPP_LOGO_USE_EMOJI=True,
|
||||
WEBAPP_LOGO_EMOJI="🔥",
|
||||
WEBAPP_LOGO_EMOJI_FONT="twemoji",
|
||||
WEBAPP_FAVICON_USE_CUSTOM=True,
|
||||
WEBAPP_FAVICON_URL="https://cdn.example.com/favicon.png",
|
||||
WEBAPP_LOGO_FAVICON_URL="/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
)
|
||||
|
||||
self.assertEqual(settings.WEBAPP_PRIMARY_COLOR, "#00fe7a")
|
||||
@@ -46,6 +49,9 @@ class SettingsTests(unittest.TestCase):
|
||||
self.assertFalse(settings.WEBAPP_LOGO_USE_EMOJI)
|
||||
self.assertEqual(settings.WEBAPP_LOGO_EMOJI, "🫥")
|
||||
self.assertEqual(settings.WEBAPP_LOGO_EMOJI_FONT, "system")
|
||||
self.assertFalse(settings.WEBAPP_FAVICON_USE_CUSTOM)
|
||||
self.assertIsNone(settings.WEBAPP_FAVICON_URL)
|
||||
self.assertIsNone(settings.WEBAPP_LOGO_FAVICON_URL)
|
||||
|
||||
def test_tariffs_config_missing_uses_legacy_fallback(self):
|
||||
settings = Settings(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
@@ -8,7 +9,10 @@ from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from bot.app.web import subscription_webapp
|
||||
from bot.app.web.admin_api_impl import themes as admin_themes
|
||||
from bot.app.web.webapp import assets as webapp_assets
|
||||
from config.settings import Settings
|
||||
from config.webapp_themes_config import builtin_webapp_themes_config
|
||||
@@ -110,6 +114,61 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
self.assertEqual(subscription_webapp._resolve_webapp_logo_url(settings), "")
|
||||
|
||||
def test_custom_webapp_favicon_takes_precedence(self):
|
||||
settings = SimpleNamespace(
|
||||
WEBAPP_FAVICON_USE_CUSTOM=True,
|
||||
WEBAPP_FAVICON_URL="/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
WEBAPP_LOGO_FAVICON_URL="/webapp-favicon/1111111111111111/icon-180.png",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
subscription_webapp._resolve_webapp_favicon_url(settings, "/logo.png"),
|
||||
"/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
)
|
||||
|
||||
def test_logo_generated_favicon_is_used_when_custom_disabled(self):
|
||||
settings = SimpleNamespace(
|
||||
WEBAPP_FAVICON_USE_CUSTOM=False,
|
||||
WEBAPP_FAVICON_URL="/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
WEBAPP_LOGO_FAVICON_URL="/webapp-favicon/1111111111111111/icon-180.png",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
subscription_webapp._resolve_webapp_favicon_url(settings, "/logo.png"),
|
||||
"/webapp-favicon/1111111111111111/icon-180.png",
|
||||
)
|
||||
|
||||
def test_logo_generated_favicon_is_not_used_without_logo(self):
|
||||
settings = SimpleNamespace(
|
||||
WEBAPP_FAVICON_USE_CUSTOM=False,
|
||||
WEBAPP_FAVICON_URL="/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
WEBAPP_LOGO_FAVICON_URL="/webapp-favicon/1111111111111111/icon-180.png",
|
||||
)
|
||||
|
||||
self.assertEqual(subscription_webapp._resolve_webapp_favicon_url(settings, ""), "")
|
||||
|
||||
def test_favicon_head_markup_includes_touch_icon(self):
|
||||
markup = subscription_webapp._favicon_head_markup(
|
||||
"/webapp-favicon/abcdef1234567890/icon-180.png"
|
||||
)
|
||||
|
||||
self.assertIn('rel="apple-touch-icon"', markup)
|
||||
self.assertIn("/webapp-favicon/abcdef1234567890/icon-32.png", markup)
|
||||
|
||||
def test_favicon_set_generation_writes_common_icon_sizes(self):
|
||||
buffer = io.BytesIO()
|
||||
Image.new("RGBA", (2, 2), (0, 254, 122, 255)).save(buffer, format="PNG")
|
||||
png_body = buffer.getvalue()
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with patch.object(admin_themes, "WEBAPP_FAVICON_DIR", Path(tmpdir)):
|
||||
payload = admin_themes._write_favicon_set(png_body, "image/png", "icon.png")
|
||||
|
||||
self.assertRegex(payload["favicon_url"], r"^/webapp-favicon/[0-9a-f]{16}/icon-180\.png$")
|
||||
digest = payload["favicon_url"].split("/")[2]
|
||||
self.assertTrue((Path(tmpdir) / digest / "icon-32.png").exists())
|
||||
self.assertTrue((Path(tmpdir) / digest / "apple-touch-icon.png").exists())
|
||||
self.assertTrue((Path(tmpdir) / digest / "favicon.ico").exists())
|
||||
|
||||
def test_initial_theme_head_markup_includes_css_and_tokens(self):
|
||||
cfg = builtin_webapp_themes_config("#123456")
|
||||
theme = cfg.theme_by_key("light")
|
||||
|
||||
@@ -143,6 +143,7 @@ class WebAppRouteContractTests(unittest.TestCase):
|
||||
("GET", "/api/admin/themes"): "admin_themes_get_route",
|
||||
("PUT", "/api/admin/themes"): "admin_themes_save_route",
|
||||
("POST", "/api/admin/appearance/logo"): "admin_appearance_logo_upload_route",
|
||||
("POST", "/api/admin/appearance/favicon"): "admin_appearance_favicon_upload_route",
|
||||
("GET", "/api/admin/panel/internal-squads"): "admin_panel_internal_squads_route",
|
||||
}
|
||||
|
||||
@@ -167,6 +168,19 @@ class WebAppRouteContractTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(match_info.handler.__name__, "index_route")
|
||||
|
||||
def test_webapp_favicon_asset_route_is_registered(self):
|
||||
app = web.Application()
|
||||
subscription_webapp.setup_subscription_webapp_routes(app)
|
||||
|
||||
request = make_mocked_request(
|
||||
"GET",
|
||||
"/webapp-favicon/abcdef1234567890/icon-180.png",
|
||||
app=app,
|
||||
)
|
||||
match_info = asyncio.run(app.router.resolve(request))
|
||||
|
||||
self.assertEqual(match_info.handler.__name__, "webapp_favicon_route")
|
||||
|
||||
|
||||
class AdminApiAuthContractTests(unittest.IsolatedAsyncioTestCase):
|
||||
def _settings(self):
|
||||
|
||||
Reference in New Issue
Block a user