mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
109 lines
5.3 KiB
JavaScript
109 lines
5.3 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;
|
||
// 1) Единый селектор содержит ровно пять актуальных действий;
|
||
// legacy cover и пустая авто-опция в UI не возвращаются.
|
||
c._setMode('devices'); await c.updateComplete;
|
||
// v1.39.0: у ЛАМП дефолт 'toggle', поэтому для проверки дефолта 'info'
|
||
// берём заведомо не-световое устройство
|
||
const dev = c._devices.find((d) => !d.virtual && d.primary && !d.primary.startsWith('light.'));
|
||
c._openMarkerDialog(dev); await c.updateComplete;
|
||
const sel = sr().querySelector('#marker-tap-action');
|
||
const values = sel ? [...sel.options].map((o) => o.value) : [];
|
||
out.fiveCurrentOptions = JSON.stringify(values) === JSON.stringify([
|
||
'info', 'more-info', 'toggle', 'run', 'none',
|
||
]);
|
||
out.noLegacyOptions = !values.includes('') && !values.includes('cover');
|
||
out.defaultInfo = sel && sel.value === 'info';
|
||
c._markerDialog = null; await c.updateComplete;
|
||
// 2) правый клик в Просмотре открывает more-info
|
||
c._setMode('view'); await c.updateComplete;
|
||
let moreInfo = null;
|
||
c._openMoreInfo = (eid) => { moreInfo = eid; };
|
||
const ev = new MouseEvent('contextmenu', { bubbles: true, cancelable: true });
|
||
c._ctxDevice(ev, dev);
|
||
out.ctxMoreInfo = moreInfo === dev.primary;
|
||
out.ctxPrevented = ev.defaultPrevented;
|
||
// 3) в редакторах правый клик не перехватывается
|
||
c._setMode('devices'); await c.updateComplete;
|
||
moreInfo = null;
|
||
const ev2 = new MouseEvent('contextmenu', { bubbles: true, cancelable: true });
|
||
c._ctxDevice(ev2, dev);
|
||
out.editorNative = moreInfo === null && !ev2.defaultPrevented;
|
||
// 4) виртуальное без primary → инфо-карточка
|
||
c._setMode('view'); await c.updateComplete;
|
||
const virt = c._devices.find((d) => d.virtual && !d.primary) || null;
|
||
if (virt) {
|
||
c._ctxDevice(new MouseEvent('contextmenu', { cancelable: true }), virt);
|
||
out.virtInfo = c._infoCard === virt;
|
||
c._infoCard = null;
|
||
} else out.virtInfo = 'no-virt';
|
||
// 5) card-wide tap_action игнорируется: без явного действия клик = инфо
|
||
c._config = { ...c._config, tap_action: 'toggle' };
|
||
const calls = [];
|
||
c.hass = { ...c.hass, callService: (d2, s2, data) => { calls.push([d2, s2, data]); return Promise.resolve(); } };
|
||
await c.updateComplete;
|
||
// card-wide tap_action игнорируется: НЕ-световое устройство остаётся на инфо
|
||
const plain = c._devices.find((d) => !d.virtual && d.primary
|
||
&& !d.primary.startsWith('light.') && !d.tapAction && !d.marker?.controls?.length);
|
||
if (plain) {
|
||
c._infoCard = null;
|
||
c._clickDevice(new MouseEvent('click'), plain);
|
||
out.cardTapIgnored = calls.length === 0 && !!c._infoCard;
|
||
c._infoCard = null;
|
||
} else out.cardTapIgnored = 'no-plain-device';
|
||
// 6) Явный none поглощает click/keyboard, но не запускает ни один эффект.
|
||
if (dev) {
|
||
const originalDevices = c._devices;
|
||
const noOp = { ...dev, tapAction: 'none', marker: { ...dev.marker, tap_action: 'none' } };
|
||
c._devices = originalDevices.map((item) => item.id === dev.id ? noOp : item);
|
||
let bindingChecks = 0;
|
||
let feedback = 0;
|
||
let toasts = 0;
|
||
let wsCalls = 0;
|
||
let serviceCalls = 0;
|
||
const originalBindingActive = c._deviceBindingActive;
|
||
const originalFeedback = c._startDevicePressFeedback;
|
||
const originalToast = c._showToast;
|
||
const originalHass = c.hass;
|
||
const originalCallWS = c.hass.callWS;
|
||
const originalCallService = c.hass.callService;
|
||
c._deviceBindingActive = () => { bindingChecks += 1; return true; };
|
||
c._startDevicePressFeedback = () => { feedback += 1; };
|
||
c._showToast = () => { toasts += 1; };
|
||
c.hass = {
|
||
...c.hass,
|
||
callWS: (...args) => { wsCalls += 1; return originalCallWS(...args); },
|
||
callService: (...args) => { serviceCalls += 1; return originalCallService(...args); },
|
||
};
|
||
c._infoCard = null;
|
||
c._tapConfirm = null;
|
||
let stopped = false;
|
||
c._clickDevice({ stopPropagation() { stopped = true; } }, noOp);
|
||
const enter = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true });
|
||
c._keyDevice(enter, noOp);
|
||
const space = new KeyboardEvent('keydown', { key: ' ', cancelable: true });
|
||
c._keyDevice(space, noOp);
|
||
out.noneConsumesActivation = stopped && enter.defaultPrevented && space.defaultPrevented;
|
||
out.noneHasNoEffects = bindingChecks === 0 && feedback === 0 && toasts === 0
|
||
&& wsCalls === 0 && serviceCalls === 0 && c._infoCard === null && c._tapConfirm === null;
|
||
c._devices = originalDevices;
|
||
c._deviceBindingActive = originalBindingActive;
|
||
c._startDevicePressFeedback = originalFeedback;
|
||
c._showToast = originalToast;
|
||
c.hass = originalHass;
|
||
} else {
|
||
out.noneConsumesActivation = false;
|
||
out.noneHasNoEffects = false;
|
||
}
|
||
return out;
|
||
});
|
||
// значения зафиксированы прогоном на v1.43.1 и сверены с кодом (audit T1)
|
||
checkAll(res, {
|
||
"virtInfo": "no-virt",
|
||
});
|
||
await finish(browser, res);
|