test(golden): cover battery and Zigbee caption overlap

Address the in-scope Medium from CODE-REVIEW-792-r1 with a deterministic
golden scenario that uses actual topology rendering and overlapping battery
ink. Keep the existing battery boards and their reviewed images unchanged.
Align the English guide's setting name with the shipped locale (Low).

Issue: #792
User-Visible: no
This commit is contained in:
Matysh
2026-10-06 16:04:07 +03:00
parent b94112b8c4
commit 88bdd513ec
6 changed files with 289 additions and 7 deletions
+182
View File
@@ -206,3 +206,185 @@ export async function inspectBatteryBoardPixels(page, png, clip, board) {
if (failures.length) throw new Error(`semantic battery golden ink missing: ${JSON.stringify(failures)}`);
return samples;
}
export const BATTERY_ZIGBEE_MARKER = 'battery-56-normal';
/** Same natural unplaced-parent arrangement as smoke_device_battery_zigbee. */
export function makeBatteryZigbeeFixture() {
const fixture = makeBatteryBoardFixture('desktop');
fixture.config.markers = fixture.config.markers.filter((marker) => marker.id === BATTERY_ZIGBEE_MARKER);
fixture.config.markers[0].size = 1.4;
fixture.config.settings = { ...fixture.config.settings, volumetric_view: false,
show_device_battery: true, moon_enabled: false, zigbee_topology: { enabled: true },
marker_area_snapshot: { [BATTERY_ZIGBEE_MARKER]: fixture.config.settings.marker_area_snapshot[BATTERY_ZIGBEE_MARKER] },
};
fixture.devices = { [BATTERY_ZIGBEE_MARKER]: fixture.devices[BATTERY_ZIGBEE_MARKER] };
fixture.entities = Object.fromEntries(Object.entries(fixture.entities)
.filter(([, entity]) => entity.device_id === BATTERY_ZIGBEE_MARKER));
fixture.states = Object.fromEntries(Object.entries(fixture.states).filter(([id]) => fixture.entities[id]));
fixture.layout = { [BATTERY_ZIGBEE_MARKER]: { s: SPACE, x: 0.34, y: 0.34 } };
fixture.zhaDevices = [
{ ieee: '00124b0000000001', nwk: 1, device_reg_id: BATTERY_ZIGBEE_MARKER, device_type: 'EndDevice',
neighbors: [{ ieee: '00124b0000000002', relationship: 'Parent', lqi: 50 }] },
{ ieee: '00124b0000000002', nwk: 2, device_reg_id: 'not_on_plan', device_type: 'Router',
name: 'Upstairs parent relay',
neighbors: [{ ieee: '00124b0000000001', relationship: 'Child', lqi: 50 }] },
];
return fixture;
}
export async function batteryZigbeeProbe(page) {
return page.evaluate((id) => {
const root = window.__goldenCard.renderRoot;
const marker = root.querySelector(`[data-hp="device"][data-id="${CSS.escape(id)}"]`);
const overlay = root.querySelector('hp-zigbee-topology-overlay')?.shadowRoot;
const rect = (node) => {
if (!node) throw new Error('battery Zigbee golden missing rendered geometry');
const r = node.getBoundingClientRect();
return { x: r.x, y: r.y, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
};
const battery = rect(marker?.querySelector('.device-battery'));
const caption = rect(overlay?.querySelector('[data-hp="zigbee-topology-parent-bubble"]'));
const core = rect(marker?.querySelector('.device-core'));
return { battery, caption, core, overlap: {
x: Math.max(battery.x, caption.x), y: Math.max(battery.y, caption.y),
right: Math.min(battery.right, caption.right), bottom: Math.min(battery.bottom, caption.bottom),
} };
}, BATTERY_ZIGBEE_MARKER);
}
/** Fixed, core-anchored focus: tiny layering changes cannot hide in a full page. */
export function batteryZigbeeClip(probe, viewport) {
const clip = { x: Math.floor(probe.core.x - 16),
y: Math.floor(probe.core.y + probe.core.height / 2 - 70), width: 420, height: 140 };
if (clip.x < 0 || clip.y < 0 || clip.x + clip.width > viewport.width
|| clip.y + clip.height > viewport.height || [probe.core, probe.battery, probe.caption].some((rect) =>
rect.x < clip.x || rect.y < clip.y || rect.right > clip.x + clip.width || rect.bottom > clip.y + clip.height))
throw new Error(`battery Zigbee golden focused clip truncates the witness: ${JSON.stringify({ clip, probe, viewport })}`);
return clip;
}
/** Executable pixel oracle shared with unit negative cases, not a z-index check. */
export function inspectBatteryZigbeePixels(images, probe, clip) {
if (probe.overlap.right - probe.overlap.x < 4 || probe.overlap.bottom - probe.overlap.y < 6)
throw new Error('battery Zigbee golden caption does not overlap battery');
const at = (image, x, y) => {
const ix = Math.floor(x - clip.x), iy = Math.floor(y - clip.y);
if (ix < 0 || iy < 0 || ix >= image.width || iy >= image.height)
throw new Error('battery Zigbee golden pixel outside capture');
const index = (iy * image.width + ix) * 4;
return image.data.slice(index, index + 3);
};
const changed = (a, b) => a.some((value, index) => Math.abs(value - b[index]) > 12);
const green = (pixel) => COLORS.normal.every((value, index) => Math.abs(value - pixel[index]) < 8);
let ink = 0, covered = 0, coreChanged = 0, exposedRoute = 0;
const centreY = probe.caption.y + probe.caption.height / 2;
for (let y = Math.ceil(Math.max(probe.overlap.y + 2, centreY - 5));
y < Math.min(probe.overlap.bottom - 2, centreY + 5); y++) {
for (let x = Math.ceil(probe.overlap.x + 2); x < probe.overlap.right - 2; x++) {
const baseline = at(images.captionHidden, x, y), painted = at(images.active, x, y);
if (!green(baseline)) continue;
ink++;
if (changed(baseline, painted) && painted[1] - Math.max(painted[0], painted[2]) < 50) covered++;
}
}
const cx = probe.core.x + probe.core.width / 2, cy = probe.core.y + probe.core.height / 2;
for (let x = Math.ceil(cx); x < probe.caption.x - 3; x++) {
for (let dy = -2; dy <= 2; dy++) {
if (!changed(at(images.active, x, cy + dy), at(images.routesHidden, x, cy + dy))) continue;
if (x < probe.core.right - 5) coreChanged++;
else if (x > probe.core.right + 3) exposedRoute++;
}
}
const evidence = { ink, covered, coreChanged, exposedRoute };
// Keep the smoke's differential thresholds; the focused zero-diff golden
// additionally catches a one-pixel edge leak that a ratio alone could miss.
if (ink < 3 || covered / ink < 0.9)
throw new Error(`battery Zigbee golden caption does not cover real battery ink: ${JSON.stringify(evidence)}`);
if (coreChanged > 2 || exposedRoute < 3)
throw new Error(`battery Zigbee golden route/core paint order is missing: ${JSON.stringify(evidence)}`);
return evidence;
}
export async function prepareBatteryZigbeeOverlap(page) {
// Open/refresh/close the real General Settings UI; __hpTest still points at
// the old demo card after golden remounts and must not seed this scene.
await page.evaluate(() => window.__goldenCard.renderRoot.querySelector('[data-hp="settings"]').click());
await page.waitForFunction(() => [...(window.__goldenCard.renderRoot
.querySelector('hp-zigbee-topology-settings')?.shadowRoot?.querySelectorAll('button') || [])]
.some((button) => button.querySelector('ha-icon[icon="mdi:access-point-network"]') && !button.disabled));
await page.evaluate(() => [...window.__goldenCard.renderRoot.querySelector('hp-zigbee-topology-settings')
.shadowRoot.querySelectorAll('button')]
.find((button) => button.querySelector('ha-icon[icon="mdi:access-point-network"]')).click());
await page.waitForFunction(() => /Received/i.test(window.__goldenCard.renderRoot
.querySelector('hp-zigbee-topology-settings')?.shadowRoot?.textContent || ''));
await page.evaluate(() => window.__goldenCard.renderRoot.querySelector('hp-dialog [data-hp="dialog-cancel"]').click());
await page.waitForFunction(() => !window.__goldenCard.renderRoot.querySelector('hp-dialog'));
const face = await page.evaluate(async (id) => {
const marker = window.__goldenCard.renderRoot.querySelector(`[data-hp="device"][data-id="${CSS.escape(id)}"]`);
const battery = marker?.querySelector('.device-battery');
const icon = battery?.querySelector('ha-icon.device-battery-icon');
await icon?.updateComplete;
const svg = icon?.shadowRoot?.querySelector('svg'), path = svg?.querySelector('path');
const rect = battery?.getBoundingClientRect(), core = marker?.querySelector('.device-core')?.getBoundingClientRect();
if (battery?.dataset.state !== 'normal' || icon?.icon !== 'mdi:battery'
|| !path || path.getAttribute('d') !== window.__ICONS?.['mdi:battery']
|| !(path.getBBox().width > 0) || !(path.getBBox().height > 0)
|| svg.getAttribute('viewBox') !== '0 0 24 24'
|| !(rect.width > 0) || Math.abs(svg.getBoundingClientRect().width - rect.width) > 0.5
|| Math.abs(svg.getBoundingClientRect().height - rect.height) > 0.5)
throw new Error('battery Zigbee golden lacks a full-size official MDI battery');
return { x: core.x + core.width / 2, y: core.y + core.height / 2,
battery: { id, state: 'normal', color: [29, 194, 29], x: rect.x, y: rect.y, width: rect.width, height: rect.height } };
}, BATTERY_ZIGBEE_MARKER);
// Before hover the very same MDI must paint. Empty glyphs cannot make the
// subsequent caption control vacuously green.
await inspectBatteryBoardPixels(page, await page.screenshot({ scale: 'css', animations: 'disabled' }),
null, { samples: [face.battery] });
await page.mouse.move(face.x, face.y);
await page.waitForFunction(() => window.__goldenCard.renderRoot.querySelector('hp-zigbee-topology-overlay')
?.shadowRoot?.querySelector('[data-hp="zigbee-topology-parent-bubble"]')?.getBoundingClientRect().width > 0);
await page.evaluate(() => {
const caption = window.__goldenCard.renderRoot.querySelector('hp-zigbee-topology-overlay').shadowRoot
.querySelector('[data-hp="zigbee-topology-parent-bubble"]');
if (!caption.textContent.includes('Upstairs parent relay'))
throw new Error('battery Zigbee golden expected real parent caption text');
});
await page.evaluate(async () => {
await window.__goldenCard.updateComplete;
await new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done)));
});
const probe = await batteryZigbeeProbe(page), clip = batteryZigbeeClip(probe, page.viewportSize());
const screenshot = () => page.screenshot({ clip, scale: 'css', animations: 'disabled', caret: 'hide' });
const controls = async (hidden) => page.evaluate((hidden) => {
const overlay = window.__goldenCard.renderRoot.querySelector('hp-zigbee-topology-overlay').shadowRoot;
for (const [name, selector] of [['caption', 'zigbee-topology-parent-bubble'], ['routes', 'zigbee-topology-lines']]) {
const node = overlay.querySelector(`[data-hp="${selector}"]`);
if (!node) throw new Error(`battery Zigbee golden missing ${name}`);
if (hidden === name) node.style.visibility = 'hidden';
else node.style.removeProperty('visibility');
}
}, hidden);
let active, captionHidden, routesHidden;
try {
active = await screenshot();
await controls('caption');
captionHidden = await screenshot();
await controls('routes');
routesHidden = await screenshot();
} finally {
await controls(null);
}
const images = await page.evaluate(async (encoded) => Object.fromEntries(await Promise.all(
Object.entries(encoded).map(async ([name, base64]) => {
const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
const bitmap = await createImageBitmap(new Blob([bytes], { type: 'image/png' }));
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height), context = canvas.getContext('2d');
context.drawImage(bitmap, 0, 0);
const image = context.getImageData(0, 0, bitmap.width, bitmap.height);
bitmap.close();
return [name, { width: image.width, height: image.height, data: Array.from(image.data) }];
}))), { active: active.toString('base64'), captionHidden: captionHidden.toString('base64'),
routesHidden: routesHidden.toString('base64') });
return { ...inspectBatteryZigbeePixels(images, probe, clip), clip, overlap: probe.overlap };
}
+6 -1
View File
@@ -1,7 +1,8 @@
import { makeLargeHouseFixture } from '../fixtures/large-house.mjs';
import { fixtureWallKey, makeVisualMatrixFixture } from '../fixtures/visual-matrix.mjs';
import { readFileSync } from 'node:fs';
import { makeBatteryBoardFixture, prepareBatteryBoard } from './device-battery.mjs';
import { makeBatteryBoardFixture, prepareBatteryBoard, makeBatteryZigbeeFixture,
prepareBatteryZigbeeOverlap, batteryZigbeeProbe, batteryZigbeeClip } from './device-battery.mjs';
const junctionArtifactsFixture = JSON.parse(readFileSync(
new URL('../../test/fixtures/302-junction-artifacts.json', import.meta.url), 'utf8',
@@ -43,6 +44,7 @@ const cardVersion = '0.0.0-golden';
const VERSION_RELOAD_ATTEMPT_KEY = 'houseplan-card:version-reload-target:v1';
const fixtureFor = (scenario) => scenario.batteryBoard ? makeBatteryBoardFixture(scenario.batteryBoard)
: scenario.batteryZigbeeOverlap ? makeBatteryZigbeeFixture()
: scenario.fixture === 'large'
? makeLargeHouseFixture({ includeStairs: false })
: makeVisualMatrixFixture({
@@ -1013,6 +1015,7 @@ export async function prepareGoldenScenario(page, scenario) {
three: { floor_id: 'three', name: 'Three', level: 2 },
},
callWS: async (message) => {
if (message.type === 'zha/devices' && fixture.zhaDevices) return structuredClone(fixture.zhaDevices);
if (message.type === 'houseplan/config/get') return {
config: structuredClone(fixture.config), rev: 1,
can_write: scenario.canWrite !== false,
@@ -2494,11 +2497,13 @@ export async function prepareGoldenScenario(page, scenario) {
await page.mouse.move(point.x, point.y);
}
if (scenario.batteryBoard) result.batteryBoard = await prepareBatteryBoard(page, scenario);
if (scenario.batteryZigbeeOverlap) result.batteryZigbeeOverlap = await prepareBatteryZigbeeOverlap(page);
return result;
}
export async function goldenClip(page, capture) {
if (capture === 'page') return null;
if (capture === 'battery-zigbee-overlap') return batteryZigbeeClip(await batteryZigbeeProbe(page), page.viewportSize());
return page.evaluate((captureKind) => {
const card = window.__goldenCard;
const target = card?.renderRoot?.querySelector('.stage');
+6 -1
View File
@@ -1,7 +1,7 @@
import { fixtureWallKey } from '../fixtures/visual-matrix.mjs';
/** Data-only HP-QA-01 capture matrix. Bump when framing or scenarios change. */
export const GOLDEN_MATRIX_VERSION = 72;
export const GOLDEN_MATRIX_VERSION = 73;
const stage = { capture: 'stage', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0005 } };
const page = { capture: 'page', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0008 } };
@@ -897,6 +897,11 @@ export const GOLDEN_SCENARIOS = Object.freeze([
})),
{ id: 'device-battery-mobile-dark', fixture: 'visual', space: 'golden-battery', mode: 'view',
batteryBoard: 'mobile', theme: 'dark', viewport: { width: 390, height: 844 }, ...stage },
// Real ZHA refresh + hover; differential raster controls prove natural
// caption/battery overlap. A focused zero-diff frame protects 1px edges.
{ id: 'device-battery-zigbee-overlap-dark', fixture: 'visual', space: 'golden-battery', mode: 'view',
batteryZigbeeOverlap: true, iconSize: 5, theme: 'dark', viewport: { width: 1200, height: 900 },
capture: 'battery-zigbee-overlap', threshold: { maxChannelDelta: 10, maxDiffRatio: 0 } },
{ id: 'device-value-face-cover-source-dark', fixture: 'visual',
space: 'golden-lighting', mode: 'view', glowEnabled: false, sunRays: false,
showNames: false,
+1 -1
View File
@@ -1429,7 +1429,7 @@ have no indicator. The normal vacuum dock marker follows the ordinary rules.
The indicator does not enlarge the device's click target; Zigbee hover
information appears above it.
**General settings → Display → Show device battery charge** is on by default
**General settings → Display → Show device battery status** is on by default
and applies installation-wide, including static space cards and the editor
preview. Full configuration export/import preserves the setting.
+91 -1
View File
@@ -1,6 +1,8 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { BATTERY_BOARD_ICONS, batteryBoardSamples, makeBatteryBoardFixture } from '../demo/golden/device-battery.mjs';
import { BATTERY_BOARD_ICONS, batteryBoardSamples, makeBatteryBoardFixture,
BATTERY_ZIGBEE_MARKER, makeBatteryZigbeeFixture, batteryZigbeeClip,
inspectBatteryZigbeePixels } from '../demo/golden/device-battery.mjs';
import { prepareGoldenFixture } from '../demo/golden/harness.mjs';
import { GOLDEN_SCENARIOS } from '../demo/golden/matrix.mjs';
@@ -61,3 +63,91 @@ test('#792 golden fixtures are fresh independent public config/HA data, without
assert.ok(!Object.keys(existing.entities).some((entityId) => entityId.includes('battery_32_')));
assert.throws(() => batteryBoardSamples('unlisted'), /unknown battery board/);
});
test('#792 fourth battery golden uses a real enabled ZHA parent and a focused zero-diff capture', () => {
const scenes = GOLDEN_SCENARIOS.filter((scenario) => scenario.batteryZigbeeOverlap);
assert.equal(scenes.length, 1);
const [scene] = scenes;
assert.equal(scene.id, 'device-battery-zigbee-overlap-dark');
assert.equal(scene.mode, 'view');
assert.equal(scene.theme, 'dark');
assert.equal(scene.capture, 'battery-zigbee-overlap');
assert.deepEqual(scene.threshold, { maxChannelDelta: 10, maxDiffRatio: 0 });
const fixture = prepareGoldenFixture(scene);
assert.equal(fixture.config.settings.zigbee_topology.enabled, true);
assert.equal(fixture.config.settings.show_device_battery, true);
assert.equal(fixture.config.markers.length, 1);
assert.equal(fixture.config.markers[0].binding, `device:${BATTERY_ZIGBEE_MARKER}`);
assert.deepEqual(fixture.layout, { [BATTERY_ZIGBEE_MARKER]: { s: scene.space, x: 0.34, y: 0.34 } });
assert.equal(Object.keys(fixture.entities).length, 2); // Measurement and its own registry battery only.
const batteryId = Object.keys(fixture.entities).find((id) => fixture.entities[id].original_device_class === 'battery');
assert.equal(fixture.entities[batteryId].device_id, BATTERY_ZIGBEE_MARKER);
assert.equal(fixture.states[batteryId].state, '80');
const [child, parent] = fixture.zhaDevices;
assert.equal(child.device_reg_id, BATTERY_ZIGBEE_MARKER);
assert.deepEqual(child.neighbors, [{ ieee: parent.ieee, relationship: 'Parent', lqi: 50 }]);
assert.deepEqual(parent.neighbors, [{ ieee: child.ieee, relationship: 'Child', lqi: 50 }]);
assert.equal(parent.name, 'Upstairs parent relay');
assert.equal(fixture.devices[parent.device_reg_id], undefined);
assert.equal(fixture.layout[parent.device_reg_id], undefined);
fixture.zhaDevices[0].neighbors[0].lqi = 255;
assert.equal(makeBatteryZigbeeFixture().zhaDevices[0].neighbors[0].lqi, 50);
const oldScene = GOLDEN_SCENARIOS.find((scenario) => scenario.batteryBoard);
assert.equal(prepareGoldenFixture(oldScene).zhaDevices, undefined);
});
function pixelWitness() {
const rect = (x, y, width, height) => ({ x, y, width, height, right: x + width, bottom: y + height });
const probe = { core: rect(4, 16, 20, 16), battery: rect(34, 14, 16, 20), caption: rect(36, 16, 24, 16),
overlap: { x: 36, y: 16, right: 50, bottom: 32 } };
const image = () => ({ width: 64, height: 48, data: new Array(64 * 48 * 4).fill(32) });
const images = { active: image(), captionHidden: image(), routesHidden: image() };
const paint = (image, x, y, width, height, color) => {
for (let py = y; py < y + height; py++) for (let px = x; px < x + width; px++) {
const offset = (py * image.width + px) * 4;
image.data.splice(offset, 4, ...color, 255);
}
};
paint(images.captionHidden, 34, 14, 16, 20, [29, 194, 29]);
paint(images.active, 28, 23, 5, 3, [220, 200, 50]);
return { images, probe, clip: { x: 0, y: 0 }, paint };
}
test('#792 Zigbee pixel oracle proves caption-over-battery and core-over-route from controls', () => {
const { images, probe, clip } = pixelWitness();
assert.deepEqual(inspectBatteryZigbeePixels(images, probe, clip),
{ ink: 100, covered: 100, coreChanged: 0, exposedRoute: 15 });
});
test('#792 Zigbee pixel oracle rejects missing overlap, invisible MDI and reversed caption layer', () => {
const missing = pixelWitness();
missing.probe.overlap.right = missing.probe.overlap.x;
assert.throws(() => inspectBatteryZigbeePixels(missing.images, missing.probe, missing.clip), /does not overlap/);
const empty = pixelWitness();
empty.images.captionHidden = structuredClone(empty.images.active);
assert.throws(() => inspectBatteryZigbeePixels(empty.images, empty.probe, empty.clip), /real battery ink/);
const under = pixelWitness();
under.paint(under.images.active, 34, 14, 16, 20, [29, 194, 29]);
assert.throws(() => inspectBatteryZigbeePixels(under.images, under.probe, under.clip), /real battery ink/);
});
test('#792 Zigbee pixel oracle rejects routes above core and an absent route control', () => {
const above = pixelWitness();
above.paint(above.images.active, 14, 23, 3, 3, [220, 200, 50]);
assert.throws(() => inspectBatteryZigbeePixels(above.images, above.probe, above.clip), /route\/core paint order/);
const absent = pixelWitness();
absent.images.routesHidden = structuredClone(absent.images.active);
assert.throws(() => inspectBatteryZigbeePixels(absent.images, absent.probe, absent.clip), /route\/core paint order/);
});
test('#792 Zigbee focus is fixed size, core-anchored and refuses a clipped witness', () => {
const { probe } = pixelWitness();
const shifted = Object.fromEntries(Object.entries(probe).map(([key, rect]) => [key, {
...rect, x: rect.x + 100, right: rect.right + 100, y: rect.y + 100, bottom: rect.bottom + 100,
}]));
assert.deepEqual(batteryZigbeeClip(shifted, { width: 1200, height: 900 }),
{ x: 88, y: 54, width: 420, height: 140 });
assert.throws(() => batteryZigbeeClip(probe, { width: 1200, height: 900 }), /truncates/);
shifted.caption.right = 540;
assert.throws(() => batteryZigbeeClip(shifted, { width: 1200, height: 900 }), /truncates/);
});
+3 -3
View File
@@ -15,7 +15,7 @@ test('golden matrix has stable unique ids and bounded comparison thresholds', ()
for (const scenario of GOLDEN_SCENARIOS) {
assert.match(scenario.id, /^[a-z0-9-]+$/);
assert.equal(['visual', 'large'].includes(scenario.fixture), true, scenario.id);
assert.equal(['page', 'stage', 'sun-window'].includes(scenario.capture), true, scenario.id);
assert.equal(['page', 'stage', 'sun-window', 'battery-zigbee-overlap'].includes(scenario.capture), true, scenario.id);
assert.equal(scenario.viewport.width > 0 && scenario.viewport.height > 0, true, scenario.id);
assert.equal(scenario.threshold.maxChannelDelta >= 0 && scenario.threshold.maxChannelDelta <= 32, true, scenario.id);
assert.equal(scenario.threshold.maxDiffRatio >= 0 && scenario.threshold.maxDiffRatio <= 0.01, true, scenario.id);
@@ -446,7 +446,7 @@ test('sun-ray golden requires browser-painted light from a state-only sun entity
assert.ok(scenario);
const fixture = prepareGoldenFixture(scenario);
const space = fixture.config.spaces.find((item) => item.id === scenario.space);
assert.equal(GOLDEN_MATRIX_VERSION, 72);
assert.equal(GOLDEN_MATRIX_VERSION, 73);
assert.equal(space.settings.sun_rays, true);
assert.equal(scenario.northDeg, 90,
'the sign-sensitive golden must keep a non-zero north direction');
@@ -940,7 +940,7 @@ test('issue 570 Stage 4 reuses the historical iso goldens for visual handoff cov
test('#673 Stage 6 designer acceptance scenes are canonical golden entries', () => {
const ids = STAGE6_ACCEPTANCE_SCENARIOS.map((scenario) => scenario.id);
assert.equal(GOLDEN_MATRIX_VERSION, 72);
assert.equal(GOLDEN_MATRIX_VERSION, 73);
assert.deepEqual(ids, [
'isometric-stage6-light-lightfloor',
'isometric-stage6-light-darkfloor',