# tests/test_hanging.py # Тесты для модуля висячей пунктуации (HangingPunctuationProcessor). import pytest from bs4 import BeautifulSoup from etpgrf.hanging import HangingPunctuationProcessor from etpgrf.config import ( CHAR_RU_QUOT1_OPEN, CHAR_RU_QUOT1_CLOSE, CHAR_EN_QUOT1_OPEN, CHAR_EN_QUOT1_CLOSE ) # Вспомогательная функция для создания soup def make_soup(html_str): return BeautifulSoup(html_str, 'html.parser') # Набор тестовых случаев в формате: # (режим, входной_html, ожидаемый_html) HANGING_TEST_CASES = [ # --- Режим 'left' (только левая пунктуация) --- ('left', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

'), ('left', f'

(Скобки)

', f'

(Скобки)

'), # Правая пунктуация игнорируется ('left', f'

Текст.

', f'

Текст.

'), # --- Режим 'right' (только правая пунктуация) --- ('right', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

'), ('right', f'

Текст.

', f'

Текст.

'), # Левая пунктуация игнорируется ('right', f'

(Скобки)

', f'

(Скобки)

'), # --- Режим 'both' (и левая, и правая) --- ('both', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Цитата{CHAR_RU_QUOT1_CLOSE}

'), ('both', f'

Текст.

', f'

Текст.

'), # Последовательность символов (точка + кавычка) ('both', f'

Текст.{CHAR_RU_QUOT1_CLOSE}

', f'

Текст.{CHAR_RU_QUOT1_CLOSE}

'), # Вложенные теги ('both', f'

{CHAR_RU_QUOT1_OPEN}Жирный{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Жирный{CHAR_RU_QUOT1_CLOSE}

'), # Смешанный контент ('both', f'

{CHAR_RU_QUOT1_OPEN}Начало курсив конец.{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Начало курсив конец.{CHAR_RU_QUOT1_CLOSE}

'), # --- Режим None / False (отключено) --- (None, f'

{CHAR_RU_QUOT1_OPEN}Текст{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Текст{CHAR_RU_QUOT1_CLOSE}

'), (False, f'

{CHAR_RU_QUOT1_OPEN}Текст{CHAR_RU_QUOT1_CLOSE}

', f'

{CHAR_RU_QUOT1_OPEN}Текст{CHAR_RU_QUOT1_CLOSE}

'), # --- Отсутствие висячих символов --- ('both', '

Простой текст без спецсимволов!

', '

Простой текст без спецсимволов!

'), # --- Проверка контекста (пробелы) --- # 1. Левая кавычка внутри слова (не должна висеть) ('both', f'

func{CHAR_RU_QUOT1_OPEN}arg{CHAR_RU_QUOT1_CLOSE}

', f'

func{CHAR_RU_QUOT1_OPEN}arg{CHAR_RU_QUOT1_CLOSE}

'), # Правая висит, т.к. конец узла # 2. Правая кавычка внутри слова (не должна висеть) ('both', f'

1{CHAR_RU_QUOT1_CLOSE}2

', f'

1{CHAR_RU_QUOT1_CLOSE}2

'), # 3. Левая кавычка после пробела (должна висеть) ('both', f'

func {CHAR_RU_QUOT1_OPEN}arg

', f'

func {CHAR_RU_QUOT1_OPEN}arg

'), # 4. Правая кавычка перед пробелом (должна висеть) ('both', f'

arg{CHAR_RU_QUOT1_CLOSE} next

', f'

arg{CHAR_RU_QUOT1_CLOSE} next

'), # 5. Точка внутри числа (не должна висеть) ('both', '

3.14

', '

3.14

'), # 6. Точка в конце предложения (должна висеть) ('both', '

End.

', '

End.

'), ] @pytest.mark.parametrize("mode, input_html, expected_html", HANGING_TEST_CASES) def test_hanging_punctuation_processor(mode, input_html, expected_html): """ Проверяет работу HangingPunctuationProcessor в различных режимах. """ # Arrange processor = HangingPunctuationProcessor(mode=mode) soup = make_soup(input_html) # Act processor.process(soup) actual_html = str(soup) # Assert assert actual_html == expected_html def test_hanging_punctuation_target_tags(): """ Отдельный тест для проверки работы со списком целевых тегов. """ mode = ['blockquote', 'h1'] input_html = (f'
{CHAR_RU_QUOT1_OPEN}Игнор{CHAR_RU_QUOT1_CLOSE}
' f'
{CHAR_RU_QUOT1_OPEN}Обработка{CHAR_RU_QUOT1_CLOSE}
' f'

{CHAR_RU_QUOT1_OPEN}Заголовок{CHAR_RU_QUOT1_CLOSE}

') expected_html = (f'
{CHAR_RU_QUOT1_OPEN}Игнор{CHAR_RU_QUOT1_CLOSE}
' f'
{CHAR_RU_QUOT1_OPEN}Обработка{CHAR_RU_QUOT1_CLOSE}
' f'

{CHAR_RU_QUOT1_OPEN}Заголовок{CHAR_RU_QUOT1_CLOSE}

') processor = HangingPunctuationProcessor(mode=mode) soup = make_soup(input_html) processor.process(soup) assert str(soup) == expected_html