Skip to content

WCAG 2.2 AA: wyłączalny skrót / oraz nawigacja po grafie bez przeciągania (2.1.4, 2.5.7, 2.1.1) - #763

Open
mpasternak wants to merge 21 commits into
devfrom
fix-wcag-2140-2570
Open

WCAG 2.2 AA: wyłączalny skrót / oraz nawigacja po grafie bez przeciągania (2.1.4, 2.5.7, 2.1.1)#763
mpasternak wants to merge 21 commits into
devfrom
fix-wcag-2140-2570

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Druga faza napraw WCAG 2.2 AA. Pierwsza to #732 (już zmergowana) — ta gałąź jest na niej odbita i przebazowana na dev, więc diff zawiera wyłącznie fazę 2.

Co domyka

2.1.4 Character Key Shortcuts — skrót / otwierający wyszukiwarkę wisiał na document i nie dawał się wyłączyć. Z trzech dopuszczonych przez kryterium wyjść wybrane zostało pierwsze: wyłączalność. Preferencja siedzi w localStorage (część publiczna nie ma kont, więc profil odpada), przełącznik jest w stopce, a wszystkie trzy publiczne handlery / pytają o nią przed otwarciem. Przy okazji zlikwidowany zdublowany handler — base.html i modal otwierały wyszukiwarkę niezależnie, więc jedno naciśnięcie wołało openGlobalSearch dwa razy.

2.5.7 Dragging Movements oraz 2.1.1 Keyboard — graf powiązań autorów dało się dotąd nawigować wyłącznie przeciąganiem. Doszło siedem przycisków (cztery kierunki w układzie kompasowym, przybliż, oddal, dopasuj) oraz obsługa klawiaturą: strzałki, +/-, Home.

Dwa sąsiednie kryteria pilnowane przy okazji, żeby naprawa jednego nie złamała drugiego: preventDefault leci wyłącznie dla obsłużonych klawiszy, więc Tab nadal wyprowadza focus (2.1.2), a kombinacje z Ctrl/Cmd/Alt są przepuszczane, żeby nie przechwycić zoomu przeglądarki (1.4.4). shiftKey świadomie nie wchodzi do tego warunku — na wielu układach klawiatury + wymaga Shifta.

Uwagi do przeglądu

Kolejność w DOM przycisków nawigacji jest celowo inna niż ich układ wizualny: kompas rozstawia grid-template-areas, a DOM zostaje w kolejności dającej sensowny Tab.

Poniżej 640 px nawigacja przestaje być nakładką i ląduje pod grafem. Nakładek nie da się przestawić z arkusza, bo max-width i bottom siedzą w style="" w graf.html — zamiast rozsiewać !important, wyprowadzamy nawigację spod nich. Legenda jest tam chowana, ale zdanie wyjaśniające kodowanie wizualne (wielkość koła, grubość linii) zostało z niej przeniesione nad graf, gdzie jest widoczne zawsze — chowanie treści na małym ekranie to strata informacji, nie dekoracji.

Testy

Nowe: 13 testów vitest na spoiwo DOM→akcja, 17 Playwright, plus asercje szablonowe. Warstwa Playwright nie jest ozdobą — łapie rzeczy, których dwie pozostałe nie łapią, i to jest zmierzone, nie zadeklarowane.

Gałąź przeszła przegląd mutacyjny (14 mutacji przez wszystkie trzy warstwy). Pierwsze podejście przepuszczało dwanaście z nich; najgroźniejsza podmieniała cel handlera keydown na document, co zamieniało +/- w globalne skróty jednoznakowe i łamało dokładnie to 2.1.4, które ta gałąź naprawia — przy całej suicie na zielono. Spoiwo zostało wyniesione z controls.js do nawigacja.js jako podepnijNawigacje() i jest teraz testowane; wszystkie te mutacje są łapane.

Weryfikacja lokalna

Na świeżym kontenerze (reuse daje niewiarygodne wyniki przy testach zależnych od stanu bazy):

gejt wynik
pytest (bez Playwright) 9406 passed, 4 skipped, 1 xfailed
Playwright 174 passed, 1 skipped
vitest 170 passed

