完善客户端模块与工作区能力
This commit is contained in:
86
tests/unit/agent-avatar-upload.test.ts
Normal file
86
tests/unit/agent-avatar-upload.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
AGENT_AVATAR_MAX_DIMENSION,
|
||||
AGENT_AVATAR_WEBP_QUALITY,
|
||||
prepareAgentAvatar,
|
||||
} from '@/lib/agent-avatar-upload';
|
||||
|
||||
const decodeMock = vi.hoisted(() => vi.fn());
|
||||
const inspectFrameCountMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/lib/browser-image-codec', () => ({
|
||||
browserImageCodec: {
|
||||
inspectFrameCount: (...args: unknown[]) => inspectFrameCountMock(...args),
|
||||
decode: (...args: unknown[]) => decodeMock(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('agent avatar preparation', () => {
|
||||
beforeEach(() => {
|
||||
decodeMock.mockReset();
|
||||
inspectFrameCountMock.mockReset();
|
||||
inspectFrameCountMock.mockResolvedValue(1);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('center-crops and compresses a large image to a small square avatar', async () => {
|
||||
const dispose = vi.fn();
|
||||
const source = {} as CanvasImageSource;
|
||||
const drawImage = vi.fn();
|
||||
const toBlob = vi.fn((callback: BlobCallback) => callback(new Blob(['avatar'], { type: 'image/webp' })));
|
||||
const context = {
|
||||
imageSmoothingEnabled: false,
|
||||
imageSmoothingQuality: 'low' as ImageSmoothingQuality,
|
||||
clearRect: vi.fn(),
|
||||
drawImage,
|
||||
} as unknown as CanvasRenderingContext2D;
|
||||
const canvas = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
getContext: vi.fn(() => context),
|
||||
toBlob,
|
||||
} as unknown as HTMLCanvasElement;
|
||||
vi.spyOn(document, 'createElement').mockReturnValue(canvas as unknown as HTMLElement);
|
||||
decodeMock.mockResolvedValue({ source, width: 1000, height: 500, dispose });
|
||||
|
||||
const result = await prepareAgentAvatar(new File(['source'], 'portrait.png', { type: 'image/png' }));
|
||||
|
||||
expect(drawImage).toHaveBeenCalledWith(
|
||||
source,
|
||||
250,
|
||||
0,
|
||||
500,
|
||||
500,
|
||||
0,
|
||||
0,
|
||||
AGENT_AVATAR_MAX_DIMENSION,
|
||||
AGENT_AVATAR_MAX_DIMENSION,
|
||||
);
|
||||
expect(toBlob.mock.calls[0]?.slice(1)).toEqual(['image/webp', AGENT_AVATAR_WEBP_QUALITY]);
|
||||
expect(result).toMatchObject({
|
||||
fileName: 'avatar.webp',
|
||||
mimeType: 'image/webp',
|
||||
width: AGENT_AVATAR_MAX_DIMENSION,
|
||||
height: AGENT_AVATAR_MAX_DIMENSION,
|
||||
previewUrl: expect.stringMatching(/^data:image\/webp;base64,/),
|
||||
});
|
||||
expect(dispose).toHaveBeenCalledOnce();
|
||||
expect(canvas.width).toBe(1);
|
||||
expect(canvas.height).toBe(1);
|
||||
});
|
||||
|
||||
it('rejects unsupported and animated images before decoding', async () => {
|
||||
await expect(prepareAgentAvatar(new File(['text'], 'avatar.gif', { type: 'image/gif' })))
|
||||
.rejects.toThrow('伙伴头像仅支持 PNG、JPEG 或 WebP 图片');
|
||||
expect(inspectFrameCountMock).not.toHaveBeenCalled();
|
||||
expect(decodeMock).not.toHaveBeenCalled();
|
||||
|
||||
inspectFrameCountMock.mockResolvedValue(2);
|
||||
await expect(prepareAgentAvatar(new File(['animated'], 'avatar.webp', { type: 'image/webp' })))
|
||||
.rejects.toThrow('伙伴头像不支持动态图片');
|
||||
expect(decodeMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user