Pagina camera 8765: i quattro visori diventano tmf-viewer, JS serviti su /visore/

Refs #122

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 22:03:58 +01:00
parent 43ef22079d
commit 84fc1a08f7
4 changed files with 245 additions and 244 deletions
+87 -80
View File
@@ -1,80 +1,87 @@
"""Test della pagina Acquisizione camera (#87): contenuto statico di PAGINA, senza servizio."""
import importlib.util
import re
from pathlib import Path
_FILE = Path(__file__).resolve().parents[1] / "calibrazione_pagina.py"
_spec = importlib.util.spec_from_file_location("calibrazione_pagina", _FILE)
_mod = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_mod)
PAGINA = _mod.PAGINA
def test_titolo_e_stile_scuro():
assert "<title>Acquisizione camera</title>" in PAGINA
assert "#0f172a" in PAGINA and "#1e293b" in PAGINA and "#3b82f6" in PAGINA
def test_rotte_usate():
for rotta in ("/camera/sources", "/camera/anteprima", "/camera/parametri",
"/camera/parametri/salva", "/camera/calibrate", "/camera/calibration"):
assert '"' + rotta in PAGINA, rotta
def test_offline_e_senza_finestre_del_browser():
for vietato in ("alert(", "confirm(", "prompt(", "http://", "https://"):
assert vietato not in PAGINA, vietato
def test_id_principali():
ids = set(re.findall(r'id="([^"]+)"', PAGINA))
attesi = {"sorgente", "avvia", "ferma", "scatta", "mirinoCasella", "immagine",
"nitidezza", "nitidezzaMax", "saturati", "media", "dimensioni",
"parametri", "salvaParametri", "quadrato", "acquisisci", "caricaImmagine", "fileImmagine",
"mostraCal", "salva", "stato", "statoSorgente"} # #91: carica/elimina sono ora per riga; #92: file e calibrata nell'Acquisizione
assert attesi <= ids, attesi - ids
# ogni id usato dal JS con $("...") esiste nel markup
usati = set(re.findall(r'\$\("([^"]+)"\)', PAGINA))
assert usati <= ids, usati - ids
def test_sezioni_e_testi_italiani():
for testo in ("Sorgente", "Anteprima", "Parametri camera", "Calibrazione",
"Salva come predefiniti", "Carica immagine da file...", "Questa sorgente non ha parametri regolabili",
"Attiva calibrazione"): # #92 #93
assert testo in PAGINA, testo
assert "it-IT" in PAGINA # numeri con la virgola
assert "Choose File" not in PAGINA
def test_localstorage_protetto():
# ogni uso di localStorage sta dentro un try
for m in re.finditer(r"localStorage[.]", PAGINA):
assert "try {" in PAGINA[max(0, m.start() - 200):m.start()]
def test_specchiatura_per_sorgente(): # #110
ids = set(re.findall(r'id="([^"]+)"', PAGINA))
assert {"specchiaO", "specchiaV", "msgSpecchia"} <= ids
assert '"/camera/orientamento' in PAGINA
assert "Specchia orizzontale" in PAGINA and "Specchia verticale" in PAGINA
def test_zoom_visori(): # #111
ids = set(re.findall(r'id="([^"]+)"', PAGINA))
for suf in ("", "Cal", "CalTab"): # tre riquadri: grezza, calibrata, tab Calibrazione
for base in ("zoomMeno", "zoomAdatta", "zoomPiu", "zoomPct"):
assert base + suf in ids, base + suf
assert "zoomata" in PAGINA
assert "latoAnteprima()" in PAGINA and "unaAnteprima(1280)" not in PAGINA # lato_max segue lo zoom
# la barra e' sorella del palco che scorre (non dentro): resta ferma durante lo scorrimento
assert PAGINA.count('<div class="visore"') == 3
assert PAGINA.count('<div class="barraZoom">') == 3
def test_nessuna_funzione_dichiarata_due_volte(): # #111: in JS vince l'ultima dichiarazione, in silenzio
script = re.search(r"<script>(.*?)</script>", PAGINA, re.S).group(1)
nomi = re.findall(r"function\s+([A-Za-z0-9_$]+)\s*\(", script)
doppi = sorted({n for n in nomi if nomi.count(n) > 1})
assert not doppi, "funzioni dichiarate piu' volte: " + ", ".join(doppi)
"""Test della pagina Acquisizione camera (#87): contenuto statico di PAGINA, senza servizio."""
import importlib.util
import re
from pathlib import Path
_FILE = Path(__file__).resolve().parents[1] / "calibrazione_pagina.py"
_spec = importlib.util.spec_from_file_location("calibrazione_pagina", _FILE)
_mod = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_mod)
PAGINA = _mod.PAGINA
def test_titolo_e_stile_scuro():
assert "<title>Acquisizione camera</title>" in PAGINA
assert "#0f172a" in PAGINA and "#1e293b" in PAGINA and "#3b82f6" in PAGINA
def test_rotte_usate():
for rotta in ("/camera/sources", "/camera/anteprima", "/camera/parametri",
"/camera/parametri/salva", "/camera/calibrate", "/camera/calibration"):
assert '"' + rotta in PAGINA, rotta
def test_offline_e_senza_finestre_del_browser():
for vietato in ("alert(", "confirm(", "prompt(", "http://", "https://"):
assert vietato not in PAGINA, vietato
def test_id_principali():
ids = set(re.findall(r'id="([^"]+)"', PAGINA))
attesi = {"sorgente", "avvia", "ferma", "scatta", "mirinoCasella", "immagine",
"nitidezza", "nitidezzaMax", "saturati", "media", "dimensioni",
"parametri", "salvaParametri", "quadrato", "acquisisci", "caricaImmagine", "fileImmagine",
"mostraCal", "salva", "stato", "statoSorgente"} # #91: carica/elimina sono ora per riga; #92: file e calibrata nell'Acquisizione
assert attesi <= ids, attesi - ids
# ogni id usato dal JS con $("...") esiste nel markup
usati = set(re.findall(r'\$\("([^"]+)"\)', PAGINA))
assert usati <= ids, usati - ids
def test_sezioni_e_testi_italiani():
for testo in ("Sorgente", "Anteprima", "Parametri camera", "Calibrazione",
"Salva come predefiniti", "Carica immagine da file...", "Questa sorgente non ha parametri regolabili",
+84
View File
@@ -0,0 +1,84 @@
"""Test delle rotte /visore/*.js di camera_service e della pagina che le usa (#122).
Uso: python -m pytest scripts/tests/test_visore_rotte.py -q
Dipende da: venv con fastapi, httpx, opencv (come test_camera_service.py).
image_viewer.js lo scrive un altro piano (V1): se manca nei sorgenti il test della rotta usa una cartella temporanea con file finti.
"""
import importlib.util
import re
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
MODULO = Path(__file__).resolve().parents[1] / "camera_service.py"
NOMI = ("image_viewer.js", "save_image.js", "vision_overlay.js")
def _carica():
spec = importlib.util.spec_from_file_location("camera_service", MODULO)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
@pytest.fixture
def client(tmp_path):
mod = _carica()
with TestClient(mod.create_app(tmp_path)) as c:
c.mod = mod
yield c
@pytest.fixture
def cartella_finta(client, tmp_path):
# file finti in una cartella temporanea: la rotta si prova senza dipendere da image_viewer.js del frontend
d = tmp_path / "js"
d.mkdir()
for n in NOMI:
(d / n).write_text("/* finto " + n + " */\n", encoding="utf-8")
client.mod._CARTELLE_VISORE[:] = [d]
return d
@pytest.mark.parametrize("nome", NOMI)
def test_rotta_serve_il_file(client, cartella_finta, nome):
r = client.get("/visore/" + nome)
assert r.status_code == 200
assert r.headers["content-type"].startswith("application/javascript")
assert r.content == (cartella_finta / nome).read_bytes()
@pytest.mark.parametrize("nome", ["altro.js", "camera_service.py", "..%2Fcamera_service.py", "image_viewer.JS", "image_viewer.js.map"])
def test_altri_nomi_danno_404(client, cartella_finta, nome):
assert client.get("/visore/" + nome).status_code == 404
def test_file_mancante_nella_cartella_da_404(client, tmp_path):
client.mod._CARTELLE_VISORE[:] = [tmp_path / "vuota"]
assert client.get("/visore/save_image.js").status_code == 404
def test_cartelle_di_default_sono_static_js_del_frontend(client):
# sviluppo: sorgenti del frontend; pacchetto Nuitka: static/ accanto al modulo (build.ps1 la include)
cartelle = _carica()._CARTELLE_VISORE
assert any(c.parts[-5:] == ("src", "frontend", "flask_app", "static", "js") for c in cartelle)
assert any(c.parts[-2:] == ("static", "js") and c.parent.parent == MODULO.parent for c in cartelle)
@pytest.mark.parametrize("nome", ("save_image.js", "vision_overlay.js", "image_viewer.js"))
def test_file_reali_del_frontend(client, nome):
reale = next((c / nome for c in client.mod._CARTELLE_VISORE if (c / nome).is_file()), None)
if reale is None:
pytest.skip(nome + " non e' ancora nei sorgenti del frontend (lo scrive il piano V1)")
r = client.get("/visore/" + nome)
assert r.status_code == 200 and r.content == reale.read_bytes()
@pytest.mark.parametrize("rotta", ("/acquisizione", "/calibrazione"))
def test_pagina_usa_quattro_visori_e_i_tre_script(client, rotta):
html = client.get(rotta).text
assert html.count("<tmf-viewer") == 4
src = re.findall(r'<script src="(/visore/[^"]+)"', html)
assert src == ["/visore/vision_overlay.js", "/visore/save_image.js", "/visore/image_viewer.js"]
assert "zoomAdatta" not in html