food-market/docs/sprint-ui-deep-progress.md
nns 51aae4482f
Some checks failed
CI / Backend (.NET 8) (push) Has been cancelled
CI / Web (React + Vite) (push) Has been cancelled
CI / POS (WPF, Windows) (push) Has been cancelled
test(ui-deep): items 10-14 — все 59/59 ✓ на стейдже
Item 10 (2 specs): OrgAuditLog после seed-demo — записи видны, diff раскрывается.

Item 11 (4 specs): 2FA flow через API (UI 2FA пока не реализован).
Самодельная TOTP-генерация (RFC 6238) на crypto.createHmac sha1 —
без otplib v13 plugin'ов.

Item 12 (4 specs): неверный пароль — читаемая ошибка не «Request failed».
Forgot-password + login OK happy-path. Known: за 10 попыток login не
получили 429 — rate-limit possibly disabled.

Item 13 (5 specs, P0): multi-tenant изоляция HOLDS. GET/PUT/DELETE
товара A с токеном B → все 404/403, UI B не видит имя/данные A.

Item 14 (5 specs): mobile viewport 375x667 — sidebar схлопывается,
drawer открывается+закрывается, products list без horizontal overflow,
ConfirmDialog влезает.

Итого: 59 specs, найдены 6 багов (починены), 2 known issues
(Supply lost-update, login rate-limit).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 13:53:57 +05:00

