mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
T1: demo/serve.mjs exports check/checkAll/finish — all 48 smokes now assert named facts and exit non-zero on a mismatch or an uncaught in-card exception (verified by breaking the kiosk guard on purpose). Informational values were frozen from a v1.43.1 run and cross-read against the source; timings assert budgets, not exact numbers. T2: new CI job 'smoke' gated on 'frontend', builds a FRESH bundle before running (the committed demo/srv/assets copy is a snapshot) and uploads per-file logs on failure. T3: [auto] now means 'a named failing check exists' and each line names it (43 lines); 72 aspirational markers honestly downgraded to [manual]. Fixed the 'ZERO edit buttons' contradiction (wrong since v1.30.1) and the opening-click line (true again since v1.43.1). Three smokes carried pre-v1.39.0/v1.25 expectations and were testing old behaviour: tap defaults for lights, card-wide tap action, label drag requiring plan mode. DEVELOPMENT.md documents the harness contract.
60 lines
3.6 KiB
JavaScript
60 lines
3.6 KiB
JavaScript
import { launch, checkAll, finish } from './serve.mjs';
|
|
const { page, browser } = await launch();
|
|
const res = await page.evaluate(async () => {
|
|
const out = {};
|
|
const c = window.__card;
|
|
const sr = () => c.shadowRoot || c.renderRoot;
|
|
c._setMode('plan'); c._tool = 'draw'; await c.updateComplete;
|
|
const g = c._gridPitch;
|
|
// r1 — комната; рисуем остров-«колонну» внутри неё
|
|
const r1 = c._spaceModel().rooms.find((r) => r.id === 'r1');
|
|
const [cx, cy] = c._roomCenter(r1).map((v) => Math.round(v / g) * g);
|
|
const pts = [[cx, cy], [cx + g * 2, cy], [cx + g * 2, cy + g * 2], [cx, cy + g * 2]];
|
|
// 1) клики внутри комнаты больше не отклоняются
|
|
for (const p of pts) c._markupClick({ clientX: 0, clientY: 0, composedPath: () => [], ...{} , __pt: p });
|
|
// _markupClick берёт координаты из _svgPoint(ev) — подменим напрямую через _path:
|
|
c._path = [];
|
|
for (const p of pts) {
|
|
const before = c._path.length;
|
|
// эмулируем клик готовой точкой: повторим логику через прямой вызов невозможен — построим путь вручную и замкнём
|
|
c._path = [...c._path, p];
|
|
if (c._path.length === before) break;
|
|
}
|
|
out.pointsAccepted = c._path.length === 4;
|
|
// замыкание: проверка пересечений должна пройти (вложенность легальна)
|
|
const clash = c._overlapRoom([...c._path]);
|
|
out.nestedAllowed = !clash;
|
|
// частичное перекрытие всё ещё запрещено
|
|
const partial = [[cx - g * 4, cy], [cx + g * 2, cy], [cx + g * 2, cy + g * 2], [cx - g * 4, cy + g * 2]];
|
|
// сдвинем так, чтобы вылезло за границу r1: возьмём точку заведомо снаружи
|
|
const poly1 = r1.poly || [[r1.x, r1.y], [r1.x + r1.w, r1.y], [r1.x + r1.w, r1.y + r1.h], [r1.x, r1.y + r1.h]];
|
|
const minX = Math.min(...poly1.map((p) => p[0]));
|
|
const cross = [[minX - g * 2, cy], [cx, cy], [cx, cy + g * 2], [minX - g * 2, cy + g * 2]];
|
|
out.partialStillRejected = !!c._overlapRoom(cross);
|
|
// 2) сохранить остров как комнату без зоны (замкнуть контур)
|
|
c._path = [...pts, pts[0]];
|
|
c._nameSel = 'Колонна'; c._areaSel = '';
|
|
c._commitRoom();
|
|
await c.updateComplete;
|
|
const island = c._spaceModel().rooms.find((r) => r.name === 'Колонна');
|
|
out.islandSaved = !!island;
|
|
// 3) внешняя комната рендерится как path с evenodd и дыркой
|
|
c._setMode('view'); await c.updateComplete;
|
|
// включим границы, чтобы r1 рендерился
|
|
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== c._space ? s : ({
|
|
...s, settings: { ...(s.settings || {}), show_borders: true } })) };
|
|
c.requestUpdate(); await c.updateComplete;
|
|
const evenodd = [...sr().querySelectorAll('path.room')].find((p) => p.getAttribute('fill-rule') === 'evenodd');
|
|
out.evenoddPath = !!evenodd;
|
|
out.holeInPath = evenodd ? (evenodd.getAttribute('d').match(/M /g) || []).length === 2 : null;
|
|
// 4) остров кликабелен (элемент существует и он поверх дырки)
|
|
const islandEl = [...sr().querySelectorAll('.room')].find((el) => {
|
|
const d = el.getAttribute('points') || el.getAttribute('d') || '';
|
|
return d.includes(String(cx)) && el !== evenodd;
|
|
});
|
|
out.islandRendered = !!islandEl;
|
|
return out;
|
|
});
|
|
checkAll(res);
|
|
await finish(browser, res);
|