Files
houseplan-card/docs/testing-notes/dialogs-and-forms.md
T
Claudeandclaude[bot] 7dc7597260 docs(process): ролевые конспекты, замер входа, Snapshot генерируется, TESTING.md разделён
Вход агента до первого файла кода стоил ≈ 26 700 слов (аудит 22.09).

- docs/process/AUTHOR.md и REVIEWER.md — выжимки PROCESS.md: каждый пункт
  ссылается на раздел канона, ключевые формулировки дословные;
  test/process-digests.test.mjs сверяет якоря, ссылки и правила.
- scripts/entry-cost.mjs — маршрут чтения по роли и бюджет (автор ≤ 12 000
  слов, AC1); AGENTS.md «Read this first» называет те же маршруты.
- docs/STATUS.md: блок Snapshot генерирует scripts/status-snapshot.mjs
  (версии — release-contract, счётчики — inventory, теги — git); feature
  surface и ранние milestones перенесены дословно в docs/STATUS-FEATURES.md.
- docs/TESTING.md — действующая инструкция (684 строки, AC3); ручные
  чек-листы и приложения по issue перенесены дословно в docs/testing-notes/
  с индексом и тестом на полноту.
- Промпт ревьюера в process.yml читает конспект вместо пересказа правил;
  машинные требования (строка вердикта, REVIEW_DOC, запрет fetch, таблица
  «чем краснеет», разделы повторного раунда) сохранены и закреплены тестом.
- PROCESS.md: правила не менялись; добавлены ссылка на конспекты в шапке и
  уточнение в §10.4, что ревьюер конвейера читает конспект.
- 7 мутантов в реестре.

Issue: #634
User-Visible: no
2026-09-24 02:33:08 +00:00