Dwa testy src/django_bpp/tests/test_auth_server.py padają — to defekt zastany na dev, nie regresja tej gałęzi. Odtworzone na czystym dev w trybie detached: baseline to te same 2 failed / 9391 passed. Objaw to TemplateDoesNotExist dla auth_server/login.html, czyli rozstrzyganie szablonu, niezależne od stanu bazy. Do osobnego zgłoszenia.

Znalezione przy okazji, celowo poza zakresem

src/django_bpp/templates/admin/base_site.html:127 wiąże / na $(document) bez guardu — ten sam wzorzec 2.1.4, tylko w panelu administracyjnym, czyli poza zadeklarowanym zakresem audytu. Naprawa nie jest jednolinijkowa, bo admin nie dziedziczy po base.html i nie ładuje modułu. Zapisane w docs/superpowers/specs/2026-08-13-wcag-stan-i-pozostale-prace.md razem z resztą pozostałych prac WCAG.

🤖 Generated with Claude Code

mpasternak and others added 19 commits August 16, 2026 12:01
Domyka dwa kryteria odroczone w iteracji z 2026-08-06 (2.1.4 skrot `/`,
2.5.7 przeciaganie grafu) plus 2.1.1 przy okazji, bo obsluga klawiatura
kosztuje niewiele gdy funkcje nawigacyjne i tak powstaja dla przyciskow.

Rozstrzygniecia:

- 2.1.4 → mechanizm wylaczania w localStorage. Wzorce branzowe (GitHub:
  ustawienie konta; Gmail: dialog pod `?`) zakladaja zalogowanego
  uzytkownika z profilem, a audytowany zakres BPP to czesc publiczna,
  anonimowa — localStorage jest funkcjonalnym odpowiednikiem. Przelacznik
  w stopce, NIE na stronie deklaracji dostepnosci: ta jest konfigurowalna
  per uczelnia (pokazuj_deklaracje_dostepnosci 1/2/brak) i na czesci
  wdrozen nie istnieje albo prowadzi na serwer uczelni.
- 2.5.7 → siedem przyciskow nawigacji (kierunki, zoom, dopasuj) nad
  wspolnym modulem czystych funkcji. Istniejaca wyszukiwarka wezlow NIE
  jest alternatywa: podswietla trafienia, ale nie przesuwa widoku.
- 2.1.1 → te same funkcje pod strzalkami/+/-/Home przy focusie na
  kontenerze grafu. Klawisze +/- sa znakami drukowalnymi, wiec podlegaja
  2.1.4 — spelniaja je trzecim wariantem (aktywne tylko przy focusie),
  wiec nie wymagaja przelacznika z czesci A.

Nowe przyciski musza spelniac 2.5.8 (cel >=24x24 px) — naprawiajac 2.5.7
nie wolno zlamac sasiedniego kryterium.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Recenzja zwrocila NIE z jednym znaleziskiem blokujacym; wszystkie
zweryfikowane w kodzie i przyjete.

BLOKUJACE — skrot `/` obslugiwany jest w CZTERECH miejscach, nie jednym.
Spec latal wylacznie base.html:42, podczas gdy global_search_modal.html:1048
(wlaczany z top_bar.html:511, czyli obecny na tej samej stronie) wiaze
wlasny, niezalezny handler, a browse/uczelnia.html:134 reaguje na `/`
zamykajac baner. Wylaczenie preferencji nie dalo by nic — modal otwieralby
wyszukiwarke mimo wszystko, a kryterium 2.1.4 pozostaloby niespelnione.
Dodano inwentaryzacje wszystkich handlerow i objeto warunkiem wszystkie
publiczne. Przy okazji: oba handlery w base.html i modalu odpalaja sie
dzis rownolegle, wiec openGlobalSearch wola sie DWA razy na jedno
nacisniecie — duplikat likwidujemy, zostaje handler w modalu.

ISTOTNE — nakladka nawigacji nie moze isc w lewy dolny rog: zajmuje go
#graf-notka (graf.html:191), pokazywana gdy siec jest przycieta, czyli
dokladnie przy duzych grafach gdzie nawigacja jest najpotrzebniejsza.
Lewy gorny to #graf-panel, prawy gorny #graf-legenda. Jedyny wolny rog
to prawy dolny.

