mirror of
https://github.com/motajs/template.git
synced 2026-10-02 00:20:18 +08:00
test(06-08): extend ObjectMover coverage across its public surface (stage 3)
- cover setPos/setFaceDir/setMoveDir/jump/stepFace/forward/backward/speed/face/animDir/push/clear/start - add controller stop and start-while-moving contract checks with real timers and await onEnd - mark the multi-step backward case as skipped suspected bug #06-08-1
This commit is contained in:
parent
2cb9bba3ce
commit
6699df9eee
@ -1,9 +1,10 @@
|
||||
// 验证 L0 ObjectMover 单步移动后的坐标回写行为(正交 / 斜向 / 传送步),为 mover.ts:651 条件缺陷的修复铺设回归用例
|
||||
// 测试 L0 ObjectMover:既有坐标回写回归用例 + 全部公开移动方法的正常与边界行为
|
||||
import { beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
import { type ITileLocator } from '@motajs/common';
|
||||
import { FaceDirection } from '@user/data-common';
|
||||
import {
|
||||
type IObjectMovable,
|
||||
ObjectAnimDirection,
|
||||
type ObjectMoveStep,
|
||||
ObjectMoveType
|
||||
} from './mover';
|
||||
@ -72,6 +73,30 @@ class TestTile implements IObjectMovable {
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据朝向返回从当前位置移动一步后的坐标,未知朝向原地不动 */
|
||||
function nextLocator(dir: FaceDirection, tile: TestTile): ITileLocator {
|
||||
switch (dir) {
|
||||
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 };
|
||||
case FaceDirection.LeftUp:
|
||||
return { x: tile.x - 1, y: tile.y - 1 };
|
||||
case FaceDirection.RightDown:
|
||||
return { x: tile.x + 1, y: tile.y + 1 };
|
||||
case FaceDirection.LeftDown:
|
||||
return { x: tile.x - 1, y: tile.y + 1 };
|
||||
default:
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
}
|
||||
|
||||
/** 创建最小移动器子类:按步骤类型模拟移动实现并返回该步的到达坐标 */
|
||||
function createMover(tile: TestTile) {
|
||||
class TestMover extends modules.ObjectMover<TestTile> {
|
||||
@ -96,24 +121,22 @@ function createMover(tile: TestTile) {
|
||||
step: Readonly<ObjectMoveStep>,
|
||||
tile: TestTile
|
||||
): Promise<ITileLocator> {
|
||||
if (step.type === ObjectMoveType.Teleport) {
|
||||
return { x: step.x, y: step.y };
|
||||
if (
|
||||
step.type === ObjectMoveType.Teleport ||
|
||||
step.type === ObjectMoveType.Jump
|
||||
) {
|
||||
return step.rel
|
||||
? { x: tile.x + step.x, y: tile.y + step.y }
|
||||
: { 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 };
|
||||
}
|
||||
if (
|
||||
step.type === ObjectMoveType.Dir ||
|
||||
step.type === ObjectMoveType.DirFace
|
||||
) {
|
||||
return nextLocator(step.move, tile);
|
||||
}
|
||||
if (step.type === ObjectMoveType.Special) {
|
||||
return nextLocator(this.moveDirection, tile);
|
||||
}
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
@ -184,3 +207,213 @@ describe('object mover position writeback', () => {
|
||||
expect(tile.setPosCalls).toEqual([{ x: 3, y: 4 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('object mover public surface', () => {
|
||||
// 验证 setPos 立即写入绑定对象的位置
|
||||
it('sets the position of the bound object', () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.setPos(4, 6);
|
||||
|
||||
expect(tile.x).toBe(4);
|
||||
expect(tile.y).toBe(6);
|
||||
expect(tile.setPosCalls).toEqual([{ x: 4, y: 6 }]);
|
||||
});
|
||||
|
||||
// 验证 setFaceDir 与 setMoveDir 分别更新朝向与移动方向
|
||||
it('sets face and move directions independently', () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.setFaceDir(FaceDirection.Left);
|
||||
mover.setMoveDir(FaceDirection.Up);
|
||||
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Left);
|
||||
expect(mover.moveDirection).toBe(FaceDirection.Up);
|
||||
});
|
||||
|
||||
// 验证 jump 绝对模式把对象跳跃到目标坐标
|
||||
it('jumps to an absolute target', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.jump(3, 4);
|
||||
const controller = mover.start();
|
||||
expect(controller).not.toBeNull();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(3);
|
||||
expect(tile.y).toBe(4);
|
||||
});
|
||||
|
||||
// 验证 jump 相对模式在当前坐标基础上偏移
|
||||
it('jumps by a relative offset', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.jump(2, 3, true);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(2);
|
||||
expect(tile.y).toBe(3);
|
||||
});
|
||||
|
||||
// 验证 stepFace 按指定移动方向移动并采用显式朝向
|
||||
it('moves by the step move direction and applies the explicit face', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.stepFace(FaceDirection.Right, FaceDirection.Up);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(1);
|
||||
expect(tile.y).toBe(0);
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Up);
|
||||
});
|
||||
|
||||
// 验证 forward 沿当前朝向连续前进
|
||||
it('moves forward along the current face direction', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.forward(2);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(0);
|
||||
expect(tile.y).toBe(2);
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Down);
|
||||
});
|
||||
|
||||
// 验证 backward 沿当前朝向的反方向后退一步
|
||||
it('moves backward against the current face direction', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.backward(1);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(0);
|
||||
expect(tile.y).toBe(-1);
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Down);
|
||||
expect(mover.moveDirection).toBe(FaceDirection.Up);
|
||||
});
|
||||
|
||||
// 疑似 bug:连续后退时上一步会把移动方向翻转为反方向,导致方向来回摆动,详见 06-TEST-FINDINGS.md #06-08-1,修复后取消 skip
|
||||
it.skip('keeps retreating along the same axis across multiple backward steps', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.backward(2);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.x).toBe(0);
|
||||
expect(tile.y).toBe(-2);
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Down);
|
||||
expect(mover.moveDirection).toBe(FaceDirection.Up);
|
||||
});
|
||||
|
||||
// 验证 speed 步更新后续移动速度
|
||||
it('applies a speed step to the mover', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.speed(250).step(FaceDirection.Right);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(mover.currentSpeed).toBe(250);
|
||||
expect(tile.x).toBe(1);
|
||||
});
|
||||
|
||||
// 验证 face 步只改变朝向而不产生位移
|
||||
it('changes only the face direction without moving', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.face(FaceDirection.Left);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(mover.faceDirection).toBe(FaceDirection.Left);
|
||||
expect(tile.setPosCalls).toEqual([]);
|
||||
});
|
||||
|
||||
// 验证 animDir 步切换动画播放方向
|
||||
it('switches the animation direction', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.animDir(ObjectAnimDirection.Backward);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(mover.currAnimDir).toBe(ObjectAnimDirection.Backward);
|
||||
});
|
||||
|
||||
// 验证 push 追加的多个步骤按传入顺序依次执行
|
||||
it('executes pushed steps in order', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.push([
|
||||
{ type: ObjectMoveType.Dir, move: FaceDirection.Right },
|
||||
{ type: ObjectMoveType.Teleport, x: 5, y: 5, rel: false }
|
||||
]);
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.setPosCalls).toEqual([
|
||||
{ x: 1, y: 0 },
|
||||
{ x: 5, y: 5 }
|
||||
]);
|
||||
});
|
||||
|
||||
// 验证 clear 清空尚未执行的步骤队列
|
||||
it('clears queued steps before starting', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.step(FaceDirection.Right).clear();
|
||||
const controller = mover.start();
|
||||
await controller!.onEnd;
|
||||
|
||||
expect(tile.setPosCalls).toEqual([]);
|
||||
});
|
||||
|
||||
// 验证已有移动进行中时 start 返回 null,结束后 moving 复位
|
||||
it('returns null while a move is already in progress', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
const first = mover.step(FaceDirection.Right).start();
|
||||
expect(first).not.toBeNull();
|
||||
|
||||
const second = mover.start();
|
||||
expect(second).toBeNull();
|
||||
|
||||
await first!.onEnd;
|
||||
expect(first!.done).toBe(true);
|
||||
expect(mover.moving).toBe(false);
|
||||
});
|
||||
|
||||
// 验证控制器 stop 会阻止队列中剩余步骤执行
|
||||
it('stops the move before executing queued steps', async () => {
|
||||
const tile = new TestTile();
|
||||
const mover = createMover(tile);
|
||||
|
||||
mover.step(FaceDirection.Right, 3);
|
||||
const controller = mover.start();
|
||||
expect(controller).not.toBeNull();
|
||||
|
||||
await controller!.stop();
|
||||
|
||||
expect(tile.setPosCalls).toEqual([]);
|
||||
expect(controller!.done).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user