fix(scanner): satisfy react-hooks/immutability and jsdom ResizeObserver in tests

- Move disambiguation-cancel tracker reset into useCameraScanner's new
  resetTrackedCard(cardId) (immutable map + setTrackedCards), wired via
  onTrackerReset — the identification hook no longer mutates state-derived
  objects, clearing the blocking react-hooks/immutability lint error.
- Stub ResizeObserver in test/setup.js so ScannerCamera's workstation
  tests render under jsdom.
- Drop unused eslint-disable directive in CollectionsPageView.

npm run lint: 0 problems; vitest 231/231.
This commit is contained in:
Randall Stillwell 2026-08-23 22:27:49 -05:00
parent 6fab22d315
commit ca3b8a78c2
5 changed files with 24 additions and 5 deletions

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element -- External or generated image URLs; next/image migration is out of scope. */
import Link from 'next/link';
import PermissionIndicator from './PermissionIndicator';
import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js';

View file

@ -176,6 +176,16 @@ export function useCameraScanner({
setTrackedCards(next);
}, []);
const resetTrackedCard = useCallback((cardId) => {
const next = trackedCardsRef.current.map((card) => (
card.id === cardId
? { ...card, status: 'detecting', scanAttempts: 0, negativeAt: undefined }
: card
));
trackedCardsRef.current = next;
setTrackedCards(next);
}, []);
const resetScanTrackers = useCallback(() => {
trackedCardsRef.current = [];
setTrackedCards([]);
@ -429,6 +439,7 @@ export function useCameraScanner({
startCamera,
stopCamera,
removeTrackedCard,
resetTrackedCard,
resetScanTrackers,
triggerManualScan,
streamRef,

View file

@ -46,6 +46,7 @@ export function useScannerIdentification({
onCardScanned,
onError,
onTrackerComplete,
onTrackerReset,
videoRef,
canvasRef,
onVerifyCardRef,
@ -118,10 +119,9 @@ export function useScannerIdentification({
};
const cancelDisambiguation = () => {
if (disambiguation?.cardTracker) {
disambiguation.cardTracker.status = 'detecting';
disambiguation.cardTracker.scanAttempts = 0;
disambiguation.cardTracker.negativeAt = undefined;
const { cardTracker } = disambiguation ?? {};
if (cardTracker) {
onTrackerReset?.(cardTracker.id);
}
setDisambiguation(null);
disambiguationRefineRef.current = null;

View file

@ -69,6 +69,7 @@ export default function Scanner() {
onCardScanned: queue.handleCardScanned,
onError: (msg) => console.error('Identification error:', msg),
onTrackerComplete: camera.removeTrackedCard,
onTrackerReset: camera.resetTrackedCard,
videoRef: camera.videoRef,
canvasRef: camera.canvasRef,
onVerifyCardRef,

View file

@ -1,2 +1,10 @@
process.env.JWT_SECRET = 'test-secret-for-vitest-only-do-not-use-in-prod';
process.env.NODE_ENV = 'test';
if (!globalThis.ResizeObserver) {
globalThis.ResizeObserver = class ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
};
}