Pozostale korekty: wlascicielem logiki przelacznika jest modul
(bppPodepnijPrzelacznikSkrotow), nie kod inline w stopce; guard `typeof`
przed wywolaniem bppSkrotyWlaczone (wzorem istniejacego guardu dla
openGlobalSearch); test Playwright na ZACHOWANIE skrotu, bez ktorego
usuniecie warunku z handlera nie wywalilo by niczego; baner reklamujacy
skrot ukrywany przy wylaczonych skrotach; rozroznienie testu `<script src>`
(sensowny dla skroty-klawiszowe.js, bezsensowny dla nawigacja.js, ktory
idzie w bundle esbuild); vitest wymaga jsdom dla modulu window-globalnego;
limity zoomu odczytywane z instancji (cy.js:99-100), nie zaszyte;
czy_pokazywac_siec_powiazan to metoda Autora, per-autor nadpisuje
per-uczelnia; zasieg stopki uscislony (bare.html jej nie wlacza, ale tam
tez nie ma skrotu).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
Osiem zadan: modul preferencji skrotow → podpiecie do WSZYSTKICH trzech
publicznych handlerow / z likwidacja duplikatu → modul nawigacji po grafie
→ przyciski → klawiatura → testy Playwright → wykaz odroczonych i
newsfragment → weryfikacja koncowa.

Testy Playwright dostaly osobne zadanie, bo bez nich usuniecie warunku
bppSkrotyWlaczone() z handlera albo preventDefault z obslugi klawiatury nie
wywalilo by zadnego testu — szablonowe dowodza tylko, ze kod jest w pliku.

Wzorzec testu przegladarkowego wziety z test_siec3d_bez_webgl.py:44-57
(channels_live_server + page: Page + transactional_db, baker.make(Autor)).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E8RjSZWMR6L7fuRDJJZW4L
…ra (2.1.4)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Handler klawiatury w grafie powiazan (Task 5) przechwytywal takze
Ctrl/Cmd+Plus/Minus (zoom przegladarki, WCAG 1.4.4), Ctrl+Home
(przewijanie na gore) i Alt+strzalka (nawigacja wstecz). Naprawiajac
2.1.1 lamalismy funkcje wazniejsza dla tej samej grupy uzytkownikow.

Logike mapowania klawisza na akcje wydzielono z controls.js do
nawigacja.js jako obsluzKlawisz(cy, e) — wczesne wyjscie na
ctrlKey/metaKey/altKey (shiftKey celowo pominiety, bo + czesto wymaga
Shift), zwraca true tylko dla obsluzonych klawiszy. controls.js
redukuje sie do wywolania i warunkowego preventDefault.

Dodano tests/js/graf-klawiatura.test.js (18 testow, atrapa cy z
nawigacja-grafu.test.js) pokrywajacy strzalki, +/-/=/_, Home, klawisze
nieobslugiwane (Tab/Escape/Enter/litera) oraz skroty z modyfikatorami.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Testy szablonowe z zadan 1-5 dowodza tylko, ze kod jest w plikach --
usuniecie warunku bppSkrotyWlaczone() z handlera skrotu / albo
preventDefault z obslugi klawiatury grafu nie wywalaloby zadnego
testu. Ten plik dowodzi zachowania w prawdziwej przegladarce
(Playwright + channels_live_server).

10 testow:
- 5x WCAG 2.1.4 (skrot / i jego wylacznik w stopce): domyslne
  otwarcie wyszukiwarki, wylaczenie dziala, ponowne wlaczenie
  przywraca skrot, aria-pressed sie aktualizuje, preferencja
  przezywa przeladowanie (localStorage).
- 5x WCAG 2.5.7/2.1.1 (nawigacja po grafie powiazan): przyciski
  widoczne + kontener fokusowalny, klik zoom-in realnie zmienia
  cy.zoom(), klik kierunkowy realnie zmienia cy.pan(), strzalka z
  klawiatury realnie zmienia cy.pan(), Tab nie jest zlapany w
  pulapke klawiaturowa (2.1.2).

Testy zoom/pan czytaja stan z container._cyreg.cy (wewnetrzny
rejestr Cytoscape.js) PRZED i PO interakcji, zeby padaly gdy handler
klikniecia zniknie -- sama widocznosc przycisku o tym nie swiadczy.

Self-review (potwierdzone recznie, patrz raport): usuniecie warunku
bppSkrotyWlaczone(), wylaczenie obsluzKlawisz() oraz odpiecie
handlerow klikniec przyciskow kierunkowych/zoom kazde osobno
wywalaly odpowiednie testy na czerwono.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…oom)

