mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 17:23:20 +03:00
226 lines
6.5 KiB
TypeScript
226 lines
6.5 KiB
TypeScript
import { ocrManager, type OcrBoundingBox } from '$lib/stores/ocr.svelte';
|
|
import { getAssetOcr } from '@immich/sdk';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
// Mock the SDK
|
|
vi.mock('@immich/sdk', () => ({
|
|
getAssetOcr: vi.fn(),
|
|
}));
|
|
|
|
const createMockOcrData = (overrides?: Partial<OcrBoundingBox>): OcrBoundingBox[] => [
|
|
{
|
|
id: '1',
|
|
assetId: 'asset-123',
|
|
x1: 0,
|
|
y1: 0,
|
|
x2: 100,
|
|
y2: 0,
|
|
x3: 100,
|
|
y3: 50,
|
|
x4: 0,
|
|
y4: 50,
|
|
boxScore: 0.95,
|
|
textScore: 0.98,
|
|
text: 'Hello World',
|
|
...overrides,
|
|
},
|
|
];
|
|
|
|
describe('OcrManager', () => {
|
|
beforeEach(() => {
|
|
// Reset the singleton state before each test
|
|
ocrManager.clear();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('initial state', () => {
|
|
it('should initialize with empty data', () => {
|
|
expect(ocrManager.data).toEqual([]);
|
|
});
|
|
|
|
it('should initialize with showOverlay as false', () => {
|
|
expect(ocrManager.showOverlay).toBe(false);
|
|
});
|
|
|
|
it('should initialize with hasOcrData as false', () => {
|
|
expect(ocrManager.hasOcrData).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getAssetOcr', () => {
|
|
it('should load OCR data for an asset', async () => {
|
|
const mockData = createMockOcrData();
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
|
|
expect(getAssetOcr).toHaveBeenCalledWith({ id: 'asset-123' });
|
|
expect(ocrManager.data).toEqual(mockData);
|
|
expect(ocrManager.hasOcrData).toBe(true);
|
|
});
|
|
|
|
it('should handle empty OCR data', async () => {
|
|
vi.mocked(getAssetOcr).mockResolvedValue([]);
|
|
|
|
await ocrManager.getAssetOcr('asset-456');
|
|
|
|
expect(ocrManager.data).toEqual([]);
|
|
expect(ocrManager.hasOcrData).toBe(false);
|
|
});
|
|
|
|
it('should reset the loader when previously cleared', async () => {
|
|
const mockData = createMockOcrData();
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
|
|
// First clear
|
|
ocrManager.clear();
|
|
expect(ocrManager.data).toEqual([]);
|
|
|
|
// Then load new data
|
|
await ocrManager.getAssetOcr('asset-789');
|
|
|
|
expect(ocrManager.data).toEqual(mockData);
|
|
expect(ocrManager.hasOcrData).toBe(true);
|
|
});
|
|
|
|
it('should handle concurrent requests safely', async () => {
|
|
const firstData = createMockOcrData({ id: '1', text: 'First' });
|
|
const secondData = createMockOcrData({ id: '2', text: 'Second' });
|
|
|
|
vi.mocked(getAssetOcr)
|
|
.mockImplementationOnce(
|
|
() =>
|
|
new Promise((resolve) => {
|
|
setTimeout(() => resolve(firstData), 100);
|
|
}),
|
|
)
|
|
.mockResolvedValueOnce(secondData);
|
|
|
|
// Start first request
|
|
const promise1 = ocrManager.getAssetOcr('asset-1');
|
|
// Start second request immediately (should wait for first to complete)
|
|
const promise2 = ocrManager.getAssetOcr('asset-2');
|
|
|
|
await Promise.all([promise1, promise2]);
|
|
|
|
// CancellableTask waits for first request, so second request is ignored
|
|
// The data should be from the first request that completed
|
|
expect(ocrManager.data).toEqual(firstData);
|
|
});
|
|
|
|
it('should handle errors gracefully', async () => {
|
|
const error = new Error('Network error');
|
|
vi.mocked(getAssetOcr).mockRejectedValue(error);
|
|
|
|
// The error should be handled by CancellableTask
|
|
await expect(ocrManager.getAssetOcr('asset-error')).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('clear', () => {
|
|
it('should clear OCR data', async () => {
|
|
const mockData = createMockOcrData({ text: 'Test' });
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
|
|
ocrManager.clear();
|
|
|
|
expect(ocrManager.data).toEqual([]);
|
|
expect(ocrManager.hasOcrData).toBe(false);
|
|
});
|
|
|
|
it('should reset showOverlay to false', () => {
|
|
ocrManager.showOverlay = true;
|
|
|
|
ocrManager.clear();
|
|
|
|
expect(ocrManager.showOverlay).toBe(false);
|
|
});
|
|
|
|
it('should mark as cleared for next load', async () => {
|
|
const mockData = createMockOcrData({ text: 'Test' });
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
|
|
ocrManager.clear();
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
|
|
// Should successfully load after clear
|
|
expect(ocrManager.data).toEqual(mockData);
|
|
});
|
|
});
|
|
|
|
describe('toggleOcrBoundingBox', () => {
|
|
it('should toggle showOverlay from false to true', () => {
|
|
expect(ocrManager.showOverlay).toBe(false);
|
|
|
|
ocrManager.toggleOcrBoundingBox();
|
|
|
|
expect(ocrManager.showOverlay).toBe(true);
|
|
});
|
|
|
|
it('should toggle showOverlay from true to false', () => {
|
|
ocrManager.showOverlay = true;
|
|
|
|
ocrManager.toggleOcrBoundingBox();
|
|
|
|
expect(ocrManager.showOverlay).toBe(false);
|
|
});
|
|
|
|
it('should toggle multiple times', () => {
|
|
ocrManager.toggleOcrBoundingBox();
|
|
expect(ocrManager.showOverlay).toBe(true);
|
|
|
|
ocrManager.toggleOcrBoundingBox();
|
|
expect(ocrManager.showOverlay).toBe(false);
|
|
|
|
ocrManager.toggleOcrBoundingBox();
|
|
expect(ocrManager.showOverlay).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('hasOcrData derived state', () => {
|
|
it('should be false when data is empty', () => {
|
|
expect(ocrManager.hasOcrData).toBe(false);
|
|
});
|
|
|
|
it('should be true when data is present', async () => {
|
|
const mockData = createMockOcrData({ text: 'Test' });
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
|
|
expect(ocrManager.hasOcrData).toBe(true);
|
|
});
|
|
|
|
it('should update when data is cleared', async () => {
|
|
const mockData = createMockOcrData({ text: 'Test' });
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
expect(ocrManager.hasOcrData).toBe(true);
|
|
|
|
ocrManager.clear();
|
|
expect(ocrManager.hasOcrData).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('data immutability', () => {
|
|
it('should return the same reference when data does not change', () => {
|
|
const firstReference = ocrManager.data;
|
|
const secondReference = ocrManager.data;
|
|
|
|
expect(firstReference).toBe(secondReference);
|
|
});
|
|
|
|
it('should return a new reference when data changes', async () => {
|
|
const firstReference = ocrManager.data;
|
|
const mockData = createMockOcrData({ text: 'Test' });
|
|
|
|
vi.mocked(getAssetOcr).mockResolvedValue(mockData);
|
|
await ocrManager.getAssetOcr('asset-123');
|
|
|
|
const secondReference = ocrManager.data;
|
|
|
|
expect(firstReference).not.toBe(secondReference);
|
|
});
|
|
});
|
|
});
|