mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
test: compare DOM nodes by identity, never through assert.equal
A failed assert.equal or deepEqual prints both operands, and a happy-dom node reaches the whole page and window: printing one exhausted memory and took the machine down. assertSameNode() compares identity and fails with a short description of each node; every node comparison in the happy-dom tests uses it.
This commit is contained in:
1 parent
e949584c0a
commit
3a2acf9b0f
8 files changed
+46
-20
No files matched your search
@@ -0,0 +1,19 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const describe = (node) =>
|
||||
node?.nodeType === 1
|
||||
? `<${node.tagName.toLowerCase()}${node.id ? `#${node.id}` : ''}>`
|
||||
: String(node);
|
||||
|
||||
/**
|
||||
* Assert that two DOM nodes (or a node and null) are the same. Never hand
|
||||
* happy-dom nodes to assert.equal or deepEqual: a failure prints both, and a
|
||||
* node reaches the whole page and window, which runs the process out of
|
||||
* memory.
|
||||
*/
|
||||
export function assertSameNode(actual, expected, message = '') {
|
||||
assert.ok(
|
||||
actual === expected,
|
||||
`${message ? `${message}: ` : ''}${describe(actual)} is not ${describe(expected)}`,
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { after, before, test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { installVueDom } from '../testSupport/vueDom.mjs';
|
||||
|
||||
let PanelNavigation;
|
||||
@@ -132,8 +133,8 @@ test('destroy removes the rails and stops following the stacks', async () => {
|
||||
const { left, right, calls, ui, navigation } = setup();
|
||||
navigation.destroy();
|
||||
navigation.destroy();
|
||||
assert.equal(left.querySelector('.gev-panel-rail'), null);
|
||||
assert.equal(right.querySelector('.gev-panel-rail'), null);
|
||||
assertSameNode(left.querySelector('.gev-panel-rail'), null);
|
||||
assertSameNode(right.querySelector('.gev-panel-rail'), null);
|
||||
navigation.select('data-panel');
|
||||
assert.deepEqual(calls, []);
|
||||
ui.destroy();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { nextTick } from 'vue';
|
||||
import { createRailCards } from './railCards.js';
|
||||
import { railDocumentFixture as railFixture } from './railTestFixture.mjs';
|
||||
@@ -94,7 +95,7 @@ test('footer buttons retain focus and current callbacks; links use safe new tabs
|
||||
},
|
||||
]),
|
||||
]);
|
||||
assert.equal(f.document.activeElement, button);
|
||||
assertSameNode(f.document.activeElement, button);
|
||||
assert.equal(button.parentElement.tagName, 'FOOTER');
|
||||
assert.equal(
|
||||
f.find((n) => n.className === 'rail-card-action-hint').textContent,
|
||||
@@ -200,7 +201,7 @@ test('setting ranges stay below presets and commit expensive filters on release'
|
||||
assert.equal(slider.value, '53', 'release keeps the handle until the owner');
|
||||
view.update([model(53)]);
|
||||
assert.equal(slider.value, '53');
|
||||
assert.equal(f.document.activeElement, slider);
|
||||
assertSameNode(f.document.activeElement, slider);
|
||||
slider.value = '41';
|
||||
slider.dispatchEvent(new Event('input'));
|
||||
await nextTick();
|
||||
@@ -334,7 +335,7 @@ test('labelled rows, lists and results retain focus and dispatch current params'
|
||||
const chip = f.find((n) => n.dataset.chipId === 'motion');
|
||||
chip.focus();
|
||||
view.update([model(true)]);
|
||||
assert.equal(f.document.activeElement, chip);
|
||||
assertSameNode(f.document.activeElement, chip);
|
||||
chip.click();
|
||||
assert.deepEqual(calls.at(-1), ['a', { paused: false }]);
|
||||
const storm = f.find(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { nextTick } from 'vue';
|
||||
import { createRailTimeline } from './railTimeline.js';
|
||||
import { railDocumentFixture as railFixture } from './railTestFixture.mjs';
|
||||
@@ -46,8 +47,8 @@ test('preview is immediate, commits are coalesced and the drag DOM and tick mapp
|
||||
index: 1,
|
||||
readout: 'Refresh',
|
||||
});
|
||||
assert.equal(slider.parentElement, row);
|
||||
assert.equal(row.children[1], slider);
|
||||
assertSameNode(slider.parentElement, row);
|
||||
assertSameNode(row.children[1], slider);
|
||||
assert.equal(slider.value, '0');
|
||||
assert.equal(slider.max, '2');
|
||||
assert.equal(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createRecentImageryPanel } from './recentImagery.js';
|
||||
import { createRecentImageryLayer } from '../layers/recentImagery/index.js';
|
||||
@@ -246,7 +247,7 @@ function part(f, id, name) {
|
||||
// ---- tests ---------------------------------------------------------------------
|
||||
test('the readout mounts in the rail body, hides while the layer is off and opens the panel once on first appearance', async () => {
|
||||
const f = fixture();
|
||||
assert.equal(f.root.parentNode, f.body);
|
||||
assertSameNode(f.root.parentNode, f.body);
|
||||
assert.equal(f.panel.hidden, true);
|
||||
assert.equal(f.count.textContent, '');
|
||||
await f.ready();
|
||||
@@ -864,7 +865,7 @@ test('the divider exists only while a swipe is live, labelled A / B or IMAGE / B
|
||||
};
|
||||
assert.equal(swap.tagName, 'BUTTON');
|
||||
assert.equal(swap.textContent, 'SWAP');
|
||||
assert.equal(swap.parentNode, f.byId('ri-controls'));
|
||||
assertSameNode(swap.parentNode, f.byId('ri-controls'));
|
||||
await f.ready();
|
||||
await f.settleLease();
|
||||
assert.equal(f.splits.length, 0, 'one image, no divider');
|
||||
@@ -993,7 +994,7 @@ test('opacity drives both images; EXPORT downloads the pin or the preview and EX
|
||||
f.readout.destroy();
|
||||
assert.equal(await f.readout.exportImage('a'), false);
|
||||
assert.equal(f.fetches.length, fetched);
|
||||
assert.equal(container.querySelector('.recent-imagery-readout'), null);
|
||||
assertSameNode(container.querySelector('.recent-imagery-readout'), null);
|
||||
});
|
||||
|
||||
test('DETAILS is a collapsed rail card holding every note, and the empty-days toggle', async () => {
|
||||
@@ -1077,7 +1078,7 @@ test('destroy returns every listener, the divider and the panel state', async ()
|
||||
assert.equal(split.destroyed, true);
|
||||
assert.equal(f.root.listenerCount(), 0);
|
||||
assert.equal(f.strip.listenerCount(), 0);
|
||||
assert.equal(f.root.parentNode, null);
|
||||
assertSameNode(f.root.parentNode, null);
|
||||
assert.equal(f.document.getElementById('recent-imagery-panel').hidden, true);
|
||||
f.layer.setMode('ab');
|
||||
assert.equal(f.splits.length, 1, 'no renders after destroy');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import test, { after, before } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { createStateChannel } from '../app/stateChannel.js';
|
||||
import { sceneElements } from './scenePresentation.js';
|
||||
import { mountSceneSharing } from './sceneSharing.js';
|
||||
@@ -192,7 +193,7 @@ test('Scene controls dispatch panel and shot-row actions, and rows outlive repub
|
||||
]);
|
||||
f.owner.renderShotList();
|
||||
await nextTick();
|
||||
assert.equal(f.elements.shots.children[0], row);
|
||||
assertSameNode(f.elements.shots.children[0], row);
|
||||
// An owner destroyed after a newer one attached leaves the newer list.
|
||||
const newer = new SceneControls({
|
||||
read: () => f.state,
|
||||
@@ -391,7 +392,7 @@ test('inline shot names save on Enter, cancel on Escape, and reject blank names'
|
||||
label.dispatchEvent(new Event('dblclick'));
|
||||
await nextTick();
|
||||
const input = label.children[0];
|
||||
assert.equal(document.activeElement, input);
|
||||
assertSameNode(document.activeElement, input);
|
||||
input.value = value;
|
||||
input.dispatchEvent(key(commitKey));
|
||||
input.dispatchEvent(new Event('blur'));
|
||||
@@ -482,7 +483,7 @@ test('Scene controls consume current state, preserve rows on progress, and unsub
|
||||
f.state.progress = 0.5;
|
||||
channel.publish({ type: 'progress-changed' });
|
||||
await nextTick();
|
||||
assert.equal(f.elements.shots.children[0], row);
|
||||
assertSameNode(f.elements.shots.children[0], row);
|
||||
f.state.status = 'Project exported';
|
||||
channel.publish({ type: 'project-exported' });
|
||||
assert.equal(f.elements.status.textContent, 'Project exported');
|
||||
@@ -619,7 +620,7 @@ test('The sharing row sits under SAVE & SHARE, after the file row', () => {
|
||||
const dispose = mountSceneSharing({ edit() {}, share() {} }, panel);
|
||||
const bar = inner.children[1];
|
||||
assert.equal(bar.className, 'scene-controls');
|
||||
assert.equal(inner.children[2], status);
|
||||
assertSameNode(inner.children[2], status);
|
||||
assert.ok(bar.children.every((button) => button.title));
|
||||
dispose();
|
||||
assert.deepEqual(inner.children, [fileRow, status]);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { createStatusBadge, updateStatusBadge } from './statusBadge.js';
|
||||
import { createRailCards } from './railCards.js';
|
||||
import { railDocumentFixture as railFixture } from './railTestFixture.mjs';
|
||||
@@ -51,7 +52,7 @@ test('painting preserves feature state, live regions and focus with zero identic
|
||||
assert.equal(badge.getAttribute('role'), 'status');
|
||||
assert.equal(badge.getAttribute('aria-live'), 'polite');
|
||||
assert.equal(badge.dataset.frameState, 'ready');
|
||||
assert.equal(document.activeElement, focusOwner);
|
||||
assertSameNode(document.activeElement, focusOwner);
|
||||
});
|
||||
|
||||
test('coverage badges keep keyed identity, hide empty coverage and never claim feed health', () => {
|
||||
@@ -74,7 +75,7 @@ test('coverage badges keep keyed identity, hide empty coverage and never claim f
|
||||
badge,
|
||||
);
|
||||
assert.deepEqual({ ...badge.dataset }, {});
|
||||
assert.equal(f.document.activeElement, header);
|
||||
assertSameNode(f.document.activeElement, header);
|
||||
view.destroy();
|
||||
view.update([card]);
|
||||
assert.equal(f.container.children.length, 0);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { assertSameNode } from '../testSupport/domAssert.mjs';
|
||||
import { setLocale } from '../i18n/index.js';
|
||||
import { createWeatherPanel } from './weatherPanel.js';
|
||||
import { railDocumentFixture as railFixture } from './railTestFixture.mjs';
|
||||
@@ -115,7 +116,7 @@ test('ordered articles omit Controls and retain history labels, forecast dates a
|
||||
view.update([radar, wind]);
|
||||
const windCard = card(f, 'wind');
|
||||
assert.equal(windCard.tagName, 'ARTICLE');
|
||||
assert.equal(windCard.parentElement.children[0], windCard);
|
||||
assertSameNode(windCard.parentElement.children[0], windCard);
|
||||
assert.equal(
|
||||
f.find((n) => n.dataset.weatherOpen),
|
||||
null,
|
||||
@@ -403,13 +404,13 @@ test('cyclones lead, and observed history owns one bordered group with only acti
|
||||
['weather-cyclones', 'wind'],
|
||||
);
|
||||
const group = f.find((n) => n.className === 'weather-observed-group');
|
||||
assert.equal(root.children[1], group);
|
||||
assertSameNode(root.children[1], group);
|
||||
assert.equal(group.children[0].textContent, 'Observed history');
|
||||
assert.equal(
|
||||
group.children[1].textContent,
|
||||
'Rain radar · Satellite clouds · Lightning density',
|
||||
);
|
||||
assert.equal(group.children[2], timelineHost(f));
|
||||
assertSameNode(group.children[2], timelineHost(f));
|
||||
assert.deepEqual(
|
||||
[...group.children[3].children].map((n) => n.dataset.cardId),
|
||||
['weather-radar', 'weather-satellite', 'weather-lightning'],
|
||||
|
||||
Reference in new issue
Block a user