/** * The top bar after its rework (#183): five icon buttons and no captions at all. What it used to * spell out moved into the one menu – the project list into "Open project…", the project name and * the start-scene switch into "Project settings…" next to the collision layers. * * Runs last, and on purpose: the project picker is opened here and left again with Cancel. Opening * or deleting a project for real would pull the project out from under every step before this one. */ export async function topbar(t) { const { check, json, has, shot, clickText, clickToolbar, typeText, press, sleep, waitUntil } = t; await press('Escape'); // 46a. Nothing in the bar is written any more. The five words it used to carry are asked for as // drawn text: the buttons are still there, only their captions are gone. const captions = { project: await has('Project'), undo: await has('Undo'), redo: await has('Redo'), play: await has('Play'), pause: await has('Pause'), }; check( 'the top bar carries no captions any more – menu, undo, redo, play and pause are icons', Object.values(captions).every((shown) => shown === false), JSON.stringify(captions), ); const buttons = await json('__gm.runButtons()'); check( 'in edit mode the run buttons offer play and nothing to pause', buttons.play === 'play' && buttons.pause === 'pause' && buttons.playEnabled === true && buttons.pauseEnabled === false, JSON.stringify(buttons), ); // The pair is centred on the bar itself. The row cannot do that on its own: the spacers around // the two buttons share the free space with the flexible notice and so get a third of it each, // which used to leave the pair well left of the middle. const play = await json('__gm.toolbarAt("play")'); const pause = await json('__gm.toolbarAt("pause")'); const barCentre = await json('window.innerWidth / 2'); check( 'play and pause sit on the middle of the bar, not on the middle of what is left over', Math.abs((play.x + pause.x) / 2 - barCentre) <= 2, JSON.stringify({ middle: (play.x + pause.x) / 2, barCentre }), ); await shot('46-topbar'); // 46b. The one menu, with the separator between what opens a project and what changes the one // that is open. Clearing the saved data is checked in 32. await clickToolbar('menu'); const rows = await json('__gm.menuRows()'); const expected = [ 'New project', 'Open project…', null, 'Snapshots…', 'Project settings…', 'Clear saved data', null, 'Plugins', ]; check( 'the menu button opens new project, the project picker, the settings, clearing saves and the plugins', JSON.stringify(rows) === JSON.stringify(expected), JSON.stringify(rows), ); // 46c. The picker lists what the account owns, with the open project picked, and Cancel leaves // that project exactly where it was. await clickText('Open project…'); // The picker fetches the list before it can open, so it is waited for rather than read at once. await waitUntil('__gm.projectPicker() !== null', { label: 'the project picker opened' }); const picker = await json('__gm.projectPicker()'); const projectId = await json('__gm.state.project.id'); const projectName = await json('__gm.state.project.name'); check( '"Open project…" lists the projects with the open one picked', picker !== null && picker.picked === projectId && picker.names.includes(projectName), JSON.stringify({ picker, projectId, projectName }), ); await clickText('Cancel'); check( 'Cancel closes the picker and leaves the project loaded', (await json('__gm.projectPicker()')) === null && (await json('__gm.state.project.id')) === projectId, JSON.stringify({ picker: await json('__gm.projectPicker()'), id: await json('__gm.state.project.id') }), ); // 46d. The settings dialog holds the name, the start-scene switch and the collision layers of // #152 – the three things the bar itself used to carry. await clickToolbar('menu'); await clickText('Project settings…'); const settings = await json('__gm.projectSettings()'); const layers = await json('__gm.collisionLayers()'); check( '"Project settings…" opens on the project as it stands', settings?.name === projectName && (settings.playFrom === 'start' || settings.playFrom === 'active') && JSON.stringify(layers.dialog) === JSON.stringify(layers.project), JSON.stringify({ settings, layers }), ); // 46e. Renaming through it, and the icon buttons undoing and redoing it. The name is read off the // project rather than written down here: CI seeds the same project under a different name. const nameField = await t.locate(projectName); await t.doubleClick(nameField.x, nameField.y); await press('a', { ctrl: true }); await typeText('Renamed by 46'); await press('Enter'); await clickText('Apply'); await sleep(200); check( 'Apply renames the project', (await json('__gm.state.project.name')) === 'Renamed by 46', await json('__gm.state.project.name'), ); await clickToolbar('undo'); await sleep(200); const undone = await json('__gm.state.project.name'); await clickToolbar('redo'); await sleep(200); const redone = await json('__gm.state.project.name'); check( 'the undo and redo buttons of the bar walk the rename back and forth', undone === projectName && redone === 'Renamed by 46', JSON.stringify({ undone, redone, was: projectName }), ); // Back to the name the rest of the world knows it by. await clickToolbar('undo'); await sleep(200); check( 'the project is handed back under its own name', (await json('__gm.state.project.name')) === projectName, await json('__gm.state.project.name'), ); // 46f. The symbol scale in the same dialog: 1 by default, typed down to 0.5, and every icon of the // scene drawn at half its side (18 → 9 world units), undone in one step. const iconSize = () => json(`(() => { const p = __gm.state.project; const scene = p.assets.find((a) => a.id === p.activeSceneId); const id = (scene?.objects ?? []).map((o) => o.id).find((id) => __gm.sceneIcon(id)?.visible); return id ? __gm.sceneIcon(id).size : null; })()`); await clickToolbar('menu'); await clickText('Project settings…'); const opened = await json('__gm.projectSettings()'); const field = await json('__gm.projectSettingsSymbolScaleAt()'); await t.click(field.x, field.y); await press('a', { ctrl: true }); await typeText('0.5'); await press('Enter'); await clickText('Apply'); await sleep(200); const scaled = { project: await json('__gm.state.project.symbolScale'), icon: await iconSize() }; await clickToolbar('undo'); await sleep(200); const restored = { project: await json('__gm.state.project.symbolScale'), icon: await iconSize() }; check( 'the symbol scale starts at 1, Apply halves every scene icon, and undo gives the size back', opened?.symbolScale === 1 && scaled.project === 0.5 && Math.abs(scaled.icon - 9) < 1e-9 && restored.project === 1 && Math.abs(restored.icon - 18) < 1e-9, JSON.stringify({ opened, scaled, restored }), ); await t.saved(); }