Scroll the image tile into view before clicking it in e2e 12
game-maker / gate (push) Successful in 3m34s
game-maker / suite (push) Successful in 10m52s

The image picker scrolls once the texture store holds more images than
fit into the dialog. The hook imagePickerTile still reports the tile
position then, so the click in module 12 landed outside the visible
list and picked nothing. Locally spare.png sat at place 27 of 32, and
the full run broke off in module 12 with "Texture not found".

- FilePicker exposes its scroll area as listView.
- The new test hook __gm.imagePickerRect() returns that area.
- Both tile clicks in module 12 wheel the tile into view first with
  the existing inView helper, as modules 41 and 43 do.

The editor behaves the same. Only the hook is new. The texture store
is shared with real projects, so the seed does not clean it up.

Tests:
- ./check.sh: 188 files, 2746 tests, CHECK EXIT 0
- GM_STEPS=10,12: ALL PASSED, 58 checks
- Full local run: ALL PASSED, 1535 checks
- Counter-check: the old module 12 against the same bundle gives
  6 FAILED (selected: null, then "Texture not found")

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-28 19:27:55 +02:00
1 parent e03a3dce97
commit d2701b3ec5
3 files changed
+16 -2

No files matched your search

+5
View File
@@ -272,6 +272,11 @@ export abstract class FilePicker<Item extends PickerItem> extends Popup {
};
}
/** The scrolled part of the dialog – what tells an entry in view from one past its edge (test hook). */
get listView(): ScrollContainer {
return this.scroll;
}
override close(): void {
super.close();
if (current === this) current = null;
+4
View File
@@ -283,6 +283,10 @@ export function uiHooks({ app, state, editor }: HookContext) {
/** The open image picker: what it offers, what is picked, and where a tile sits. */
imagePicker: () => currentImagePicker()?.debugInfo() ?? null,
imagePickerTile: (file: string) => currentImagePicker()?.tileGlobal(file) ?? null,
imagePickerRect: () => {
const picker = currentImagePicker();
return picker ? rectOf(picker.listView) : null;
},
/** The same for the font picker, whose rows are the preview. */
fontPicker: () => currentFontPicker()?.debugInfo() ?? null,
fontPickerRow: (file: string) => currentFontPicker()?.rowGlobal(file) ?? null,
+7 -2
View File
@@ -1,4 +1,5 @@
import { colourDistance } from '../lib/harness.mjs';
import { inView } from './lib/scaffold.mjs';
/**
* Uploading a picture and wearing it. Runs last because it adds an asset and repaints the
@@ -363,7 +364,11 @@ export async function textures(t) {
);
check('building the tiles draws no complaint from Pixi', tileComplaints.length === 0, tileComplaints);
const tile = await json(`__gm.imagePickerTile(${JSON.stringify(spare)})`);
// Scrolled into view first: the server holds every picture ever uploaded, so on a long-lived
// one the spare's tile sits below the dialog's fold, where a click lands on nothing.
const tileOf = (file) =>
inView(t, '__gm.imagePickerRect()', `__gm.imagePickerTile(${JSON.stringify(file)})`);
const tile = await tileOf(spare);
await click(tile.x, tile.y);
check(
'clicking a tile picks that image',
@@ -424,7 +429,7 @@ export async function textures(t) {
(reopened?.used ?? []).includes(spare),
JSON.stringify(reopened?.used),
);
const sameTile = await json(`__gm.imagePickerTile(${JSON.stringify(spare)})`);
const sameTile = await tileOf(spare);
await doubleClick(sameTile.x, sameTile.y);
await sleep(600);
const afterSecondPick = await json(