1
This commit is contained in:
117
src/services/puzzleReferenceImage.test.ts
Normal file
117
src/services/puzzleReferenceImage.test.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
PUZZLE_REFERENCE_IMAGE_MAX_DATA_URL_LENGTH,
|
||||
readPuzzleReferenceImageAsDataUrl,
|
||||
} from './puzzleReferenceImage';
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function stubFileReader(dataUrl: string) {
|
||||
class MockFileReader {
|
||||
result: string | null = null;
|
||||
error: Error | null = null;
|
||||
onload: null | (() => void) = null;
|
||||
onerror: null | (() => void) = null;
|
||||
|
||||
readAsDataURL() {
|
||||
this.result = dataUrl;
|
||||
this.onload?.();
|
||||
}
|
||||
}
|
||||
|
||||
vi.stubGlobal('FileReader', MockFileReader as unknown as typeof FileReader);
|
||||
}
|
||||
|
||||
function stubImage(width = 4096, height = 3072) {
|
||||
class MockImage {
|
||||
onload: null | (() => void) = null;
|
||||
onerror: null | (() => void) = null;
|
||||
naturalWidth = width;
|
||||
naturalHeight = height;
|
||||
width = width;
|
||||
height = height;
|
||||
|
||||
set src(_value: string) {
|
||||
this.onload?.();
|
||||
}
|
||||
}
|
||||
|
||||
vi.stubGlobal('Image', MockImage as unknown as typeof Image);
|
||||
}
|
||||
|
||||
function stubCanvas(dataUrls: string[]) {
|
||||
const drawImage = vi.fn();
|
||||
const toDataURL = vi
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
() => dataUrls.shift() ?? 'data:image/jpeg;base64,small',
|
||||
);
|
||||
const originalCreateElement = document.createElement.bind(document);
|
||||
vi.spyOn(document, 'createElement').mockImplementation((tagName) => {
|
||||
if (tagName !== 'canvas') {
|
||||
return originalCreateElement(tagName);
|
||||
}
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
getContext: () => ({
|
||||
drawImage,
|
||||
fillRect: vi.fn(),
|
||||
fillStyle: '',
|
||||
imageSmoothingEnabled: false,
|
||||
imageSmoothingQuality: 'low',
|
||||
}),
|
||||
toDataURL,
|
||||
} as unknown as HTMLCanvasElement;
|
||||
});
|
||||
return { drawImage, toDataURL };
|
||||
}
|
||||
|
||||
describe('readPuzzleReferenceImageAsDataUrl', () => {
|
||||
test('compresses large puzzle reference images before JSON upload', async () => {
|
||||
stubFileReader(`data:image/png;base64,${'A'.repeat(3 * 1024 * 1024)}`);
|
||||
stubImage();
|
||||
const { drawImage, toDataURL } = stubCanvas([
|
||||
`data:image/jpeg;base64,${'B'.repeat(1200)}`,
|
||||
`data:image/jpeg;base64,${'C'.repeat(1000)}`,
|
||||
`data:image/jpeg;base64,${'D'.repeat(1400)}`,
|
||||
]);
|
||||
|
||||
const file = new File(['x'.repeat(2 * 1024 * 1024)], 'reference.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
const dataUrl = await readPuzzleReferenceImageAsDataUrl(file);
|
||||
|
||||
expect(dataUrl).toBe(`data:image/jpeg;base64,${'C'.repeat(1000)}`);
|
||||
expect(drawImage).toHaveBeenCalledWith(expect.anything(), 0, 0, 1536, 1152);
|
||||
expect(toDataURL).toHaveBeenCalledWith('image/jpeg', 0.84);
|
||||
expect(toDataURL).toHaveBeenCalledWith('image/jpeg', 0.76);
|
||||
expect(toDataURL).toHaveBeenCalledWith('image/jpeg', 0.68);
|
||||
});
|
||||
|
||||
test('rejects reference images that still exceed the upload budget', async () => {
|
||||
stubFileReader(
|
||||
`data:image/png;base64,${'A'.repeat(PUZZLE_REFERENCE_IMAGE_MAX_DATA_URL_LENGTH + 1)}`,
|
||||
);
|
||||
stubImage();
|
||||
stubCanvas([
|
||||
`data:image/jpeg;base64,${'B'.repeat(PUZZLE_REFERENCE_IMAGE_MAX_DATA_URL_LENGTH + 1)}`,
|
||||
`data:image/jpeg;base64,${'C'.repeat(PUZZLE_REFERENCE_IMAGE_MAX_DATA_URL_LENGTH + 2)}`,
|
||||
`data:image/jpeg;base64,${'D'.repeat(PUZZLE_REFERENCE_IMAGE_MAX_DATA_URL_LENGTH + 3)}`,
|
||||
]);
|
||||
|
||||
const file = new File(['x'.repeat(2 * 1024 * 1024)], 'reference.png', {
|
||||
type: 'image/png',
|
||||
});
|
||||
|
||||
await expect(readPuzzleReferenceImageAsDataUrl(file)).rejects.toThrow(
|
||||
'参考图过大,请换一张尺寸更小的图片。',
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user