Files
feuerwerk/tools/e2e/01-hierarchy.mjs
T
admin 7e5003b590
game-maker / suite (push) Successful in 6m48s
A test that waits for a deadline somebody guessed is waiting for the wrong thing, so it waits for the editor (#93)
The suite spent 88% of its step time asleep: 564 s of steps, 496 s of
them in sleep(n) - fixed spans estimated by hand and then copied from
step to step. That is why it was slow, and why it flickered.

The signal is afterFrames(count, timeout) in ui/render.ts, exposed as
__gm.afterFrames(). It wakes the loop itself, because an editor that
draws nothing would never redeem a promise of a frame, and it gives up
after a timeout instead of hanging the run. settle() in the harness
waits two drawn frames (~32 ms) instead of 150-300 ms and falls back to
sleep(150) when the hook is missing (an old build): silently not
waiting would make every later check a coin toss.

Every action goes through it now - click, doubleClick, wheel, drag,
dragAsset, press, typeText, select, mutate, undo, redo - and with that
about 550 sleep() calls behind those actions became redundant, helpers
that are nothing but a mutate in a coat included. What a frame cannot
answer got its own condition instead: waitUntil(expr), play()/stop()
waiting on __gm.state.mode rather than on 300 ms, saved() for the
autosave (the two hand-written polling loops sampled at 300 ms for
something that arrives after a second), and showTab() as a real tool -
47 places called evaluate("__gm.showTab(...)") raw, and 24 of them
waited for nothing at all and read the old panel.

564 s -> 243 s of step time, the waiting share 88% -> 76%. Three kinds
of wait were kept, and each one bit when it was taken away: loading
bytes (textures, fonts), game time that a repaint cannot stand in for
(time scale, shader and particle clocks, the live values a running
graph carries), and the inertia the scroll step measures on purpose.

Two things the new speed uncovered, both real: 25-animation read the
frame menu unfiltered and had frozen an incomplete list (the CI failure
on c990dbc), and 33-render-on-change measured idleness while the
autosave of the step before was still painting - it waits for
saveStatus to leave dirty/saving now, so it measures that nothing
happens any more instead of that it waited long enough.
2026-09-16 08:08:10 +02:00

59 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { same } from '../lib/harness.mjs';
/**
* Hierarchy folding. A freshly opened scene is fully collapsed, so only its top-level objects
* are listed (o4/oc1 hang under o1, oc2 under o2). The chevron folds a row open and shut
* without touching the selection, and selecting a nested object unfolds the way down to it.
*/
export async function hierarchy(t) {
const { check, evaluate, json, click, doubleClick, select, state, shot } = t;
const rowIds = () => json('__gm.hierarchyOrder()');
const rows = async () => JSON.stringify(await rowIds());
const selectionBefore = (await state()).selection;
check(
'hierarchy starts collapsed – only top-level objects are listed',
same(await rowIds(), ['o1', 'o2', 'o3', 'o5']),
await rows(),
);
check('a leaf row offers no chevron', (await evaluate(`__gm.hierarchyChevron('o3')`)) === null);
const chevron = await json(`__gm.hierarchyChevron('o2')`);
await click(chevron.x, chevron.y);
check(
'clicking the chevron unfolds the row, leaving the selection alone',
same(await rowIds(), ['o1', 'o2', 'oc2', 'o3', 'o5']) && same((await state()).selection, selectionBefore),
await rows(),
);
await shot('01a-hierarchy-unfolded');
await click(chevron.x, chevron.y);
check(
'clicking it again folds the subtree away',
same(await rowIds(), ['o1', 'o2', 'o3', 'o5']),
await rows(),
);
// The row itself folds on a double-click, the same as its chevron. Pixi leaves `detail` at 0 on
// a press, so this needs a real double-click from the harness, not two `click()` calls.
const row = await json(`__gm.hierarchyRow('o2')`);
await doubleClick(row.x, row.y);
check(
'double-clicking the row unfolds it as well',
same(await rowIds(), ['o1', 'o2', 'oc2', 'o3', 'o5']),
await rows(),
);
await doubleClick(row.x, row.y);
check(
'double-clicking it again folds the subtree away',
same(await rowIds(), ['o1', 'o2', 'o3', 'o5']),
await rows(),
);
await select({ kind: 'object', holderId: 'scene1', objectId: 'oc1' });
check(
'selecting a nested object unfolds its ancestors',
same(await rowIds(), ['o1', 'o4', 'oc1', 'o2', 'o3', 'o5']),
await rows(),
);
await evaluate('__gm.state.select(null); __gm.hierarchyCollapseAll()');
check('Collapse all folds everything back', same(await rowIds(), ['o1', 'o2', 'o3', 'o5']), await rows());
}