Dwie przyczyny, obie ujawnione dopiero przy powtarzanych przebiegach.

1. `_url_grafu` tworzyl autora BEZ wspolautorow, wiec BFS zwracal siec
   jednowezlowa, a `renderujSiec()` (graph.js:172-178) asynchronicznie
   chowal `#cytoscape-container`. Klawisz albo klik trafial czasem w juz
   ukryty kontener — 20-30% awaryjnosci na niezmienionym, poprawnym
   kodzie. Autor testowy dostaje teraz wspolautora (jeden wiersz
   AuthorConnection wystarczy: `_kandydaci_frontu` czyta relacje
   dwukierunkowo), wiec ta galaz nie moze wystapic.

2. `_czekaj_na_graf` czekalo na `_cyreg.cy`, ktore powstaje synchronicznie
   w `utworzCy()` — ZANIM fetch `siec.json` wystartuje. Bylo to czekanie
   na nic. Teraz czekamy na sygnal pozytywny: `cy.nodes().length > 0`,
   ktory zapelnia dopiero `renderujSiec()` po odpowiedzi serwera. Warunek
   "kontener nie jest ukryty" bylby tu bezuzyteczny — szablon nie nadaje
   `display`, wiec spelnialby sie przed renderem.

Naprawa (1) obnazyla druga usterke: przy dwuwezlowej sieci `cy.fit()`
dobija do `maxZoom` (4), wiec `zoomuj()` przycina wynik i przyblizanie
jest poprawnie operacja pusta — asercja "zoom wzrosl" padala mimo
sprawnego handlera. Wczesniej test przechodzil z niewlasciwego powodu:
przy jednym wezle kontener byl chowany, a `fit()` na pustym grafie
zostawial zoom 1. Test ustawia teraz punkt startowy jawnie
(`_ustaw_zoom_z_zapasem`) zamiast liczyc na geometrie ukladu.

Przy okazji: `page.wait_for_timeout(500)` w tescie wylacznika zastapiony
asercja warunkowa (handler "/" jest synchroniczny), `ruff format`.

Weryfikacja: 150/150 (15 x caly plik). Wartosc testow potwierdzona
mutacjami — wygaszenie handlera zoom-in i warunku `obsluzKlawisz`
zapala odpowiednio test przyblizania i test strzalki.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wykazy odroczonych niezgodnosci w obu specyfikacjach (08-05 i 08-06)
twierdzily, ze 2.1.4 i 2.5.7 sa "niezgodne, swiadomie odroczone" — nadal
prawdziwe w chwili pisania, nieprawdziwe od wczoraj. Oba wpisy zastapione
jednym akapitem o domknieciu, z odsylaczem do specyfikacji 08-07.

Poza wykazami dokument 08-05 twierdzil to samo w pieciu innych miejscach,
kazde z wlasna korekta z 2026-08-06: tabela zrodel werdyktu, naglowek
sekcji "Naruszenia stwierdzone", obie podsekcje kryteriow, kolejnosc prac
(krok 3) i sekcja otwartych decyzji. Kazde dostalo `Korekta (2026-08-07)`
zgodnie z konwencja dokumentu — bez kasowania oryginalu, zeby dalo sie
odtworzyc, co i kiedy uznano za niezgodnosc. Sekcja otwartych decyzji jest
tu najciekawsza: rekomendacja (zawezenie skrotu do focusa) nadal NIE
zostala przyjeta, ale kryterium naprawiono inaczej — zalozenie, ze
wylaczalnosc wymaga profilu uzytkownika, bylo zbyt ostrozne.

Sprawdzone i pozostawione bez zmian: naglowek "Blok 5" (2.5.7 nadal jest
przedmiotem audytu, teraz jako weryfikacja naprawy), wiersz 2.5.1 (osobne
kryterium, gesty wielopunktowe — nietkniete), listy wykrywalnosci
automatem (fakt o axe, niezalezny od stanu naprawy), opisy zakresu
wymieniajace graf powiazan (nadal w probce). Wiekszosc trafien grepu z
briefu to falszywe alarmy: "bibliograficzny" zawiera "graf".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 4 zalozyl `src/powiazania_autorow/tests/__init__.py` obok istniejacego
`src/powiazania_autorow/tests.py` (17 KB, 18 testow). Python rozwiazuje
`powiazania_autorow.tests` na PAKIET, wiec modul zostal przesloniety, a
pytest odmawial kolekcji `tests.py` ("import file mismatch"). Skutek gorszy
niz czerwona suita: te 18 testow po cichu przestalo sie wykonywac, a caly
przebieg konczyl sie bledem.

