|
|
"""
|
|
|
Test suite for Persian to English translation
|
|
|
Tests verify that the index.html file has been properly translated
|
|
|
"""
|
|
|
import re
|
|
|
from pathlib import Path
|
|
|
from bs4 import BeautifulSoup
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
def html_content():
|
|
|
"""Load and parse the index.html file"""
|
|
|
html_path = Path("index.html")
|
|
|
with open(html_path, "r", encoding="utf-8") as f:
|
|
|
content = f.read()
|
|
|
return content
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
def soup(html_content):
|
|
|
"""Parse HTML content with BeautifulSoup"""
|
|
|
return BeautifulSoup(html_content, "html.parser")
|
|
|
|
|
|
|
|
|
class TestHTMLAttributes:
|
|
|
"""
|
|
|
Unit tests for HTML attributes
|
|
|
Feature: persian-to-english-translation, Property 2: HTML language attributes are English
|
|
|
Validates: Requirements 2.1, 2.2
|
|
|
"""
|
|
|
|
|
|
def test_html_lang_attribute_is_english(self, soup):
|
|
|
"""Test that the HTML lang attribute is set to 'en'"""
|
|
|
html_tag = soup.find("html")
|
|
|
assert html_tag is not None, "HTML tag not found"
|
|
|
assert html_tag.get("lang") == "en", f"Expected lang='en', got lang='{html_tag.get('lang')}'"
|
|
|
|
|
|
def test_html_dir_attribute_is_ltr(self, soup):
|
|
|
"""Test that the HTML dir attribute is set to 'ltr'"""
|
|
|
html_tag = soup.find("html")
|
|
|
assert html_tag is not None, "HTML tag not found"
|
|
|
assert html_tag.get("dir") == "ltr", f"Expected dir='ltr', got dir='{html_tag.get('dir')}'"
|
|
|
|
|
|
|
|
|
|
|
|
class TestSpecificElements:
|
|
|
"""
|
|
|
Unit tests for specific HTML elements
|
|
|
"""
|
|
|
|
|
|
def test_page_title_is_english(self, soup):
|
|
|
"""
|
|
|
Feature: persian-to-english-translation, Property 4: Page title is in English
|
|
|
Validates: Requirements 2.3
|
|
|
"""
|
|
|
title_tag = soup.find("title")
|
|
|
assert title_tag is not None, "Title tag not found"
|
|
|
title_text = title_tag.get_text()
|
|
|
|
|
|
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
assert not persian_pattern.search(title_text), f"Title contains Persian characters: {title_text}"
|
|
|
|
|
|
|
|
|
assert "Crypto Intelligence Hub" in title_text, f"Expected 'Crypto Intelligence Hub' in title, got: {title_text}"
|
|
|
|
|
|
def test_navigation_tabs_are_english(self, soup):
|
|
|
"""
|
|
|
Feature: persian-to-english-translation, Property 3: Navigation tabs are in English
|
|
|
Validates: Requirements 1.1
|
|
|
"""
|
|
|
expected_tabs = [
|
|
|
"Dashboard",
|
|
|
"Market",
|
|
|
"AI Models",
|
|
|
"Sentiment",
|
|
|
"News",
|
|
|
"Providers",
|
|
|
"Diagnostics",
|
|
|
"API"
|
|
|
]
|
|
|
|
|
|
nav = soup.find("nav", class_="tabs-nav")
|
|
|
assert nav is not None, "Navigation tabs not found"
|
|
|
|
|
|
tab_buttons = nav.find_all("button", class_="tab-btn")
|
|
|
assert len(tab_buttons) >= len(expected_tabs), f"Expected at least {len(expected_tabs)} tabs, found {len(tab_buttons)}"
|
|
|
|
|
|
|
|
|
tab_texts = []
|
|
|
for button in tab_buttons:
|
|
|
text = button.get_text().strip()
|
|
|
|
|
|
text = re.sub(r'[\U0001F300-\U0001F9FF]', '', text).strip()
|
|
|
tab_texts.append(text)
|
|
|
|
|
|
|
|
|
for expected_tab in expected_tabs:
|
|
|
assert expected_tab in tab_texts, f"Expected tab '{expected_tab}' not found in {tab_texts}"
|
|
|
|
|
|
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
for tab_text in tab_texts:
|
|
|
assert not persian_pattern.search(tab_text), f"Tab contains Persian characters: {tab_text}"
|
|
|
|
|
|
def test_status_messages_are_english(self, soup):
|
|
|
"""
|
|
|
Feature: persian-to-english-translation, Property 5: Status messages are in English
|
|
|
Validates: Requirements 1.3
|
|
|
"""
|
|
|
status_badge = soup.find("div", class_="status-badge")
|
|
|
assert status_badge is not None, "Status badge not found"
|
|
|
|
|
|
status_text = status_badge.get_text()
|
|
|
|
|
|
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
assert not persian_pattern.search(status_text), f"Status badge contains Persian characters: {status_text}"
|
|
|
|
|
|
|
|
|
assert "Checking" in status_text or "Connected" in status_text or "Online" in status_text, \
|
|
|
f"Expected English status message, got: {status_text}"
|
|
|
|
|
|
|
|
|
|
|
|
from hypothesis import given, strategies as st, settings
|
|
|
|
|
|
|
|
|
class TestPersianCharacterDetection:
|
|
|
"""
|
|
|
Property-based tests for Persian character detection
|
|
|
Feature: persian-to-english-translation, Property 1: No Persian text in user interface
|
|
|
Validates: Requirements 1.2, 1.4, 3.1, 3.2, 3.3, 3.4, 5.1, 5.2, 5.3, 5.4, 5.5
|
|
|
"""
|
|
|
|
|
|
def test_no_persian_characters_in_visible_text(self, soup):
|
|
|
"""Test that no visible text elements contain Persian characters"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
|
|
|
|
|
|
text_elements = soup.find_all(text=True)
|
|
|
|
|
|
|
|
|
visible_texts = []
|
|
|
for element in text_elements:
|
|
|
parent = element.parent
|
|
|
if parent.name not in ['script', 'style', 'head']:
|
|
|
text = element.strip()
|
|
|
if text:
|
|
|
visible_texts.append(text)
|
|
|
|
|
|
|
|
|
persian_found = []
|
|
|
for text in visible_texts:
|
|
|
if persian_pattern.search(text):
|
|
|
persian_found.append(text)
|
|
|
|
|
|
assert len(persian_found) == 0, f"Found {len(persian_found)} text elements with Persian characters: {persian_found[:5]}"
|
|
|
|
|
|
def test_no_persian_in_button_labels(self, soup):
|
|
|
"""Test that all button labels are in English"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
|
|
|
buttons = soup.find_all("button")
|
|
|
for button in buttons:
|
|
|
button_text = button.get_text().strip()
|
|
|
assert not persian_pattern.search(button_text), f"Button contains Persian characters: {button_text}"
|
|
|
|
|
|
def test_no_persian_in_form_labels(self, soup):
|
|
|
"""Test that all form labels are in English"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
|
|
|
labels = soup.find_all("label")
|
|
|
for label in labels:
|
|
|
label_text = label.get_text().strip()
|
|
|
assert not persian_pattern.search(label_text), f"Label contains Persian characters: {label_text}"
|
|
|
|
|
|
def test_no_persian_in_placeholders(self, soup):
|
|
|
"""Test that all placeholder attributes are in English"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
|
|
|
|
|
|
elements_with_placeholder = soup.find_all(attrs={"placeholder": True})
|
|
|
for element in elements_with_placeholder:
|
|
|
placeholder = element.get("placeholder", "")
|
|
|
assert not persian_pattern.search(placeholder), f"Placeholder contains Persian characters: {placeholder}"
|
|
|
|
|
|
def test_no_persian_in_headings(self, soup):
|
|
|
"""Test that all headings (h1-h6) are in English"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
|
|
|
for heading_level in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
|
|
|
headings = soup.find_all(heading_level)
|
|
|
for heading in headings:
|
|
|
heading_text = heading.get_text().strip()
|
|
|
assert not persian_pattern.search(heading_text), \
|
|
|
f"{heading_level.upper()} contains Persian characters: {heading_text}"
|
|
|
|
|
|
@settings(max_examples=100)
|
|
|
@given(st.text(alphabet=st.characters(min_codepoint=0x0600, max_codepoint=0x06FF), min_size=1))
|
|
|
def test_persian_character_detection_works(self, persian_text):
|
|
|
"""
|
|
|
Property-based test to verify our Persian character detection works correctly
|
|
|
This test generates random Persian text and verifies our regex can detect it
|
|
|
"""
|
|
|
persian_pattern = re.compile(r'[\u0600-\u06FF]')
|
|
|
assert persian_pattern.search(persian_text), \
|
|
|
f"Persian pattern should detect Persian text: {persian_text}"
|
|
|
|
|
|
def test_consistent_terminology(self, soup):
|
|
|
"""Test that consistent English terminology is used throughout"""
|
|
|
|
|
|
refresh_buttons = soup.find_all("button", class_="btn-refresh")
|
|
|
for button in refresh_buttons:
|
|
|
button_text = button.get_text().strip()
|
|
|
|
|
|
button_text = re.sub(r'[\U0001F300-\U0001F9FF]', '', button_text).strip()
|
|
|
assert "Refresh" in button_text, f"Expected 'Refresh' in button, got: {button_text}"
|
|
|
|
|
|
|
|
|
analyze_buttons = soup.find_all("button", string=re.compile(r"Analyze", re.IGNORECASE))
|
|
|
assert len(analyze_buttons) > 0, "Expected to find 'Analyze' buttons"
|
|
|
|
|
|
for button in analyze_buttons:
|
|
|
button_text = button.get_text().strip()
|
|
|
|
|
|
button_text = re.sub(r'[\U0001F300-\U0001F9FF]', '', button_text).strip()
|
|
|
assert "Analyze" in button_text or "Analysis" in button_text, \
|
|
|
f"Expected 'Analyze' or 'Analysis' in button, got: {button_text}"
|
|
|
|