88 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sprint UI-deep — глубокое браузерное тестирование stage
Цель: пройти `https://test.admin.food-market.kz` через **реальный Chromium**
(Playwright Test) и найти UX-баги, которые axios-проверки не видят:
console errors, network 5xx/4xx, layout breaks, missing loading states,
проблемы responsive, отсутствие confirm/validation/disabled-state и
multi-tenant утечки через URL.
Старт: 2026-05-30. Исполнитель: Claude Opus 4.7 (автономный режим).
## Стек
- `@playwright/test` runner — параллельные специ, trace-on-failure, screenshot-on-failure.
- `otplib` — генерация TOTP-кодов для item 11 (2FA flow).
- Все спецы лежат в `tests/e2e/scenarios/stage-ui-*.spec.ts`.
- `tests/e2e/playwright.config.ts` — конфиг с `BASE`, `headless: true`,
`screenshot: 'only-on-failure'`, `trace: 'retain-on-failure'`.
## Принципы
- Каждый пункт = отдельный spec-файл (.spec.ts).
- Каждый баг: воспроизвести в test() → починить код → `dotnet build` + локальные тесты → `~/deploy-stage.sh` → retest spec на стейдже зелёный → коммит фикса → коммит spec → `[x]` в этом доке.
- НЕ трогать: `global.json`, прод-стек, POS WPF.
## Чек-лист
- [x] **1. Signup → onboarding → первая работа**`stage-ui-1-signup-flow.spec.ts` (5 specs ✓). Найден баг: ProductEditPage race на currencies — теперь disabled пока не подгрузились + canSave проверяет currencyId. Form-level error display переведён на `humanizeError()` — больше не «Request failed with status code 400».
- [x] **2. Дашборд + навигация**`stage-ui-2-nav.spec.ts` (4 ✓). 27 sidebar-страниц последовательно открыты в Chromium, 0 console-errors, 0 5xx. Активный пункт (aria-current="page") и labels проверены.
- [x] **3. Каталог (товары) full CRUD**`stage-ui-3-products-crud.spec.ts` (5 ✓). Найдены 2 бага: race на currencies (item 1) + ghost-404 toast после Delete (refetch на удалённый id из-за invalidate). Также Modal a11y улучшен. Image upload — через `setInputFiles()`, проверяем response code.
- [x] **4. Контрагенты / Группы / Единицы / Типы цен**`stage-ui-4-references-crud.spec.ts` (4 ✓). Контрагенты: modal CRUD с ConfirmDialog. Группы: create через UI. Типы цен: bootstrap + новая. Единицы — smoke.
- [x] **5. Сотрудники + Роли**`stage-ui-5-employees-roles.spec.ts` (3 ✓). 2 бага: 1) EmployeesPage save показывал «Request failed with status code 400» — фикс через humanizeError; 2) После create list не refetch'ался — фикс qc.invalidateQueries после direct api.post.
- [x] **6. Приёмка (Supply)**`stage-ui-6-supply.spec.ts` (3 ✓). Save disabled на пустом черновике, UI правильно показывает Posted после API post, остаток обновлён. **Найден P2 баг (known)**: Supply нет optimistic concurrency — 2 вкладки могут перезаписать друг друга (lost-update). Зафиксирован как known issue для будущего фикса.
- [x] **7. RetailSale + CustomerReturn**`stage-ui-7-retail-sale.spec.ts` (4 ✓). Oversell на Post возвращает понятное русское сообщение. Payment validation работает. Кнопка «Возврат» доступна на проведённом чеке.
- [x] **8. Складские документы**`stage-ui-8-inventory-docs.spec.ts` (5 ✓). Все 6 doc-форм рендерятся с правильным Submit state. Transfer ToStore фильтрует выбранный FromStore. Inventory CSV-import видна на draft. Enter Post через UI ✓. Demand oversell — понятный русский текст.
- [x] **9. Отчёты — Sales/Stock/Profit/ABC**`stage-ui-9-reports.spec.ts` (6 ✓). Все 4 отчёта рендерятся без console-errors. Sales CSV скачивается через `page.waitForEvent('download')`. Stock XLSX endpoint возвращает корректный MIME+body.
- [x] **10. OrgAuditLog UI**`stage-ui-10-audit-log.spec.ts` (2 ✓). После seed-demo записи видны, diff `<details>/<summary>` раскрывается.
- [x] **11. 2FA flow**`stage-ui-11-2fa.spec.ts` (4 ✓). API-only (UI 2FA не реализован пока). Минимальная TOTP-генерация (RFC 6238) на crypto.createHmac sha1 — без зависимостей. Enroll/Verify/Disable работают, status флипается.
- [x] **12. Login edge**`stage-ui-12-login-edge.spec.ts` (4 ✓). Неверный пароль показывает читаемую ошибку (не «Request failed»). Forgot-password flow + happy-path login → redirect. **Known issue**: за 10 попыток login не словили 429 — rate-limit либо отключён, либо окно длиннее 10 попыток.
- [x] **13. Multi-tenant изоляция через URL**`stage-ui-13-multitenant.spec.ts` (5 ✓). **P0 ПРОВЕРКА — изоляция HOLDS**. GET/PUT/DELETE товара A с токеном B → все 404/403. UI B на /products/{id-A} НЕ показывает имя A. Список B показывает EmptyState.
- [x] **14. Mobile viewport 375x667**`stage-ui-14-mobile.spec.ts` (5 ✓). Sidebar схлопывается на md, гамбургер виден, drawer открывается+закрывается, products list без horizontal overflow, ConfirmDialog влезает.
## Журнал
### 2026-05-30 — старт
- Создан этот файл. Sprint 7 (UX-полировка) закрыт ранее — теперь смотрим уже на «улучшенный» UI и ищем оставшиеся дыры.
- Подготовка: устанавливаю `@playwright/test`, `otplib`. Конфиг + helper'ы.
### 2026-05-30 — итог
**59/59 спецификаций ✓** на `https://test.admin.food-market.kz` после последнего deploy-stage.
**Найдено и починено (6 багов):**
1. **ProductEditPage race на currencies** — если юзер кликнул цену до загрузки справочника валют, в payload уходил `currencyId=''` → server 400 с криптичным JSON-validation. Фикс: MoneyInput disabled пока `!currencies.data`, canSave проверяет row.currencyId.
2. **Generic axios error в form-level error display** — пользователь видел «Request failed with status code 400» вместо реальной API-подсказки. Экспортировал `humanizeError()` из `@/lib/api`, применил в ProductEditPage и EmployeesPage.
3. **Modal a11y** — компонент `<Modal>` не имел `role="dialog"` / `aria-modal` / `aria-labelledby`. Screen reader не определял диалог. Также добавил `aria-label="Закрыть"` на крестик.
4. **Ghost-404 toast после Delete товара** — ProductEditPage.remove делал `invalidateQueries({queryKey:['/api/catalog/products']})` до navigate; TanStack Query refetch'ил конкретно `['/api/catalog/products', id]` (тот что живёт на той же странице) → 404 → toast «Не найдено» поверх редиректа. Фикс: просто `navigate()`, без cache-touch. Refetch list при заходе на ProductsPage сам обновит.
5. **EmployeesPage save error** — тоже показывал «Request failed with status code 400». Через humanizeError.
6. **EmployeesPage create не обновлял list** — direct `api.post` без invalidateQueries (мутации с custom-response shape для generated password). Фикс: `await qc.invalidateQueries({queryKey:[URL]})` после успеха.
**Known issues (documented, не блокирующие):**
- **Supply lost-update**: нет optimistic concurrency. 2 вкладки → обе сохраняются успешно (HTTP 204), второй overwrite'ит первый. P2 для будущего sprint'а — добавить ETag или RowVersion.
- **Login rate-limit**: за 10 попыток `/connect/token` подряд (с разными username) ни одна не получила 429. Либо rate-limit отключён, либо настроен слишком широко (>10/min). Стоит проверить configuration.
**P0 проверка прошла:** multi-tenant изоляция работает. GET/PUT/DELETE товара A с токеном B → все 404/403. UI B на /products/{id-A} НЕ показывает имя A.
**Покрытие 14 пунктов:**
| # | Тема | specs | результат |
|---|---|---|---|
| 1 | Signup + first work | 5 | ✓ + 1 bug fixed |
| 2 | Dashboard + navigation | 4 | ✓ (27 страниц без errors) |
| 3 | Products CRUD | 5 | ✓ + 2 bugs fixed |
| 4 | References CRUD | 4 | ✓ |
| 5 | Employees + Roles | 3 | ✓ + 2 bugs fixed |
| 6 | Supply UI | 3 | ✓ + 1 known issue |
| 7 | RetailSale + CustomerReturn | 4 | ✓ |
| 8 | Inventory documents | 5 | ✓ |
| 9 | Reports + downloads | 6 | ✓ |
| 10 | OrgAuditLog UI | 2 | ✓ |
| 11 | 2FA flow (API-only) | 4 | ✓ |
| 12 | Login edge cases | 4 | ✓ + 1 known issue |
| 13 | Multi-tenant URL isolation (P0) | 5 | ✓ |
| 14 | Mobile viewport 375x667 | 5 | ✓ |
| **Σ** | | **59** | **59/59 ✓** |