# Manual testing checklist > **Policy:** this checklist is updated **in the same commit** as any functional > change (like CHANGELOG.md). Every release: run at least the smoke column on the > synthetic demo (`demo/`), and the full list before major releases. Items marked > `[manual]` are covered by unit tests or the headless smokes in `demo/` — they still > deserve an occasional eyeball. File every failure as a GitHub issue before fixing. > **What `[manual]` means (since 2026-07-27).** A named check exists that FAILS > when the behaviour breaks — in `npm test` or in the smoke suite, both of which > run in CI on every push. Where the check lives is written next to the marker > (`[auto: smoke_modes]`). Before this date the marker described an intention: > the smoke suite printed values and always exited 0, so 96 markers guarded > nothing (external audit T1/T3). If you add a checklist line marked `[manual]`, > add the failing check in the same commit. - [ ] Smoke harness itself (v1.43.2, audit T1/T2): every smoke asserts named facts via `check`/`checkAll` and exits non-zero on any mismatch or uncaught in-card exception; the suite runs in CI against a FRESHLY built bundle. Sanity ritual: break one invariant on purpose (e.g. remove the kiosk editor guard) and confirm the matching smoke goes red [auto: CI job "smoke"] - [ ] Room gear discoverability (v1.43.3, user feedback): in the Plan editor every room card carries a pill button "⚙ Room" of a FIXED readable size (independent of the card font) — including rooms without a name; it opens Room settings [auto: smoke_feedback_v2] - [ ] Metrics readability (v1.43.3): the metrics line is 0.75 of the room name (was 0.62 — unreadable on tablets); per-room sliders still apply on top [auto: smoke_feedback_v2] - [ ] Touch tooltips, take two (v1.43.3): a hover tooltip never appears after ANY touch/pen pointer event, even if the browser claims `hover: hover` (stylus, paired mouse, vendor skins) [auto: smoke_feedback_v2] - [ ] Light-source flag (v1.44.0, user feedback): a smart SWITCH driving dumb fixtures glows in the "Light sources" fill once "This device is a light source" is ticked (its own entity or the lights bound under "Controls"); unticked devices without a light entity never glow [auto: smoke_glow] - [ ] Device card controls (v1.44.0): the device card opens with its controllable entities FIRST — toggles right there (≥30 px tap targets), cover/lock/climate open HA more-info; model, links and manuals moved below; config/diagnostic entities are not listed; locks never toggle from the card [auto: smoke_card_controls] - [ ] Lock invariant, all paths (v1.44.2, review CR-1): icon tap, controls[], device card and _cardToggle refuse locks/alarm panels entirely; the door card's Unlock asks for confirmation, Lock does not [auto: smoke_lock_invariant] - [ ] Attachment migration is transactional (v1.44.2, review CR-2/CR-3): rebinding COPIES files, saves the config, and only then deletes the old folder; a rejected save leaves the old files and urls intact; a name collision in the destination gets a unique name (the pre-existing file is never silently linked); urls are rewritten only for confirmed copies [auto: unit logic.test + tests_backend] - [ ] Plans and PDFs load in a real browser (v1.44.3, B1 regression): open a dashboard with an uploaded plan — the background renders and a manual link opens; DevTools shows /api/houseplan/content/... returning 200 via a signed url, while the same url without authSig returns 401 [auto: tests_backend + manual] - [ ] Auth policy is single-sourced (v1.44.4, B2): the HTTP upload and every WS write use the same `may_write`, which denies non-admins when the config entry is unavailable [auto: tests_backend] - [ ] Coordinates and caps (v1.44.4, B5): NaN/Infinity are refused on room rects, polygon vertices, view_box and openings — not only in layout; the openings list honours MAX_OPENINGS [auto: tests_backend] - [ ] Drag hardening (v1.44.4, L4 sub-item): every drag pipeline captures the pointer through the tolerant helper; decor shapes cannot be dragged more than a quarter of the plan outside the viewBox [auto: smoke_decor] - [ ] Room climate counts hidden sensors (v1.44.5): a thermometer that is NOT placed on the plan (hidden by curation or by the user) still feeds the room card, the tooltip and the temperature fill; fridges/TRVs still do not; an explicit per-room source still wins [auto: unit devices.test] - [ ] Room tooltip wording (v1.44.5): hovering a room shows its name (plus temperature/signal when available) and no longer claims "open the area" — room clicks were removed in v1.40.1 [manual] ## Environments matrix Run the *core flows* (marked ★ below) in each environment at least once per minor release: - [ ] Chrome / Edge (desktop, Windows or Linux) - [ ] Firefox (desktop) — SVG viewBox math and container queries differ historically - [ ] Safari (macOS) — pointer events / pinch behavior - [ ] HA Companion app, Android (cold start! the v1.7.2 race lived here) - [ ] HA Companion app, iOS - [ ] Tablet in kiosk/panel mode (wall-tablet scenario, landscape) - [ ] Phone portrait, narrow ≤400 px (adaptive header ≤620 px) - [ ] Dark theme and light theme (badges, dialogs, plan contrast) - [ ] RU profile locale and EN profile locale (+ `language:` card option forcing each) ## Installation / upgrade / removal - [ ] Fresh install via HACS custom repository → integration appears, card auto-registers as a Lovelace resource (`?v=` matches manifest); no manual resource setup - [ ] `single_config_entry`: adding a second entry is impossible [manual] - [ ] Upgrade via HACS: `?v=` bumps after HA restart, browser picks the new bundle without cache clearing - [ ] YAML-mode Lovelace: falls back to `extra_module_url` (card loads) - [ ] Removal: delete entry → Lovelace resource entry disappears; `.storage/houseplan.*` survives; reinstall picks the old config up - [ ] Diagnostics download works; personal fields (name/link/description/pdfs) are `**REDACTED**` [manual] ## Modes (v1.25.0) ★ - [ ] The card always loads in **View**; edit modes are never restored [manual] - [ ] View: pan/zoom/space-switch/tap/long-press/tooltips only — dragging an icon, label or opening does nothing; panning may start on top of an icon [manual] - [ ] View header: space tabs + count + zoom + editor tabs, the general-settings cog and the per-space gears (visible in EVERY mode since v1.30.1/v1.30.3 for users who may edit); no editor toolbars [auto: smoke_modes] - [ ] Plan: markup toolbar, space gears, +space, ⚙ palette; device icons hidden, labels/openings draggable; orange stage frame [manual] - [ ] Devices: icon drag works, click opens the marker editor directly; +/👁/↺/⬡ buttons; accent stage frame [manual] - [ ] Mode tabs hidden for non-admin users; segmented control highlights the active mode - [ ] Openings in View (v1.28.1): the door/window itself is a pure drawing — no cursor change, no hover outline, no hit target, no click, regardless of bindings [manual] - [ ] The LOCK BADGE is the one exception: when a lock is bound it is shown and clickable in View (pointer cursor, click → door/lock info card); inert in Plan so it does not fight editing [manual] - [ ] Device icons in View show a pointer cursor (no grab); grab only in the Devices mode [manual] - [ ] In Plan an opening is interactive: grab cursor, hover outline, drag along walls, click (any tool) opens its properties — with a 3 px drag threshold since v1.43.1, so a tap is never swallowed [auto: smoke_inert_openings] ## Onboarding ★ - [ ] Empty config, HA has floors → floors-import wizard offers them sorted by level [manual] - [ ] Wizard: uncheck all → "Create" disabled; "Start from scratch" → classic dialog - [ ] Wizard: N floors → space dialog per floor with prefilled name and progress "i of N"; Skip skips one; Cancel aborts the whole queue [manual] - [ ] After the last wizard space (or first manual space) → markup mode auto-opens with a toast - [ ] Empty config, no floors → classic "New space" dialog auto-opens once per session - [ ] All floors skipped, nothing created → empty state with "Add space" button remains usable ## Spaces ★ - [ ] Create with an image (SVG, PNG, JPG, WebP) → correct aspect, crisp at zoom (SVG) - [ ] Oversized plan (>8 MB) → readable error toast, dialog stays open - [ ] Create with "No image — I'll outline rooms by hand": orientation landscape/portrait/square respected [manual]; borders+names default ON [manual] - [ ] Draw-space (no background) renders a WHITE canvas (paper-like), markup works on it; room borders/names stay legible on white [manual] - [ ] Edit: rename; replace image; **switch image→draw detaches the plan** [manual] - [ ] Delete space with rooms/devices → tab disappears, layout of other spaces untouched - [ ] Display settings: borders toggle, names toggle, color picker + opacity slider live-preview after save, fill selector [manual] - [ ] Fill "zigbee": rooms tint red→green by average LQI; rooms without zigbee stay unfilled [manual] - [ ] Fill "lights": yellow when any light on, grey when all off, unfilled when the room has no lights [manual]; toggling a light from the plan recolors the room - [ ] Fill "temperature": blue below the comfort range, green inside, yellow above [manual]; comfort bounds editable inline on the radio row (swapped bounds tolerated [manual], clearing a field cannot zero a bound [manual]); rooms without a temperature reading stay unfilled [manual] - [ ] Fill mode is a radio group (no dropdown); labels carry no color legend - [ ] Room hover darkens the current fill (no recolor to blue); unfilled rooms hover light grey - [ ] Room tooltip shows the average room temperature when any thermometer reports [manual] - [ ] Average room temperature counts ONLY thermometer/air-monitor devices — fridges, TRV heads, smart-plug chip temperatures (`*_device_temperature`) and diagnostic-category temps are excluded [manual] - [ ] Space dialog is 500 px wide; the comfort-bounds inputs are compact (56 px) - [ ] The scale (cm per cell) input is compact (72 px), not full-width [manual] - [ ] General settings (⚙ in the header): fill colors grouped by mode (lights on/off/none, temp cold/comfy/hot, LQI weak/strong), each with its own opacity slider [manual]; Reset restores defaults; saving defaults stores nothing [manual] - [ ] Custom fill colors apply to the full card AND the static space-card - [ ] LQI gradient interpolates between the configured weak/strong colors [manual] - [ ] Per-space "Show zigbee signal (LQI)" toggle hides/shows the badges next to devices and the signal line in room tooltips for that space only [manual] - [ ] Device icon badge is centred exactly on its point (no 1 px down-right drift) [manual] - [ ] Device glyph is centred within its badge (no vertical drift — real ha-icon is block+line-height) [manual] - [ ] Room hover highlight still works when custom borders/fills are on - [ ] Settings persist across reload and other browsers (server-side) ## Room markup editor ★ - [ ] Grid appears; dots snap; the outline draws pair-by-pair; shared walls reused - [ ] Ruler: while drawing, the length of the current segment follows the cursor (metres, or feet+inches on an imperial HA); scale = space "cm per cell" (default 5) - [ ] A line cannot exist on its own: start an outline, do NOT close it, leave markup — no lines are left behind (nothing was written to the config) - [ ] Deleting a room removes its walls, EXCEPT those shared with a neighbouring room (the neighbour still yields them); deleting the neighbour too removes them as well - [ ] There is no "Erase" tool in the markup toolbar (removed in v1.19.0) - [ ] Rooms never overlap (v1.20.0): a click strictly inside an existing room is refused with a toast; a click ON a shared wall (including mid-span of a longer neighbour wall) still works - [ ] Closing an outline drawn AROUND an existing room is refused; the outline stays open - [ ] Merge (v1.21.0): two rooms sharing a wall merge into one; the dialog picks the surviving name/area; rooms touching only at a corner or apart are refused with a toast - [ ] Split (v1.21.0): click a room, then two points on its walls — the bigger part keeps the name/area/devices, the smaller opens the new-room dialog; Cancel leaves the room whole - [ ] Split: a cut with an end off the wall, or along a wall, is refused with a toast - [ ] Split: the click snaps to the nearest wall, so it works on non-grid-aligned rooms (imported/legacy polygons), not only on rooms drawn on the current grid [manual] - [ ] Split: a click far from any wall (middle of the room) is a miss with a toast — the wall-snap pull is capped, accidental clicks do not pick a wall [manual] - [ ] Esc / Ctrl+Z removes the last dot (and its line); Reset clears the path - [ ] Closing the contour (click the first dot, ≥4 points) opens the room dialog - [ ] Room dialog: area list shows only unassigned areas; picking an area prefills the name - [ ] "No area" room (decorative) requires a name; saves with `area: null` - [ ] Cancel in the dialog reopens the contour (last point undone) - [ ] Saving a room with an area: area devices appear with icons; positions are fixed into the layout [manual] - [ ] Erase tool removes exactly the clicked line; Delete-room removes the polygon after confirm - [ ] Device icons hidden during markup; visible again on exit ## Devices on the plan ★ - [ ] Auto devices appear only in rooms bound to their area [manual] - [ ] Curation hides bridges/groups/scenes/excluded integrations; 👁 "show all" reveals [manual] - [ ] Duplicate "name|area" numbered ("Lamp", "Lamp 2") [manual] - [ ] Light groups fold their single lamps; `group_lights=false` unfolds [manual] - [ ] Drag anywhere (no edit mode), snaps to grid, persists after reload, per space - [ ] ↺ reset restores auto layout after confirm - [ ] Temperature badge on thermometers; LQI value under zigbee icons with red→green color - [ ] Live states: light on = yellow, open cover/lock/door = orange, unavailable = faded - [ ] State icons (v1.26.0): auto icons morph with state — door/window/garage open↔closed, lock locked↔unlocked, bulb on; custom icons and unavailable states never morph [manual] - [ ] display "Value instead of an icon": the marker shows the measurement (°/%/unit) as its body, small badges hidden; non-numeric fallback keeps the icon [manual] - [ ] RGB lights (v1.27.0): an on light with a color tints its icon/glow and the ripple (explicit ripple color still wins); off/white lights unchanged [manual] - [ ] Alarm pulse (v1.27.0): leak/smoke/gas/CO/siren in 'on' pulse a red ring over any display mode; clears on 'off'; unavailable never alarms [manual]; reduced-motion static - [ ] Render cost (v1.43.1, audit L1): geometry (space model, open pairs) is computed once per config change, not per HA state push — smoke asserts zero recomputations across 10 state pushes and recomputation after an edit; the plan still renders dashes/islands correctly [auto: smoke_render_perf] - [ ] Opening tap vs drag (v1.43.1, audit L4): a tap on a door in the Plan editor opens its properties (3 px threshold like the other pipelines) and writes nothing; a real drag that ends where it started also writes nothing [auto: smoke_render_perf] - [ ] Concave containment (v1.43.1, audit G2): an island room inside a U- or L-shaped parent is accepted and punches the evenodd hole; a traced duplicate outline is still NOT containment [auto: smoke_inert_openings] - [ ] Backend hardening (v1.43.1, audit B2-B5): the admin check fails closed when the entry is unavailable; layout/set honours expected_rev; a config/set without expected_rev over a non-empty store logs a warning; NaN/Infinity coordinates and oversized collections are rejected [auto: unit: logic.test] - [ ] Save race (v1.43.0, audit L2): make a markup edit, then press Save in any dialog within 500 ms (or let another client save) — the markup edit must survive and reach the server; a failed reload now shows a toast [auto: unit: tests_backend] - [ ] Niche split (v1.43.0, audit G1): a cut that starts AND ends on the same wall carves a niche; the two parts' areas must sum to the original (the invariant is enforced in code and asserted for every split test) [auto: smoke_save_race] - [ ] Authenticated content (v1.43.0, audit B1): plan images and marker files are only reachable through /api/houseplan/content/… with a session; the old /houseplan_files/plans|files paths return 404 after a restart; old stored URLs keep working (rewritten on read) [auto+manual] - [ ] Every editor option is storable (v1.45.3, issue #3): set a sensor to "value instead of an icon" and save — no validation error, the value shows on the plan after a reload. Same for each tap action and each fill mode [auto: backend test_every_display_mode_the_editor_offers_is_accepted and neighbours, test_a_marker_showing_its_value_can_be_saved] - [ ] Detaching a plan keeps the file (v1.46.6): switch a space to "draw" and SAVE — the image is still in `config/houseplan/plans/` right afterwards, and after a restart, and can be re-attached. Deleting the space keeps it too. Replacing a plan still removes the one it replaced, immediately. Check straight after the save: the earlier bug deleted the file at that moment, while every scheduled-pass test passed [auto: unit: test_plan_collection_matrix, test_attachment_collection_matrix, backend test_detaching_a_plan_keeps_the_file] - [ ] Rebinding a device does not eat its manuals (v1.46.5): attach two files to a device, rebind it to another HA device — both are readable afterwards. If a copy failed, the file it failed on is still there rather than deleted with the folder [auto: backend test_files_cleanup_keeps_referenced_files] - [ ] Nothing accumulates on an idle instance (v1.46.2/v1.46.3, HP-1461-01, HP-1462-01): attach a file, cancel the dialog, and do not save anything else — the file is gone after a restart AND after the daily pass, while every file the configuration still references is untouched. Seed the strays AFTER the last save, or `config/set` collects them and the check proves nothing [auto: backend test_startup_sweep_collects_what_no_commit_will, test_daily_sweep_callback_collects_too, test_sweep_and_a_config_write_do_not_race] - [ ] A drag wins over a concurrent remote move (v1.46.2, HP-1461-02): drag an icon and, while the save is still in flight, have another window move a different icon — your icon stays where you put it and the other one updates [auto: smoke_layout_sync] - [ ] Concurrent uploads of one name (v1.46.1, HP-1460-01): attach the same file from two browser tabs at once — two attachments, two sets of bytes, neither lost. A file whose name is at the length limit still downloads [auto: unit: test_reserve_filename_is_safe_under_concurrency and neighbours] - [ ] No temporary files survive (v1.46.1, HP-1460-02): abort a large upload mid-transfer, send two files in one request, make promotion fail — in each case the files folder holds no `.upload-*`. An old one is swept at startup [auto: backend test_upload_leaves_no_temporary_behind + unit: sweep_upload_temps] - [ ] Two full cards agree on positions (v1.46.1, HP-1460-03): open the plan in two windows, drag an icon in one — it moves in the other without a reload; a drag in progress in the second window is not thrown away [auto: smoke_layout_sync] - [ ] Uploaded SVG is inert as a document (v1.46.0, HP-1454-01): open a plan's signed url directly in a tab — a `