"""app.text_content.parse_rich_text -- pure parsing logic, no HTTP, no DB. This is the sanitization boundary for the text widget's dialog (widget_dialog_text.js posts contenteditable innerHTML here, see routers/api_widgets.py's api_widget_config_save "text" branch); the main thing under test is that only recognized style flags survive and everything else (unknown tags/attributes, unparseable colors, excess length) is silently dropped rather than round-tripped.""" from __future__ import annotations from app.text_content import MAX_TOTAL_CHARS, has_text, parse_rich_text def test_plain_text_is_one_paragraph_one_run(): paragraphs = parse_rich_text("Hello world") assert paragraphs == [[{"text": "Hello world", "bold": False, "italic": False, "underline": False, "color": None, "bg": None}]] def test_bold_italic_underline_tags(): paragraphs = parse_rich_text("bold italic under") runs = paragraphs[0] assert runs[0]["bold"] is True and runs[0]["text"] == "bold" assert runs[2]["italic"] is True and runs[2]["text"] == "italic" assert runs[4]["underline"] is True and runs[4]["text"] == "under" def test_strong_and_em_are_treated_like_b_and_i(): paragraphs = parse_rich_text("bolditalic") runs = paragraphs[0] assert runs[0]["bold"] is True assert runs[1]["italic"] is True def test_span_style_color_and_background(): html = 'hi' run = parse_rich_text(html)[0][0] assert run["color"] == "#cf000f" assert run["bg"] == "#ffdb00" def test_span_style_font_weight_and_style_and_decoration(): html = 'x' run = parse_rich_text(html)[0][0] assert run["bold"] is True assert run["italic"] is True assert run["underline"] is True def test_font_tag_color_attribute(): run = parse_rich_text('green')[0][0] assert run["color"] == "#00ff00" def test_short_hex_color_expands(): run = parse_rich_text('red')[0][0] assert run["color"] == "#ff0000" def test_unparseable_color_is_dropped(): run = parse_rich_text('x')[0][0] assert run["color"] is None def test_nested_styles_combine(): run = parse_rich_text("both")[0][0] assert run["bold"] is True assert run["italic"] is True def test_style_does_not_leak_past_closing_tag(): paragraphs = parse_rich_text("boldplain") runs = paragraphs[0] assert runs[0]["bold"] is True assert runs[1]["bold"] is False def test_div_per_line_becomes_separate_paragraphs(): paragraphs = parse_rich_text("