Extract upload dropzone and queue UI with shared pick validation utils.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { analyzePickedUploadFiles } from '@/utils/upload/analyzePickedUploadFiles';
|
||||
|
||||
const makeFile = (name: string, type: string, size: number): File =>
|
||||
new File([new Uint8Array(size)], name, { type });
|
||||
|
||||
describe('analyzePickedUploadFiles', () => {
|
||||
const maxImageBytes = 100;
|
||||
const maxQueueSize = 2;
|
||||
|
||||
it('accepts images within size and queue limits', () => {
|
||||
const picked = [makeFile('a.png', 'image/png', 50), makeFile('b.jpg', 'image/jpeg', 80)];
|
||||
|
||||
const result = analyzePickedUploadFiles(picked, maxImageBytes, maxQueueSize);
|
||||
|
||||
expect(result.accepted).toHaveLength(2);
|
||||
expect(result.nonImageCount).toBe(0);
|
||||
expect(result.oversizedImagesCount).toBe(0);
|
||||
expect(result.queueOmittedImagesCount).toBe(0);
|
||||
});
|
||||
|
||||
it('skips non-images and oversized files and caps the queue', () => {
|
||||
const picked = [
|
||||
makeFile('doc.pdf', 'application/pdf', 10),
|
||||
makeFile('big.png', 'image/png', 200),
|
||||
makeFile('1.png', 'image/png', 10),
|
||||
makeFile('2.png', 'image/png', 10),
|
||||
makeFile('3.png', 'image/png', 10)
|
||||
];
|
||||
|
||||
const result = analyzePickedUploadFiles(picked, maxImageBytes, maxQueueSize);
|
||||
|
||||
expect(result.accepted.map(f => f.name)).toEqual(['1.png', '2.png']);
|
||||
expect(result.nonImageCount).toBe(1);
|
||||
expect(result.oversizedImagesCount).toBe(1);
|
||||
expect(result.queueOmittedImagesCount).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { PickedUploadAnalysis } from '@/types/upload/PickedUploadAnalysis';
|
||||
|
||||
export const analyzePickedUploadFiles = (
|
||||
picked: File[],
|
||||
maxImageBytes: number,
|
||||
maxQueueSize: number
|
||||
): PickedUploadAnalysis => {
|
||||
const images = picked.filter(file => file.type.startsWith('image/'));
|
||||
const nonImageCount = picked.length - images.length;
|
||||
const imagesWithinSizeLimit = images.filter(file => file.size <= maxImageBytes);
|
||||
const oversizedImagesCount = images.length - imagesWithinSizeLimit.length;
|
||||
const accepted = imagesWithinSizeLimit.slice(0, maxQueueSize);
|
||||
const queueOmittedImagesCount = imagesWithinSizeLimit.length - accepted.length;
|
||||
|
||||
return {
|
||||
accepted,
|
||||
nonImageCount,
|
||||
oversizedImagesCount,
|
||||
queueOmittedImagesCount
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildPickedUploadWarning } from '@/utils/upload/buildPickedUploadWarning';
|
||||
|
||||
describe('buildPickedUploadWarning', () => {
|
||||
it('returns null when nothing was skipped', () => {
|
||||
expect(
|
||||
buildPickedUploadWarning(
|
||||
{ nonImageCount: 0, oversizedImagesCount: 0, queueOmittedImagesCount: 0 },
|
||||
5
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('joins skip reasons into one message', () => {
|
||||
const message = buildPickedUploadWarning(
|
||||
{ nonImageCount: 1, oversizedImagesCount: 2, queueOmittedImagesCount: 3 },
|
||||
4
|
||||
);
|
||||
|
||||
expect(message).toContain('1 non-image');
|
||||
expect(message).toContain('2 image(s) exceeded');
|
||||
expect(message).toContain('first 4 valid images');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { PickedUploadAnalysis } from '@/types/upload/PickedUploadAnalysis';
|
||||
|
||||
export const buildPickedUploadWarning = (
|
||||
summary: Pick<PickedUploadAnalysis, 'nonImageCount' | 'oversizedImagesCount' | 'queueOmittedImagesCount'>,
|
||||
maxQueueSize: number
|
||||
): string | null => {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (summary.nonImageCount > 0) {
|
||||
parts.push(`${summary.nonImageCount} non-image file(s) were skipped.`);
|
||||
}
|
||||
|
||||
if (summary.oversizedImagesCount > 0) {
|
||||
parts.push(`${summary.oversizedImagesCount} image(s) exceeded the size limit and were skipped.`);
|
||||
}
|
||||
|
||||
if (summary.queueOmittedImagesCount > 0) {
|
||||
parts.push(`Only the first ${maxQueueSize} valid images were added.`);
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : null;
|
||||
};
|
||||
Reference in New Issue
Block a user