160 lines
5.0 KiB
TypeScript
160 lines
5.0 KiB
TypeScript
import { discoverMapEntries, resolveProjectMap, visualMapAssets } from './MapLoader';
|
|
import type { ProjectFile, ProjectManifest } from '../project/types';
|
|
|
|
const encode = (value: string) => new TextEncoder().encode(value);
|
|
const file = (path: string, value = ''): ProjectFile => ({
|
|
path,
|
|
data: encode(value),
|
|
size: encode(value).byteLength,
|
|
source: 'directory',
|
|
mimeType: '',
|
|
});
|
|
|
|
const descriptor = JSON.stringify({
|
|
schemaVersion: 1,
|
|
id: 'warehouse',
|
|
name: '仓库',
|
|
coordinateSystem: { units: 'm', up: 'Z', forward: '+X' },
|
|
physics: { source: 'physics/world.xml' },
|
|
visual: { source: 'visuals/scene.glb' },
|
|
spawnPoints: [{ id: 'door', name: '入口', position: [1, 2, 0.3], yawDeg: 90 }],
|
|
});
|
|
|
|
describe('MapLoader', () => {
|
|
it('发现 map.json 并解析工程内资源', () => {
|
|
const files = [
|
|
file('maps/warehouse/map.json', descriptor),
|
|
file('maps/warehouse/physics/world.xml', '<mujoco><worldbody/></mujoco>'),
|
|
file('maps/warehouse/visuals/scene.glb'),
|
|
];
|
|
expect(discoverMapEntries(files)).toEqual([
|
|
{
|
|
descriptorPath: 'maps/warehouse/map.json',
|
|
schemaVersion: 1,
|
|
id: 'warehouse',
|
|
name: '仓库',
|
|
physicsPath: 'maps/warehouse/physics/world.xml',
|
|
visualPath: 'maps/warehouse/visuals/scene.glb',
|
|
spawnPoints: [{ id: 'door', name: '入口' }],
|
|
},
|
|
]);
|
|
const manifest: ProjectManifest = {
|
|
id: 'test',
|
|
name: 'test',
|
|
files,
|
|
entries: [],
|
|
maps: discoverMapEntries(files),
|
|
totalBytes: files.reduce((sum, item) => sum + item.size, 0),
|
|
};
|
|
expect(resolveProjectMap(manifest, 'maps/warehouse/map.json')).toMatchObject({
|
|
physicsPath: 'maps/warehouse/physics/world.xml',
|
|
visualPath: 'maps/warehouse/visuals/scene.glb',
|
|
});
|
|
});
|
|
|
|
it('只为已放置实例解析视觉地图,并保留实例 id', () => {
|
|
const files = [
|
|
file('maps/warehouse/map.json', descriptor),
|
|
file('maps/warehouse/physics/world.xml', '<mujoco><worldbody/></mujoco>'),
|
|
file('maps/warehouse/visuals/scene.glb', 'glb'),
|
|
];
|
|
const projectManifest: ProjectManifest = {
|
|
id: 'test',
|
|
name: 'test',
|
|
files,
|
|
entries: [],
|
|
maps: discoverMapEntries(files),
|
|
totalBytes: files.reduce((sum, item) => sum + item.size, 0),
|
|
};
|
|
expect(visualMapAssets(projectManifest, [])).toEqual([]);
|
|
expect(
|
|
visualMapAssets(projectManifest, [
|
|
{
|
|
id: 'instance-a',
|
|
name: '仓库副本',
|
|
selection: {
|
|
kind: 'project',
|
|
descriptorPath: 'maps/warehouse/map.json',
|
|
positionX: 2,
|
|
positionY: -1,
|
|
yawDeg: 30,
|
|
},
|
|
},
|
|
]),
|
|
).toEqual([
|
|
expect.objectContaining({
|
|
id: 'instance-a',
|
|
name: '仓库副本',
|
|
path: 'maps/warehouse/visuals/scene.glb',
|
|
positionX: 2,
|
|
positionY: -1,
|
|
yawDeg: 30,
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it('发现 schema V2 authoring 创作层', () => {
|
|
const editable = JSON.stringify({
|
|
...JSON.parse(descriptor),
|
|
schemaVersion: 2,
|
|
authoring: { source: 'authoring/map.scene.json' },
|
|
});
|
|
const files = [
|
|
file('maps/warehouse/map.json', editable),
|
|
file('maps/warehouse/physics/world.xml'),
|
|
file('maps/warehouse/visuals/scene.glb'),
|
|
file(
|
|
'maps/warehouse/authoring/map.scene.json',
|
|
JSON.stringify({
|
|
schemaVersion: 1,
|
|
mapId: 'warehouse',
|
|
revision: 0,
|
|
objects: [],
|
|
spawnPoints: [],
|
|
}),
|
|
),
|
|
];
|
|
expect(discoverMapEntries(files)[0]).toMatchObject({
|
|
schemaVersion: 2,
|
|
authoringPath: 'maps/warehouse/authoring/map.scene.json',
|
|
});
|
|
});
|
|
|
|
it('拒绝没有物理产物的 authoring 地图', () => {
|
|
const invalid = JSON.stringify({
|
|
schemaVersion: 2,
|
|
id: 'editable',
|
|
name: 'editable',
|
|
coordinateSystem: { units: 'm', up: 'Z', forward: '+X' },
|
|
visual: { source: 'scene.glb' },
|
|
authoring: { source: 'map.scene.json' },
|
|
spawnPoints: [],
|
|
});
|
|
expect(() => discoverMapEntries([file('map.json', invalid)])).toThrow('physics.source');
|
|
});
|
|
|
|
it('拒绝坐标约定错误、缺失资源和重复地图 id', () => {
|
|
const wrongCoordinates = descriptor.replace('"Z"', '"Y"');
|
|
expect(() => discoverMapEntries([file('map.json', wrongCoordinates)])).toThrow(
|
|
'coordinateSystem',
|
|
);
|
|
expect(() => discoverMapEntries([file('map.json', descriptor)])).toThrow('资源不存在');
|
|
const physics = file('physics/world.xml');
|
|
const visual = file('visuals/scene.glb');
|
|
expect(() =>
|
|
discoverMapEntries([
|
|
file(
|
|
'a/map.json',
|
|
descriptor.replaceAll('physics/', '../physics/').replaceAll('visuals/', '../visuals/'),
|
|
),
|
|
file(
|
|
'b/map.json',
|
|
descriptor.replaceAll('physics/', '../physics/').replaceAll('visuals/', '../visuals/'),
|
|
),
|
|
physics,
|
|
visual,
|
|
]),
|
|
).toThrow('地图 id 重复');
|
|
});
|
|
});
|