One.
" b"Two.
" b""""Parser tests.""" import time from lxml import html from openbb_news import parser def test_strip_html_empty(): assert parser.strip_html("") == "" def test_strip_html_plain(): assert parser.strip_html("hello world") == "hello world" def test_strip_html_tags_and_whitespace(): assert parser.strip_html("
Hello world\n!
") == "Hello world !" def test_html_to_markdown_empty(): assert parser.html_to_markdown("") == "" def test_html_to_markdown_preserves_anchor_links(): out = parser.html_to_markdown( 'NYT headline ' 'BBC headline' ) assert "[NYT headline](https://nyt.test/x)" in out assert "[BBC headline](https://bbc.test/x)" in out def test_html_to_markdown_strips_non_anchor_tags(): out = parser.html_to_markdown("Hello bold world
") assert "bold" in out assert "<" not in out def test_html_to_markdown_anchor_without_href(): out = parser.html_to_markdown('just text') assert "just text" in out assert "[" not in out def test_html_to_markdown_anchor_without_text(): out = parser.html_to_markdown('') assert "[" not in out def test_html_to_markdown_handles_paragraphs_and_breaks(): out = parser.html_to_markdown("Line one
Line two
") assert "Line one" in out assert "Line two" in out assert "Line one" in out.split("\n")[0] def test_html_to_markdown_handles_br(): out = parser.html_to_markdown("Firstparagraph
") assert "intro text" in out assert "paragraph" in out lines = [line for line in out.split("\n") if line.strip()] assert lines.index("intro text") < lines.index("paragraph") def test_html_to_markdown_lxml_parse_failure_falls_back(monkeypatch): from lxml import html as _html def boom(*_a, **_kw): raise ValueError("synthetic parse failure") monkeypatch.setattr(_html, "fragment_fromstring", boom) out = parser.html_to_markdown('link') assert out == "link" def test_truncate_short(): assert parser.truncate("abc", limit=10) == "abc" def test_truncate_long(): text = "word " * 100 truncated = parser.truncate(text, limit=50) assert len(truncated) <= 60 assert truncated.endswith("…") def test_struct_time_to_iso_none(): iso = parser.struct_time_to_iso(None) assert "T" in iso and iso.endswith("+00:00") def test_struct_time_to_iso_value(): st = time.struct_time((2026, 5, 19, 12, 0, 0, 0, 139, 0)) assert parser.struct_time_to_iso(st) == "2026-05-19T12:00:00+00:00" def test_article_markdown_joins_with_blank_lines(sample_article_html): doc = html.fromstring(sample_article_html) article = doc.xpath("//article")[0] text = parser._article_markdown(article) assert "First paragraph of the article." in text assert text.count("\n\n") == 2 def test_article_markdown_requires_two_paragraphs(): doc = html.fromstring(b"only one paragraph
Real opening paragraph with content.
" b"2:26
" b"12:44
" b"0:58
" b"1:30:00
" b"Real closing paragraph.
" b"The video begins at 2:26 with the host introducing.
" b"By 12:44 the segment closes.
" b"One.
" b"Two.
" b"One.
Two.
One.
" b'
Two.
" b"kept one
kept two
First sentence.
Second sentence.
Related card one.
Related card two.
Main story paragraph one with more substance.
" b"Main story paragraph two with detail.
" b"Main story paragraph three closing the piece.
" b"Story paragraph one with content.
" b"Story paragraph two with more detail.
Only one paragraph here.
Main paragraph one.
Main paragraph two.
" b"Main paragraph three.
Main paragraph four.
" b"Article paragraph one.
Article paragraph two.
[[ type === 'moc' ? 'MARKET ON CLOSE' : 'FREE WEBINAR' ]]
" b"[[ timeLabel ]] WATCH LIVE
" b"banner text
banner two
Real story paragraph.
Story continues here.
Fallback one.
Fallback two.
Graph fallback one.
Graph fallback two.
Clean paragraph one.
Clean paragraph two.
The Court’s ruling — issued Tuesday — was clear.
" "Dominion’s lawyers said “we won”.
" "Opening paragraph.
" b'
'
b"Closing paragraph.
" b"Lead in.
" b'
'
b"After the photo.
" b"Paragraph one.
Paragraph two.
" b"Lead.
" b'
'
b"Body.
" b"Lead
'
)
assert "" in out
assert "Lead" in out
def test_html_to_markdown_img_tail_text():
out = parser.html_to_markdown(
'
trailing text after img'
)
assert "" in out
assert "trailing text after img" in out
def test_jsonld_field_list_of_strings():
payload = (
b""
b'"
)
body = parser._extract_body(payload)
assert body is None or body.startswith("")
def test_jsonld_field_list_of_image_objects():
payload = (
b""
b'"
b"One.
Two.
One.
Two.
Keep one.
" b"" b"Keep two.
" b"Newsletter pitch.
" b"One.
Two.
One.
Two.
") assert parser._site_body(doc, parser._SITE_RULES["drugs.com"]) == "" def test_site_body_picks_largest_container(): doc = html.fromstring( b"" b"Short one.
Short two.
Longer container paragraph one with much more substance.
" b"Longer container paragraph two with additional detail.
[[ template ]] junk
{{ binding }} more junk
" b"Lede paragraph of the story.
Second paragraph.
" b"Generic paragraph one.
Generic paragraph two.
" b"One.
Two.