Give /scanner a md+ camera, live match inspector, and history strip (with device picker, batch scan, and tips) without regressing the mobile immersive checkout. Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
4.2 KiB
JavaScript
122 lines
4.2 KiB
JavaScript
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();
|
|
});
|
|
});
|