Recenzja Taska 4 tego nie zlapala, bo uruchamiala tylko 4 nowe testy —
kolizja nazw ujawnia sie dopiero przy kolekcji calego drzewa.

Konwencja tej aplikacji to moduly testowe w korzeniu pakietu
(`test_views.py`, `test_pbn_root_per_uczelnia.py`, `tests.py`) — to
katalog `tests/` byl odstepstwem. Plik wraca wiec o poziom wyzej, pakiet
znika.

Przy okazji `SZABLON` przestaje liczyc `parents[n]` od `__file__`, a
kotwiczy sie na katalogu pakietu: poprzedni wariant psul sie przy kazdym
przeniesieniu pliku (co wlasnie zaszlo).

Weryfikacja: `pytest src/powiazania_autorow/` 63 passed (bylo 56 + 7
niezbieranych).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… z triazu

Cztery pozycje z listy odlozonych drobiazgow w ledgerze.

1. Guardy w `browse/uczelnia.html` (oba miejsca) nie mialy zadnego testu —
   strona uczelni ma WLASNY, inline'owy skrypt: baner reklamujacy skrot `/`
   oraz handler zamykajacy ten baner. Zaden test Pythona jej nie renderowal,
   Playwright jej nie odwiedza, wiec usuniecie ktoregokolwiek guardu
   przechodzilo CI. To byla jedyna pozycja z triazu, przy ktorej regresja
   przeszlaby niezauwazona. Trzy nowe testy, kazdy zweryfikowany mutacja:
   usuniecie guardu banera, guardu handlera i guardu `typeof` zapala
   dokladnie po jednym, wlasciwym tescie.

2. Martwy kod w `catch` w `bppSkrotyWlaczone`: warunek na wejsciu zwraca juz
   `stanAwaryjny`, gdy ten jest ustawiony, wiec w `catch` jest on ZAWSZE
   `null`, a ternary zawsze dawal `true`. Zostaje `return true` z komentarzem,
   czemu tak.

3. `.footer__link-button` przeniesiony do bloku `.footer` jako
   `&__link-button` — konwencja BEM reszty pliku. Skompilowany CSS jest
   bajt-w-bajt taki sam (sprawdzone w app-blue.css), to zmiana czysto
   redakcyjna.

4. Test skalowania kroku sprawdzal tylko os X. Dopisany blizniaczy dla osi Y:
   podmiana `cy.height()` na `cy.width()` w `przesun` NIE wywalala starego
   testu (tam wysokosc jest stala), a wywala nowy — zweryfikowane mutacja.

Weryfikacja: pytest 89 (test_wcag + powiazania_autorow), vitest 152/152,
grunt build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wiedza o tym, co w temacie WCAG zostalo do zrobienia, zyla dotad wylacznie
w `.superpowers/` — katalogu wykluczonym przez .gitignore (linia 229),
a wiec znikajacym razem z worktree. Po scaleniu fazy 2 i usunieciu
`bpp-wcag-faza2` przepadlby zarowno ledger, jak i HANDOFF: lista otwartych
decyzji, powod rebase'u, hipotezy do zbadania i pulapki hosta.

Dokument zbiera to w jednym, wersjonowanym miejscu: mape czterech
dokumentow WCAG, stan obu faz, sciezke do scalenia z komenda rebase'u
i uzasadnieniem, triaz otwartych decyzji projektowych oraz pozostale kroki
programu (skan szeroki, baseline freeze, bramka axe-core, audyt WCAG-EM,
raport zgodnosci) wraz z hipotezami i zgodnosciami warunkowymi.

`docs/superpowers/` jest wykluczone z mkdocs przez `exclude_docs`, wiec
plik nie trafia na strone dokumentacji i nie rusza gejtu
`mkdocs build --strict`. HANDOFF.md w .superpowers zostaje jako skrot
operacyjny i wskazuje na ten dokument.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…a mobile

