26bb5634bd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
103 lines
4.1 KiB
TypeScript
103 lines
4.1 KiB
TypeScript
import { act, renderHook } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { downloadBytes, readCachedText } from '../../project/cachedFiles';
|
|
import type { ProjectManifest } from '../../project/types';
|
|
import type { PhysicsAdapter } from '../../simulation/PhysicsAdapter';
|
|
import { useProjectSource } from './useProjectSource';
|
|
|
|
vi.mock('../../project/cachedFiles', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('../../project/cachedFiles')>()),
|
|
downloadBytes: vi.fn(),
|
|
}));
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
function fixture() {
|
|
const text = '<robot name="test"><link name="base"/></robot>',
|
|
data = new TextEncoder().encode(text);
|
|
const manifest = {
|
|
current: {
|
|
id: 'p',
|
|
name: '测试 工程.zip',
|
|
files: [{ path: 'robot.urdf', data, size: data.length, source: 'zip', mimeType: '' }],
|
|
entries: [{ path: 'robot.urdf', format: 'urdf', label: 'robot' }],
|
|
maps: [],
|
|
totalBytes: data.length,
|
|
} as ProjectManifest | null,
|
|
};
|
|
const exported = new TextEncoder().encode('<mujoco><worldbody/></mujoco>');
|
|
const backend = {
|
|
describeRobot: () => ({ profileId: 'lekiwi-v1' }),
|
|
exportMjcf: vi.fn(() => exported),
|
|
cachedSupportFiles: () => [],
|
|
};
|
|
const events: string[] = [];
|
|
const options: Parameters<typeof useProjectSource>[0] = {
|
|
manifest,
|
|
adapter: { current: backend as unknown as PhysicsAdapter },
|
|
robotProfiles: { current: new Map() },
|
|
selectedEntry: 'robot.urdf',
|
|
selectedFormat: 'urdf',
|
|
onProjectUpdated: vi.fn(() => {
|
|
events.push('project');
|
|
}),
|
|
onDiagnostic: vi.fn(),
|
|
notify: vi.fn(() => {
|
|
events.push('notice');
|
|
}),
|
|
loadEntry: vi.fn(async () => {
|
|
events.push('load');
|
|
return false;
|
|
}),
|
|
};
|
|
return { options, backend, events, text, exported };
|
|
}
|
|
|
|
describe('工程源码与导出动作', () => {
|
|
it('先保留 profile/写缓存/发布清单和通知,再调用加载;编译失败仍保留可修改源码', async () => {
|
|
const { options, events } = fixture();
|
|
const { result } = renderHook(() => useProjectSource(options));
|
|
await act(async () => {
|
|
await result.current.saveCachedSource('cache.xml', '<mujoco model="edited"/>');
|
|
});
|
|
expect(events).toEqual(['project', 'notice', 'load']);
|
|
expect(options.robotProfiles.current.get('cache.xml')).toBe('lekiwi-v1');
|
|
expect(readCachedText(options.manifest.current!, 'cache.xml')).toBe('<mujoco model="edited"/>');
|
|
expect(options.loadEntry).toHaveBeenCalledWith('cache.xml');
|
|
});
|
|
|
|
it('URDF 导出读取当前缓存,切换格式后入口失效;MJCF 保留 XML 资源提示', () => {
|
|
const { options, text, exported } = fixture();
|
|
const { result, rerender } = renderHook((opts) => useProjectSource(opts), {
|
|
initialProps: options,
|
|
});
|
|
result.current.exportUrdf();
|
|
expect(downloadBytes).toHaveBeenCalledWith(new TextEncoder().encode(text), '测试_工程.urdf');
|
|
vi.mocked(downloadBytes).mockClear();
|
|
rerender({ ...options, selectedFormat: 'mjcf' });
|
|
result.current.exportUrdf();
|
|
expect(downloadBytes).not.toHaveBeenCalled();
|
|
result.current.exportMjcf();
|
|
expect(downloadBytes).toHaveBeenCalledWith(exported, '测试_工程.xml');
|
|
expect(options.notify).toHaveBeenCalledWith('MJCF 已导出', expect.stringContaining('仅 XML'));
|
|
});
|
|
|
|
it('MJCF/ZIP 导出异常保留不同诊断类别与入口路径', () => {
|
|
const { options, backend } = fixture();
|
|
backend.exportMjcf.mockImplementation(() => {
|
|
throw new Error('导出失败');
|
|
});
|
|
const { result } = renderHook(() => useProjectSource(options));
|
|
result.current.exportMjcf();
|
|
result.current.exportProjectPackage();
|
|
expect(options.onDiagnostic).toHaveBeenNthCalledWith(
|
|
1,
|
|
expect.objectContaining({ category: '模型编译', path: 'robot.urdf', detail: '导出失败' }),
|
|
);
|
|
expect(options.onDiagnostic).toHaveBeenNthCalledWith(
|
|
2,
|
|
expect.objectContaining({ category: '文件系统', path: 'robot.urdf', detail: '导出失败' }),
|
|
);
|
|
expect(downloadBytes).not.toHaveBeenCalled();
|
|
});
|
|
});
|