test(02-01): 铺设 L0 mover 坐标回写回归测试脚手架(skip 状态)

- 4 个 it.skip 用例:x 正交 / y 正交 / 斜向 / 传送步的 setPos 回写
- 测试桩逐字复刻 eventDispatch.test.ts 模式(main/location stub + getOrInsertComputed polyfill)
- 最小 ObjectMover 子类驱动 moveProgress,零新依赖;待 02-02 修复 mover.ts:651 后翻绿
This commit is contained in:
unanmed 2026-09-09 15:33:18 +08:00
parent a9b1ba1b22
commit 9477b2c78a

View File

@ -0,0 +1,188 @@
// 验证 L0 ObjectMover 单步移动后的坐标回写行为(正交 / 斜向 / 传送步),为 mover.ts:651 条件缺陷的修复铺设回归用例
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { type ITileLocator } from '@motajs/common';
import {
FaceDirection
} from '@user/data-common';
import {
type IObjectMovable,
type ObjectMoveStep,
ObjectMoveType
} from './mover';
vi.hoisted(() => {
vi.stubGlobal('main', { replayChecking: true });
vi.stubGlobal('location', { origin: 'http://localhost' });
Map.prototype.getOrInsertComputed ??= function <K, V>(
this: Map<K, V>,
key: K,
callback: (key: K) => V
): V {
const existing = this.get(key);
if (existing !== undefined) return existing;
const value = callback(key);
this.set(key, value);
return value;
};
});
interface TestModules {
ObjectMover: typeof import('./mover').ObjectMover;
Dir8FaceHandler: typeof import('@user/data-common').Dir8FaceHandler;
}
let modules: TestModules;
beforeAll(async () => {
vi.stubGlobal('main', { replayChecking: true });
vi.stubGlobal('location', { origin: 'http://localhost' });
const moverModule = await import('./mover');
const commonModule = await import('@user/data-common');
modules = {
ObjectMover: moverModule.ObjectMover,
Dir8FaceHandler: commonModule.Dir8FaceHandler
};
});
/** 测试用可移动对象,记录每次 setPos 调用以断言坐标回写 */
class TestTile implements IObjectMovable {
/** 当前横坐标 */
x: number = 0;
/** 当前纵坐标 */
y: number = 0;
/** setPos 调用记录 */
readonly setPosCalls: ITileLocator[] = [];
/** 当前朝向 */
face: FaceDirection = FaceDirection.Down;
/**
*
* @param x
* @param y
*/
setPos(x: number, y: number): void {
this.x = x;
this.y = y;
this.setPosCalls.push({ x, y });
}
/**
*
*/
getCurrentFaceDirection(): FaceDirection {
return this.face;
}
}
/** 创建最小移动器子类:按步骤类型模拟移动实现并返回该步的到达坐标 */
function createMover(tile: TestTile) {
class TestMover extends modules.ObjectMover<TestTile> {
readonly tile: TestTile;
constructor(tile: TestTile) {
super(new modules.Dir8FaceHandler(), FaceDirection.Down);
this.tile = tile;
}
protected override async onMoveStart(): Promise<void> {}
protected override async onMoveEnd(): Promise<void> {}
protected override async onStepStart(): Promise<number> {
// 移动代码对基类不透明,固定传 0 即可
return 0;
}
protected override async onStepEnd(
_code: number,
step: Readonly<ObjectMoveStep>,
tile: TestTile
): Promise<ITileLocator> {
if (step.type === ObjectMoveType.Teleport) {
return { x: step.x, y: step.y };
}
if (step.type === ObjectMoveType.Dir) {
switch (step.move) {
case FaceDirection.Right:
return { x: tile.x + 1, y: tile.y };
case FaceDirection.Left:
return { x: tile.x - 1, y: tile.y };
case FaceDirection.Up:
return { x: tile.x, y: tile.y - 1 };
case FaceDirection.Down:
return { x: tile.x, y: tile.y + 1 };
case FaceDirection.RightUp:
return { x: tile.x + 1, y: tile.y - 1 };
default:
return { x: tile.x, y: tile.y };
}
}
return { x: tile.x, y: tile.y };
}
protected override async onStepSettled(): Promise<void> {}
}
return new TestMover(tile);
}
describe('object mover position writeback', () => {
// 验证正交步(仅 x 变化)移动后经 setPos 回写 x 轴坐标,覆盖 mover.ts:651 条件缺陷场景
it.skip('writes back the x position after an orthogonal x-only step', async () => {
const tile = new TestTile();
tile.x = 0;
tile.y = 0;
const mover = createMover(tile);
mover.step(FaceDirection.Right);
const controller = mover.start();
expect(controller).not.toBeNull();
await controller!.onEnd;
expect(tile.x).toBe(1);
expect(tile.y).toBe(0);
expect(tile.setPosCalls).toEqual([{ x: 1, y: 0 }]);
});
// 验证正交步(仅 y 变化)移动后经 setPos 回写 y 轴坐标,覆盖另一正交方向
it.skip('writes back the y position after an orthogonal y-only step', async () => {
const tile = new TestTile();
tile.x = 0;
tile.y = 0;
const mover = createMover(tile);
mover.step(FaceDirection.Down);
const controller = mover.start();
expect(controller).not.toBeNull();
await controller!.onEnd;
expect(tile.x).toBe(0);
expect(tile.y).toBe(1);
expect(tile.setPosCalls).toEqual([{ x: 0, y: 1 }]);
});
// 验证斜向步移动后经 setPos 双轴同时回写,保证修复不破坏双轴步语义
it.skip('writes back both axes after a diagonal step', async () => {
const tile = new TestTile();
tile.x = 1;
tile.y = 1;
const mover = createMover(tile);
mover.step(FaceDirection.RightUp);
const controller = mover.start();
expect(controller).not.toBeNull();
await controller!.onEnd;
expect(tile.x).toBe(2);
expect(tile.y).toBe(0);
expect(tile.setPosCalls).toEqual([{ x: 2, y: 0 }]);
});
// 验证传送步ObjectMoveType.Teleport移动后经 setPos 双轴回写至目标坐标
it.skip('writes back both axes after a teleport step', async () => {
const tile = new TestTile();
tile.x = 1;
tile.y = 1;
const mover = createMover(tile);
mover.tp(3, 4);
const controller = mover.start();
expect(controller).not.toBeNull();
await controller!.onEnd;
expect(tile.x).toBe(3);
expect(tile.y).toBe(4);
expect(tile.setPosCalls).toEqual([{ x: 3, y: 4 }]);
});
});