deckhearth/test/lib/scanner-batch-identify.test.js

123 lines
4.2 KiB
JavaScript
Raw Permalink Normal View History

import { describe, expect, it, vi } from 'vitest';
import { runSequentialGalleryIdentify } from '../../lib/scanner-batch-identify.js';
function makeFile(name) {
return new File(['x'], name, { type: 'image/jpeg' });
}
describe('runSequentialGalleryIdentify', () => {
it('processes files one-at-a-time in order on success', async () => {
const files = [makeFile('a.jpg'), makeFile('b.jpg'), makeFile('c.jpg')];
const callOrder = [];
const progressEvents = [];
const successEvents = [];
const identifyFn = vi.fn(async (file) => {
callOrder.push(file.name);
});
const { results, cancelled } = await runSequentialGalleryIdentify(files, identifyFn, {
onProgress: (p) => progressEvents.push({ current: p.current, total: p.total, name: p.file.name }),
onFileSuccess: ({ file, index }) => successEvents.push({ name: file.name, index }),
});
expect(cancelled).toBe(false);
expect(callOrder).toEqual(['a.jpg', 'b.jpg', 'c.jpg']);
expect(identifyFn).toHaveBeenCalledTimes(3);
expect(progressEvents).toEqual([
{ current: 1, total: 3, name: 'a.jpg' },
{ current: 2, total: 3, name: 'b.jpg' },
{ current: 3, total: 3, name: 'c.jpg' },
]);
expect(successEvents).toEqual([
{ name: 'a.jpg', index: 0 },
{ name: 'b.jpg', index: 1 },
{ name: 'c.jpg', index: 2 },
]);
expect(results).toHaveLength(3);
expect(results.every((r) => r.ok === true)).toBe(true);
expect(results.map((r) => r.file.name)).toEqual(['a.jpg', 'b.jpg', 'c.jpg']);
});
it('stops remaining files when cancelRef is set mid-batch', async () => {
const files = [makeFile('a.jpg'), makeFile('b.jpg'), makeFile('c.jpg')];
const cancelRef = { current: false };
const identifyFn = vi.fn(async (file) => {
if (file.name === 'a.jpg') {
cancelRef.current = true;
}
});
const { results, cancelled } = await runSequentialGalleryIdentify(files, identifyFn, {
cancelRef,
});
expect(cancelled).toBe(true);
expect(identifyFn).toHaveBeenCalledTimes(1);
expect(results).toEqual([{ file: files[0], ok: true }]);
});
it('invokes onFileError and continues after a per-file failure', async () => {
const files = [makeFile('a.jpg'), makeFile('b.jpg'), makeFile('c.jpg')];
const err = new Error('identify failed');
const errorEvents = [];
const callOrder = [];
const identifyFn = vi.fn(async (file) => {
callOrder.push(file.name);
if (file.name === 'b.jpg') {
throw err;
}
});
const { results, cancelled } = await runSequentialGalleryIdentify(files, identifyFn, {
onFileError: ({ file, index, error }) => {
errorEvents.push({ name: file.name, index, error });
},
});
expect(cancelled).toBe(false);
expect(callOrder).toEqual(['a.jpg', 'b.jpg', 'c.jpg']);
expect(identifyFn).toHaveBeenCalledTimes(3);
expect(errorEvents).toEqual([{ name: 'b.jpg', index: 1, error: err }]);
expect(results).toHaveLength(3);
expect(results[0]).toMatchObject({ file: files[0], ok: true });
expect(results[1]).toMatchObject({ file: files[1], ok: false, error: err });
expect(results[2]).toMatchObject({ file: files[2], ok: true });
});
it('accepts FileList-like iterables via Array.from', async () => {
const files = [makeFile('solo.jpg')];
const fileList = {
length: files.length,
0: files[0],
[Symbol.iterator]() {
let i = 0;
return {
next: () => {
if (i < files.length) {
return { value: files[i++], done: false };
}
return { done: true };
},
};
},
};
const identifyFn = vi.fn(async () => {});
const { results } = await runSequentialGalleryIdentify(fileList, identifyFn);
expect(identifyFn).toHaveBeenCalledTimes(1);
expect(results).toEqual([{ file: files[0], ok: true }]);
});
it('returns empty results for null/undefined files', async () => {
const identifyFn = vi.fn(async () => {});
const { results, cancelled } = await runSequentialGalleryIdentify(null, identifyFn);
expect(cancelled).toBe(false);
expect(results).toEqual([]);
expect(identifyFn).not.toHaveBeenCalled();
});
});