mirror of
https://github.com/PRVTPRO/Amnezia-Web-Panel.git
synced 2026-09-18 01:44:10 +02:00
96 lines
4.2 KiB
Python
96 lines
4.2 KiB
Python
"""Design-system regression guards (accessibility, icons, i18n, type scale)."""
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
TEMPLATES = ROOT / 'templates'
|
|
STYLE = (ROOT / 'static' / 'css' / 'style.css').read_text(encoding='utf-8')
|
|
|
|
|
|
class TestDesignSystem(unittest.TestCase):
|
|
def test_global_focus_visible_outline(self):
|
|
self.assertRegex(STYLE, r':focus-visible\s*\{[^}]*outline:\s*2px solid var\(--accent\)')
|
|
|
|
def test_global_reduced_motion(self):
|
|
blocks = STYLE.split('@media (prefers-reduced-motion: reduce)')
|
|
self.assertGreaterEqual(len(blocks), 2, 'expected a global reduced-motion block')
|
|
self.assertIn('animation-duration: 0.01ms !important', STYLE)
|
|
self.assertIn('transition-duration: 0.01ms !important', STYLE)
|
|
|
|
def test_muted_text_passes_aa_contrast(self):
|
|
dark = STYLE.split(':root', 1)[1].split('[data-theme="light"]', 1)[0]
|
|
self.assertIn('--text-muted: #83839c', dark)
|
|
self.assertIn('--text-secondary: #8b8ba0', dark)
|
|
light = STYLE.split('[data-theme="light"]', 1)[1]
|
|
self.assertIn('--text-muted: #6b6b83', light)
|
|
self.assertIn('--text-secondary: #5a5a78', light)
|
|
|
|
def test_tabbar_and_section_icons_use_svg_not_emoji(self):
|
|
for name in ('base.html', 'index.html', 'users.html', 'my_connections.html', 'settings.html'):
|
|
src = (TEMPLATES / name).read_text(encoding='utf-8')
|
|
self.assertNotRegex(
|
|
src,
|
|
r'class="tab-icon"[^>]*>\s*[^<]{1,4}\s*<',
|
|
f'{name}: tab-icon must wrap an SVG, not an emoji',
|
|
)
|
|
self.assertNotRegex(
|
|
src,
|
|
r'class="icon">\s*[^<]{1,4}\s*<',
|
|
f'{name}: section icon must wrap an SVG, not an emoji',
|
|
)
|
|
|
|
def test_icon_constants_defined(self):
|
|
base = (TEMPLATES / 'base.html').read_text(encoding='utf-8')
|
|
for key in (
|
|
'ICON_SERVERS', 'ICON_USERS', 'ICON_SETTINGS', 'ICON_LINK',
|
|
'ICON_FILE', 'ICON_PLUS', 'ICON_POWER', 'ICON_SHARE', 'ICON_LOCK',
|
|
'ICON_GLOBE', 'ICON_KEY', 'ICON_INFO', 'ICON_REFRESH', 'ICON_COPY',
|
|
'ICON_DOWNLOAD', 'ICON_UPLOAD', 'ICON_DOCS', 'ICON_CHECK', 'ICON_X',
|
|
):
|
|
self.assertIn(f'window.{key}', base, f'missing {key}')
|
|
|
|
def test_no_hardcoded_russian_or_telemt_labels(self):
|
|
for name in ('users.html', 'server.html'):
|
|
src = (TEMPLATES / name).read_text(encoding='utf-8')
|
|
self.assertNotIn('Без срока', src)
|
|
self.assertNotIn('Secret (Hex 32 chars)', src)
|
|
self.assertNotIn('Max TCP Conns', src)
|
|
self.assertNotIn('Quota (Bytes)', src)
|
|
self.assertNotIn('Max Unique IPs', src)
|
|
|
|
def test_translation_keys_added(self):
|
|
import json
|
|
for lang in ('en', 'ru'):
|
|
d = json.loads((ROOT / 'translations' / f'{lang}.json').read_text(encoding='utf-8'))
|
|
for key in (
|
|
'no_expiry', 'telemt_secret_label', 'telemt_ad_tag_label',
|
|
'telemt_max_conns_label', 'telemt_quota_label',
|
|
'telemt_max_ips_label', 'telemt_expiry_label', 'optional',
|
|
):
|
|
self.assertIn(key, d, f'{lang}.json missing {key}')
|
|
|
|
def test_type_scale_tokens_defined(self):
|
|
self.assertIn('--text-xs:', STYLE)
|
|
self.assertIn('--text-sm:', STYLE)
|
|
self.assertIn('--text-base:', STYLE)
|
|
self.assertIn('--text-lg:', STYLE)
|
|
self.assertIn('--text-xl:', STYLE)
|
|
self.assertIn('--text-2xl:', STYLE)
|
|
|
|
def test_tabular_numerics_on_metrics(self):
|
|
self.assertRegex(STYLE, r'\.client-meta[^}]*font-variant-numeric:\s*tabular-nums')
|
|
self.assertRegex(STYLE, r'\.ping-ms[^}]*font-variant-numeric:\s*tabular-nums')
|
|
|
|
def test_settings_has_section_nav(self):
|
|
settings = (TEMPLATES / 'settings.html').read_text(encoding='utf-8')
|
|
self.assertIn('settings-nav', settings)
|
|
self.assertIn('id="settings-appearance"', settings)
|
|
self.assertIn('id="settings-tunnels"', settings)
|
|
self.assertIn('id="settings-ssl"', settings)
|
|
self.assertRegex(STYLE, r'\.settings-nav\s*\{[^}]*position:\s*sticky')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|