deckhearth/test/pages/scanner.test.js

185 lines
5.3 KiB
JavaScript
Raw Normal View History

// @vitest-environment jsdom
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen } from '@testing-library/react';
const mockRouterPush = vi.fn();
const mockRouterBack = vi.fn();
vi.mock('next/router', () => ({
useRouter: () => ({
push: mockRouterPush,
back: mockRouterBack,
pathname: '/scanner',
asPath: '/scanner',
query: {},
prefetch: vi.fn().mockResolvedValue(undefined),
events: { on: vi.fn(), off: vi.fn(), emit: vi.fn() },
}),
}));
vi.mock('next/link', () => ({
__esModule: true,
default: ({ href, children, ...rest }) => (
<a href={typeof href === 'string' ? href : ''} {...rest}>
{children}
</a>
),
}));
vi.mock('../../lib/theme-context', () => ({
useTheme: () => ({ theme: 'light', toggleTheme: vi.fn() }),
}));
const layoutPropsRef = { current: null };
vi.mock('../../components/Layout', () => ({
default: ({ children, chrome, user }) => {
layoutPropsRef.current = { chrome, user };
return (
<div data-testid="layout" data-chrome={chrome}>
{children}
</div>
);
},
}));
vi.mock('../../components/scanner/ScannerCamera', () => ({
default: ({ variant }) => (
<div data-testid="scanner-camera" data-variant={variant} />
),
}));
vi.mock('../../components/scanner/ScannerCheckoutSheet', () => ({
default: () => <div data-testid="scanner-checkout-sheet" />,
}));
vi.mock('../../components/scanner/ScannerResultPanel', () => ({
default: () => <div data-testid="scanner-result-panel" />,
}));
vi.mock('../../components/scanner/ScannerHistoryStrip', () => ({
default: () => <div data-testid="scanner-history-strip" />,
TAB_RECENT: 'recent',
TAB_QUEUE: 'queue',
}));
vi.mock('../../components/scanner/ScannerTips', () => ({
default: () => <button type="button">Scanner Tips</button>,
}));
vi.mock('../../components/scanner/ScannerToast', () => ({
default: () => null,
}));
const queueFixture = {
scannedCards: [],
collections: [],
selectedCards: new Set(),
isProcessing: false,
addingCardIds: new Set(),
ownershipMap: {},
unprocessedCount: 0,
handleCardScanned: vi.fn(),
updateCardMetadata: vi.fn(),
addSingleCardToOwned: vi.fn(async () => true),
addSingleCardToCollection: vi.fn(async () => true),
commitSelectedToCollection: vi.fn(async () => []),
commitSelectedToOwned: vi.fn(async () => []),
clearScannedCards: vi.fn(),
};
const cameraFixture = {
videoRef: { current: null },
canvasRef: { current: null },
videoDevices: [{ deviceId: 'cam-1', label: 'FaceTime HD Camera' }],
selectedDeviceId: 'cam-1',
setSelectedDeviceId: vi.fn(),
devicePickerStatus: 'ready',
devicePickerMessage: '',
};
const identificationFixture = {
disambiguation: null,
isIdentifying: false,
identifyFromGalleryFile: vi.fn(async () => {}),
};
vi.mock('../../lib/use-auth', () => ({
useAuth: () => ({
user: { email: 'scanner@test.com', role: 'user' },
loading: false,
}),
}));
vi.mock('../../lib/use-scanner-session.js', () => ({
useScannerSession: () => ({
scanDefaults: { condition: 'NM', isFoil: false },
}),
}));
vi.mock('../../lib/use-scanner-queue.js', () => ({
useScannerQueue: () => queueFixture,
}));
vi.mock('../../lib/use-camera-scanner.js', () => ({
useCameraScanner: () => cameraFixture,
}));
vi.mock('../../lib/use-scanner-identification.js', () => ({
useScannerIdentification: () => identificationFixture,
}));
vi.mock('../../lib/scanner-session.js', () => ({
clearScannerCartStorage: vi.fn(),
}));
import Scanner from '../../pages/scanner.js';
function setMatchMedia(matches) {
window.matchMedia = vi.fn((query) => ({
matches,
media: query,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
}));
}
describe('Scanner page viewport composition', () => {
beforeEach(() => {
layoutPropsRef.current = null;
vi.clearAllMocks();
});
afterEach(() => cleanup());
it('uses immersive Layout chrome and hides workstation surfaces on mobile', () => {
setMatchMedia(false);
render(<Scanner />);
expect(layoutPropsRef.current?.chrome).toBe('immersive');
expect(screen.getByTestId('scanner-camera').getAttribute('data-variant')).toBe('default');
expect(screen.queryByTestId('scanner-result-panel')).toBeNull();
expect(screen.queryByTestId('scanner-history-strip')).toBeNull();
expect(screen.queryByTestId('scanner-checkout-sheet')).toBeNull();
expect(screen.queryByRole('button', { name: 'Scanner Tips' })).toBeNull();
});
it('uses default Layout chrome and mounts workstation surfaces at md+', () => {
setMatchMedia(true);
render(<Scanner />);
expect(layoutPropsRef.current?.chrome).toBe('default');
expect(screen.getByTestId('scanner-camera').getAttribute('data-variant')).toBe('workstation');
expect(screen.getByTestId('scanner-result-panel')).toBeTruthy();
expect(screen.getByTestId('scanner-history-strip')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Scanner Tips' })).toBeTruthy();
expect(screen.getByLabelText('Camera')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Upload Image' })).toBeTruthy();
expect(screen.getByRole('button', { name: 'Batch Scan' })).toBeTruthy();
expect(screen.getByRole('switch', { name: 'Auto-detect' })).toBeTruthy();
expect(screen.queryByTestId('scanner-checkout-sheet')).toBeNull();
});
});