170 lines
6.5 KiB
Python
170 lines
6.5 KiB
Python
"""
|
|
Regression tests for inline content inside block containers (spec S1).
|
|
|
|
Inline tags are registered to ignore_handler because they are meant to be
|
|
consumed by extract_text_content. Only <p> and <h1>-<h6> ever called it, so
|
|
every other container - div, li, td, th, blockquote - iterated its children as
|
|
blocks, and inline tags returned None. Their text was silently discarded, and
|
|
bare text nodes each became a separate paragraph.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from pyWebLayout.abstract.block import (
|
|
HList,
|
|
Paragraph,
|
|
Quote,
|
|
Table,
|
|
)
|
|
from pyWebLayout.abstract.inline import LinkedWord, Word
|
|
from pyWebLayout.io.readers.html_extraction import parse_html_string
|
|
|
|
|
|
def words_of(block):
|
|
return [w.text for w in getattr(block, 'words', [])]
|
|
|
|
|
|
def all_words(blocks):
|
|
out = []
|
|
for block in blocks:
|
|
out.extend(words_of(block))
|
|
return out
|
|
|
|
|
|
def cell_blocks(table):
|
|
for _, row in table.all_rows():
|
|
for cell in row.cells():
|
|
yield list(cell.blocks())
|
|
|
|
|
|
EXPECTED = ["hello", "world", "again"]
|
|
|
|
|
|
class TestInlineContentIsKept:
|
|
"""The same markup must survive in every container."""
|
|
|
|
def test_paragraph_control(self):
|
|
"""<p> already worked - this is the reference behaviour."""
|
|
blocks = parse_html_string("<p>hello <b>world</b> again</p>")
|
|
assert all_words(blocks) == EXPECTED
|
|
|
|
def test_div(self):
|
|
blocks = parse_html_string("<div>hello <b>world</b> again</div>")
|
|
assert all_words(blocks) == EXPECTED
|
|
|
|
def test_list_item(self):
|
|
blocks = parse_html_string("<ul><li>hello <b>world</b> again</li></ul>")
|
|
hlist = next(b for b in blocks if isinstance(b, HList))
|
|
item = list(hlist.items())[0]
|
|
assert all_words(item.blocks()) == EXPECTED
|
|
|
|
def test_table_cell(self):
|
|
blocks = parse_html_string(
|
|
"<table><tr><td>hello <b>world</b> again</td></tr></table>")
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
assert all_words(next(cell_blocks(table))) == EXPECTED
|
|
|
|
def test_table_header_cell(self):
|
|
blocks = parse_html_string(
|
|
"<table><tr><th>hello <b>world</b> again</th></tr></table>")
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
assert all_words(next(cell_blocks(table))) == EXPECTED
|
|
|
|
def test_blockquote(self):
|
|
blocks = parse_html_string("<blockquote>hello <b>world</b> again</blockquote>")
|
|
quote = next(b for b in blocks if isinstance(b, Quote))
|
|
assert all_words(quote.blocks()) == EXPECTED
|
|
|
|
|
|
class TestInlineRunsCoalesce:
|
|
"""A run of inline content is one paragraph, not one per text node."""
|
|
|
|
def test_div_yields_a_single_paragraph(self):
|
|
blocks = parse_html_string("<div>a <b>b</b> c</div>")
|
|
paragraphs = [b for b in blocks if isinstance(b, Paragraph)]
|
|
assert len(paragraphs) == 1, f"expected one paragraph, got {len(blocks)} blocks"
|
|
assert words_of(paragraphs[0]) == ["a", "b", "c"]
|
|
|
|
def test_cell_yields_a_single_paragraph(self):
|
|
blocks = parse_html_string("<table><tr><td>a <b>b</b> c</td></tr></table>")
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
cell = next(cell_blocks(table))
|
|
assert len(cell) == 1
|
|
assert words_of(cell[0]) == ["a", "b", "c"]
|
|
|
|
def test_block_child_splits_the_run(self):
|
|
"""Inline runs either side of a block child stay separate, in order."""
|
|
blocks = parse_html_string(
|
|
"<table><tr><td>before<p>middle</p>after</td></tr></table>")
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
cell = next(cell_blocks(table))
|
|
assert [words_of(b) for b in cell] == [["before"], ["middle"], ["after"]]
|
|
|
|
def test_line_break_splits_the_run(self):
|
|
blocks = parse_html_string("<div>first<br>second</div>")
|
|
paragraphs = [b for b in blocks if isinstance(b, Paragraph)]
|
|
assert [words_of(p) for p in paragraphs] == [["first"], ["second"]]
|
|
|
|
def test_whitespace_between_blocks_makes_no_paragraph(self):
|
|
blocks = parse_html_string("<div>\n <p>one</p>\n <p>two</p>\n</div>")
|
|
assert [words_of(b) for b in blocks] == [["one"], ["two"]]
|
|
|
|
|
|
class TestLinksSurvive:
|
|
"""<a href> must produce LinkedWord wherever it appears."""
|
|
|
|
def test_link_in_cell(self):
|
|
blocks = parse_html_string(
|
|
'<table><tr><td><a href="http://x">link</a> text</td></tr></table>')
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
cell = next(cell_blocks(table))
|
|
found = [w for b in cell for w in getattr(b, 'words', [])]
|
|
|
|
assert [w.text for w in found] == ["link", "text"]
|
|
linked = [w for w in found if isinstance(w, LinkedWord)]
|
|
assert len(linked) == 1
|
|
assert linked[0].location == "http://x"
|
|
|
|
def test_link_in_div(self):
|
|
blocks = parse_html_string('<div>see <a href="#s2">Section 2</a> now</div>')
|
|
found = [w for b in blocks for w in getattr(b, 'words', [])]
|
|
assert [w.text for w in found] == ["see", "Section", "2", "now"]
|
|
assert all(isinstance(w, LinkedWord) for w in found[1:3])
|
|
|
|
def test_link_in_list_item(self):
|
|
blocks = parse_html_string('<ul><li><a href="u">click</a> here</li></ul>')
|
|
hlist = next(b for b in blocks if isinstance(b, HList))
|
|
item = hlist._items[0]
|
|
found = [w for b in item.blocks() for w in getattr(b, 'words', [])]
|
|
assert [w.text for w in found] == ["click", "here"]
|
|
assert isinstance(found[0], LinkedWord)
|
|
|
|
|
|
class TestNestedContainers:
|
|
|
|
def test_div_in_div(self):
|
|
blocks = parse_html_string("<div>outer <div>inner</div> tail</div>")
|
|
assert [words_of(b) for b in blocks] == [["outer"], ["inner"], ["tail"]]
|
|
|
|
def test_block_children_still_pass_through(self):
|
|
blocks = parse_html_string("<div><h1>Title</h1><p>Body</p></div>")
|
|
assert len(blocks) == 2
|
|
assert words_of(blocks[0]) == ["Title"]
|
|
assert words_of(blocks[1]) == ["Body"]
|
|
|
|
def test_cell_containing_a_list(self):
|
|
blocks = parse_html_string(
|
|
"<table><tr><td>intro<ul><li>item</li></ul></td></tr></table>")
|
|
table = next(b for b in blocks if isinstance(b, Table))
|
|
cell = next(cell_blocks(table))
|
|
assert isinstance(cell[0], Paragraph)
|
|
assert words_of(cell[0]) == ["intro"]
|
|
assert isinstance(cell[1], HList)
|
|
|
|
|
|
class TestComments:
|
|
|
|
def test_comment_text_is_not_content(self):
|
|
blocks = parse_html_string("<div>real<!-- hidden note -->text</div>")
|
|
assert all_words(blocks) == ["real", "text"]
|