improve bazdid 4

This commit is contained in:
javad committed 2026-10-21 15:13:35 +03:30
1 parent f86d060e4d
commit 8fd881e0ea
10 files changed
+1752 -320

No files matched your search

+113
View File
@@ -9,6 +9,8 @@
هیچ پاسخی نباید کد ۵۰۰ داشته باشد.
"""
import json
import re
from html.parser import HTMLParser
from django.test import TestCase
from django.urls import reverse
@@ -257,3 +259,114 @@ class ViewSmokeTests(TestCase):
f"داده‌اند: {failures}"
),
)
class _DivBalanceParser(HTMLParser):
"""شمارندهٔ عمق <div> با احترام به محتوای <script>/<style>.
نکته: HTMLParser محتوای script/style را متن خام می‌داند، پس `<div>` داخل
رشتهٔ جاوااسکریپت به‌اشتباه شمرده نمی‌شود — همان چیزی که شمارش متنی ساده
اشتباه می‌کند.
"""
def __init__(self):
super().__init__(convert_charrefs=True)
self.depth = 0
self.min_depth = 0
self.depth_at_body = None
def handle_starttag(self, tag, attrs):
if tag == 'div':
self.depth += 1
def handle_endtag(self, tag):
if tag == 'div':
self.depth -= 1
self.min_depth = min(self.min_depth, self.depth)
elif tag == 'body':
self.depth_at_body = self.depth
class BaseTemplateStructureTests(TestCase):
"""ساختار HTML قالب پایه.
چرا؟ قالب `base.html` یک `<div class="">` باز می‌کرد که هرگز بسته نمی‌شد.
مرورگر آن را در EOF خودکار می‌بست، پس هیچ خطایی دیده نمی‌شد؛ ولی همهٔ
صفحات HTML نامعتبر تولید می‌کردند و بازسازی DOM توسط جاوااسکریپت و
پارسرهای سخت‌گیر می‌توانست بشکند.
"""
URLS = ['/panel/', '/panel/customers/', '/panel/entezar/', '/panel/rezayat/',
'/panel/hesabdari/', '/panel/report/']
def setUp(self):
self.user = make_user('structureAdmin', superuser=True)
self.client.force_login(self.user)
def _parsed_pages(self):
"""صفحاتی که در محیط تست کد ۲۰۰ می‌دهند را پارس می‌کند.
برخی صفحات روی دیتابیس خالی خطای داخلی می‌دهند (باگ مستقل، پوشش‌داده‌شده
در تست دودی)؛ آن‌ها اینجا کنار گذاشته می‌شوند. برای اینکه تست بی‌اثر
نشود، تعداد صفحات بررسی‌شده assert می‌شود.
"""
parsed = []
for url in self.URLS:
response = self.client.get(url)
if response.status_code != 200:
continue
parser = _DivBalanceParser()
parser.feed(response.content.decode('utf-8', 'replace'))
parsed.append((url, parser))
return parsed
def test_every_page_has_balanced_divs(self):
"""در پایان سند، هیچ <div> بازی نباید باقی بماند."""
parsed = self._parsed_pages()
self.assertGreaterEqual(len(parsed), 5,
msg='صفحات کافی برای بررسی ساختار رندر نشدند')
for url, parser in parsed:
with self.subTest(url=url):
self.assertEqual(
parser.depth_at_body, 0,
msg='در %s هنگام </body> تعداد %d تگ <div> بسته‌نشده ماند'
% (url, parser.depth_at_body),
)
def test_no_stray_closing_div(self):
"""عمق هرگز نباید منفی شود؛ یعنی </div> اضافی هم وجود ندارد."""
parsed = self._parsed_pages()
self.assertGreaterEqual(len(parsed), 5)
for url, parser in parsed:
with self.subTest(url=url):
self.assertGreaterEqual(parser.min_depth, 0,
msg='در %s تگ </div> اضافی وجود دارد' % url)
def test_base_template_comments_are_single_line(self):
"""کامنت‌های {# ... #} باید تک‌خطی باشند.
موتور قالب جنگو (۳.۲) الگوی کامنت را بدون re.DOTALL می‌شناسد؛ کامنت
چندخطی حذف نمی‌شود و عیناً در HTML چاپ می‌شود — در همین اصلاح، یک
`<div class="">` داخل کامنت چندخطی، به‌عنوان تگ واقعی به خروجی اضافه
شده بود و تگ بستن را می‌بلعید.
"""
import io
import os
templates_root = os.path.join('admin_panel', 'templates')
offenders = []
for base, _dirs, files in os.walk(templates_root):
for name in files:
if not name.endswith('.html'):
continue
path = os.path.join(base, name)
text = io.open(path, encoding='utf-8', errors='replace').read()
for match in re.finditer(r'\{#', text):
tail = text[match.start():match.start() + 500]
line_end = tail.find('\n')
end = line_end if line_end > -1 else len(tail)
if tail.find('#}', 0, end) == -1:
offenders.append('%s:%d' % (
path, text[:match.start()].count('\n') + 1))
self.assertEqual(offenders, [],
msg='کامنت چندخطی در این قالب‌ها حذف نمی‌شود: %s' % offenders)