Files
Mujoco_WASM/web_platform/src/map/MapLoader.test.ts
T
chenlin a9b07e0abf
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
feat(web-platform): release V0.8.1 常规地图优化
2026-09-03 13:18:35 +08:00

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 重复');
});
});