feat(prezentacja): wspolne dane formularza miedzy zakladkami + imie i nazwisko (PRE-21)
build / build (push) Successful in 58s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m4s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 38s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
build / build (push) Successful in 58s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m4s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 38s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
Trzecia wskazowka partnerow. Kazda zakladke (Horoskop / Interpretacje / Kalendarz) wypelnialo sie od nowa — te same imie, data, godzina, strefa i miejsce. Teraz raz wpisane dane wedruja za uzytkownikiem, a zmiana w jednej zakladce przenosi sie na pozostale. Nowy formsync.js trzyma stan w localStorage. Dlaczego nie sesja na serwerze: serwer zostaje BEZSTANOWY — zadnego magazynu sesji, zadnych danych urodzeniowych trzymanych po stronie uslugi (spojne z postawa z LOG-32/PRE-16). Przy okazji dane synchronizuja sie tez miedzy osobnymi kartami przegladarki, bo zdarzenie `storage` daje to za darmo. Dolozone pole „imie i nazwisko" (wszystkie trzy zakladki) — potrzebne do naglowka raportu PDF (PRE-24). Handlery przyjmuja je i oddaja, wiec nie znika po przeliczeniu. Dwie rzeczy, ktore trzeba bylo domknac, zeby to dzialalo naprawde: 1. KOLEJNOSC SKRYPTOW. formsync.js ladowany w <head> z `defer` — skrypty defer wykonuja sie w kolejnosci dokumentu, wiec ten zdazy odtworzyc wspolrzedne, ZANIM geo.js zbuduje mape. Mapa startuje od razu we wlasciwym miejscu, zamiast przeskakiwac po chwili. 2. geo.js ustawia pola z KODU (`.value = ...`), co samo z siebie NIE wywoluje zdarzen — bez tego synchronizacja przegapilaby kazdy wybor z mapy, z wyszukiwarki i z „Tu i teraz". Dolozony setVal(), ktory jawnie zglasza `change`. Zakladka bez danego pola (np. Sygnifikatory) nie kasuje wartosci zapamietanej gdzie indziej; uszkodzony wpis w localStorage nie blokuje formularza. Q-14 rozstrzygniete: lancuch LaTeX->PDF stanie jako OSOBNA USLUGA render — spojne z izolacja swisseph (LOG-27), obraz produktu zostaje maly. Weryfikacja na zywym stacku (data+logika+prezentacja) w przegladarce: dane wpisane w Horoskopie pojawily sie w Interpretacjach; zmiana godziny w Interpretacjach dotarla do Kalendarza; pola nieobecne na zakladce (zodiak, system domow) przetrwaly; po POST imie zostalo, a horoskop policzyl sie normalnie. Testy: 22 nowe strukturalne. Calosc: prezentacja 67 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #28.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""Wspólne dane formularza między zakładkami + pole imienia (PRE-21).
|
||||
|
||||
Znów test strukturalny na źródle: synchronizacja żyje w JS, a jej awaria jest
|
||||
CICHA — formularz po prostu przestaje pamiętać dane i użytkownik wpisuje je od
|
||||
nowa. Pilnujemy więc reguł, które łatwo zgubić przy kolejnych zmianach.
|
||||
"""
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
APP = pathlib.Path(__file__).resolve().parents[1] / "app"
|
||||
TEMPLATES = APP / "templates"
|
||||
SYNC_JS = (APP / "static" / "formsync.js").read_text(encoding="utf-8")
|
||||
GEO_JS = (APP / "static" / "geo.js").read_text(encoding="utf-8")
|
||||
BASE_HTML = (TEMPLATES / "base.html").read_text(encoding="utf-8")
|
||||
MAIN_PY = (APP / "main.py").read_text(encoding="utf-8")
|
||||
|
||||
WITH_FORM = ("chart.html", "interpret.html", "timeline.html")
|
||||
|
||||
# Pola, które mają wędrować między zakładkami. Gdy dojdzie nowe wspólne pole,
|
||||
# ten wykaz ma się nie rozjechać z formsync.js.
|
||||
SHARED = ("person", "date", "time", "tz_offset", "lat", "lon",
|
||||
"house_system", "zodiac", "place")
|
||||
|
||||
|
||||
# ─────────────────────────────── pole imienia ────────────────────────────
|
||||
|
||||
@pytest.mark.parametrize("name", WITH_FORM)
|
||||
def test_person_field_present_on_every_form(name):
|
||||
"""Imię i nazwisko trafia do nagłówka raportu PDF (PRE-24) — musi dać się
|
||||
je wpisać z każdej zakładki, bo dane są wspólne."""
|
||||
html = (TEMPLATES / name).read_text(encoding="utf-8")
|
||||
assert 'name="person"' in html, f"{name}: brak pola imienia"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", WITH_FORM)
|
||||
def test_person_value_survives_submit(name):
|
||||
"""Po wysłaniu formularza pole ma pokazywać to, co wpisano — inaczej znikałoby
|
||||
przy każdym przeliczeniu."""
|
||||
html = (TEMPLATES / name).read_text(encoding="utf-8")
|
||||
assert "form.person" in html, f"{name}: wartość imienia nie wraca po POST"
|
||||
|
||||
|
||||
def test_handlers_accept_and_echo_person():
|
||||
"""Trzy handlery renderujące formularz przyjmują `person` i wkładają go
|
||||
z powrotem do `form`."""
|
||||
assert MAIN_PY.count("person: str = Form") == 3
|
||||
assert MAIN_PY.count('"person": person') == 3
|
||||
|
||||
|
||||
# ─────────────────────── synchronizacja między zakładkami ────────────────
|
||||
|
||||
@pytest.mark.parametrize("key", SHARED)
|
||||
def test_every_shared_field_is_synced(key):
|
||||
assert f"'{key}'" in SYNC_JS, f"pole {key} nie jest synchronizowane"
|
||||
|
||||
|
||||
def test_sync_script_loaded_before_map_script():
|
||||
"""KOLEJNOŚĆ MA ZNACZENIE: formsync w <head> z `defer` wykonuje się przed
|
||||
geo.js (skrypty defer idą w kolejności dokumentu), więc mapa startuje już
|
||||
z odtworzonymi współrzędnymi zamiast skakać po chwili."""
|
||||
assert "formsync.js" in BASE_HTML, "formsync.js nie jest ładowany globalnie"
|
||||
assert "defer" in BASE_HTML.split("formsync.js")[0].rsplit("<script", 1)[-1] + "defer"
|
||||
|
||||
|
||||
def test_sync_reacts_to_other_browser_tab():
|
||||
"""Zmiana w drugiej otwartej karcie ma dociągnąć się tutaj (zdarzenie storage)."""
|
||||
assert "'storage'" in SYNC_JS
|
||||
|
||||
|
||||
def test_sync_persists_on_submit():
|
||||
assert "'submit'" in SYNC_JS
|
||||
|
||||
|
||||
def test_corrupt_storage_does_not_break_the_page():
|
||||
"""Uszkodzony wpis w localStorage nie może zablokować formularza."""
|
||||
assert "catch" in SYNC_JS and "JSON.parse" in SYNC_JS
|
||||
|
||||
|
||||
def test_map_changes_are_reported_to_sync():
|
||||
"""geo.js ustawia pola z KODU (`.value = ...`), co samo z siebie nie wywołuje
|
||||
zdarzeń — bez jawnego `change` synchronizacja przegapiłaby wybór z mapy."""
|
||||
assert "function setVal" in GEO_JS
|
||||
assert "dispatchEvent(new Event('change'" in GEO_JS
|
||||
# wszystkie miejsca ustawiające pola idą przez setVal
|
||||
assert "latEl.value =" not in GEO_JS.replace("el.value = value", "")
|
||||
assert "searchEl.value = d.name" not in GEO_JS
|
||||
|
||||
|
||||
def test_sync_does_not_wipe_fields_missing_on_a_tab():
|
||||
"""Zakładka bez danego pola (np. Sygnifikatory) nie może wyczyścić wartości
|
||||
zapamiętanej z innej zakładki."""
|
||||
assert "if (!el) return;" in SYNC_JS
|
||||
@@ -74,7 +74,9 @@ def test_reverse_name_clears_field_before_lookup():
|
||||
src = _geo_source()
|
||||
start = src.index("function reverseName")
|
||||
body = src[start:src.index("\n }", start)]
|
||||
clear_at = body.find("searchEl.value = ''")
|
||||
# sposób zapisu może się zmienić (bezpośrednie .value albo setVal, które
|
||||
# dodatkowo zgłasza `change`) — pilnujemy KOLEJNOŚCI, nie składni
|
||||
clear_at = max(body.find("searchEl.value = ''"), body.find("setVal(searchEl, '')"))
|
||||
fetch_at = body.find("fetch(")
|
||||
assert clear_at != -1, "reverseName nie czyści pola nazwy"
|
||||
assert clear_at < fetch_at, "pole czyszczone dopiero po zapytaniu — stara nazwa zdąży zostać"
|
||||
|
||||
Reference in New Issue
Block a user