270 lines
18 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.
# Диалоги, формы и сводная панель
> Приложение к [`docs/TESTING.md`](../TESTING.md): перенесено оттуда дословно (#634).
> Индекс всех приложений — [`README.md`](README.md).
## Вопрос о несохранённых настройках (#610)
- [ ] В русской локали четыре формы настроек показывают **Вернуться** и
**Не сохранять**: первая кнопка сохраняет черновик, вторая закрывает
форму без сохранения [auto: `demo/smoke_discard_copy.mjs`].
- [ ] Заголовок и кнопка потери черновика используют
`mdi:content-save-off-outline`; обычный warning без override сохраняет
иконки открытого замка [auto: `demo/smoke_discard_copy.mjs`; mutation:
`discard-confirm-action-icon-falls-back-to-lock`].
- [ ] RU/EN/DE/FR на 320–640 px, light/dark и DPR 1/2 сохраняют одну строку,
полный текст, безопасный autofocus и возврат к форме
[auto: `demo/smoke_dialog_polish_603.mjs`].
## Числовые поля со слайдером (#608)
- [ ] В настройках комнаты поле размера имени принимает `120` посимвольно:
до выхода из поля черновик и слайдер остаются на прежнем значении, после
выхода оба показывают 120; очистка поля возвращает последнее
подтверждённое значение [auto: `demo/smoke_range_line_draft.mjs`].
- [ ] В настройках пространства `123` становится 125 только после завершения
ввода; движение слайдера во время незавершённого ввода отбрасывает текст
и сразу синхронизирует поле [auto: `demo/smoke_range_line_draft.mjs`,
`demo/smoke_dialog_config_parity.mjs`].
- [ ] Возврат раннего clamp на каждом `input` делает свидетель красным
[mutation: `range-line-clamps-every-keystroke`].
## Toggle confirmation state (#103)
- [ ] Every executable `ToggleNextEffect` formats current and expected lines
without deriving direction from the state label; `toggle` names Home
Assistant as the authority and a no-operation intent produces no lines
[unit: `device-toggle.test.mjs`].
- [ ] All-off/mixed/partial groups use only executable targets for their
active/total count and show skipped targets as a separate line
[unit: `device-toggle.test.mjs`].
- [ ] EN/RU confirmation renders prompt → current → expected → skipped before
the buttons, wraps a long name at 390 px and has no horizontal scroll
[auto: `smoke_toggle_confirmation.mjs`].
- [ ] A state race with the same target executes the newly resolved direction;
a changed target set makes zero service calls and shows the existing
retry toast [auto: `smoke_toggle_confirmation.mjs`].
- [ ] Cover, virtual-light, HA-control and Run confirmations keep their
existing actuation/cancel contracts [auto: `smoke_cover_tap`,
`smoke_virtual_light_toggle`, `smoke_ha_controls`, `smoke_controls`,
`smoke_tap_run`].
## Unified color and opacity picker (#57)
- [ ] RGB↔HSV round trips stay within one RGB channel; 3/6-digit HEX input is
normalized and invalid drafts never become persisted colors
[unit: `color-picker.test.mjs`].
- [ ] Every existing `hp-color-opacity` consumer receives per-card localized
labels through the unchanged color/opacity event contract, and the shared
component contains no native `input[type=color]`
[unit: `color-picker.test.mjs`].
- [ ] The localized full-width OK button is the final picker control, remains at
least 40 CSS px high in native and fallback surfaces, and closes without a
duplicate value event or click-through to the parent card
[unit: `color-picker.test.mjs`, auto: `smoke_color_picker.mjs`,
`smoke_help_affordance.mjs`].
- [ ] Invalid HEX keeps the picker open and focused on its error even after a
repeated OK; only new valid HEX input unlocks confirmation, while live
color/opacity updates and the existing outside/trigger/Escape close paths
retain their values [auto: `smoke_color_picker.mjs`].
- [ ] At 390 px the one surface exposes hue, saturation, brightness, HEX and
opacity without horizontal overflow; keyboard Shift+Arrow, touch pointer
cancellation, invalid HEX recovery, Escape focus return and disabled mode
remain safe [auto: `smoke_color_picker.mjs`].
- [ ] The color-only Glow consumer keeps the same picker without an opacity row,
and native/fallback floating surfaces remain mutually exclusive with help
[auto: `smoke_help_affordance.mjs`].
- [ ] The Hue range keeps `0…359`, step 1 and its existing input events while its
WebKit/Blink and Gecko tracks expose the same cyclic spectrum. A dual
theme-aware ring keeps the native thumb distinct from every hue, while
forced-colors falls back to system track/thumb rendering [unit:
`color-picker.test.mjs`, auto: `smoke_color_picker.mjs`].
- [ ] The dark mobile and light desktop open-picker goldens are reviewed from the
complete Linux artifact before a beta; baseline acceptance is not part of
the implementation loop [golden: `decor-color-popover-mobile-ru`,
`decor-color-popover-desktop-en`].
## Unified picker coverage for every color field (#180)
- [ ] A recursive source contract rejects every native `input[type=color]` in
product TypeScript and fixes the complete shared-component inventory at
13 template instances [unit: `color-picker.test.mjs`].
- [ ] The 11 general light/temperature/LQI/Glow/wall palettes and the space room
colour move their existing opacity into the unified picker; color and
alpha update one parent draft atomically [unit: `color-picker.test.mjs`,
auto: `smoke_color_picker_consumers.mjs`].
- [ ] Global background, space background and activity ripple are color-only;
opening/closing does not materialize an inherited/default value, and
Default/Inherit restore `null` without adding alpha
[auto: `smoke_color_picker_consumers.mjs`].
- [ ] General settings keep one exclusive picker open among 12 swatches; marker
activity colour and ripple size use separate, non-overlapping mobile rows,
ripple size remains independent, and cancelling the space dialog writes
no color draft [unit: `color-picker.test.mjs`, auto:
`smoke_color_picker_consumers.mjs`].
- [ ] The three new dialog families are reviewed from the complete Linux
artifact before a beta; implementation does not accept their baselines
[golden: `general-color-popover-desktop-en`,
`device-ripple-color-popover-mobile-ru`,
`space-room-color-popover-desktop-ru`].
## Contextual help (issues #68 and #86)
- [ ] A setting with complete EN/RU/DE/FR help body and ARIA copy shows one 32 px
desktop / 40 px coarse-pointer button with the outlined circled-question
icon. Mouse hover, keyboard focus and tap open the same text surface
[auto: `smoke_help_affordance`].
- [ ] Empty or whitespace-only help body produces no trigger. A non-empty body
without a complete ARIA label also produces no trigger; neither case adds
a tab stop or reserves visible space [auto: `smoke_help_affordance`].
- [ ] Escape, outside pointer, owning-dialog scroll, toast and a competing colour
picker close help in the documented order. The Popover and portal fallback
paths stay inside the visual viewport [auto: `smoke_help_affordance`].
- [ ] Opening help by hover, focus or tap changes neither `clientHeight`,
`scrollHeight` nor `scrollTop` of the owning dialog body. The native
Popover and forced portal fallback have the same no-layout-shift contract
[auto: `smoke_help_affordance`].
- [ ] Party 1 contains exactly the 11 agreed logical settings: space scale,
general and device Glow radius, room fill, source role and controlled
sources, general/space north, general/space background, zero-thickness
wall style and the show-hidden catalog filter. The regular and onboarding
space dialogs expose the same five space-help controls; opening help does
not change a draft setting or the session-only filter
[unit: `i18n.test`; auto: `smoke_help_affordance`;
mutation: `settings-help-party1-placement-removed`].
- [ ] `marker.controls_hint`, `gs.bg_daynight_hint`, `gs.north_hint` and
`space.zero_wall_help` are absent from all four dictionaries and production
templates; state-specific sun/Glow notes remain visible [unit: `i18n.test`].
## Help & private feedback (#43)
- [ ] Header order is Fit/zoom → General settings → Help; Help has a 44×44
target, remains available in View and all editors, and is absent in kiosk.
About appears once in Help, Russian routes to the Russian User Guide and
every other locale routes to English [unit: `support-feedback.test.mjs`;
pre-beta: support dialog smoke/golden matrix].
- [ ] A fresh dialog has empty message/contact and attachment off. Empty or
over-limit Unicode input cannot submit; Ctrl/Cmd+Enter uses the same
guard [unit: `support-feedback.test.mjs`; pre-beta: phone validation
smoke].
- [ ] Preview builds no external request and exposes exact JSON bytes, size and
hash. Download equals preview; refresh changes the package namespace;
expiry/discard/replacement and successful submit invalidate only the
intended owner/draft token [backend: `test_support_package.py`,
`test_ha_websocket.py`; pre-beta: browser network capture].
- [ ] Forbidden sentinels (raw and escaped/base64), unknown fields, names, HA
ids, URLs/paths and live states never reach package bytes. Geometry and
referential pseudonyms survive [backend: `test_support_package.py`].
- [ ] Relay transport uses only the compiled HTTPS host, follows no redirect,
bounds timeouts/response and never reflects provider text. Relay request,
rate/idempotency, spool-before-delivery and purge tests run in CI
[backend: `test_ha_support_transport.py`; relay: `python -m unittest
discover -s scripts/support-relay/tests -q`].
- [ ] Success keeps a copyable report id; failure keeps the draft and exact
attachment with Retry, Copy message, Download and manual links. Old or
mismatched backend leaves About/Guide usable but exposes no fake submit
[pre-beta: success/429/timeout/unknown-command smokes in light/dark].
## v1.71 audit polish (#434)
- [ ] Physical decor inventory counts exact lower-hex allow-listed blob files
and their real sizes independently of sidecars; a valid-shaped sidecar
without its blob remains absent from catalog/list/resolve
[backend: `test_decor_assets.py`; mutations:
`decor-physical-inventory-follows-sidecars`,
`decor-catalog-accepts-sidecar-without-blob`].
- [ ] Exact orphan re-upload repairs metadata at a full physical quota with
`reused:false`; valid catalog reuse stays `true`, and digest mismatch
changes no file. Explicit delete removes all and only exact allow-listed
blob names plus the sidecar [HA: `test_ha_websocket.py`; mutations:
`decor-orphan-repair-runs-after-quota`,
`decor-orphan-repair-claims-reuse`, `decor-delete-skips-orphan-blobs`].
- [ ] Static cards learn exact `decor_assets_api:1` only from fresh config/get,
revoke it on downgrade and never resolve without it. Positive and missing
resolve caches share only connection + config revision + sorted id set;
failed calls retry [unit: `config-store`, `space-card-audit-lows`,
`decor-assets`; smoke: `smoke_space_card_decor_capability`].
- [ ] Ready→warm cancels an existing dangerous-action promise and removes its
dialog while preserving the committed body; a request in warm refuses,
and warm→ready allows immediately before another render
[smoke: `smoke_danger_confirm_branches`; mutations:
`danger-confirm-warm-language-guard-removed`,
`danger-confirm-warm-transition-cancel-removed`].
- [ ] Area absence evidence cannot outlive its current snapshot binding
[unit: `device-area-relocation`; mutation:
`area-cleanup-keeps-candidate-outside-current-snapshot`].
- [ ] The smoke job has its own 20-minute bound and each file is wrapped by GNU
`timeout --kill-after=10s 180s`; both German route waits have one-second
diagnostics [unit: `smoke-exception-guard`; CI: `validate.yml`].
- [ ] Every well-formed token from an invalid, stale or locally unadoptable
support preview response is discarded exactly once; malformed tokens are
not echoed and cleanup never hides the original failure
[smoke: `smoke_support_feedback`; mutation:
`support-invalid-response-leaks-issued-token`].
## Надёжность сводной панели (#493)
- [ ] `test/summary-panel.test.mjs` строит индекс по 10 000 HA entities,
находит exact `entity_id` через полный индекс, ограничивает выдачу 100
строками и доказывает 0 rebuild при смене state value и ровно один при
add/remove/friendly-name change.
- [ ] `test/summary-panel-runtime.test.mjs` доказывает authority локальных
200%/150% при repeated same-key `setConfig`, stable-id ownership после
reorder/delete и lifecycle generation при route/user/permission/kiosk,
disconnect/reconnect и поздних async completion.
- [ ] `tests_backend/test_summary_panel.py`, `test_ha_websocket.py` и
`test_ha_import_export.py` проверяют одну writer matrix: omission/explicit
empty, readable и старую broken reference, Optimize, full/space/plan-only
import, Optimize/Import Undo, copy и delete вместе с independent-setting
sentinels.
- [ ] Перед `S7-code-review`: `node demo/smoke_summary_panel.mjs`. Fixture
содержит 200 rows и 10 000 states; закрытые строки не содержат entity
options, открыт ровно один picker (до 100 entity + 3 system + current
broken), а после трёх warmups и 20 samples действуют бюджеты open p95
≤250 ms и input p95 ≤50 ms. Тот же smoke проверяет 320/390 CSS px,
RU/EN, light/dark, admin/household/kiosk и 200% text без горизонтального
overflow и с targets не меньше 44 px.
- [ ] `node scripts/mutation-gate.mjs --changed origin/dev..HEAD` обязан поймать
снятие DOM bound, state-value rebuild, local-scale authority, lifecycle
generation и Optimize writer guard. Полные golden/smoke/performance и
Linux CI HA harness остаются обязательным гейтом точного SHA беты.
## Идентичность сводной панели в Masonry (#561)
- [ ] `test/summary-panel.test.mjs` пересоздаёт весь production-shaped
`hui-masonry-view`: canonical `cards=[A,B,C]`, wide-колонки `[A,C] [B]`
и новый narrow DOM `[A,B,C]`. Slots обязаны остаться
`masonry-v2:0/1/2`, без коллизии old C → new B.
- [ ] Тот же unit проверяет live reflow, nested stack через ShadowRoot,
remount внутренней House Plan card, два nested instances и повторную
попытку resolution после временно отсутствующего `masonry.cards`.
- [ ] `test/summary-panel-runtime.test.mjs` доказывает отсутствие localStorage
read/write при unresolved identity, отсутствие импорта/удаления старого
DOM-path key и независимое восстановление show/scale одинаковых карточек
после полного reload.
- [ ] Мутант `summary-masonry-identity-uses-visual-dom-path` возвращает
структурный DOM path вместо canonical index; focused #561 unit обязан
покраснеть. Перед бетой selected summary-panel smoke проверяет ротацию
touch/kiosk viewport по общему release-процессу.
## Доступность основного View (#565)
- [ ] `test/device-presentation.test.mjs` доказывает whole-segment
дедупликацию доступного имени во всех четырёх локалях, сохранение порядка
и отсутствие ложного удаления при частичном совпадении.
- [ ] `demo/smoke_household_journeys.mjs` проверяет именованную навигацию и
единственный `aria-current="page"`, реальный Tab-focus tooltip, переход к
следующему устройству, blur-cleanup и попадание подсказки в viewport.
Комбинированный mouse+keyboard сценарий доказывает, что комната не
заменяет focus-tooltip, другое устройство сохраняет обычный hover, а
pointerleave восстанавливает подсказку всё ещё сфокусированного устройства.
- [ ] `demo/smoke_space_card.mjs` считает сегменты тревоги в доступном имени
статической карточки и не добавляет ей интерактивность.
- [ ] Мутанты `view-current-space-aria-removed`,
`device-accessible-label-dedup-removed`,
`device-focus-tooltip-handler-removed` и
`device-focus-tooltip-blur-cleanup-removed`, а также мутанты раунда r2
`device-focus-tooltip-room-hover-overwrites` и
`device-pointer-leave-clears-focus-fallback` обязаны покраснеть на своих
заявленных гардах.