diff --git a/demo/smoke_led_strip_bind.mjs b/demo/smoke_led_strip_bind.mjs index 90894752..627d7457 100644 --- a/demo/smoke_led_strip_bind.mjs +++ b/demo/smoke_led_strip_bind.mjs @@ -21,23 +21,25 @@ await evaluate(async () => { if (m.type === 'houseplan/config/set') { if (window.__ledFail) throw new Error('offline'); window.__ledSaves++; - return { ok: true, rev: (c._cfgRev || 0) + 1 }; } return orig.call(c.hass, m); }; const service = c.hass.callService; c.hass.callService = async (...args) => { window.__ledServices.push(args.slice(0, 2).join('.')); return service(...args); }; // One unbound strip in the Living room of the demo floor. - c._serverCfg.spaces[0].led_strips = [{ id: 'led-a', points: [[0.12, 0.22], [0.40, 0.22], [0.40, 0.40]], marker: null }]; - c._modelCache = null; c._frame = null; c._cfgEpoch++; - c._setMode('devices'); await c.updateComplete; + await window.__hpTest.setServerConfig((cfg) => { + cfg.spaces[0].led_strips = [{ id: 'led-a', points: [[0.12, 0.22], [0.40, 0.22], [0.40, 0.40]], marker: null }]; + }); + await window.__hpTest.setMode('devices'); }); await page.waitForTimeout(800); check('an editable strip loads the tool for the Devices editor', await evaluate(() => !!window.__card._ledEditor)); const icon = (id) => evaluate((id) => !!window.__card.shadowRoot.querySelector(`.dev[data-id="${id}"], [data-hp="device"][data-id="${id}"]:not(.led-hit)`), id); check('the light starts as an ordinary icon', await icon('d_bedlight')); -await evaluate(() => { window.__card._ledEditor.select('led-a'); window.__card._ledEditor.picker = 'led-a'; window.__card.requestUpdate(); }); +await evaluate(() => window.__card._ledEditor.select('led-a')); +await page.waitForTimeout(200); +await page.click('[data-led-action="bind"]'); await page.waitForTimeout(200); check('lights first in the picker', await evaluate(() => { const picks = [...window.__card.shadowRoot.querySelectorAll('[data-led-pick]')].map((b) => b.dataset.ledPick); @@ -58,10 +60,10 @@ check('the device keeps its catalogue place', await evaluate(() => window.__card // An ordinary icon while the strip is selected: its own dialog, no LED tray. await evaluate(() => window.__card._ledEditor.select('led-a')); await page.waitForTimeout(100); -await evaluate(() => window.__card._editorRuntime._openMarkerDialog(window.__card._devices.find((d) => d.id === 'd_lamp'))); +await evaluate(() => window.__hpTest.openMarkerDialog('d_lamp')); await page.waitForTimeout(200); check('an icon dialog drops the strip selection', await evaluate(() => window.__card._ledEditor.sel), null); -await evaluate(() => window.__card._editorRuntime._closeMarkerDialog()); +await evaluate(() => window.__hpTest.close()); await page.waitForTimeout(150); check('closing that dialog shows no LED tray', await evaluate(() => !/Delete strip/.test(window.__card.shadowRoot.querySelector('.editor-secondary')?.textContent || ''))); @@ -83,7 +85,7 @@ await page.keyboard.press('Control+Shift+z'); await page.waitForTimeout(500); check('Redo hides it again', await evaluate(() => window.__card._serverCfg.spaces[0].led_strips[0].active), false); // Device dialog: «Show as LED strip» restores the hidden shape at once. -await evaluate(() => window.__card._editorRuntime._openMarkerDialog(window.__card._devices.find((d) => d.id === 'd_bedlight'))); +await evaluate(() => window.__hpTest.openMarkerDialog('d_bedlight')); await page.waitForTimeout(300); check('the dialog offers unbind/delete for a hidden shape', await evaluate(() => [...window.__card.shadowRoot .querySelectorAll('[data-led-representation] [data-led-action]')].map((b) => b.dataset.ledAction).join()), 'show-strip,unbind,delete'); @@ -105,7 +107,7 @@ check('failed write: no new command', await evaluate(() => window.__card._device await evaluate(() => { window.__ledFail = false; }); // Converting a plain icon by drawing: one write, the same marker. -await evaluate(() => window.__card._editorRuntime._openMarkerDialog(window.__card._devices.find((d) => d.id === 'd_lamp'))); +await evaluate(() => window.__hpTest.openMarkerDialog('d_lamp')); await page.waitForTimeout(300); await page.click('[data-led-representation] [data-led-action="show-strip"]'); await page.waitForTimeout(400); @@ -121,14 +123,15 @@ check('drawing converted the lamp in one write, no picker', await evaluate(() => return !!strip && strip.active === true && !c._ledEditor.picker && c._serverCfg.markers.filter((m) => m.id === 'd_lamp').length === 1; })); -// Deleting the bound marker leaves an unbound strip with its geometry. -await evaluate(async () => { - const c = window.__card; - c._confirmDanger = async () => true; - c._editorRuntime._openMarkerDialog(c._devices.find((d) => d.id === 'd_bedlight')); - await c.updateComplete; - await c._editorRuntime._deleteMarker(); -}); +// Deleting the bound marker leaves an unbound strip with its geometry — +// through the strip's own «Device settings» and the dialog's Delete + confirm. +await evaluate(() => window.__card._ledEditor.select('led-a')); +await page.waitForTimeout(150); +await page.click('[data-led-action="settings"]'); +await page.waitForTimeout(300); +await page.click('hp-dialog[data-kind="marker"] .markeractions .btn.danger'); +await page.waitForTimeout(200); +await page.click('hp-confirm [data-hp="dialog-confirm"]'); await page.waitForTimeout(500); check('deleting the marker unbinds the strip in the same write', await evaluate(() => { const s = window.__card._serverCfg.spaces[0].led_strips.find((x) => x.id === 'led-a'); diff --git a/demo/smoke_led_strip_draw.mjs b/demo/smoke_led_strip_draw.mjs index c58c5741..958ac139 100644 --- a/demo/smoke_led_strip_draw.mjs +++ b/demo/smoke_led_strip_draw.mjs @@ -18,22 +18,19 @@ await evaluate(async () => { window.__ledSaves = []; const orig = c.hass.callWS; c.hass.callWS = async (m) => { - if (m.type === 'houseplan/config/set') { - window.__ledSaves.push(JSON.parse(JSON.stringify(m.config))); - return { ok: true, rev: (c._cfgRev || 0) + 1 }; - } + if (m.type === 'houseplan/config/set') window.__ledSaves.push(JSON.parse(JSON.stringify(m.config))); return orig.call(c.hass, m); }; // A thick partition across a plain room: the placement body of AC5. - c._serverCfg = { model_version: 10, spaces: [{ id: 'led', title: 'LED', cell_cm: 5, view_box: [0, 0, 1, 0.7], + await window.__hpTest.setServerConfig((cfg) => ({ ...cfg, spaces: [{ id: 'led', title: 'LED', cell_cm: 5, view_box: [0, 0, 1, 0.7], rooms: [{ id: 'room', name: 'Room', area: null, poly: [[0.1, 0.1], [0.9, 0.1], [0.9, 0.6], [0.1, 0.6]] }], - wall_segments: [], partitions: [{ id: 'p1', a: [0.5, 0.15], b: [0.5, 0.55], cm: 40 }] }], markers: [], settings: {} }; - c._layout = {}; c._space = 'led'; c._modelCache = null; c._frame = null; c._cfgEpoch++; - c._setMode('plan'); await c.updateComplete; + wall_segments: [], partitions: [{ id: 'p1', a: [0.5, 0.15], b: [0.5, 0.55], cm: 40 }] }], markers: [] })); + await window.__hpTest.setLayout({}); + await window.__hpTest.setMode('plan'); }); await page.waitForTimeout(300); check('plan editor has no LED tool', await evaluate(() => !window.__card.shadowRoot.querySelector('[data-tool="led-strip"]'))); -await evaluate(async () => { window.__card._setMode('devices'); await window.__card.updateComplete; }); +await evaluate(() => window.__hpTest.setMode('devices')); await page.waitForTimeout(300); check('devices editor has the LED tool next to Add', await evaluate(() => { const tools = [...window.__card.shadowRoot.querySelectorAll('.devbar [data-hp="tool"]')].map((b) => b.dataset.tool); @@ -141,10 +138,9 @@ check('dropping the selection writes nothing', await evaluate(() => window.__led // «Optimize plans» reports a strip that passes through the partition and changes nothing in it. const optimizeNote = await evaluate(async () => { const c = window.__card; - const space = c._serverCfg.spaces[0]; - space.led_strips = [...space.led_strips, { id: 'through', points: [[0.3, 0.3], [0.7, 0.3]], marker: null }]; - c._modelCache = null; c._frame = null; c._cfgEpoch++; - await c.updateComplete; + await window.__hpTest.setServerConfig((cfg) => { + cfg.spaces[0].led_strips = [...cfg.spaces[0].led_strips, { id: 'through', points: [[0.3, 0.3], [0.7, 0.3]], marker: null }]; + }); c._editorRuntime.optimizePlans.open(); await c.updateComplete; for (let i = 0; i < 40 && !c.shadowRoot.querySelector('[data-led-walls]'); i++) { @@ -153,7 +149,7 @@ const optimizeNote = await evaluate(async () => { } const note = c.shadowRoot.querySelector('[data-led-walls]'); const kept = JSON.stringify(c._alignDialog?.config?.spaces?.[0]?.led_strips?.find((s) => s.id === 'through')?.points); - c._alignDialog = null; await c.updateComplete; + await window.__hpTest.close(note?.closest('hp-dialog') || undefined); return { n: note?.dataset.ledWalls, text: note?.textContent || '', kept }; }); check('optimize reports strips through walls', optimizeNote.n, '1'); @@ -161,7 +157,7 @@ check('the report names the space', /Strips passing through walls: 1 \(LED\)/.te check('optimize keeps the strip as drawn', optimizeNote.kept, JSON.stringify([[0.3, 0.3], [0.7, 0.3]])); // Plan/Background: passive translucent marks only, no handles, no targets. -await evaluate(async () => { window.__card._setMode('decor'); await window.__card.updateComplete; }); +await evaluate(() => window.__hpTest.setMode('decor')); await page.waitForTimeout(300); check('Background editor: no LED editor layer or tool', await evaluate(() => { const r = window.__card.shadowRoot;