feedback7: merge fb7-dragsnap — a dragged operator stands on the tile under the pointer while that is a legal target, as in the owner's recording of the official deployment (the report in PR #403 by @bubububaoshe; the pick, highlight, wheel and drop stay the pointer's tile); §27.62; golden: unchanged
This commit is contained in:
commit
775d4f8515
11 files changed
+228
-53
No files matched your search
@@ -8,7 +8,8 @@
|
||||
// local art, 标准 = 战场#01 only; user playtest #3 (DESIGN §17): temp overflow kept until the first prep its player can
|
||||
// act in (never wiped at the round start), the 回环射手 boomerang and 蕾缪安's shells one by one, the live LP, the detail
|
||||
// card order and the static game data; user playtest #4 (DESIGN §18): picking by the tile under the pointer and the
|
||||
// dragged model held under it, a single human untimed, the strategy draft's one countdown, 机变 two taps, knocked-out
|
||||
// dragged model held under it (standing on that tile while it is a legal target, §27.62), a single human untimed, the
|
||||
// strategy draft's one countdown, 机变 two taps, knocked-out
|
||||
// operators and the official element gauges, live stats, the shop-only items, skill summons, 炎佑; user playtest #5
|
||||
// (DESIGN §19): blocking by contact radius, 联防 forced exit, huge-boss hit areas and 自缚, the element pipeline rules,
|
||||
// boss-field deployment, the phone prep camera — and the normative §3 / §5.1 / §5.5 / §6.1 / §7 lines that changed; user
|
||||
@@ -294,11 +295,15 @@ test('lost models, live LP, detail card order, static game data (user playtest #
|
||||
});
|
||||
|
||||
test('user playtest #4 (DESIGN §18): picking by tile, timers, 机变 two taps, down / element state, content — code and every doc agree', () => {
|
||||
// #1 the tile under the pointer; the dragged model held under the pointer (no touch lift, probe or body shapes)
|
||||
// #1 the tile under the pointer; the dragged model held under the pointer (no touch lift, probe or body shapes) —
|
||||
// standing on the pointer's tile while that is a legal target (§27.62, the official deploy drag)
|
||||
assert.equal(ENEMY_REACH, 0.6);
|
||||
const app = readFileSync(join(ROOT, 'public/js/render/app.js'), 'utf8');
|
||||
const tune = readFileSync(join(ROOT, 'public/js/render/app/tune.js'), 'utf8');
|
||||
assert.match(tune, /export const DRAG_HOLD_TILES = 0\.45;/);
|
||||
assert.match(app, /dragStandTile\(p\)/);
|
||||
assert.match(DESIGN, /`dragStandTile/);
|
||||
assert.match(PLAYING, /干员模型直接站在这一格上/);
|
||||
assert.ok(!/TOUCH_LIFT_TILES|drawnAt|pickShape|pieceDragOver/.test(app), 'no touch lift, pixel probe or body shapes (user playtest #4 item 1)');
|
||||
assert.match(DESIGN, /`DRAG_HOLD_TILES` = 0\.45 tile/);
|
||||
assert.match(DESIGN, /`ENEMY_REACH` 0\.6 tile/);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// test/render/drag.test.js — drop target resolution and the pointer state machine (no DOM, fake timers).
|
||||
// test/render/drag.test.js — drop target resolution, the pointer state machine (no DOM, fake timers) and the tile a
|
||||
// dragged unit is drawn standing on (dragStandTile; the harness's 100 px grid stands in for the camera's tile pick).
|
||||
|
||||
import { test, describe } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { GEO } from '../../shared/constants.js';
|
||||
import { tileSlot, pieceSlot, pieceTile, resolveDrop, isLegal, sameSlot, createDragController } from '../../public/js/render/drag.js';
|
||||
import { tileSlot, pieceSlot, pieceTile, resolveDrop, isLegal, sameSlot, createDragController, dragStandTile } from '../../public/js/render/drag.js';
|
||||
|
||||
describe('slots & targets', () => {
|
||||
test('tileSlot classifies hand, temp, board and everything else', () => {
|
||||
@@ -293,3 +294,54 @@ describe('drag controller', () => {
|
||||
} finally { console.error = orig; }
|
||||
});
|
||||
});
|
||||
|
||||
// The official deploy drag (the owner's recording of 2026-10-09; the report in PR #403): the dragged unit is drawn
|
||||
// standing on the tile under the pointer while that is a legal target, and held under the pointer elsewhere. The tile
|
||||
// is the drag target itself, so the pick, tileHover, the highlight and the drop stay the pointer's tile.
|
||||
describe('the tile a dragged unit stands on (dragStandTile)', () => {
|
||||
test('a legal board tile or bench slot is stood on; an illegal tile, a temp slot, no target hold it under the pointer', () => {
|
||||
assert.deepEqual(dragStandTile({ target: { area: 'board', row: 10, col: 3 }, legal: true }), { row: 10, col: 3, bench: false });
|
||||
assert.deepEqual(dragStandTile({ target: { area: 'hand', idx: 6 }, legal: true }), { row: GEO.HAND_ROW, col: 6, bench: true });
|
||||
assert.equal(dragStandTile({ target: { area: 'board', row: 10, col: 3 }, legal: false }), null, 'an illegal tile');
|
||||
assert.equal(dragStandTile({ target: { area: 'hand', idx: 6 }, legal: false }), null, 'the own bench slot / an illegal slot');
|
||||
assert.equal(dragStandTile({ target: { area: 'temp', idx: 1 }, legal: true }), null, 'a temp slot is never a target');
|
||||
assert.equal(dragStandTile({ target: { area: 'outside', clientX: 1, clientY: 2 }, legal: true }), null);
|
||||
assert.equal(dragStandTile({ target: null, legal: false }), null, 'off the grid, over the shop bar');
|
||||
assert.equal(dragStandTile({ target: { area: 'board', row: 10, col: 3 }, legal: 1 }), null, 'legal must be true');
|
||||
assert.equal(dragStandTile({ target: { area: 'board', row: 9.5, col: 3 }, legal: true }), null);
|
||||
assert.equal(dragStandTile({ target: { area: 'hand' }, legal: true }), null);
|
||||
assert.equal(dragStandTile(null), null);
|
||||
assert.equal(dragStandTile(undefined), null);
|
||||
assert.deepEqual(dragStandTile({ target: { area: 'hand', idx: 2 }, legal: true }, { HAND_ROW: 3 }), { row: 3, col: 2, bench: true }, 'geo is a parameter');
|
||||
});
|
||||
|
||||
test('over the drag path: one tile while the pointer moves inside it, the next legal tile at once, none where no drop lands (mouse and touch)', () => {
|
||||
for (const pointerType of ['mouse', 'touch']) {
|
||||
const h = harness({ domCover: true, canPlace: (p, r, c) => !(r === 11 && c === 4) });
|
||||
h.c.setEditable(true);
|
||||
const stand = () => dragStandTile(h.events.filter((e) => e[0] === 'pieceDragMove').pop()[1]);
|
||||
const at = (x, y) => { h.ev('pointerMove', x, y, { pointerType }); return stand(); };
|
||||
h.ev('pointerDown', 50, 550, { pointerType });
|
||||
assert.equal(at(50, 530), null, `${pointerType}: on its own bench slot (no target) it is held under the pointer`);
|
||||
assert.ok(h.c.dragging);
|
||||
// the pointer anywhere on (9,4) (x 400–499, y 300–399 in the harness grid): the unit stands on (9,4) the whole time
|
||||
const inside = [[410, 390], [450, 350], [495, 305], [402, 301]].map(([x, y]) => at(x, y));
|
||||
assert.deepEqual(inside, Array(4).fill({ row: 9, col: 4, bench: false }), `${pointerType}: moving inside the tile leaves it there`);
|
||||
assert.deepEqual(at(450, 299), { row: 10, col: 4, bench: false }, `${pointerType}: the next tile takes it at once`);
|
||||
assert.equal(at(450, 150), null, `${pointerType}: an illegal tile (11,4)`);
|
||||
assert.equal(at(550, 450), null, `${pointerType}: a temp slot`);
|
||||
assert.deepEqual(at(250, 550), { row: GEO.HAND_ROW, col: 2, bench: true }, `${pointerType}: another bench slot`);
|
||||
assert.equal(at(250, 950), null, `${pointerType}: over the shop bar (DOM over the canvas)`);
|
||||
assert.equal(at(-20, 300), null, `${pointerType}: off the grid`);
|
||||
assert.deepEqual(at(350, 250), { row: 10, col: 3, bench: false });
|
||||
h.ev('pointerUp', 350, 250, { pointerType });
|
||||
assert.deepEqual(h.events.find((e) => e[0] === 'pieceDrop')[1].target, { area: 'board', row: 10, col: 3 }, `${pointerType}: dropped on the tile it stood on`);
|
||||
// a board piece lifts on its own tile (a legal drop: the direction wheel re-orients it in place)
|
||||
h.events.length = 0;
|
||||
h.ev('pointerDown', 450, 250, { pointerType });
|
||||
assert.deepEqual(at(470, 230), { row: 10, col: 4, bench: false }, `${pointerType}: a board piece stands on its own tile`);
|
||||
h.ev('pointerUp', 470, 230, { pointerType });
|
||||
assert.deepEqual(h.events.find((e) => e[0] === 'pieceDrop')[1].target, { area: 'board', row: 10, col: 4 });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
// test/render/fieldview.browser.test.js — field-view features in headless Chrome through the dev demo
|
||||
// (public/dev/render-demo.html): the enemy preview pen (research 09 §2.2 / 08 §4.2) on the 3D and the 2D board and
|
||||
// the 0.25 s pan to it and back, the Final Assault prep on the player's half of the boss field (research 09 §1.2:
|
||||
// drop targets / directions stay in board space, the right half mirrored) with the round's leader on its spawn tile and
|
||||
// drop targets / directions stay in board space, the right half mirrored; a dragged unit stands on its target's
|
||||
// boss-field tile) with the round's leader on its spawn tile and
|
||||
// its red hit tiles beside a range preview (community report #12), damage numbers that never touch, the
|
||||
// automatic rebuild of a 3D board whose WebGL context was lost, and frame rates on a 4× throttled CPU.
|
||||
// Screenshots → test/e2e/out/pen-*.png, fa-prep-*.png, fa-leader-*.png, dmgnum-*.png; perf → test/e2e/out/fieldview-perf.json.
|
||||
@@ -194,6 +195,9 @@ describe('field view features in headless Chrome', { skip }, () => {
|
||||
await page.mouse.move(cx, cy);
|
||||
await page.mouse.down();
|
||||
for (let i = 1; i <= 12; i++) await page.mouse.move(cx + (target.x - cx) * i / 12, cy + (target.y - cy) * i / 12);
|
||||
// over the legal target the dragged unit stands on it (the official deploy drag): on that board tile's boss-field
|
||||
// tile, as the drop will place it — row − 7, the column mirrored on the right half
|
||||
const stood = await page.evaluate((uid) => { const v = window.__demo.view.debug.views.get('p:' + uid); return { x: v.x, y: v.y, lift: v.lift }; }, info.hand.uid);
|
||||
await page.mouse.up();
|
||||
await wait(400);
|
||||
const drops = await page.evaluate(() => window.__drops);
|
||||
@@ -222,6 +226,7 @@ describe('field view features in headless Chrome', { skip }, () => {
|
||||
await page.close();
|
||||
assert.deepEqual(problems, []);
|
||||
assert.deepEqual(drops[0], { area: 'board', row: target.row, col: target.col }, `drop target in board space: ${JSON.stringify(drops)}`);
|
||||
assert.deepEqual(stood, { x: side === 'R' ? 20 - target.col : target.col, y: target.row - 7, lift: 0.3 }, 'while dragged over it: standing on the target\'s boss-field tile');
|
||||
assert.deepEqual(dirs, { a: side === 'R' ? 'LEFT' : 'RIGHT', u: 'UP' });
|
||||
assert.deepEqual(past, { field: true, wallBelow: null, below: null, wallAbove: true, pen: null, pad: true });
|
||||
});
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
// #4.1 picking by tile ("地上都画好了一个一个方格,点击对应方格就选中那个方格的人物就行"): a press anywhere on a tile — board
|
||||
// (adjacent rows and columns, high ground), bench, temporary bench — selects the unit on it and an empty tile
|
||||
// nothing (a press on a unit's head, drawn over the tile behind it, is a press on that tile), in prep and in battle
|
||||
// (allies by their tile, enemies by their ground position or drawn body); a dragged unit is held under the pointer — its drawn
|
||||
// feet DRAG_HOLD_TILES below it, the model around it — for a mouse and a finger (phone, 844×390); the drop, its
|
||||
// tileHover and the direction wheel are the tile under the pointer; equipment dropped on a tile equips the unit
|
||||
// on it (not the one in front whose head is drawn there); on a phone a release on a bench slot lands there and one
|
||||
// over the shop bar goes back.
|
||||
// (allies by their tile, enemies by their ground position or drawn body); a dragged unit stands on the tile under
|
||||
// the pointer while that is a legal target — wherever the pointer is on the tile, as in the official deploy drag
|
||||
// (the owner's recording of 2026-10-09; PR #403's report) — and is held under the pointer elsewhere (over the
|
||||
// shop bar: its drawn feet DRAG_HOLD_TILES below it, the model around it), for a mouse and a finger (phone,
|
||||
// 844×390); the drop, its tileHover and the direction wheel are the tile under the pointer; equipment dropped on
|
||||
// a tile equips the unit on it (not the one in front whose head is drawn there); on a phone a release on a bench
|
||||
// slot lands there and one over the shop bar goes back.
|
||||
//
|
||||
// Opt-in (starts Chrome): RENDER_E2E=1 node --test test/render/models.browser.test.js
|
||||
// Chrome path: $CHROME_PATH or the macOS default. Screenshots → test/e2e/out/models-*.png.
|
||||
@@ -315,13 +317,23 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
assert.deepEqual(problems, []);
|
||||
});
|
||||
|
||||
/** The dragged view against the pointer (client px): its drawn feet, px per tile, and the Spine model's drawn bounds. */
|
||||
/** The dragged view against the pointer (client px): its drawn feet, px per tile, and the Spine model's drawn bounds;
|
||||
* its world point (wx = col, wy = row, wz) and the height of the board tile under it (hz). */
|
||||
const ghostAt = (page, uid) => page.evaluate((u) => {
|
||||
const R = globalThis.__SP_VIEW__.raw, cv = R.debug.app.view.getBoundingClientRect(), v = R.debug.views.get('p:' + u);
|
||||
const b = v.actor && v.spineReady && !v.imp ? v.actor.spine.getBounds() : null;
|
||||
return { lift: v.lift, x: cv.left + v.screen.x, y: cv.top + v.screen.y, s: v.screen.s, model: b && { x0: cv.left + b.x, y0: cv.top + b.y, x1: cv.left + b.x + b.width, y1: cv.top + b.y + b.height } };
|
||||
return { lift: v.lift, x: cv.left + v.screen.x, y: cv.top + v.screen.y, s: v.screen.s, model: b && { x0: cv.left + b.x, y0: cv.top + b.y, x1: cv.left + b.x + b.width, y1: cv.top + b.y + b.height },
|
||||
wx: v.x, wy: v.y, wz: v.z, hz: R.debug.tiles.heightAt(Math.round(v.y), Math.round(v.x)) };
|
||||
}, uid);
|
||||
/** The model is under the pointer: its feet DRAG_HOLD_TILES (0.45) below it, the pointer inside its drawn body. */
|
||||
/** Over a legal target the model stands on the pointer's tile (the official deploy drag): its world point is that
|
||||
* tile — the tile's top on the board, the bench pad on the bench — and it is lifted. */
|
||||
const assertStands = (g, [row, col], label) => {
|
||||
assert.ok(g.lift > 0, `${label}: lifted`);
|
||||
assert.ok(Math.abs(g.wx - col) < 1e-6 && Math.abs(g.wy - row) < 1e-6, `${label}: stands on (${row},${col}) (at ${g.wx.toFixed(3)},${g.wy.toFixed(3)})`);
|
||||
if (row !== 7) assert.ok(Math.abs(g.wz - g.hz) < 1e-6, `${label}: on the tile's top (z ${g.wz} vs ${g.hz})`);
|
||||
else assert.ok(g.wz > 0, `${label}: on the bench pad (z ${g.wz})`);
|
||||
};
|
||||
/** Elsewhere the model is under the pointer: its feet DRAG_HOLD_TILES (0.45) below it, the pointer inside its drawn body. */
|
||||
const assertHeld = (g, ptr, label) => {
|
||||
assert.ok(g.lift > 0, `${label}: lifted`);
|
||||
assert.ok(Math.abs(g.x - ptr.x) < 1.5 && Math.abs(g.y - ptr.y - 0.45 * g.s) < 1.5, `${label}: feet 0.45 tile below the pointer (feet ${g.x.toFixed(1)},${g.y.toFixed(1)}, pointer ${ptr.x.toFixed(1)},${ptr.y.toFixed(1)}, s ${g.s.toFixed(1)})`);
|
||||
@@ -331,7 +343,7 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
}
|
||||
};
|
||||
|
||||
test('#4.1 drag (mouse): the unit is held under the pointer; target, tileHover and the direction wheel are the pointer\'s tile', async () => {
|
||||
test('#4.1 drag (mouse): the unit stands on the pointer\'s tile over a legal target, held under the pointer elsewhere; target, tileHover and the direction wheel are the pointer\'s tile', async () => {
|
||||
const { page, problems } = await open('phase=PREP');
|
||||
const hand = await page.evaluate(() => { const p = globalThis.__MOCK__.S().priv; const i = p.hand.findIndex((x) => x && x.kind === 'chess'); return { uid: p.hand[i].uid, idx: i }; });
|
||||
await page.evaluate(() => { window.__hover = []; globalThis.__SP_VIEW__.raw.on('tileHover', (t) => window.__hover.push(t && [t.row, t.col])); });
|
||||
@@ -340,17 +352,46 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
await page.mouse.move(from.x, from.y); await page.mouse.down();
|
||||
for (let i = 1; i <= 12; i++) { await page.mouse.move(from.x + ((to.x - from.x) * i) / 12, from.y + ((to.y - from.y) * i) / 12); await sleep(16); }
|
||||
await sleep(150);
|
||||
assertHeld(await ghostAt(page, hand.uid), to, 'mouse');
|
||||
const first = await ghostAt(page, hand.uid);
|
||||
assertStands(first, [9, 4], 'mouse');
|
||||
assert.deepEqual(await page.evaluate(() => window.__hover.at(-1)), [9, 4], 'tileHover = the tile under the pointer');
|
||||
await page.screenshot({ path: path.join(OUT, 'models-drag-ghost.png') });
|
||||
// the whole way: the model stays under the pointer (sampled at every step of a second drag leg)
|
||||
// the pointer moving about inside the tile leaves the model where it is (the official recording: the finger moves,
|
||||
// the model stays on its tile)
|
||||
for (const [u, v] of [[-0.6, -0.3], [0.6, -0.3], [-0.5, 0.7], [0, 0]]) { // (the nearest strip is hidden by row 8: SPOTS)
|
||||
const pt = await tileSpot(page, 9, 4, u, v);
|
||||
await page.mouse.move(pt.x, pt.y);
|
||||
await sleep(40);
|
||||
const g = await ghostAt(page, hand.uid);
|
||||
assertStands(g, [9, 4], `mouse at (${u}, ${v}) of the tile`);
|
||||
assert.ok(Math.abs(g.x - first.x) < 0.5 && Math.abs(g.y - first.y) < 0.5, `mouse at (${u}, ${v}): the model did not move on screen`);
|
||||
}
|
||||
// the whole way: on a legal tile the model stands on the pointer's tile, elsewhere it is under the pointer (sampled
|
||||
// at every step of a second drag leg; the legal tiles are the view's own 'legal' highlight)
|
||||
const legal = new Set(await page.evaluate(() => globalThis.__SP_VIEW__.raw.debug.tiles.highlights.get('legal').tiles.map(([r, c]) => `${r},${c}`)));
|
||||
const leg = await tileSpot(page, 10, 5);
|
||||
const seen = { stands: 0, held: 0 };
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
const pt = { x: to.x + ((leg.x - to.x) * i) / 6, y: to.y + ((leg.y - to.y) * i) / 6 };
|
||||
await page.mouse.move(pt.x, pt.y);
|
||||
await sleep(40);
|
||||
assertHeld(await ghostAt(page, hand.uid), pt, `mouse step ${i}`);
|
||||
const tile = await page.evaluate(() => window.__hover.at(-1));
|
||||
if (legal.has(`${tile}`)) { seen.stands++; assertStands(await ghostAt(page, hand.uid), tile, `mouse step ${i}`); }
|
||||
else { seen.held++; assertHeld(await ghostAt(page, hand.uid), pt, `mouse step ${i}`); }
|
||||
}
|
||||
assert.ok(seen.stands > 0, `the leg crosses legal tiles (${JSON.stringify(seen)})`);
|
||||
// over the shop bar (DOM over the canvas: no target) the model is held under the pointer, so the player still sees
|
||||
// what they carry
|
||||
const bar = await page.evaluate(() => { const el = [...document.querySelectorAll('.scard')][2], r = el && el.getBoundingClientRect(); return r && { x: r.left + r.width / 2, y: r.top + r.height * 0.3 }; });
|
||||
assert.ok(bar, 'premise: a shop card');
|
||||
assert.ok(await page.evaluate((x, y) => !!document.elementFromPoint(x, y)?.closest('.shopbar'), bar.x, bar.y), 'premise: the point is on the shop bar');
|
||||
await page.mouse.move(bar.x, bar.y, { steps: 6 });
|
||||
await sleep(60);
|
||||
assert.equal(await page.evaluate(() => window.__hover.at(-1)), null, 'no tile under the shop bar');
|
||||
assertHeld(await ghostAt(page, hand.uid), bar, 'mouse over the shop bar');
|
||||
await page.mouse.move(to.x, to.y, { steps: 6 });
|
||||
await sleep(60);
|
||||
assertStands(await ghostAt(page, hand.uid), [9, 4], 'mouse back on (9,4)');
|
||||
await page.mouse.move(to.x, to.y);
|
||||
await sleep(60);
|
||||
await page.mouse.up();
|
||||
@@ -402,10 +443,10 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
assert.deepEqual(problems, []);
|
||||
});
|
||||
|
||||
// a phone (touch, landscape 844×390): the finger holds the model like the mouse (no lift above the finger); on the
|
||||
// bench (the lowest canvas row, the shop bar right under it) a release on the slot lands there, one on the shop bar
|
||||
// goes back
|
||||
test('#4.1 touch (phone): held under the finger, dropped on the tile under it; a release over the shop bar goes back', async () => {
|
||||
// a phone (touch, landscape 844×390): the finger drags the model like the mouse — standing on the finger's tile over a
|
||||
// legal target, under the finger elsewhere (no lift above the finger); on the bench (the lowest canvas row, the shop
|
||||
// bar right under it) a release on the slot lands there, one on the shop bar goes back
|
||||
test('#4.1 touch (phone): stands on the finger\'s tile (under the finger over the shop bar), dropped on the tile under it; a release over the shop bar goes back', async () => {
|
||||
const page = await browser.newPage();
|
||||
const problems = [];
|
||||
page.on('pageerror', (e) => problems.push(`pageerror: ${e.message}`));
|
||||
@@ -436,10 +477,10 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
await page.keyboard.press('Escape');
|
||||
await sleep(200);
|
||||
}
|
||||
// a bench operator onto the board: held under the finger, the wheel on the finger's tile
|
||||
// a bench operator onto the board: standing on the finger's tile, the wheel on that tile
|
||||
const hand = await page.evaluate(() => { const p = globalThis.__MOCK__.S().priv; const i = p.hand.findIndex((x) => x && x.kind === 'chess'); return { uid: p.hand[i].uid, idx: i }; });
|
||||
const to = await tileSpot(page, 9, 4, -0.3, 0.3);
|
||||
await touchDrag(await tileSpot(page, 7, hand.idx), to, { hold: async () => assertHeld(await ghostAt(page, hand.uid), to, 'touch') });
|
||||
await touchDrag(await tileSpot(page, 7, hand.idx), to, { hold: async () => assertStands(await ghostAt(page, hand.uid), [9, 4], 'touch') });
|
||||
await page.waitForSelector('.fwheel__dia', { timeout: 3000 });
|
||||
const w = await page.evaluate(() => { const r = document.querySelector('.fwheel__dia').getBoundingClientRect(); return { x: r.left + r.width / 2, y: r.top + r.height / 2 }; });
|
||||
const c = await tileSpot(page, 9, 4);
|
||||
@@ -457,9 +498,9 @@ describe('user playtest #3 items 1 and 7 (mock match, headless Chrome)', { skip
|
||||
return null;
|
||||
}, slot);
|
||||
assert.ok(bar, 'premise: the shop bar covers the canvas right below the bench slot');
|
||||
await touchDrag(await tileSpot(page, unit.row, unit.col), bar);
|
||||
await touchDrag(await tileSpot(page, unit.row, unit.col), bar, { hold: async () => assertHeld(await ghostAt(page, unit.uid), bar, 'touch over the shop bar') });
|
||||
assert.ok(await page.evaluate((u) => globalThis.__MOCK__.S().priv.board.some((b) => b.uid === u), unit.uid), 'released over the shop bar: back on the board');
|
||||
await touchDrag(await tileSpot(page, unit.row, unit.col), slot);
|
||||
await touchDrag(await tileSpot(page, unit.row, unit.col), slot, { hold: async () => assertStands(await ghostAt(page, unit.uid), [7, 5], 'touch on the free bench slot') });
|
||||
assert.equal(await page.evaluate(() => globalThis.__MOCK__.S().priv.hand[5]?.uid ?? null), unit.uid, 'released on the bench slot: it is there');
|
||||
await page.screenshot({ path: path.join(OUT, 'models-touch-phone.png') });
|
||||
await page.close();
|
||||
|
||||
Reference in new issue
Block a user