Files
astrololo/services/presentation/tests/test_static_js.py
T
gitea f34016a4a5
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
feat(prezentacja): wspolne dane formularza miedzy zakladkami + imie i nazwisko (PRE-21)
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>
2026-07-24 15:12:49 +00:00

89 lines
3.8 KiB
Python

"""Poprawki zgłoszone przez partnerów: offset względem GMT i lokalizacja po
kliknięciu „Tu i teraz" (PRE-19, PRE-20).
JS-a tu nie uruchomimy, więc pilnujemy tego strukturalnie — tak samo jak
niezmienników klienta HTTP. Obie regresje są ciche (nic się nie wywala, tylko
formularz pokazuje nieprawdę), więc test na źródle jest tu wart więcej niż zwykle.
"""
import pathlib
import pytest
APP = pathlib.Path(__file__).resolve().parents[1] / "app"
TEMPLATES = APP / "templates"
GEO_JS = APP / "static" / "geo.js"
WITH_OFFSET = ("chart.html", "interpret.html", "timeline.html")
# ───────────────────────────── PRE-19: offset względem GMT ───────────────
@pytest.mark.parametrize("name", WITH_OFFSET)
def test_offset_label_says_relative_to_gmt(name):
"""Etykieta musi mówić, WZGLĘDEM CZEGO liczymy przesunięcie."""
html = (TEMPLATES / name).read_text(encoding="utf-8")
assert "Offset względem GMT" in html, f"{name}: etykieta nie mówi o GMT"
@pytest.mark.parametrize("name", WITH_OFFSET)
def test_offset_step_is_quarter_hour(name):
"""Krok 15 minut = 0,25 h. Strefy pół- i ćwierćgodzinne (Indie +5:30,
Nepal +5:45) są realne — przy kroku 1 h nie dało się ich ustawić."""
html = (TEMPLATES / name).read_text(encoding="utf-8")
assert 'name="tz_offset" step="0.25"' in html, f"{name}: krok inny niż 15 minut"
@pytest.mark.parametrize("name", WITH_OFFSET)
def test_offset_range_covers_real_zones(name):
html = (TEMPLATES / name).read_text(encoding="utf-8")
assert 'min="-12"' in html and 'max="14"' in html, f"{name}: brak sensownego zakresu stref"
# ──────────────────── PRE-20: „Tu i teraz" odświeża nazwę miejsca ────────
def _geo_source() -> str:
return GEO_JS.read_text(encoding="utf-8")
def _handler_body() -> str:
"""Ciało handlera zdarzenia astrololo:coords (wysyłanego przez „Tu i teraz").
Szukamy KONKRETNIE `addEventListener`, nie samej nazwy zdarzenia — ta pada
także w komentarzu nagłówkowym pliku i wtedy wycięlibyśmy komentarz zamiast
kodu (test przechodziłby albo padał z niewłaściwego powodu).
"""
src = _geo_source()
start = src.index("addEventListener('astrololo:coords'")
end = src.index("});", start)
return src[start:end]
def test_now_button_handler_refreshes_place_name():
"""REGRESJA: po „Tu i teraz" pineska i współrzędne skakały na nowe miejsce,
a w polu tekstowym zostawała stara nazwa — formularz pokazywał jedno miejsce,
a liczył dla innego."""
assert "reverseName(" in _handler_body(), (
"handler astrololo:coords nie odświeża nazwy miejsca (reverseName)"
)
def test_reverse_name_clears_field_before_lookup():
"""Nazwa jest czyszczona ZANIM przyjdzie odpowiedź. Gdyby /reverse nie
odpowiedziało (brak sieci), zostałaby stara nazwa — czyli pole kłamałoby
o lokalizacji. Lepiej puste pole niż nieprawdziwe."""
src = _geo_source()
start = src.index("function reverseName")
body = src[start:src.index("\n }", start)]
# 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ć"
def test_now_button_still_syncs_map():
"""Odświeżenie nazwy nie może zastąpić przesunięcia pineski i widoku mapy."""
body = _handler_body()
assert "marker.setLatLng" in body and "map.setView" in body