"""
def test_parse_does_not_capture_span_outside_element(gen):
rows = gen.parse(PAGE)
assert rows == [
{"name": "Back Squat", "category": "SQUAT", "exercise": "BACK_SQUAT"}
]
def test_parse_keeps_unclosed_items_separate(gen):
# No after the first item: the next
start tag is the
# boundary, so the attribute-only item must not merge with (or borrow
# the span of) the following one.
rows = gen.parse(
'
"
)
monkeypatch.setattr("sys.argv", ["generate_exercises.py", str(html_file)])
with pytest.raises(SystemExit) as exc_info:
gen.main()
# The refusal must name positions only, never the suspected text.
assert "user@example.com" not in str(exc_info.value)
assert "session data" in str(exc_info.value)
@pytest.mark.parametrize(
"label",
[
"see HTTPS://EXAMPLE.COM/x", # uppercase URL scheme
"id 9F8E7D6C-5B4A-4C3D-8E9F-0A1B2C3D4E5F", # uppercase GUID
],
)
def test_suspect_regex_is_case_insensitive(gen, label):
assert gen.SUSPECT.search(label)
def test_parse_accumulates_text_across_nested_tags(gen):
# A nested inside the span splits the label into two handle_data
# calls; the "https://" prefix must not be dropped, or the SUSPECT
# filter never sees the full (sensitive) label.
rows = gen.parse(
'
'
"https://host/?ticket=secret
"
)
assert rows == [
{
"name": "https://host/?ticket=secret",
"category": "X",
"exercise": "Y",
}
]
assert gen.SUSPECT.search(rows[0]["name"])
def test_parse_flushes_final_item_without_closing_tag(gen):
# HTMLParser.close() does not synthesize a missing ; the last
# item in the source HTML must still be captured.
rows = gen.parse(
'