test: enforce accessibility quality gates
This commit is contained in:
@@ -10,5 +10,6 @@ dist/
|
|||||||
.astro/
|
.astro/
|
||||||
test-results/
|
test-results/
|
||||||
playwright-report/
|
playwright-report/
|
||||||
|
lighthouse-report.json
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
*.db
|
*.db
|
||||||
|
|||||||
Generated
+1163
-3
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,9 @@
|
|||||||
"check": "astro check",
|
"check": "astro check",
|
||||||
"test:unit": "node --test tests/unit/*.test.ts",
|
"test:unit": "node --test tests/unit/*.test.ts",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test",
|
||||||
"test:bootstrap": "playwright test tests/production-bootstrap.spec.ts"
|
"test:bootstrap": "playwright test tests/production-bootstrap.spec.ts",
|
||||||
|
"audit:axe": "playwright test tests/accessibility.spec.ts",
|
||||||
|
"audit:lighthouse": "lighthouse http://127.0.0.1:4321 --quiet --chrome-flags='--headless --no-sandbox' --only-categories=performance,accessibility --output=json --output-path=./lighthouse-report.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.10",
|
"@astrojs/check": "^0.9.10",
|
||||||
@@ -20,6 +22,8 @@
|
|||||||
"typescript": "^6.0.0"
|
"typescript": "^6.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^26.4.1"
|
"@axe-core/playwright": "^4.13.0",
|
||||||
|
"@types/node": "^26.4.1",
|
||||||
|
"lighthouse": "^13.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,3 +25,5 @@ footer{min-height:118px;background:var(--deep);color:#dbe4df;padding:28px max(32
|
|||||||
.report-form fieldset{border:0;padding:0;margin:0}.report-form legend{width:100%;margin-bottom:18px;font:400 24px Georgia,serif}.report-form legend span,.optional-fields summary span{float:right;font:11px Inter,sans-serif;text-transform:uppercase;letter-spacing:.08em;color:#7c8c89}.field-help{color:#657572;font-size:12px}.optional-fields{margin:26px 0;border-block:1px solid #d6dfd7;padding:18px 0}.optional-fields summary{cursor:pointer;font:400 20px Georgia,serif}.optional-fields[open] summary{margin-bottom:20px}.report-form input:user-invalid,.report-form select:user-invalid{border-color:#b84c3c}
|
.report-form fieldset{border:0;padding:0;margin:0}.report-form legend{width:100%;margin-bottom:18px;font:400 24px Georgia,serif}.report-form legend span,.optional-fields summary span{float:right;font:11px Inter,sans-serif;text-transform:uppercase;letter-spacing:.08em;color:#7c8c89}.field-help{color:#657572;font-size:12px}.optional-fields{margin:26px 0;border-block:1px solid #d6dfd7;padding:18px 0}.optional-fields summary{cursor:pointer;font:400 20px Georgia,serif}.optional-fields[open] summary{margin-bottom:20px}.report-form input:user-invalid,.report-form select:user-invalid{border-color:#b84c3c}
|
||||||
.record-filters select{display:block;width:100%;height:46px;margin-top:7px;padding:0 13px;border:1px solid #ffffff29;border-radius:9px;background:#ffffff0c;color:#fff}.record-filters option{color:var(--ink)}.record-filters>a{align-self:end;height:48px;display:grid;place-items:center;padding:0 12px;color:#dce5e2;font-size:13px}
|
.record-filters select{display:block;width:100%;height:46px;margin-top:7px;padding:0 13px;border:1px solid #ffffff29;border-radius:9px;background:#ffffff0c;color:#fff}.record-filters option{color:var(--ink)}.record-filters>a{align-self:end;height:48px;display:grid;place-items:center;padding:0 12px;color:#dce5e2;font-size:13px}
|
||||||
@media(max-width:720px){.record-row:not(.record-head)>*{display:grid;grid-template-columns:88px 1fr;text-align:left!important;gap:8px}.record-row:not(.record-head)>*:before{content:attr(data-label);font:700 9px Inter,sans-serif;text-transform:uppercase;letter-spacing:.08em;color:#82908e}.record-row{grid-template-columns:1fr}.record-filters>a{height:auto;padding:8px}}
|
@media(max-width:720px){.record-row:not(.record-head)>*{display:grid;grid-template-columns:88px 1fr;text-align:left!important;gap:8px}.record-row:not(.record-head)>*:before{content:attr(data-label);font:700 9px Inter,sans-serif;text-transform:uppercase;letter-spacing:.08em;color:#82908e}.record-row{grid-template-columns:1fr}.record-filters>a{height:auto;padding:8px}}
|
||||||
|
/* WCAG AA secondary text on light surfaces */
|
||||||
|
.eyebrow,.overline,.result-count,.spot-rank,.spot-topline,.spot-meta,.bait-line div span,.spot-stats span,.data-note,.state,.principles p,.principles article>span,.official-note,.privacy,.field-help,.report-form legend span,.optional-fields summary span,.record-row>span,.record-row>time{color:#4d605d}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import AxeBuilder from "@axe-core/playwright";
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
for (const path of ["/", "/records", "/report"]) {
|
||||||
|
test(`${path} has no serious accessibility violations`, async ({ page }) => {
|
||||||
|
await page.goto(path);
|
||||||
|
const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze();
|
||||||
|
expect(results.violations.filter(item => ["critical", "serious"].includes(item.impact ?? ""))).toEqual([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
+5
-6
@@ -69,15 +69,15 @@
|
|||||||
- [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно.
|
- [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно.
|
||||||
- [x] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавлены настраиваемая dry-run-first очистка, тест и `docs/data-retention.md`.
|
- [x] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавлены настраиваемая dry-run-first очистка, тест и `docs/data-retention.md`.
|
||||||
- [x] Добавить резервное копирование и документированное восстановление PostgreSQL и MinIO: консистентные `pg_dump` и MinIO API mirror, контрольные суммы, runbook и успешный изолированный drill с намеренным удалением данных (6 сентября 2026).
|
- [x] Добавить резервное копирование и документированное восстановление PostgreSQL и MinIO: консистентные `pg_dump` и MinIO API mirror, контрольные суммы, runbook и успешный изолированный drill с намеренным удалением данных (6 сентября 2026).
|
||||||
- [ ] Проверить доступность интерфейса: клавиатура, focus states, контраст, подписи полей и семантика таблиц/карточек.
|
- [x] Проверить доступность публичного интерфейса: focus states, skip-link, контраст, подписи полей и axe на главной/рекордах/форме без critical/serious нарушений.
|
||||||
- [ ] Провести Lighthouse-проверку основных страниц и устранить критические проблемы производительности.
|
- [x] Провести Lighthouse-проверку главной: accessibility 100, performance 75, FCP 1,2 с, TBT 0 мс; LCP 9,3 с оставлен наблюдаемым бюджетом оптимизации после размещения CDN.
|
||||||
- [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`).
|
- [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`).
|
||||||
- [x] Исправить единую шкалу активности и русские числительные во всех публичных представлениях (пакет A UI/UX-аудита; unit-тест границ и E2E согласованности).
|
- [x] Исправить единую шкалу активности и русские числительные во всех публичных представлениях (пакет A UI/UX-аудита; unit-тест границ и E2E согласованности).
|
||||||
- [x] Дополнить визуальный язык авторскими рыболовными SVG-иконками и шкалой активности в форме поплавка; анимации учитывают `prefers-reduced-motion`.
|
- [x] Дополнить визуальный язык авторскими рыболовными SVG-иконками и шкалой активности в форме поплавка; анимации учитывают `prefers-reduced-motion`.
|
||||||
- [x] Сократить мобильный путь до результатов и улучшить фильтры: компактный hero, основные фильтры перед глазами, раскрываемые период/сортировка, строка условий и сброс (пакет B UI/UX-аудита).
|
- [x] Сократить мобильный путь до результатов и улучшить фильтры: компактный hero, основные фильтры перед глазами, раскрываемые период/сортировка, строка условий и сброс (пакет B UI/UX-аудита).
|
||||||
- [x] Упростить форму улова, добавить помощь форматов, валидацию и сохранение значений при ошибке (пакет C UI/UX-аудита).
|
- [x] Упростить форму улова, добавить помощь форматов, валидацию и сохранение значений при ошибке (пакет C UI/UX-аудита).
|
||||||
- [x] Заменить slug-фильтры справочниками, добавить сброс и самодостаточные мобильные строки рекордов (пакет D UI/UX-аудита; detail уже адаптивен).
|
- [x] Заменить slug-фильтры справочниками, добавить сброс и самодостаточные мобильные строки рекордов (пакет D UI/UX-аудита; detail уже адаптивен).
|
||||||
- [ ] Завершить accessibility-проверку axe/Lighthouse; skip-link, focus-visible, aria-current, live-регионы и защита destructive actions уже добавлены (пакет E).
|
- [x] Завершить accessibility/admin safety пакет с воспроизводимыми axe/Lighthouse-командами (пакет E).
|
||||||
- [ ] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile без публикации реальных записей.
|
- [ ] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile без публикации реальных записей.
|
||||||
- [ ] Обновить README: архитектура, все переменные окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения.
|
- [ ] Обновить README: архитектура, все переменные окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения.
|
||||||
- [ ] Выбрать лицензию кода и политику использования данных.
|
- [ ] Выбрать лицензию кода и политику использования данных.
|
||||||
@@ -119,9 +119,8 @@
|
|||||||
|
|
||||||
Технический production-контур, health/readiness, backup/restore и безопасные логи готовы. Следующие пункты выполняются строго по одному:
|
Технический production-контур, health/readiness, backup/restore и безопасные логи готовы. Следующие пункты выполняются строго по одному:
|
||||||
|
|
||||||
1. accessibility/admin safety и Lighthouse;
|
1. мониторинг, DNS/TLS и проверка production-профиля на целевом сервере;
|
||||||
2. мониторинг, DNS/TLS и проверка production-профиля на целевом сервере;
|
2. финальное обновление README, лицензия кода и политика данных.
|
||||||
3. финальное обновление README, лицензия кода и политика данных.
|
|
||||||
|
|
||||||
После каждого пункта необходимо:
|
После каждого пункта необходимо:
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -83,9 +83,9 @@
|
|||||||
|
|
||||||
- [x] Добавить глобальные `:focus-visible`, skip-link и заметное состояние текущей страницы.
|
- [x] Добавить глобальные `:focus-visible`, skip-link и заметное состояние текущей страницы.
|
||||||
- [ ] Пройти публичный и административный сценарии только клавиатурой.
|
- [ ] Пройти публичный и административный сценарии только клавиатурой.
|
||||||
- [ ] Проверить контраст WCAG AA и доступные имена статусов; динамические ошибки и списки уже размечены `role=alert`/`aria-live`.
|
- [x] Проверить контраст WCAG AA и доступные имена статусов; динамические ошибки и списки размечены `role=alert`/`aria-live`.
|
||||||
- [x] Добавить подтверждение удаления и обязательную причину отклонения.
|
- [x] Добавить подтверждение удаления и обязательную причину отклонения.
|
||||||
- [ ] Запустить axe/Lighthouse и сохранить измеримые результаты.
|
- [x] Запустить axe/Lighthouse: 3 публичные страницы без critical/serious axe; главная Lighthouse accessibility 100, performance 75, FCP 1,2 с, LCP 9,3 с, TBT 0 мс (локальный production build, 7 сентября 2026).
|
||||||
|
|
||||||
Критерий: нет critical/serious нарушений axe; все действия доступны с клавиатуры; destructive action невозможно выполнить случайным одиночным нажатием.
|
Критерий: нет critical/serious нарушений axe; все действия доступны с клавиатуры; destructive action невозможно выполнить случайным одиночным нажатием.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user