feat: tune deeplink fallback page

This commit is contained in:
3252a8
2026-05-23 23:26:10 +03:00
parent 5918a6cc71
commit 46391b10e2
12 changed files with 635 additions and 213 deletions
+68 -1
View File
@@ -1,5 +1,7 @@
import asyncio
import json
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
@@ -10,6 +12,8 @@ from bot.app.web import admin_api, subscription_webapp
from bot.app.web.admin_api_impl import auth as admin_auth_routes
from bot.app.web.webapp_auth import create_webapp_session_token
REPO_ROOT = Path(__file__).resolve().parents[1]
class _Request(dict):
def __init__(self, *, path="/", app=None, headers=None, cookies=None):
@@ -31,6 +35,26 @@ class _AsyncSessionFactory:
return False
class _I18n:
locales_data = {
"en": {
"wa_app_launch_title": "Localized launch",
"wa_app_launch_opening_hint": "Localized opening hint",
"wa_app_launch_hint": "Localized manual hint",
"wa_app_launch_button": "Localized open",
"wa_app_launch_retry_button": "Localized retry",
"wa_app_launch_done_title": "Localized done",
"wa_app_launch_done_hint": "Localized done hint",
"wa_app_launch_close_button": "Localized close",
"wa_app_launch_unavailable_title": "Localized unavailable",
"wa_app_launch_unavailable_hint": "Localized unavailable hint",
}
}
def gettext(self, lang_code, key, **kwargs):
return self.locales_data.get(lang_code, {}).get(key, key)
def _route_map(app: web.Application) -> dict[tuple[str, str], str]:
return {
(route.method, route.resource.canonical): route.handler.__name__
@@ -202,6 +226,7 @@ class WebAppRouteContractTests(unittest.TestCase):
"settings": SimpleNamespace(
WEBAPP_ENABLED=True,
WEBAPP_TITLE="/minishop",
DEFAULT_LANGUAGE="en",
)
}
)
@@ -212,11 +237,53 @@ class WebAppRouteContractTests(unittest.TestCase):
self.assertEqual(response.status, 200)
self.assertEqual(response.headers["Cache-Control"], "no-store")
self.assertIn('nonce="nonce-value"', response.text)
self.assertNotIn("__MESSAGES_JSON__", response.text)
self.assertIn("window.location.hash", response.text)
self.assertIn("URLSearchParams", response.text)
self.assertIn("The app link is unavailable.", response.text)
self.assertIn("Settings added", response.text)
self.assertIn("window.close()", response.text)
self.assertIn(r"/^(?:javascript|data|vbscript|https?):/i", response.text)
def test_app_deeplink_gateway_uses_i18n_template(self):
request = _Request(
app={
"settings": SimpleNamespace(
WEBAPP_ENABLED=True,
WEBAPP_TITLE="/minishop",
DEFAULT_LANGUAGE="en",
),
"i18n": _I18n(),
}
)
request["csp_nonce"] = "nonce-value"
response = asyncio.run(subscription_webapp.app_deeplink_route(request))
self.assertEqual(response.status, 200)
self.assertIn("Localized launch", response.text)
self.assertIn("Localized done hint", response.text)
self.assertIn("<title>/minishop - Localized launch</title>", response.text)
self.assertTrue(
(REPO_ROOT / "backend/bot/app/web/templates/open_app_gateway.html").is_file()
)
def test_app_launch_i18n_keys_are_available_to_webapp_bootstrap(self):
required_keys = {
"wa_app_launch_title",
"wa_app_launch_opening_hint",
"wa_app_launch_hint",
"wa_app_launch_button",
"wa_app_launch_retry_button",
"wa_app_launch_done_title",
"wa_app_launch_done_hint",
"wa_app_launch_close_button",
"wa_app_launch_unavailable_title",
"wa_app_launch_unavailable_hint",
}
for locale in ("en", "ru"):
messages = json.loads((REPO_ROOT / f"locales/{locale}.json").read_text("utf-8"))
self.assertLessEqual(required_keys, set(messages))
class AdminApiAuthContractTests(unittest.IsolatedAsyncioTestCase):
def _settings(self):