Dwie decyzje z triazu, obie z dowodem wizualnym ze zrzutow.

1. Siedem przyciskow ukladalo sie w gridzie 3-kolumnowym jako
   `↑ ← ⤢` / `→ ↓ +` / `−`, czyli w KOLEJNOSCI CZYTANIA, nie przestrzennie:
   "w gore" sasiadowalo poziomo z "w lewo". Dla pomocy nawigacyjnej afordans
   jest istotny — uzytkownik ma trafiac w przycisk bez czytania etykiet.
   Teraz krzyz kierunkowy, zoom w osobnej kolumnie obok. Uklad robi
   `grid-template-areas`, wiec KOLEJNOSC W DOM zostaje nietknieta — Tab
   nadal idzie gora, lewo, dopasuj, prawo, dol, zoom+, zoom-.

2. Przy 390 px legenda (260 px) zaslaniala wiekszosc obszaru rysowania,
   a notka (320 px) stykala sie z nawigacja — graf byl na telefonie
   praktycznie bezuzyteczny. Nakladek nie da sie przestawic z arkusza,
   bo `max-width` i `bottom` siedza w `style=""` w graf.html i styl inline
   wygrywa. Zamiast rozsiewac `!important` — na malych ekranach nawigacja
   przestaje byc pozycjonowana absolutnie i lezy POD grafem. Jednym ruchem
   znika kolizja ze wszystkimi czterema naroznikami, a cele dotykowe sa
   latwiejsze do trafienia. Legenda, ktora opisuje interakcje myszy
   ("Najedz na wezel…"), na ekranie dotykowym jest chowana.

Dwa testy Playwright pilnuja obu stron media query: przy 390 px nawigacja
musi lezec ponizej plotna i legenda ma byc schowana, przy 1440 px odwrotnie.
Kontrola jest konieczna, bo bez niej "naprawa" mobile mogla by po cichu
zjechac przyciski pod graf na wszystkich rozdzielczosciach. Usuniecie media
query zapala test waski i zostawia szeroki zielony — zweryfikowane mutacja.

Weryfikacja: Playwright 12/12 w tym pliku, vitest 152/152, pytest 89
(test_wcag + powiazania_autorow), grunt build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Recenzja calego diffu fazy 2 wylapala, ze
`src/django_bpp/templates/admin/base_site.html:127` wiaze skrot `/` na
`$(document)` z `preventDefault()` — ten sam wzorzec 2.1.4, ktory faza 2
naprawila po stronie publicznej, tylko nietkniety.

Poza zadeklarowanym zakresem audytu (czesc publiczna dla anonima), wiec
NIE wciagam tego do tej galezi. Ale kryterium obowiazuje per strona, a
naprawa nie jest jednolinijkowa: panel administracyjny nie dziedziczy po
`base.html`, wiec nie laduje `skroty-klawiszowe.js` — potrzebny jest i
skrypt, i guard. Zapisane, zeby nie przepadlo do czasu, gdy raport
zgodnosci obejmie panel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…artwa

 nawigacja, tresc chowana na mobile, cel dotykowy

Recenzja mutacyjna przepuscila 14 mutacji przez wszystkie trzy warstwy
testow. DWANASCIE przeszlo na zielono. To commit zamykajacy najpowazniejsze
z nich plus trzy znaleziska recenzji kodu.

**Spoiwo DOM -> akcja bylo nietestowane w ogole.** `controls.js` nie mial
zadnego testu jednostkowego, wiec przechodzily: zamiana "gora" z "dol",
wszystkie cztery strzalki robiace to samo, odpiety zoom-out albo "dopasuj",
usuniety `preventDefault` i — najgorsze — `document` zamiast kontenera jako
cel handlera keydown. Ta ostatnia zamienia `+` i `-` w GLOBALNE skroty
jednoznakowe, czyli lamie dokladnie to 2.1.4, ktore ta galaz naprawia,
przy calej suicie na zielono.

Spoiwo przeniesione do `nawigacja.js` jako `podepnijNawigacje(cy, dok)` —
zgodnie z zasada, ktora komentarz w `controls.js` juz formulowal dla
`obsluzKlawisz`, ale ktorej samo spoiwo nie dotrzymywalo. Funkcja zwraca
element, do ktorego podpieto keydown, zeby dalo sie na to asertowac.
13 testow w `tests/js/podepnij-nawigacje.test.js`; wszystkie cztery mutacje
sprawdzone i lapane.

