203 lines
6.2 KiB
TypeScript
203 lines
6.2 KiB
TypeScript
// src/hooks/useDataExtraction.test.ts
|
|
import { renderHook, act } from '@testing-library/react';
|
|
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
|
import { useDataExtraction } from './useDataExtraction';
|
|
import type { Flyer } from '../types';
|
|
import { getFlyerBaseUrl } from '../tests/utils/testHelpers';
|
|
|
|
const FLYER_BASE_URL = getFlyerBaseUrl();
|
|
|
|
// Create a mock flyer for testing
|
|
const createMockFlyer = (id: number, storeName: string = `Store ${id}`): Flyer => ({
|
|
flyer_id: id,
|
|
store: {
|
|
store_id: id,
|
|
name: storeName,
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
},
|
|
file_name: `flyer${id}.jpg`,
|
|
image_url: `${FLYER_BASE_URL}/flyer${id}.jpg`,
|
|
icon_url: `${FLYER_BASE_URL}/flyer${id}_icon.jpg`,
|
|
status: 'processed',
|
|
item_count: 0,
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
});
|
|
|
|
describe('useDataExtraction Hook', () => {
|
|
let mockOnFlyerUpdate: Mock<(flyer: Flyer) => void>;
|
|
|
|
beforeEach(() => {
|
|
mockOnFlyerUpdate = vi.fn();
|
|
});
|
|
|
|
describe('Initial State', () => {
|
|
it('should return handleDataExtracted as a function', () => {
|
|
const mockFlyer = createMockFlyer(1);
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
expect(typeof result.current.handleDataExtracted).toBe('function');
|
|
});
|
|
|
|
it('should maintain stable function reference across re-renders when dependencies are unchanged', () => {
|
|
const mockFlyer = createMockFlyer(1);
|
|
const { result, rerender } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
const initialHandler = result.current.handleDataExtracted;
|
|
rerender();
|
|
expect(result.current.handleDataExtracted).toBe(initialHandler);
|
|
});
|
|
});
|
|
|
|
describe('Store Name Extraction', () => {
|
|
it('should update store name when type is store_name', () => {
|
|
const mockFlyer = createMockFlyer(1, 'Original Store');
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
act(() => {
|
|
result.current.handleDataExtracted('store_name', 'New Store Name');
|
|
});
|
|
|
|
expect(mockOnFlyerUpdate).toHaveBeenCalledTimes(1);
|
|
const updatedFlyer = mockOnFlyerUpdate.mock.calls[0][0];
|
|
expect(updatedFlyer.store?.name).toBe('New Store Name');
|
|
// Ensure other properties are preserved
|
|
expect(updatedFlyer.flyer_id).toBe(1);
|
|
expect(updatedFlyer.image_url).toBe(`${FLYER_BASE_URL}/flyer1.jpg`);
|
|
});
|
|
|
|
it('should preserve store_id when updating store name', () => {
|
|
const mockFlyer = createMockFlyer(42, 'Original Store');
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
act(() => {
|
|
result.current.handleDataExtracted('store_name', 'Updated Store');
|
|
});
|
|
|
|
const updatedFlyer = mockOnFlyerUpdate.mock.calls[0][0];
|
|
expect(updatedFlyer.store?.store_id).toBe(42);
|
|
});
|
|
});
|
|
|
|
describe('Date Extraction', () => {
|
|
it('should call onFlyerUpdate when type is dates', () => {
|
|
const mockFlyer = createMockFlyer(1);
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
act(() => {
|
|
result.current.handleDataExtracted('dates', '2024-01-15 - 2024-01-21');
|
|
});
|
|
|
|
// The hook is called but date parsing is not implemented yet
|
|
// It should still call onFlyerUpdate with the unchanged flyer
|
|
expect(mockOnFlyerUpdate).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('Null Flyer Handling', () => {
|
|
it('should not call onFlyerUpdate when selectedFlyer is null', () => {
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: null,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
act(() => {
|
|
result.current.handleDataExtracted('store_name', 'New Store');
|
|
});
|
|
|
|
expect(mockOnFlyerUpdate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not throw when selectedFlyer is null', () => {
|
|
const { result } = renderHook(() =>
|
|
useDataExtraction({
|
|
selectedFlyer: null,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
);
|
|
|
|
expect(() => {
|
|
act(() => {
|
|
result.current.handleDataExtracted('store_name', 'New Store');
|
|
});
|
|
}).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('Callback Stability', () => {
|
|
it('should update handler when selectedFlyer changes', () => {
|
|
const mockFlyer1 = createMockFlyer(1, 'Store 1');
|
|
const mockFlyer2 = createMockFlyer(2, 'Store 2');
|
|
|
|
const { result, rerender } = renderHook(
|
|
({ selectedFlyer }) =>
|
|
useDataExtraction({
|
|
selectedFlyer,
|
|
onFlyerUpdate: mockOnFlyerUpdate,
|
|
}),
|
|
{ initialProps: { selectedFlyer: mockFlyer1 } },
|
|
);
|
|
|
|
const handler1 = result.current.handleDataExtracted;
|
|
|
|
rerender({ selectedFlyer: mockFlyer2 });
|
|
|
|
const handler2 = result.current.handleDataExtracted;
|
|
|
|
// Handler should be different since selectedFlyer changed
|
|
expect(handler1).not.toBe(handler2);
|
|
});
|
|
|
|
it('should update handler when onFlyerUpdate changes', () => {
|
|
const mockFlyer = createMockFlyer(1);
|
|
const mockOnFlyerUpdate2: Mock<(flyer: Flyer) => void> = vi.fn();
|
|
|
|
const { result, rerender } = renderHook(
|
|
({ onFlyerUpdate }) =>
|
|
useDataExtraction({
|
|
selectedFlyer: mockFlyer,
|
|
onFlyerUpdate,
|
|
}),
|
|
{ initialProps: { onFlyerUpdate: mockOnFlyerUpdate } },
|
|
);
|
|
|
|
const handler1 = result.current.handleDataExtracted;
|
|
|
|
rerender({ onFlyerUpdate: mockOnFlyerUpdate2 });
|
|
|
|
const handler2 = result.current.handleDataExtracted;
|
|
|
|
// Handler should be different since onFlyerUpdate changed
|
|
expect(handler1).not.toBe(handler2);
|
|
});
|
|
});
|
|
});
|