mews.page/tests/test_lint_dtd.py

79 lines
2.7 KiB
Python
Raw Normal View History

"""The DTD check, and the mapping from libxml2's messages to spec sections.
One test per message shape, so the mapping table in mews.lint stays pinned to
what libxml2 actually says.
"""
import pytest
from mews.lint import validate_bytes
# fixture name -> (section, level, code)
CASES = {
"script": ("4.1", "must", "dtd-element"),
"div": ("4.1", "must", "dtd-element"),
"sup": ("4.1", "must", "dtd-element"),
"style-element": ("5", "must", "dtd-styling"),
"style-attr": ("5", "must", "dtd-styling"),
"class-on-p": ("4.1", "must", "dtd-attribute"),
"img-width": ("4.1", "must", "dtd-attribute"),
"img-no-alt": ("4.1", "must", "dtd-required-attribute"),
"input-type-email": ("4.1", "must", "dtd-value"),
"nested-table": ("4.2", "must", "dtd-nested-table"),
"table-in-li": ("4.2", "must", "dtd-nested-table"),
"bad-body-class": ("5.3", "must", "dtd-variant"),
"empty-body": ("4.1", "must", "dtd-content"),
"head-link-before-meta": ("3.3", "must", "dtd-head"),
"link-bad-rel": ("3.3", "must", "link-rel"),
}
@pytest.mark.parametrize(("name", "expected"), CASES.items())
def test_dtd_failure_maps_to_its_section(fail_page, name, expected):
report = validate_bytes(fail_page(name))
section, level, code = expected
match = [f for f in report.findings if f.code == code]
assert match, f"{name}: no {code} finding in {[f.code for f in report.findings]}"
assert match[0].section == section
assert match[0].level == level
assert not report.conforms
def test_one_mistake_is_one_finding(fail_page):
"""A <script> makes libxml2 say three things; the author sees one."""
report = validate_bytes(fail_page("script"))
assert [f.code for f in report.findings] == ["dtd-element"]
def test_findings_carry_a_line_number(fail_page):
report = validate_bytes(fail_page("div"))
assert report.findings[0].location.startswith("line ")
def test_messages_name_the_element(fail_page):
report = validate_bytes(fail_page("div"))
assert "<div>" in report.findings[0].message
def test_enumerated_values_come_from_the_dtd(fail_page):
report = validate_bytes(fail_page("input-type-email"))
message = report.findings[0].message
assert "text, password, checkbox, radio, submit, reset, hidden" in message
def test_unmapped_message_still_gets_a_section():
from mews.lint import _map_error
code, message = _map_error("Something libxml2 made up", set())
assert code == "dtd-other"
assert "Mews DTD" in message
def test_entities_the_doctype_defines_are_resolved(conforming):
report = validate_bytes(conforming(body="<p>a&nbsp;b&mdash;c</p>"))
assert report.conforms
def test_a_conforming_page_passes(conforming):
assert validate_bytes(conforming()).conforms