**Guardy `uczelnia.html` byly testowane regexem po zrodle**, wiec odwrocenie
logiki (`|| !bppSkrotyWlaczone()`) przechodzilo: guard nadal tam jest, nadal
ma `typeof`, nadal nazywa sie `skrotyWl` — a baner reklamuje skrot dokladnie
wtedy, gdy uzytkownik go WYLACZYL. Doszedl test Playwright na realnej
stronie uczelni. Zweryfikowane: na tej mutacji nowy test pada, a wszystkie
26 testow szablonowych przechodzi.

**Martwa nawigacja nad pustym grafem.** Gdy siec ma <=1 wezel,
`renderujSiec()` chowa plotno, ale nie nakladke. `#graf-wrapper` zapada sie
do wysokosci akapitu, a panel wychodzi ~70 px PONAD niego i nachodzi na
komunikat "Brak powiazan". Przyciski sa przy tym martwe (`cy.width()` == 0)
i zostaja w kolejnosci Taba. Autor bez policzonych powiazan to nie egzotyka:
AuthorConnection liczy sie raz na dobe, wiec kazdy swiezo dodany autor
trafia tu zawsze.

**Media query chowala TRESC, nie dekoracje.** Legenda niosla jedyne w calym
szablonie wyjasnienie kodowania wizualnego ("wielkosc kola odpowiada
metryce, grubosc linii — liczbie publikacji"), a moj wlasny test to
zabetonowal asercja `not_to_be_visible`. Zdanie przenioslem do `<small>`
nad grafem, widocznego zawsze; w nakladce zostaly wylacznie podpowiedzi
o najechaniu kursorem, ktorych na dotyku nie ma.

**Cel dotykowy przelacznika.** `.footer__content` ma `font-size: 70%`, wiec
przycisk mial 134x15 px przy progu 24x24 z 2.5.8 — a to jedyny sposob
skorzystania z naprawy 2.1.4, adresowanej do osob ze sterowaniem glosem
i zaburzeniami motoryki. Sam padding, bez ruszania `font-size`.

Dolozone tez asercje na rozmiar celu przyciskow grafu i na pierscien focusa
kontenera (2.4.7) — oba byly deklarowane w komentarzach SCSS jako spelnione
i oba byly niechronione: zejscie do 10px albo skasowanie `:focus-visible`
przechodzilo cala suite.

Weryfikacja: Playwright 17/17 w tym pliku, vitest 170/170, pytest 106
(test_wcag + powiazania_autorow + integration).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mpasternak and others added 2 commits August 16, 2026 14:46
CI wywalilo `fallback: gdy setItem rzuca...` na `expected '0' to be null`,
choc lokalnie caly plik przechodzil. Przyczyna nie byla srodowiskowa —
technika stubowania byla zla, a roznica wersji jsdom tylko ja obnazyla.

`window.localStorage.setItem = () => { throw ... }` NIE przeslania metody.
`Storage` w jsdom to legacy platform object: przypisanie nieznanej
wlasciwosci ZAPISUJE POZYCJE o kluczu "setItem". Prawdziwy `setItem`
wykonywal sie wiec dalej i zapisywal "0", a asercja o pustym localStorage
padala.

Gorsze jest to, czego CI nie pokazalo: przy nieskutecznym stubie pozostale
trzy testy uzywajace tej techniki przechodzily NIEZALEZNIE od tego, czy
stub zadzialal — sprawdzaly `not.toThrow()` albo wartosc domyslna, czyli
to samo, co bez stubu. Nie pilnowaly niczego. Padl tylko ten jeden, ktory
faktycznie potrzebowal dzialajacego stubu.

Zamiast lepic metody na obiekcie Storage podmieniamy caly `localStorage`
przez `Object.defineProperty` (atrapa delegujaca do oryginalu plus
nadpisania) i przywracamy go w `finally`, zeby wyjatek w tescie nie
zostawial zepsutego globala kolejnym testom.

Wartosc mutacyjna sprawdzona: usuniecie `stanAwaryjny = !!wlaczone` z
`catch` w `bppUstawSkroty` zapala teraz DWA testy. Wczesniej stub bywal
ignorowany, wiec nie zapalalo nic.

vitest: 170/170.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant