CI run 35 was red: the suite died after step 15 with `widget with text "Container" not found`. Nothing in the editor broke — the tenth tab (#73) pushed Container behind the `»` button at the test window's 1280 px, exactly the case #84 was built for, and three steps still clicked the tab name directly. openTab(t, tab) in tools/e2e/lib/scaffold.mjs asks tabRow() whether the tab is hidden and goes through the menu when it is, so the fix lives in one place rather than in every step that opens a tab. The LABELS record moves there as TAB_LABELS, since both the helper and 14-overflow need it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
132 lines
3.9 KiB
JavaScript
132 lines
3.9 KiB
JavaScript
/**
|
||
* The throwaway rig steps 17–20 share in the fixture scene: a behavior asset cloned off `mover` with the
|
||
* step's graph, a crate to hold it, and a logic child that runs it.
|
||
*/
|
||
|
||
/**
|
||
* Where each step parks its box. The fixture scene is shared, so two steps at the same y would
|
||
* overlap and each would fail for reasons belonging to the other.
|
||
*/
|
||
export const LANES = {
|
||
editorRun: 400,
|
||
editorRunContainer: 800,
|
||
editorRunEdits: 1200,
|
||
pause: 300,
|
||
nested: 700,
|
||
keyRepeat: -300,
|
||
stateMachine: -700,
|
||
console: -1100,
|
||
transform: -1500,
|
||
font: -1900,
|
||
particles: -2300,
|
||
};
|
||
|
||
/**
|
||
* Source for a `make(id, assetId, parentId, y, name)` factory, to paste inside a `mutate()`
|
||
* string. It clones `scene.objects[0]`, so a new object carries whatever shape the fixture has.
|
||
*/
|
||
export const MAKE_OBJECT = `
|
||
const make = (id, assetId, parentId, y, name = '') => {
|
||
const o = structuredClone(scene.objects[0]);
|
||
Object.assign(o, {
|
||
id,
|
||
assetId,
|
||
name,
|
||
position: { x: 0, y, z: 0 },
|
||
parentId,
|
||
overrides: {},
|
||
instanceEdits: {},
|
||
});
|
||
return o;
|
||
};`;
|
||
|
||
/**
|
||
* Returns the created ids plus `remove()`, so a step's cleanup matches what it made. `front` puts the
|
||
* asset at the head of the list: a row appended at the end scrolls out of `dragAsset`'s reach.
|
||
*/
|
||
export async function scaffoldBehavior(t, options) {
|
||
const {
|
||
assetId,
|
||
name,
|
||
graph,
|
||
boxId,
|
||
boxName = 'Box',
|
||
logicId,
|
||
y,
|
||
front = false,
|
||
props = { enabled: true, runInEditor: false },
|
||
} = options;
|
||
|
||
await t.mutate(`
|
||
const p = __gm.state.project;
|
||
const scene = __gm.state.activeScene;
|
||
const asset = structuredClone(p.assets.find((a) => a.id === 'mover'));
|
||
Object.assign(asset, {
|
||
id: ${JSON.stringify(assetId)},
|
||
name: ${JSON.stringify(name)},
|
||
props: ${JSON.stringify(props)},
|
||
variables: [],
|
||
graph: ${JSON.stringify(graph)},
|
||
});
|
||
p.assets.${front ? 'unshift' : 'push'}(asset);
|
||
${MAKE_OBJECT}
|
||
scene.objects.push(
|
||
make(${JSON.stringify(boxId)}, 'crate', '', ${y}, ${JSON.stringify(boxName)}),
|
||
make(${JSON.stringify(logicId)}, ${JSON.stringify(assetId)}, ${JSON.stringify(boxId)}, 0, ${JSON.stringify(name)}),
|
||
);
|
||
`);
|
||
await t.sleep(400);
|
||
|
||
return {
|
||
assetId,
|
||
boxId,
|
||
logicId,
|
||
/** Removes everything this rig added and reports what survived (should be all zeroes). */
|
||
async remove() {
|
||
await t.evaluate('__gm.state.select(null)');
|
||
await t.mutate(`
|
||
const p = __gm.state.project;
|
||
const scene = __gm.state.activeScene;
|
||
const objects = ${JSON.stringify([boxId, logicId])};
|
||
const assets = ${JSON.stringify([assetId])};
|
||
scene.objects = scene.objects.filter((o) => !objects.includes(o.id));
|
||
p.assets = p.assets.filter((a) => !assets.includes(a.id));
|
||
`);
|
||
await t.sleep(300);
|
||
return t.json(`{
|
||
objects: __gm.sceneObjects().filter((o) => ${JSON.stringify([boxId, logicId])}.includes(o.id)).length,
|
||
assets: __gm.state.project.assets.filter((a) => a.id === ${JSON.stringify(assetId)}).length,
|
||
}`);
|
||
},
|
||
};
|
||
}
|
||
|
||
/** What each tab button reads – the text a check has to click to open it. */
|
||
export const TAB_LABELS = {
|
||
scene: 'Scene',
|
||
game: 'Game',
|
||
graph: 'Behavior',
|
||
statemachine: 'State machine',
|
||
shader: 'Shader',
|
||
texture: 'Texture',
|
||
animation: 'Animation',
|
||
container: 'Container',
|
||
list: 'List',
|
||
particles: 'Particles',
|
||
};
|
||
|
||
/**
|
||
* Opens a tab by clicking it, through the `»` menu when the row had to hide it. Since the tenth tab
|
||
* (#73) that is the normal case at the test window's width: a plain `clickText('Container')` found
|
||
* nothing and took the whole suite down with it.
|
||
*/
|
||
export async function openTab(t, tab) {
|
||
const row = await t.json('__gm.tabRow()');
|
||
if (row.hidden.includes(tab)) {
|
||
await t.clickText('»');
|
||
await t.sleep(300);
|
||
}
|
||
await t.clickText(TAB_LABELS[tab]);
|
||
await t.sleep(400);
|
||
}
|