# tests/test_typograph.py # Тестирует основной класс Typographer и его конвейер обработки. import pytest from etpgrf import Typographer from etpgrf.config import CHAR_NBSP, CHAR_THIN_SP, CHAR_NDASH, CHAR_MDASH, SANITIZE_ETPGRF, SANITIZE_ALL_HTML TYPOGRAPHER_HTML_TEST_CASES = [ # --- Базовая обработка без HTML --- ('mnemonic', 'Простой текст с "кавычками".', f'Простой текст с «кавычками».'), ('mixed', 'Простой текст с "кавычками".', f'Простой текст с «кавычками».'), ('unicode', 'Простой текст с "кавычками".', f'Простой текст с{CHAR_NBSP}«кавычками».'), # --- Базовая обработка с HTML --- ('mnemonic', '
Простой параграф с «кавычками».
', 'Простой параграф с «кавычками».
'), ('mixed', 'Простой параграф с "кавычками".
', 'Простой параграф с «кавычками».
'), ('unicode', 'Простой параграф с "кавычками".
', f'Простой параграф с{CHAR_NBSP}«кавычками».
'), # --- Рекурсивный обход --- ('mnemonic', 'Текст, а внутри для проверки "жирный" текст.
Текст, а внутри для проверки «жирный» текст.
Текст, а внутри для проверки "жирный" текст.
Текст, а внутри для проверки «жирный» текст.
Текст, а внутри для проверки "жирный" текст.
Текст, а{CHAR_NBSP}внутри для{CHAR_NBSP}проверки «жирный» текст.
Текст с предлогом в доме.
Текст с предлогом в доме.
Текст с предлогом в доме.
Текст с предлогом в доме.
Текст с предлогом в доме.
Текст с{CHAR_NBSP}предлогом в{CHAR_NBSP}доме.
Союз и слово и еще один союз а текст.
', 'Союз и слово и еще один союз а текст.
'), ('mixed', 'Союз и слово и еще один союз а текст.
', 'Союз и слово и еще один союз а текст.
'), ('unicode', 'Союз и слово и еще один союз а текст.
', f'Союз и{CHAR_NBSP}слово и{CHAR_NBSP}еще один союз а{CHAR_NBSP}текст.
'), # --- Проверка тегов ', 'Текст «до».
'), ('mixed', 'Текст "до".
', 'Текст «до».
'), ('mixed', 'Текст "до".
Ctrl + C', 'Текст «до».
Ctrl + C'), ('mixed', 'Текст "до".
Sample "text"', 'Текст «до».
Sample "text"'), ('mixed', 'Текст "до".
', 'Текст «до».
'), # --- Проверка тегов с атрибутами --- ('mixed', 'Текст "снаружи"', 'Текст «снаружи»'), ('mixed', 'Текст "снаружи"', 'Текст «снаружи»'), ('mixed', 'Текст "снаружи"', 'Текст «снаружи»'), ('mnemonic', 'Текст "снаружи"', 'Текст «снаружи»'), # --- Комплексный интеграционный тест --- ('mnemonic', 'Он сказал: "В 1941-1945 гг. -- было 100 тыс. руб. и т. д."
', 'Он сказал: «В 1941–1945 гг. – было 100 тыс. руб.' ' и т. д.»
'), ('mixed', 'Он сказал: "В 1941-1945 гг. -- было 100 тыс. руб. и т. д."
', 'Он сказал: «В 1941–1945 гг. – было 100 тыс. руб.' ' и т. д.»
'), ('unicode', 'Он сказал: "В 1941-1945 гг. -- было 100 тыс. руб. и т. д."
', f'Он{CHAR_NBSP}сказал: «В{CHAR_NBSP}1941{CHAR_NDASH}1945{CHAR_NBSP}гг.{CHAR_NBSP}{CHAR_NDASH} было' f' 100{CHAR_NBSP}тыс.{CHAR_THIN_SP}руб. и{CHAR_NBSP}т.{CHAR_THIN_SP}д.»
'), # --- Теги внутри кавычек --- ('mnemonic', '"Почему", "зачем" и "кому это выгодно" -- вопросы требующие ответа.
', '«Почему», «зачем» и «кому это выгодно' '» – вопросы требующие ответа.
'), ('mixed', '"Почему", "зачем" и "кому это выгодно" -- вопросы требующие ответа.
', '«Почему», «зачем» и «кому это выгодно» – вопросы требующие ответа.
'), ('unicode', '"Почему", "зачем" и "кому это выгодно" -- вопросы требующие ответа.
', f'«Почему», «зачем» и{CHAR_NBSP}«кому это выгодно»{CHAR_NBSP}{CHAR_NDASH} вопросы требующие ответа.
'), # --- Проверка пустого текста и узлов с пробелами --- ('mnemonic', '
Слово
', '
Слово
'), ('mixed', '
Слово
', '
Слово
'), ('unicode', '
Слово
', '
Слово
'), # --- Самозакрывающиеся теги и теги с атрибутами --- # ВАЖНО: 1. Порядок атрибутов в типографированном тексте может быть произвольным # 2. Любое число пробельных символов внутри "пустых" тегов будут редуцированы до одного пробела или # перевода строки. # 3. Самозакрывающиеся теги будут приведены к единому виду с косой чертой в конце. ТипаТекст с картинкой
и текстом.
Текст с картинкой
и текстом.
Текст с <br>
А это новая строка.
Текст с <br>
А это новая строка.
Текст с картинкой
и текстом.
Текст с картинкой
и текстом.
Текст с <br>
А это новая строка.
Текст с <br>
А это новая строка.
Текст с картинкой
и текстом.
Текст с{CHAR_NBSP}картинкой
и{CHAR_NBSP}текстом.
Текст с <br>
А это новая строка.
Текст с{CHAR_NBSP}<br>
А{CHAR_NBSP}это новая строка.
Текст со "старой" разметкой.
' # Ожидаем, что "старая" разметка будет удалена, а "новая" (кавычки-елочки) будет добавлена. expected_html = 'Текст со «старой» разметкой.
' typo = Typographer(langs='ru', process_html=True, sanitizer=SANITIZE_ETPGRF, mode='mixed') actual_html = typo.process(input_html) assert actual_html == expected_html def test_typographer_sanitizer_all_html_integration(): """ Интеграционный тест: проверяет, что Typographer вызывает Sanitizer для полной очистки HTML. """ input_html = 'Текст с "кавычками" и жирным текстом.
' # Ожидаем, что все теги будут удалены, а к чистому тексту применится типографика. expected_text = 'Текст с «кавычками» и жирным текстом.' typo = Typographer(langs='ru', process_html=True, sanitizer=SANITIZE_ALL_HTML, mode='mixed') actual_text = typo.process(input_html) assert actual_text == expected_text # --- Новые тесты на структуру HTML (проверка отсутствия лишних оберток) --- HTML_STRUCTURE_TEST_CASES = [ # 1. Фрагмент HTML (без html/body) -> должен остаться фрагментом ('Текст
', 'Текст
'), # 2. Голый текст -> должен остаться голым текстом (без, ,
) ('Текст без тегов', 'Текст без тегов'), # Исправлено: ожидаем nbsp ('Текст с тегом внутри', 'Текст с тегом внутри'), # 3. Полноценный html-документ -> должен сохранить структуру ('Текст
', 'Текст
'), ('Текст
', 'Текст
'), # BS может добавить перенос строки после doctype # 4. Кривой html -> будет "починен" ('Текст', '
Текст
'), ('Текст жирный курсив', 'Текст жирный курсив'), # Используем валидный HTML для теста с DOCTYPE ('Текст
', 'Текст
'), # Тест на совсем кривой HTML (см ниже) не проходит: весь текст после незарытогоТекст', '
Текст
'), ] @pytest.mark.parametrize("input_html, expected_html", HTML_STRUCTURE_TEST_CASES) def test_typographer_html_structure_preservation(input_html, expected_html): """ Проверяет, что Typographer не добавляет лишние теги (html, body, p) вокруг фрагментов и текста, но сохраняет их, если они были. """ # Отключаем все "украшательства" (кавычки, неразрывные пробелы), # чтобы проверять только структуру тегов. typo = Typographer( langs='ru', process_html=True, mode='mixed', hyphenation=False, quotes=False, unbreakables=True, # Оставим unbreakables, чтобы проверить, что добавляются, но теги не ломаются layout=False, symbols=False ) actual_html = typo.process(input_html) # Для теста с doctype может быть нюанс с форматированием (переносы строк), # поэтому нормализуем пробелы перед сравнением if '' in actual_html assert '' in actual_html assert 'Текст
' in actual_html else: assert actual_html == expected_html