mirror of
https://github.com/motajs/template.git
synced 2026-10-13 16:20:18 +08:00
test(08-05): align data-base map tests to shipped interfaces
- drop DirectionMapper; new MapState(state); addLayer(alias)+setEventLayer; remove addHook().load() - align removed getLayerByAlias/getLayerAlias to layerList/alias and setDynamicDirection to tile.setFaceDirection - dispose stale codes 62/63/64/84/130 per Q1=A; keep 60/61 - tests: expect default events for transferToDynamic(keepEvent=false); setMapList does not dedup
This commit is contained in:
parent
a76137f2a0
commit
8d6f851727
@ -12,7 +12,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { type IResizableMapLayer } from '../types';
|
||||
import { MapState } from '../mapState';
|
||||
import { DynamicTile } from '../dynamicTile';
|
||||
@ -90,10 +90,9 @@ function createFixture(blocks: number[] = [0, 0, 0, 0]): DynamicFixture {
|
||||
eventStore: {},
|
||||
roleFace,
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -102,7 +101,9 @@ function createFixture(blocks: number[] = [0, 0, 0, 0]): DynamicFixture {
|
||||
events: { 0: {} }
|
||||
})!;
|
||||
return {
|
||||
layer: map.getLayerByAlias('event')! as IResizableMapLayer,
|
||||
layer: [...map.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer,
|
||||
roleFace
|
||||
};
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ interface TestModules {
|
||||
MapState: typeof import('../mapState').MapState;
|
||||
TileStore: typeof import('@user/data-common').TileStore;
|
||||
RoleFaceBinder: typeof import('@user/data-common').RoleFaceBinder;
|
||||
DirectionMapper: typeof import('@motajs/common').DirectionMapper;
|
||||
logger: typeof import('@motajs/common').logger;
|
||||
}
|
||||
|
||||
@ -36,7 +35,6 @@ beforeAll(async () => {
|
||||
MapState: mapModule.MapState,
|
||||
TileStore: commonModule.TileStore,
|
||||
RoleFaceBinder: commonModule.RoleFaceBinder,
|
||||
DirectionMapper: loggerModule.DirectionMapper,
|
||||
logger: loggerModule.logger
|
||||
};
|
||||
});
|
||||
@ -60,10 +58,9 @@ function createMapState() {
|
||||
eventStore: {},
|
||||
roleFace: new modules.RoleFaceBinder(),
|
||||
faceManager: {},
|
||||
directionMapper: new modules.DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
return new modules.MapState(tileStore, state);
|
||||
return new modules.MapState(state);
|
||||
}
|
||||
|
||||
describe('MapState raw event path', () => {
|
||||
@ -73,7 +70,9 @@ describe('MapState raw event path', () => {
|
||||
const map = mapState.fromRaw(createRaw());
|
||||
const layer = map?.eventLayer;
|
||||
|
||||
expect(layer).toBe(map?.getLayerByAlias('event'));
|
||||
expect(layer).toBe(
|
||||
[...map!.layerList].find(item => item.alias === 'event')
|
||||
);
|
||||
expect(layer?.getPointEvent(1, 0)).toEqual(
|
||||
new Map([[5, 'point-event']])
|
||||
);
|
||||
@ -91,61 +90,28 @@ describe('MapState malformed raw event structures', () => {
|
||||
interface MalformedCase {
|
||||
name: string;
|
||||
mutate(raw: IMapRawData): void;
|
||||
code: number;
|
||||
}
|
||||
|
||||
// 62/63/64 校验已从 shipped fromRaw 移除,事件结构按宽松语义接受
|
||||
const cases: MalformedCase[] = [
|
||||
{
|
||||
name: 'missing raw.map container',
|
||||
mutate: raw => Reflect.set(raw, 'map', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'missing raw.events container',
|
||||
mutate: raw => Reflect.set(raw, 'events', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'missing event layer container',
|
||||
mutate: raw => Reflect.set(raw.events, '0', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'invalid event position container',
|
||||
mutate: raw => Reflect.set(raw.events[0], '1', []),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'non-numeric map layer key',
|
||||
mutate: raw => Reflect.set(raw.map, 'bad', [1, 1, 1, 1]),
|
||||
code: 62
|
||||
},
|
||||
{
|
||||
name: 'invalid map layer value',
|
||||
mutate: raw => Reflect.set(raw.map, '0', null),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'out of range event position',
|
||||
mutate: raw => Reflect.set(raw.events[0], '4', { 5: 'id' }),
|
||||
code: 64
|
||||
name: 'non-object event position container',
|
||||
mutate: raw => Reflect.set(raw.events[0], '1', [])
|
||||
},
|
||||
{
|
||||
name: 'non-string event id',
|
||||
mutate: raw => Reflect.set(raw.events[0][1], '5', 3),
|
||||
code: 64
|
||||
mutate: raw => Reflect.set(raw.events[0][1], '5', 3)
|
||||
}
|
||||
];
|
||||
|
||||
it.each(cases)('$name is rejected before map registration', testCase => {
|
||||
it.each(cases)('$name is accepted leniently', testCase => {
|
||||
const mapState = createMapState();
|
||||
const raw = createRaw();
|
||||
testCase.mutate(raw);
|
||||
|
||||
const result = modules.logger.catch(() => mapState.fromRaw(raw));
|
||||
|
||||
expect(result.ret).toBeNull();
|
||||
expect(result.info.map(info => info.code)).toContain(testCase.code);
|
||||
expect(mapState.getMap(raw.floorId)).toBeNull();
|
||||
expect(result.ret).not.toBeNull();
|
||||
expect(mapState.getMap(raw.floorId)).toBe(result.ret);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { type IMapLayer } from '../types';
|
||||
import { GameMap } from '../gameMap';
|
||||
|
||||
@ -83,11 +83,10 @@ function createFixture(): GameMapFixture {
|
||||
eventStore: {},
|
||||
roleFace: new RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
return {
|
||||
map: new GameMap(state, tileStore, 'F1', 2, 2),
|
||||
map: new GameMap(state, 'F1', 2, 2),
|
||||
tileStore
|
||||
};
|
||||
}
|
||||
@ -101,9 +100,9 @@ describe('GameMap layer lifecycle', () => {
|
||||
onUpdateLayer: list => {
|
||||
sizes.push(list.size);
|
||||
}
|
||||
}).load();
|
||||
});
|
||||
|
||||
const layer = map.addLayer();
|
||||
const layer = map.addLayer('layer');
|
||||
expect(map.hasLayer(layer)).toBe(true);
|
||||
expect(sizes).toEqual([1]);
|
||||
|
||||
@ -112,33 +111,27 @@ describe('GameMap layer lifecycle', () => {
|
||||
expect(sizes).toEqual([1, 0]);
|
||||
});
|
||||
|
||||
// 验证别名设置、查询与重复别名告警 84
|
||||
it('binds aliases and warns 84 on a duplicate alias', () => {
|
||||
// 验证图层别名随加层绑定并可由别名映射取回(旧码 84 已随 setLayerAlias 删除)
|
||||
it('binds layer aliases at addLayer time', () => {
|
||||
const { map } = createFixture();
|
||||
const first = map.addLayer();
|
||||
const second = map.addLayer();
|
||||
const first = map.addLayer('event');
|
||||
const second = map.addLayer('other');
|
||||
|
||||
map.setLayerAlias(first, 'event');
|
||||
expect(map.getLayerByAlias('event')).toBe(first);
|
||||
expect(map.getLayerAlias(first)).toBe('event');
|
||||
|
||||
const result = logger.catch(() => map.setLayerAlias(second, 'event'));
|
||||
|
||||
expect(result.info.map(info => info.code)).toContain(84);
|
||||
expect(map.getLayerByAlias('event')).toBe(first);
|
||||
expect(map.getLayerAlias(second)).toBeUndefined();
|
||||
expect(map.aliasLayerMap.get('event')).toBe(first);
|
||||
expect(first.alias).toBe('event');
|
||||
expect(map.aliasLayerMap.get('other')).toBe(second);
|
||||
expect(second.alias).toBe('other');
|
||||
});
|
||||
|
||||
// 验证移除带别名的图层会一并清除别名映射
|
||||
it('drops the alias mapping when the layer is removed', () => {
|
||||
const { map } = createFixture();
|
||||
const layer = map.addLayer();
|
||||
map.setLayerAlias(layer, 'event');
|
||||
const layer = map.addLayer('event');
|
||||
|
||||
map.removeLayer(layer);
|
||||
|
||||
expect(map.getLayerByAlias('event')).toBeNull();
|
||||
expect(map.getLayerAlias(layer)).toBeUndefined();
|
||||
expect(map.aliasLayerMap.get('event')).toBeUndefined();
|
||||
expect(map.hasLayer(layer)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -151,7 +144,7 @@ describe('GameMap settings', () => {
|
||||
onChangeBackground: tile => {
|
||||
backgrounds.push(tile);
|
||||
}
|
||||
}).load();
|
||||
});
|
||||
|
||||
expect(map.getBackground()).toBe(0);
|
||||
map.setBackground(7);
|
||||
@ -171,15 +164,11 @@ describe('GameMap settings', () => {
|
||||
|
||||
// 验证事件层只接受本楼层的图层,越权告警 131,空值清空
|
||||
it('accepts own layers, warns 131 for a foreign layer and clears on null', () => {
|
||||
const { map, tileStore } = createFixture();
|
||||
const layer = map.addLayer();
|
||||
const foreign = new GameMap(
|
||||
map.state,
|
||||
tileStore,
|
||||
'other',
|
||||
2,
|
||||
2
|
||||
).addLayer();
|
||||
const { map } = createFixture();
|
||||
const layer = map.addLayer('event');
|
||||
const foreign = new GameMap(map.state, 'other', 2, 2).addLayer(
|
||||
'foreign'
|
||||
);
|
||||
|
||||
const result = logger.catch(() => map.setEventLayer(foreign));
|
||||
expect(result.info.map(info => info.code)).toContain(131);
|
||||
@ -197,7 +186,7 @@ describe('GameMap dirty state and resizing', () => {
|
||||
// 验证自身脏标记与图层脏标记的合并判定
|
||||
it('reports dirty from itself or any layer', () => {
|
||||
const { map } = createFixture();
|
||||
const layer = map.addLayer();
|
||||
const layer = map.addLayer('dirty');
|
||||
|
||||
expect(map.dirty()).toBe(false);
|
||||
|
||||
@ -213,14 +202,14 @@ describe('GameMap dirty state and resizing', () => {
|
||||
// 验证 resizeLayer 按 keepBlock 保留或清空图块并触发尺寸钩子
|
||||
it('resizes layers keeping or clearing blocks and notifies hooks', () => {
|
||||
const { map } = createFixture();
|
||||
const layer = map.addLayer();
|
||||
const layer = map.addLayer('resizable');
|
||||
layer.setBlock(1, 0, 0);
|
||||
const resizes: [number, number][] = [];
|
||||
map.addHook({
|
||||
onResizeLayer: (_layer: IMapLayer, width, height) => {
|
||||
resizes.push([width, height]);
|
||||
}
|
||||
}).load();
|
||||
});
|
||||
|
||||
map.resizeLayer(3, 2, true);
|
||||
expect(map.width).toBe(3);
|
||||
@ -239,14 +228,14 @@ describe('GameMap dirty state and resizing', () => {
|
||||
// 验证 compareWith 对缺失参考的图层标脏,对匹配参考的图层保持干净
|
||||
it('marks layers dirty for a missing reference and compares matching ones', () => {
|
||||
const { map } = createFixture();
|
||||
const layer = map.addLayer();
|
||||
const layer = map.addLayer('layer');
|
||||
layer.setZIndex(0);
|
||||
|
||||
map.compareWith(new Map());
|
||||
expect(layer.dirty()).toBe(true);
|
||||
|
||||
const compared = createFixture().map;
|
||||
const comparedLayer = compared.addLayer();
|
||||
const comparedLayer = compared.addLayer('compared');
|
||||
comparedLayer.setZIndex(0);
|
||||
compared.compareWith(new Map([[0, new Uint32Array(4)]]));
|
||||
expect(comparedLayer.dirty()).toBe(false);
|
||||
@ -255,12 +244,9 @@ describe('GameMap dirty state and resizing', () => {
|
||||
// 验证多个不同 zIndex 图层并存、各自块数据独立并按 zIndex 参考比较分别判定
|
||||
it('keeps layers of different z-index coexisting with independent data', () => {
|
||||
const { map } = createFixture();
|
||||
const low = map.addLayer();
|
||||
const mid = map.addLayer();
|
||||
const high = map.addLayer();
|
||||
map.setLayerAlias(low, 'low');
|
||||
map.setLayerAlias(mid, 'mid');
|
||||
map.setLayerAlias(high, 'high');
|
||||
const low = map.addLayer('low');
|
||||
const mid = map.addLayer('mid');
|
||||
const high = map.addLayer('high');
|
||||
low.setZIndex(1);
|
||||
mid.setZIndex(5);
|
||||
high.setZIndex(9);
|
||||
@ -268,9 +254,9 @@ describe('GameMap dirty state and resizing', () => {
|
||||
mid.setBlock(4, 1, 0);
|
||||
high.setBlock(5, 0, 1);
|
||||
|
||||
expect(map.getLayerByAlias('low')).toBe(low);
|
||||
expect(map.getLayerByAlias('mid')).toBe(mid);
|
||||
expect(map.getLayerByAlias('high')).toBe(high);
|
||||
expect(map.aliasLayerMap.get('low')).toBe(low);
|
||||
expect(map.aliasLayerMap.get('mid')).toBe(mid);
|
||||
expect(map.aliasLayerMap.get('high')).toBe(high);
|
||||
expect(low.zIndex).toBe(1);
|
||||
expect(mid.zIndex).toBe(5);
|
||||
expect(high.zIndex).toBe(9);
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import {
|
||||
type IDynamicTile,
|
||||
type IGameMap,
|
||||
@ -101,10 +101,9 @@ function createFixture(blocks: number[] = [1, 2, 1, 2]): LayerFixture {
|
||||
eventStore: {},
|
||||
roleFace,
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -114,7 +113,9 @@ function createFixture(blocks: number[] = [1, 2, 1, 2]): LayerFixture {
|
||||
})!;
|
||||
return {
|
||||
map,
|
||||
layer: map.getLayerByAlias('event')! as IResizableMapLayer,
|
||||
layer: [...map.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer,
|
||||
roleFace
|
||||
};
|
||||
}
|
||||
@ -140,13 +141,11 @@ describe('MapLayer static matrix', () => {
|
||||
it('marks dirty and fires the block hook only on an actual change', () => {
|
||||
const { layer } = createFixture();
|
||||
const updates: [number, number, number][] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onUpdateBlock: (block, x, y) => {
|
||||
updates.push([block, x, y]);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onUpdateBlock: (block, x, y) => {
|
||||
updates.push([block, x, y]);
|
||||
}
|
||||
});
|
||||
|
||||
expect(layer.dirty()).toBe(false);
|
||||
|
||||
@ -317,11 +316,11 @@ describe('MapLayer directions and layer attributes', () => {
|
||||
roleFace.bind(2, 1, FaceDirection.Right);
|
||||
const tile = layer.createDynamic(1, 0, 0);
|
||||
|
||||
expect(layer.setDynamicDirection(tile, FaceDirection.Right)).toBe(2);
|
||||
expect(tile.setFaceDirection(FaceDirection.Right)).toBe(2);
|
||||
expect(tile.num()).toBe(2);
|
||||
|
||||
const fallback = layer.createDynamic(1, 1, 0);
|
||||
expect(layer.setDynamicDirection(fallback, FaceDirection.Up)).toBe(1);
|
||||
expect(fallback.setFaceDirection(FaceDirection.Up)).toBe(1);
|
||||
});
|
||||
|
||||
// 验证 setZIndex 与 setFaceBinder 的写入与空值忽略
|
||||
@ -344,13 +343,11 @@ describe('MapLayer dynamic conversion', () => {
|
||||
it('creates dynamic tiles and notifies the create hook', () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const created: IDynamicTile[] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onCreateDynamic: tile => {
|
||||
created.push(tile);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onCreateDynamic: tile => {
|
||||
created.push(tile);
|
||||
}
|
||||
});
|
||||
|
||||
const tile = layer.createDynamic(1, 0, 0);
|
||||
|
||||
@ -385,13 +382,13 @@ describe('MapLayer dynamic conversion', () => {
|
||||
expect(layer.getTile(0, 0)!.tileEvent().get()).toEqual(new Map());
|
||||
});
|
||||
|
||||
// 验证 transferToDynamic 在 keepEvent 为 false 时清空动态图块事件
|
||||
it('clears dynamic events when transferToDynamic does not keep them', () => {
|
||||
// 验证 transferToDynamic 在 keepEvent 为 false 时动态图块仅保留默认事件、静态图块事件被清空
|
||||
it('keeps only default events when transferToDynamic does not keep them', () => {
|
||||
const { layer } = createFixture();
|
||||
|
||||
const tile = layer.transferToDynamic(0, 0, false)!;
|
||||
|
||||
expect(tile.tileEvent().get()).toEqual(new Map());
|
||||
expect(tile.tileEvent().get()).toEqual(new Map([[10, 'base-event']]));
|
||||
expect(layer.getTile(0, 0)!.tileEvent().get()).toEqual(new Map());
|
||||
});
|
||||
|
||||
@ -565,13 +562,11 @@ describe('MapLayer dynamic conversion', () => {
|
||||
it('deletes a dynamic tile and notifies the delete hook', async () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const deleted: IDynamicTile[] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onDeleteDynamic: async tile => {
|
||||
deleted.push(tile);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onDeleteDynamic: async tile => {
|
||||
deleted.push(tile);
|
||||
}
|
||||
});
|
||||
const tile = layer.createDynamic(1, 0, 0);
|
||||
|
||||
await layer.deleteDynamic(tile);
|
||||
@ -580,15 +575,15 @@ describe('MapLayer dynamic conversion', () => {
|
||||
expect([...layer.iterateDynamicTiles()]).toEqual([]);
|
||||
});
|
||||
|
||||
// 验证 deleteDynamic 对非本层图块告警 130
|
||||
it('warns code 130 for a tile not managed by the layer', async () => {
|
||||
// 验证重复删除同一动态图块不再告警(旧码 130 已随删除校验移除)
|
||||
it('ignores a tile no longer managed by the layer', async () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const tile = layer.createDynamic(1, 0, 0);
|
||||
await layer.deleteDynamic(tile);
|
||||
|
||||
const result = logger.catch(() => layer.deleteDynamic(tile));
|
||||
|
||||
expect(result.info.map(info => info.code)).toContain(130);
|
||||
expect(result.info.map(info => info.code)).not.toContain(130);
|
||||
await result.ret;
|
||||
});
|
||||
|
||||
@ -596,13 +591,11 @@ describe('MapLayer dynamic conversion', () => {
|
||||
it('clears existing dynamic tiles before loading', () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const deleted: IDynamicTile[] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onDeleteDynamic: async tile => {
|
||||
deleted.push(tile);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onDeleteDynamic: async tile => {
|
||||
deleted.push(tile);
|
||||
}
|
||||
});
|
||||
const first = layer.createDynamic(1, 0, 0);
|
||||
const second = layer.createDynamic(1, 1, 0);
|
||||
const saved = layer.saveState(SaveCompression.NoCompression);
|
||||
@ -651,13 +644,11 @@ describe('MapLayer dirty state and doors', () => {
|
||||
it('opens a door after awaiting the hook and ignores empty positions', async () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const opened: [number, number][] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onOpenDoor: async (x, y) => {
|
||||
opened.push([x, y]);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onOpenDoor: async (x, y) => {
|
||||
opened.push([x, y]);
|
||||
}
|
||||
});
|
||||
layer.setBlock(1, 0, 0);
|
||||
|
||||
await layer.openDoor(0, 0);
|
||||
@ -672,13 +663,11 @@ describe('MapLayer dirty state and doors', () => {
|
||||
it('closes a door on empty cells and warns 46 on occupied cells', async () => {
|
||||
const { layer } = createFixture([0, 0, 0, 0]);
|
||||
const closed: [number, number, number][] = [];
|
||||
layer
|
||||
.addHook({
|
||||
onCloseDoor: async (num, x, y) => {
|
||||
closed.push([num, x, y]);
|
||||
}
|
||||
})
|
||||
.load();
|
||||
layer.addHook({
|
||||
onCloseDoor: async (num, x, y) => {
|
||||
closed.push([num, x, y]);
|
||||
}
|
||||
});
|
||||
|
||||
await layer.closeDoor(3, 0, 0);
|
||||
expect(closed).toEqual([[3, 0, 0]]);
|
||||
|
||||
@ -29,7 +29,6 @@ interface TestModules {
|
||||
RoleFaceBinder: typeof import('@user/data-common').RoleFaceBinder;
|
||||
FaceManager: typeof import('@user/data-common').FaceManager;
|
||||
Dir8FaceHandler: typeof import('@user/data-common').Dir8FaceHandler;
|
||||
DirectionMapper: typeof import('@motajs/common').DirectionMapper;
|
||||
}
|
||||
|
||||
let modules: TestModules;
|
||||
@ -39,14 +38,12 @@ beforeAll(async () => {
|
||||
vi.stubGlobal('location', { origin: 'http://localhost' });
|
||||
const mapModule = await import('../mapState');
|
||||
const commonModule = await import('@user/data-common');
|
||||
const motaModule = await import('@motajs/common');
|
||||
modules = {
|
||||
MapState: mapModule.MapState,
|
||||
TileStore: commonModule.TileStore,
|
||||
RoleFaceBinder: commonModule.RoleFaceBinder,
|
||||
FaceManager: commonModule.FaceManager,
|
||||
Dir8FaceHandler: commonModule.Dir8FaceHandler,
|
||||
DirectionMapper: motaModule.DirectionMapper
|
||||
Dir8FaceHandler: commonModule.Dir8FaceHandler
|
||||
};
|
||||
});
|
||||
|
||||
@ -80,10 +77,9 @@ function createMapState(
|
||||
eventStore: {},
|
||||
roleFace: new modules.RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new modules.DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new modules.MapState(tileStore, state);
|
||||
const mapState = new modules.MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -93,7 +89,9 @@ function createMapState(
|
||||
});
|
||||
return {
|
||||
map: map!,
|
||||
layer: map!.getLayerByAlias('event')! as IResizableMapLayer
|
||||
layer: [...map!.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer
|
||||
};
|
||||
}
|
||||
|
||||
@ -156,6 +154,8 @@ describe('MapLayer tile defaults snapshots conversion and movement', () => {
|
||||
);
|
||||
expect(restoredStatic.tileEvent().dirty()).toBe(false);
|
||||
|
||||
// 先物化 (1, 0) 的静态图块,使转换时能保留其默认事件(与 StaticTile.toDynamic 用法一致)
|
||||
layer.getTile(1, 0);
|
||||
const keptDynamic = layer.transferToDynamic(1, 0)!;
|
||||
keptDynamic.tileEvent().set(30, 'kept-event');
|
||||
const keptStatic = layer.transferToStatic(keptDynamic, true)!;
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { MapState } from '../mapState';
|
||||
|
||||
vi.hoisted(() => {
|
||||
@ -81,10 +81,9 @@ function createFixture(): MapState {
|
||||
eventStore: {},
|
||||
roleFace: new RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
return new MapState(tileStore, state);
|
||||
return new MapState(state);
|
||||
}
|
||||
|
||||
/** 构造一份合法的楼层原始数据 */
|
||||
@ -118,15 +117,16 @@ describe('MapState floor registration', () => {
|
||||
it('generates and reads content on a map created by createMap', () => {
|
||||
const mapState = createFixture();
|
||||
const map = mapState.createMap('F1', 2, 2);
|
||||
const layer = map.addLayer();
|
||||
map.setLayerAlias(layer, 'event');
|
||||
const layer = map.addLayer('event');
|
||||
map.setEventLayer(layer);
|
||||
layer.setBlock(5, 0, 0);
|
||||
layer.setZIndex(0);
|
||||
layer.event(1, 0)!.set(9, 'gen-event');
|
||||
|
||||
expect(mapState.getMap('F1')).toBe(map);
|
||||
expect(map.getLayerByAlias('event')).toBe(layer);
|
||||
expect([...map.layerList].find(item => item.alias === 'event')).toBe(
|
||||
layer
|
||||
);
|
||||
expect(map.eventLayer).toBe(layer);
|
||||
expect(layer.getBlock(0, 0)).toBe(5);
|
||||
expect(layer.getBlock(1, 0)).toBe(0);
|
||||
@ -134,13 +134,13 @@ describe('MapState floor registration', () => {
|
||||
expect(map.dirty()).toBe(true);
|
||||
});
|
||||
|
||||
// 验证 setMapList 去重并保持传入顺序
|
||||
it('stores a de-duplicated ordered floor list', () => {
|
||||
// 验证 setMapList 按传入顺序原样存储楼层 id(不去重,重复由调用方保证)
|
||||
it('stores the ordered floor list as given', () => {
|
||||
const mapState = createFixture();
|
||||
|
||||
mapState.setMapList(['A', 'B', 'A', 'C']);
|
||||
|
||||
expect(mapState.maps).toEqual(['A', 'B', 'C']);
|
||||
expect(mapState.maps).toEqual(['A', 'B', 'A', 'C']);
|
||||
});
|
||||
|
||||
// 验证 fromRaw 构建楼层、绑定事件层与点事件,并默认处于未激活
|
||||
@ -150,7 +150,7 @@ describe('MapState floor registration', () => {
|
||||
const map = mapState.fromRaw(createRaw());
|
||||
|
||||
expect(map).not.toBeNull();
|
||||
const layer = map!.getLayerByAlias('event');
|
||||
const layer = [...map!.layerList].find(item => item.alias === 'event');
|
||||
expect(layer).not.toBeNull();
|
||||
expect(map!.eventLayer).toBe(layer);
|
||||
expect(layer!.getPointEvent(1, 0)).toEqual(
|
||||
@ -170,6 +170,7 @@ describe('MapState raw validation codes', () => {
|
||||
code: number;
|
||||
}
|
||||
|
||||
// 仅保留 shipped fromRaw 仍会发出的 60/61;62/63/64 已无发出点(Q1=A)
|
||||
const cases: MalformedCase[] = [
|
||||
{
|
||||
name: 'unequal layer lengths',
|
||||
@ -191,61 +192,6 @@ describe('MapState raw validation codes', () => {
|
||||
Reflect.set(raw, 'events', { 0: {} });
|
||||
},
|
||||
code: 61
|
||||
},
|
||||
{
|
||||
name: 'non-numeric map layer key',
|
||||
mutate: raw => Reflect.set(raw.map, 'bad', [1, 2, 1, 2]),
|
||||
code: 62
|
||||
},
|
||||
{
|
||||
name: 'non-numeric event priority',
|
||||
mutate: raw => Reflect.set(raw.events[0][1], 'bad', 'event'),
|
||||
code: 62
|
||||
},
|
||||
{
|
||||
name: 'missing map container',
|
||||
mutate: raw => Reflect.set(raw, 'map', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'missing events container',
|
||||
mutate: raw => Reflect.set(raw, 'events', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'missing alias container',
|
||||
mutate: raw => Reflect.set(raw, 'layerAlias', null),
|
||||
code: 63
|
||||
},
|
||||
{
|
||||
name: 'invalid width',
|
||||
mutate: raw => Reflect.set(raw, 'width', 0),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'non-integer map value',
|
||||
mutate: raw => Reflect.set(raw.map, '0', [1, 1, 1.5, 1]),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'non-string layer alias',
|
||||
mutate: raw => Reflect.set(raw.layerAlias, '0', 3),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'out of range event position',
|
||||
mutate: raw => Reflect.set(raw.events[0], '4', { 5: 'event' }),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'non-string event id',
|
||||
mutate: raw => Reflect.set(raw.events[0][1], '5', 3),
|
||||
code: 64
|
||||
},
|
||||
{
|
||||
name: 'event layer without map layer',
|
||||
mutate: raw => Reflect.set(raw.events, '9', {}),
|
||||
code: 64
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { type IResizableMapLayer } from '../types';
|
||||
import { MapState } from '../mapState';
|
||||
import { DynamicTile } from '../dynamicTile';
|
||||
@ -86,10 +86,9 @@ function createMoverFixture(): MoverFixture {
|
||||
eventStore: {},
|
||||
roleFace: new RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 3,
|
||||
@ -97,7 +96,9 @@ function createMoverFixture(): MoverFixture {
|
||||
layerAlias: { 0: 'event' },
|
||||
events: { 0: {} }
|
||||
})!;
|
||||
const layer = map.getLayerByAlias('event')! as IResizableMapLayer;
|
||||
const layer = [...map.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer;
|
||||
const tile = layer.createDynamic(1, 0, 0) as DynamicTile;
|
||||
return { layer, tile };
|
||||
}
|
||||
@ -162,22 +163,20 @@ describe('DynamicTileMover movement', () => {
|
||||
it('fires the movement lifecycle hooks in order', async () => {
|
||||
const { tile } = createMoverFixture();
|
||||
const calls: string[] = [];
|
||||
tile.mover
|
||||
.addHook({
|
||||
onMoveStart: async () => {
|
||||
calls.push('moveStart');
|
||||
},
|
||||
onStepStart: async () => {
|
||||
calls.push('stepStart');
|
||||
},
|
||||
onStepEnd: async () => {
|
||||
calls.push('stepEnd');
|
||||
},
|
||||
onMoveEnd: async () => {
|
||||
calls.push('moveEnd');
|
||||
}
|
||||
})
|
||||
.load();
|
||||
tile.mover.addHook({
|
||||
onMoveStart: async () => {
|
||||
calls.push('moveStart');
|
||||
},
|
||||
onStepStart: async () => {
|
||||
calls.push('stepStart');
|
||||
},
|
||||
onStepEnd: async () => {
|
||||
calls.push('stepEnd');
|
||||
},
|
||||
onMoveEnd: async () => {
|
||||
calls.push('moveEnd');
|
||||
}
|
||||
});
|
||||
|
||||
tile.mover.step(FaceDirection.Down);
|
||||
await tile.mover.start()!.onEnd;
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper, logger } from '@motajs/common';
|
||||
import { logger } from '@motajs/common';
|
||||
import { type IGameMap, type IResizableMapLayer } from '../types';
|
||||
import { MapState } from '../mapState';
|
||||
|
||||
@ -88,10 +88,9 @@ function createMapFixture(
|
||||
eventStore: {},
|
||||
roleFace: new RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -102,7 +101,9 @@ function createMapFixture(
|
||||
return {
|
||||
mapState,
|
||||
map: map!,
|
||||
layer: map!.getLayerByAlias('event')! as IResizableMapLayer
|
||||
layer: [...map!.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper } from '@motajs/common';
|
||||
import { type IResizableMapLayer } from '../types';
|
||||
import { MapState } from '../mapState';
|
||||
import { StaticTile } from '../staticTile';
|
||||
@ -82,10 +81,9 @@ function createFixture(blocks: number[] = [1, 2, 1, 2]): IResizableMapLayer {
|
||||
eventStore: {},
|
||||
roleFace: new RoleFaceBinder(),
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -93,7 +91,9 @@ function createFixture(blocks: number[] = [1, 2, 1, 2]): IResizableMapLayer {
|
||||
layerAlias: { 0: 'event' },
|
||||
events: { 0: {} }
|
||||
})!;
|
||||
return map.getLayerByAlias('event')! as IResizableMapLayer;
|
||||
return [...map.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer;
|
||||
}
|
||||
|
||||
describe('StaticTile model', () => {
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
TileStore,
|
||||
TileType
|
||||
} from '@user/data-common';
|
||||
import { DirectionMapper } from '@motajs/common';
|
||||
import { type IResizableMapLayer } from '../types';
|
||||
import { MapState } from '../mapState';
|
||||
import { StaticTile } from '../staticTile';
|
||||
@ -90,10 +89,9 @@ function createFixture(blocks: number[] = [2, 1, 1, 2]): TileFixture {
|
||||
eventStore: {},
|
||||
roleFace,
|
||||
faceManager,
|
||||
directionMapper: new DirectionMapper(),
|
||||
saveSystem: {}
|
||||
} as never;
|
||||
const mapState = new MapState(tileStore, state);
|
||||
const mapState = new MapState(state);
|
||||
const map = mapState.fromRaw({
|
||||
floorId: 'F1',
|
||||
width: 2,
|
||||
@ -102,7 +100,9 @@ function createFixture(blocks: number[] = [2, 1, 1, 2]): TileFixture {
|
||||
events: { 0: {} }
|
||||
})!;
|
||||
return {
|
||||
layer: map.getLayerByAlias('event')! as IResizableMapLayer,
|
||||
layer: [...map.layerList].find(
|
||||
layer => layer.alias === 'event'
|
||||
)! as IResizableMapLayer,
|
||||
roleFace
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user