mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-13 03:43:50 +02:00
179 lines
6.0 KiB
JavaScript
179 lines
6.0 KiB
JavaScript
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
// Mock transitive deps to avoid i18n init errors
|
|
vi.mock('vue-sonner', () => ({
|
|
toast: { error: vi.fn() }
|
|
}));
|
|
|
|
vi.mock('../../../services/request', () => ({
|
|
$throw: vi.fn()
|
|
}));
|
|
|
|
vi.mock('../../../services/appConfig', () => ({
|
|
AppDebug: { endpointDomain: 'https://api.vrchat.cloud/api/1' }
|
|
}));
|
|
|
|
vi.mock('../../../shared/utils', () => ({
|
|
extractFileId: vi.fn()
|
|
}));
|
|
|
|
vi.mock('../../../api', () => ({
|
|
imageRequest: {}
|
|
}));
|
|
|
|
import { toast } from 'vue-sonner';
|
|
import { withUploadTimeout } from '../imageUpload';
|
|
import { handleImageUploadInput } from '../../../coordinators/imageUploadCoordinator';
|
|
|
|
// ─── withUploadTimeout ───────────────────────────────────────────────
|
|
|
|
describe('withUploadTimeout', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
test('resolves if promise resolves before timeout', async () => {
|
|
const promise = withUploadTimeout(Promise.resolve('done'));
|
|
await expect(promise).resolves.toBe('done');
|
|
});
|
|
|
|
test('rejects with timeout error if promise is too slow', async () => {
|
|
const neverResolves = new Promise(() => {});
|
|
const result = withUploadTimeout(neverResolves);
|
|
|
|
vi.advanceTimersByTime(30_000);
|
|
|
|
await expect(result).rejects.toThrow('Upload timed out');
|
|
});
|
|
|
|
test('resolves if promise finishes just before timeout', async () => {
|
|
const slowPromise = new Promise((resolve) => {
|
|
setTimeout(() => resolve('just in time'), 29_999);
|
|
});
|
|
const result = withUploadTimeout(slowPromise);
|
|
|
|
vi.advanceTimersByTime(29_999);
|
|
|
|
await expect(result).resolves.toBe('just in time');
|
|
});
|
|
|
|
test('rejects if underlying promise rejects', async () => {
|
|
const failingPromise = Promise.reject(new Error('upload failed'));
|
|
await expect(withUploadTimeout(failingPromise)).rejects.toThrow(
|
|
'upload failed'
|
|
);
|
|
});
|
|
});
|
|
|
|
// ─── handleImageUploadInput ──────────────────────────────────────────
|
|
|
|
describe('handleImageUploadInput', () => {
|
|
const makeFile = (size = 1000, type = 'image/png') => ({
|
|
size,
|
|
type
|
|
});
|
|
const makeEvent = (file) => ({
|
|
target: { files: file ? [file] : [] }
|
|
});
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
test('returns null file when no files in event', () => {
|
|
const { file } = handleImageUploadInput({ target: { files: [] } });
|
|
expect(file).toBeNull();
|
|
});
|
|
|
|
test('returns null file when event has no target', () => {
|
|
const { file } = handleImageUploadInput({});
|
|
expect(file).toBeNull();
|
|
});
|
|
|
|
test('returns file for valid image within size limit', () => {
|
|
const mockFile = makeFile(5000, 'image/png');
|
|
const { file } = handleImageUploadInput(makeEvent(mockFile));
|
|
expect(file).toBe(mockFile);
|
|
});
|
|
|
|
test('returns null file when file exceeds maxSize', () => {
|
|
const mockFile = makeFile(20_000_001, 'image/png');
|
|
const { file } = handleImageUploadInput(makeEvent(mockFile));
|
|
expect(file).toBeNull();
|
|
});
|
|
|
|
test('shows toast error when file exceeds maxSize and tooLargeMessage provided', () => {
|
|
const mockFile = makeFile(20_000_001, 'image/png');
|
|
handleImageUploadInput(makeEvent(mockFile), {
|
|
tooLargeMessage: 'File too large!'
|
|
});
|
|
expect(toast.error).toHaveBeenCalledWith('File too large!');
|
|
});
|
|
|
|
test('supports function as tooLargeMessage', () => {
|
|
const mockFile = makeFile(20_000_001, 'image/png');
|
|
handleImageUploadInput(makeEvent(mockFile), {
|
|
tooLargeMessage: () => 'Dynamic error'
|
|
});
|
|
expect(toast.error).toHaveBeenCalledWith('Dynamic error');
|
|
});
|
|
|
|
test('returns null file when file type does not match acceptPattern', () => {
|
|
const mockFile = makeFile(1000, 'text/plain');
|
|
const { file } = handleImageUploadInput(makeEvent(mockFile));
|
|
expect(file).toBeNull();
|
|
});
|
|
|
|
test('shows toast error for invalid type when invalidTypeMessage provided', () => {
|
|
const mockFile = makeFile(1000, 'text/plain');
|
|
handleImageUploadInput(makeEvent(mockFile), {
|
|
invalidTypeMessage: 'Wrong type!'
|
|
});
|
|
expect(toast.error).toHaveBeenCalledWith('Wrong type!');
|
|
});
|
|
|
|
test('respects custom maxSize', () => {
|
|
const mockFile = makeFile(600, 'image/png');
|
|
const { file } = handleImageUploadInput(makeEvent(mockFile), {
|
|
maxSize: 500
|
|
});
|
|
expect(file).toBeNull();
|
|
});
|
|
|
|
test('respects custom acceptPattern as string', () => {
|
|
const mockFile = makeFile(1000, 'video/mp4');
|
|
const { file } = handleImageUploadInput(makeEvent(mockFile), {
|
|
acceptPattern: 'video.*'
|
|
});
|
|
expect(file).toBe(mockFile);
|
|
});
|
|
|
|
test('returns clearInput function', () => {
|
|
const mockFile = makeFile(1000, 'image/png');
|
|
const { clearInput } = handleImageUploadInput(makeEvent(mockFile));
|
|
expect(typeof clearInput).toBe('function');
|
|
});
|
|
|
|
test('calls onClear callback when clearing', () => {
|
|
const onClear = vi.fn();
|
|
const { clearInput } = handleImageUploadInput(
|
|
{ target: { files: [] } },
|
|
{ onClear }
|
|
);
|
|
// clearInput is called automatically for empty files, but let's call explicitly
|
|
clearInput();
|
|
expect(onClear).toHaveBeenCalled();
|
|
});
|
|
|
|
test('reads files from dataTransfer when target.files absent', () => {
|
|
const mockFile = makeFile(1000, 'image/png');
|
|
const event = { dataTransfer: { files: [mockFile] } };
|
|
const { file } = handleImageUploadInput(event);
|
|
expect(file).toBe(mockFile);
|
|
});
|
|
});
|