validator/ holds mewslint (lint, css, fetch, DTD/CSS data); directory/ holds the mews.page service (app, db, check, pages, cli) and depends on validator/; quickstart/ holds mews-quickstart and md2mews.py for third parties who want to create Mews content without cloning the repo; spec/ consolidates doc/ and dtd/ with the canonical mews-0.1.css; site/ becomes a Pelican project with the forward-facing pages as Markdown, the check form kept as a raw HTML snippet. Root pyproject.toml is now a virtual uv workspace over validator, directory and quickstart, with cross-cutting tests that span more than one of them staying at the repo root. Makefile and flake.nix updated to match.
78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""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 b—c</p>"))
|
|
assert report.conforms
|
|
|
|
|
|
def test_a_conforming_page_passes(conforming):
|
|
assert validate_bytes(conforming()).conforms
|