game-maker / suite (push) Successful in 11m11s
The height cap used to be a constant 420, and the check wrote that number down. Since #43 the cap is the canvas minus a tenth at either end, which is 640 on an 800px screen - so CI failed on a picker that is behaving exactly as intended. The check now reads the budget off the screen and reports rect, budget and screen height when it trips, so the next change to the margin cannot strand it again.
75 lines
3.7 KiB
JavaScript
75 lines
3.7 KiB
JavaScript
/**
|
||
* Menus that can grow with the project. Runs last: it fills the scene with throwaway objects
|
||
* to make the parent picker overflow, which the earlier steps' object counts would not survive.
|
||
*/
|
||
export async function menus(t) {
|
||
const { check, sleep, evaluate, json, shot, click, locate, mouse, mutate } = t;
|
||
/** Like locate(), but a row scrolled out of view answers with null instead of throwing. */
|
||
const rowAt = async (text) =>
|
||
(await evaluate(`__gm.locate(${JSON.stringify(text)}, true)`)) ?? { x: -1, y: -1 };
|
||
|
||
await mutate(`
|
||
const scene = __gm.state.activeScene;
|
||
// Any plain stored object will do – not a placed container, whose content the picker
|
||
// would list forty times over as well.
|
||
const source = scene.objects.find(o => o.id === 'otext');
|
||
for (let i = 1; i <= 40; i++) {
|
||
const copy = structuredClone(source);
|
||
copy.id = 'filler' + i;
|
||
copy.name = 'Filler ' + i;
|
||
copy.parentId = '';
|
||
copy.overrides = {};
|
||
scene.objects.push(copy);
|
||
}
|
||
__gm.state.select({ kind: 'object', holderId: 'scene1', objectIds: ['otext'], objectId: 'otext' })`);
|
||
await sleep(150);
|
||
|
||
// 11a. A picker with 40+ entries is capped in height and stays fully on screen instead of
|
||
// running off the bottom edge. The cap is a share of the canvas, not a constant: a tenth is
|
||
// left free at either end, so the number to beat is read off the screen rather than written
|
||
// down here – 40 rows would be well over a thousand pixels without it.
|
||
const parentRow = await json("__gm.inspectorField('Parent')");
|
||
await click(parentRow.x, parentRow.y);
|
||
const rect = await json('__gm.overlayRect()');
|
||
const screenHeight = await json('window.innerHeight');
|
||
const budget = screenHeight - Math.round(screenHeight * 0.1) * 2;
|
||
check(
|
||
'a long parent picker is capped to the canvas budget instead of growing with the object count',
|
||
rect && rect.h <= budget,
|
||
JSON.stringify({ rect, budget, screenHeight }),
|
||
);
|
||
check(
|
||
'the capped picker stays fully on screen',
|
||
rect && rect.y >= 0 && rect.y + rect.h <= screenHeight,
|
||
JSON.stringify({ rect, screenHeight }),
|
||
);
|
||
await shot('31-long-parent-menu');
|
||
|
||
// 11b. The entries scroll under the wheel; the search box above them does not move.
|
||
// Anchored on a filler row read off the open menu: "None" could match an inspector field behind it,
|
||
// and which fillers are drawn depends on what earlier steps left in the scene.
|
||
const visibleFillers = (await json('__gm.overlayTexts()')).filter((e) => e.startsWith('Filler '));
|
||
const anchor = visibleFillers[visibleFillers.length - 1];
|
||
check('the picker lists the filler objects', Boolean(anchor), JSON.stringify(visibleFillers));
|
||
const firstBefore = await rowAt(anchor);
|
||
const searchBefore = await locate('Search…', true);
|
||
// Move onto the list first: a wheel event goes to whatever is hovered, and the pointer last
|
||
// touched the field above the menu.
|
||
await mouse('mouseMoved', rect.x + rect.w / 2, rect.y + rect.h / 2);
|
||
await sleep(50);
|
||
await mouse('mouseWheel', rect.x + rect.w / 2, rect.y + rect.h / 2, 'none', { deltaX: 0, deltaY: 240 });
|
||
// Measured here: the list had not moved 40 ms after the wheel but had at 340 ms. Both
|
||
// readings are reported so an early one cannot look like a mystery failure again.
|
||
await sleep(40);
|
||
const rightAfter = await rowAt(anchor);
|
||
await sleep(300);
|
||
const firstAfter = await rowAt(anchor);
|
||
const searchAfter = await locate('Search…', true);
|
||
check(
|
||
'the wheel scrolls the entries',
|
||
firstAfter.y >= 0 && firstAfter.y < firstBefore.y - 100,
|
||
JSON.stringify({ firstBefore, rightAfter, firstAfter }),
|
||
);
|
||
check('the search box stays put while the entries scroll', searchAfter.y === searchBefore.y);
|
||
}
|