48 lines
2.0 KiB
Python
48 lines
2.0 KiB
Python
"""Comprueba enlaces y anclas del HTML compilado, sin dependencias externas."""
|
|
from html.parser import HTMLParser
|
|
from pathlib import Path
|
|
from urllib.parse import unquote, urlsplit
|
|
|
|
ROOT = Path(__file__).resolve().parents[1] / 'dist'
|
|
class Document(HTMLParser):
|
|
def __init__(self, source):
|
|
super().__init__()
|
|
self.ids = set()
|
|
self.links = []
|
|
self.feed(source)
|
|
def handle_starttag(self, tag, attrs):
|
|
attrs = dict(attrs)
|
|
if 'id' in attrs:
|
|
self.ids.add(attrs['id'])
|
|
if tag == 'a' and 'href' in attrs:
|
|
self.links.append(attrs['href'])
|
|
|
|
pages = {p: Document(p.read_text()) for p in ROOT.rglob('*.html')}
|
|
if ROOT / 'index.html' not in pages:
|
|
raise SystemExit('Primero ejecuta npm run build.')
|
|
errors = set()
|
|
count = 0
|
|
for page, document in pages.items():
|
|
for href in document.links:
|
|
url = urlsplit(href)
|
|
if url.scheme or url.netloc:
|
|
continue
|
|
dest = (ROOT / unquote(url.path.lstrip('/')) if url.path.startswith('/')
|
|
else page.parent / unquote(url.path) if url.path else page)
|
|
if dest.is_dir():
|
|
dest /= 'index.html'
|
|
dest = dest.resolve()
|
|
count += 1
|
|
if not dest.exists():
|
|
errors.add(f'{page.relative_to(ROOT)} → {href}: archivo ausente')
|
|
elif url.fragment and dest in pages and unquote(url.fragment) not in pages[dest].ids:
|
|
errors.add(f'{page.relative_to(ROOT)} → {href}: ancla ausente')
|
|
expected = {'administrador', 'solicitante', 'comprador', 'autorizador', 'almacen', 'contabilidad', 'proveedor'}
|
|
actual = {x.strip('/').split('/')[1] for x in pages[ROOT / 'index.html'].links if x.startswith('/roles/')}
|
|
if actual != expected:
|
|
errors.add(f'Los accesos de inicio no coinciden con los siete roles: {actual}')
|
|
print(f'{len(pages)} páginas HTML; {count} enlaces internos; {len(errors)} errores.')
|
|
for error in sorted(errors):
|
|
print(error)
|
|
raise SystemExit(bool(errors))
|