diff --git a/src/jira_hub/modules/wiki/convert.py b/src/jira_hub/modules/wiki/convert.py index 8876b5d..b6c24cf 100644 --- a/src/jira_hub/modules/wiki/convert.py +++ b/src/jira_hub/modules/wiki/convert.py @@ -103,9 +103,18 @@ def _strip_private(soup) -> None: el[key] = _PRIVATE.sub("", value) +def _strip_editor_ui(soup) -> None: + """Колонка управления строками таблицы редактора Eva (TinyMCE: номер строки, «+», точка) сохраняется в HTML + страницы блоком `table-row-controls` и давала абзацы «№», «1», «2»… перед таблицей (#95). Блок убирается целиком; + прочие `mceNonEditable` остаются: у карточек вложений тот же класс, в них ссылки на файлы.""" + for el in soup.select(".table-row-controls"): + el.decompose() + + def to_markdown(html: str, attachments: list[dict] | None, links: list[dict] | None) -> tuple[str, list[dict]]: """HTML страницы → (Markdown, вложения страницы [{url, name}]).""" soup = BeautifulSoup(html, "html.parser") + _strip_editor_ui(soup) _strip_private(soup) conv = _Converter(attachments or [], links or [], heading_style="ATX", autolinks=False) md = conv.convert_soup(soup).strip() + "\n" diff --git a/tests/fixtures/wiki/page_table_controls.html b/tests/fixtures/wiki/page_table_controls.html new file mode 100644 index 0000000..d4eb503 --- /dev/null +++ b/tests/fixtures/wiki/page_table_controls.html @@ -0,0 +1,38 @@ +

Замечания к модели

+
+ model.dbml +
+
+
+
+ + + + + + + +

№

Замечание

Решение

1

Первое замечание

Первое решение

2

Второе замечание

Второе решение

2-бис

Дополнение

Принято

+
+
+
+
+
+
№
+
+
+
+
+
1
+
+
+
+
2
+
+
+
+
3
+
+
+
+

Текст после таблицы.

diff --git a/tests/test_wiki_convert.py b/tests/test_wiki_convert.py index 496484b..632d60c 100644 --- a/tests/test_wiki_convert.py +++ b/tests/test_wiki_convert.py @@ -87,6 +87,29 @@ def test_fill_ids(): assert convert.fill_ids("[x](attachment:@1)", [7, 9]) == "[x](attachment:9)" +# Колонка управления строками таблицы редактора Eva (TinyMCE, `table-row-controls`) попадает в HTML страницы и +# раньше давала абзацы «№», «1», «2»… перед таблицей (#95) +CARD_REF = "/files/obj/CmfDocument/CmfDocument%3Aa08/CmfDocument%3A3f2a9c1e-0b7d-4e8a-9c21-5d6e7f8a9b0c/model.dbml?version=1" + + +def test_table_row_controls_removed(): + md, _ = convert.to_markdown(_html("page_table_controls.html"), [], []) + lines = [line.strip() for line in md.splitlines() if line.strip()] + assert not [line for line in lines if re.fullmatch(r"№|\d{1,3}", line)], lines + assert "| № | Замечание | Решение |" in lines + assert "| 2-бис | Дополнение | Принято |" in lines + assert "Текст после таблицы." in lines + + +def test_card_preview_kept_with_controls(): + md, atts = convert.to_markdown( + _html("page_table_controls.html"), + [{"ref": CARD_REF, "url": "https://eva.example.test" + CARD_REF, "name": "model.dbml"}], + [], + ) + assert "[📎 model.dbml](attachment:@0)" in md and len(atts) == 1 + + # --- fix round 1 (review Task 6: I1, I2, I3, M3, M4) --- def test_unsafe_schemes_dropped_from_links():