/** * 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, json, shot, click, locate, mouse, mutate } = t; await mutate(` const scene = __gm.state.activeScene; const source = scene.objects.find(o => o.id === 'o1'); for (let i = 1; i <= 40; i++) { const copy = structuredClone(source); copy.id = 'filler' + i; copy.name = 'Filler ' + i; copy.parentId = ''; scene.objects.push(copy); } __gm.state.select({ kind: 'object', sceneId: 'scene1', objectIds: ['otext'], objectId: 'otext' })`); await sleep(150); // 18. A picker with 40+ entries is capped in height and stays fully on screen instead of // running off the bottom edge. // The dropdown sits right of the 64 px label column; locate() gives the label's text centre. const parentRow = await locate('Parent'); await click(parentRow.x + 55, parentRow.y); const rect = await json('__gm.overlayRect()'); const screenHeight = await json('window.innerHeight'); check( 'a long parent picker is capped instead of growing with the object count', rect && rect.h < 420, JSON.stringify(rect), ); 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'); // 18a. The entries scroll under the wheel; the search box above them does not move. // Anchored on a filler row: "None" is also what an empty dropdown reads, so locating it could // find an inspector field behind the menu instead – one that never moves. const firstBefore = await locate('Filler 1', true); 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 locate('Filler 1', true); await sleep(300); const firstAfter = await locate('Filler 1', true); const searchAfter = await locate('Search…', true); check( 'the wheel scrolls the entries', firstAfter.y < firstBefore.y - 100, JSON.stringify({ firstBefore, rightAfter, firstAfter }), ); check('the search box stays put while the entries scroll', searchAfter.y === searchBefore.y); }