"""Unit tests for the Lexical JSON → sx converter.""" from __future__ import annotations import sys import os import pytest # Add project root so shared.sx.html_to_sx resolves, plus the ghost dir. _project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) sys.path.insert(0, _project_root) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "bp", "blog", "ghost")) from lexical_to_sx import lexical_to_sx # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _doc(*children): """Wrap children in a minimal Lexical document.""" return {"root": {"children": list(children)}} def _text(s, fmt=0): return {"type": "text", "text": s, "format": fmt} def _paragraph(*children): return {"type": "paragraph", "children": list(children)} def _heading(tag, *children): return {"type": "heading", "tag": tag, "children": list(children)} def _link(url, *children): return {"type": "link", "url": url, "children": list(children)} # --------------------------------------------------------------------------- # Basic text # --------------------------------------------------------------------------- class TestBasicText: def test_empty_doc(self): result = lexical_to_sx(_doc()) assert result == '(<> (p ""))' def test_single_paragraph(self): result = lexical_to_sx(_doc(_paragraph(_text("Hello")))) assert result == '(p "Hello")' def test_two_paragraphs(self): result = lexical_to_sx(_doc( _paragraph(_text("Hello")), _paragraph(_text("World")), )) assert "(p " in result assert '"Hello"' in result assert '"World"' in result def test_heading(self): result = lexical_to_sx(_doc(_heading("h2", _text("Title")))) assert result == '(h2 "Title")' def test_h3(self): result = lexical_to_sx(_doc(_heading("h3", _text("Sub")))) assert result == '(h3 "Sub")' # --------------------------------------------------------------------------- # Formatting # --------------------------------------------------------------------------- class TestFormatting: def test_bold(self): result = lexical_to_sx(_doc(_paragraph(_text("hi", 1)))) assert "(strong " in result def test_italic(self): result = lexical_to_sx(_doc(_paragraph(_text("hi", 2)))) assert "(em " in result def test_strikethrough(self): result = lexical_to_sx(_doc(_paragraph(_text("hi", 4)))) assert "(s " in result def test_bold_italic(self): result = lexical_to_sx(_doc(_paragraph(_text("hi", 3)))) assert "(strong " in result assert "(em " in result def test_code(self): result = lexical_to_sx(_doc(_paragraph(_text("x", 16)))) assert "(code " in result # --------------------------------------------------------------------------- # Links # --------------------------------------------------------------------------- class TestLinks: def test_link(self): result = lexical_to_sx(_doc( _paragraph(_link("https://example.com", _text("click"))) )) assert '(a :href "https://example.com"' in result assert '"click"' in result # --------------------------------------------------------------------------- # Lists # --------------------------------------------------------------------------- class TestLists: def test_unordered_list(self): result = lexical_to_sx(_doc({ "type": "list", "listType": "bullet", "children": [ {"type": "listitem", "children": [_text("one")]}, {"type": "listitem", "children": [_text("two")]}, ] })) assert "(ul " in result assert "(li " in result assert '"one"' in result def test_ordered_list(self): result = lexical_to_sx(_doc({ "type": "list", "listType": "number", "children": [ {"type": "listitem", "children": [_text("first")]}, ] })) assert "(ol " in result # --------------------------------------------------------------------------- # Block elements # --------------------------------------------------------------------------- class TestBlocks: def test_hr(self): result = lexical_to_sx(_doc({"type": "horizontalrule"})) assert result == "(hr)" def test_quote(self): result = lexical_to_sx(_doc({ "type": "quote", "children": [_text("wisdom")] })) assert '(blockquote "wisdom")' == result def test_codeblock(self): result = lexical_to_sx(_doc({ "type": "codeblock", "code": "print('hi')", "language": "python" })) assert '(pre (code :class "language-python"' in result assert "print" in result # --------------------------------------------------------------------------- # Cards # --------------------------------------------------------------------------- class TestCards: def test_image(self): result = lexical_to_sx(_doc({ "type": "image", "src": "photo.jpg", "alt": "test" })) assert '(~kg_cards/kg-image :src "photo.jpg" :alt "test")' == result def test_image_wide_with_caption(self): result = lexical_to_sx(_doc({ "type": "image", "src": "p.jpg", "alt": "", "cardWidth": "wide", "caption": "Fig 1" })) assert ':width "wide"' in result assert ':caption "Fig 1"' in result def test_image_html_caption(self): result = lexical_to_sx(_doc({ "type": "image", "src": "p.jpg", "alt": "", "caption": 'Photo by Author' })) assert ':caption (<> "Photo by " (a :href "https://x.com" "Author"))' in result def test_bookmark(self): result = lexical_to_sx(_doc({ "type": "bookmark", "url": "https://example.com", "metadata": {"title": "Example", "description": "A site"} })) assert "(~kg_cards/kg-bookmark " in result assert ':url "https://example.com"' in result assert ':title "Example"' in result def test_callout(self): result = lexical_to_sx(_doc({ "type": "callout", "backgroundColor": "blue", "calloutEmoji": "💡", "children": [_text("Note")] })) assert "(~kg_cards/kg-callout " in result assert ':color "blue"' in result def test_button(self): result = lexical_to_sx(_doc({ "type": "button", "buttonText": "Click", "buttonUrl": "https://example.com" })) assert "(~kg_cards/kg-button " in result assert ':text "Click"' in result def test_toggle(self): result = lexical_to_sx(_doc({ "type": "toggle", "heading": "FAQ", "children": [_text("Answer")] })) assert "(~kg_cards/kg-toggle " in result assert ':heading "FAQ"' in result def test_html(self): result = lexical_to_sx(_doc({ "type": "html", "html": "
custom
" })) assert result == '(~kg_cards/kg-html (div "custom"))' def test_embed(self): result = lexical_to_sx(_doc({ "type": "embed", "html": "", "caption": "Video" })) assert "(~kg_cards/kg-embed " in result assert ':caption "Video"' in result def test_markdown(self): result = lexical_to_sx(_doc({ "type": "markdown", "markdown": "**bold** text" })) assert result.startswith("(~kg_cards/kg-md ") assert "(p " in result assert "(strong " in result def test_video(self): result = lexical_to_sx(_doc({ "type": "video", "src": "v.mp4", "cardWidth": "wide" })) assert "(~kg_cards/kg-video " in result assert ':width "wide"' in result def test_audio(self): result = lexical_to_sx(_doc({ "type": "audio", "src": "s.mp3", "title": "Song", "duration": 195 })) assert "(~kg_cards/kg-audio " in result assert ':duration "3:15"' in result def test_file(self): result = lexical_to_sx(_doc({ "type": "file", "src": "f.pdf", "fileName": "doc.pdf", "fileSize": 2100000 })) assert "(~kg_cards/kg-file " in result assert ':filename "doc.pdf"' in result assert "MB" in result def test_paywall(self): result = lexical_to_sx(_doc({"type": "paywall"})) assert result == "(~kg_cards/kg-paywall)" # --------------------------------------------------------------------------- # Escaping # --------------------------------------------------------------------------- class TestEscaping: def test_quotes_in_text(self): result = lexical_to_sx(_doc(_paragraph(_text('He said "hello"')))) assert '\\"hello\\"' in result def test_backslash_in_text(self): result = lexical_to_sx(_doc(_paragraph(_text("a\\b")))) assert "a\\\\b" in result # --------------------------------------------------------------------------- # JSON string input # --------------------------------------------------------------------------- class TestJsonString: def test_string_input(self): import json doc = _doc(_paragraph(_text("test"))) result = lexical_to_sx(json.dumps(doc)) assert '(p "test")' == result