IIIF Viewer Context Refactoring - Testing Guide
Phase 1 Testing
1.1 Context Provider Unit Tests
import { render, screen } from '@testing-library/react';
import type { FC } from 'react';
import ItemViewerContextV2, { ItemViewerContextV2Props } from './index';
import { mockDefaultContext } from './test-utils';
// Test component that displays context values
const ContextValueDisplay: FC = () => {
const {
currentCanvas,
currentCanvasIndex,
mainImageService,
hasMultipleCanvases,
isCurrentCanvasRestricted,
isFirstCanvas,
isLastCanvas,
canNavigateNext,
canNavigatePrevious,
hasIiifImageService,
isImageZoomable,
isWorkBornDigital,
} = ItemViewerContextV2._currentValue;
return (
<div>
<div data-testid="currentCanvasIndex">{currentCanvasIndex}</div>
<div data-testid="currentCanvasId">{currentCanvas?.id || 'none'}</div>
<div data-testid="mainImageServiceId">{mainImageService['@id'] || 'none'}</div>
<div data-testid="hasMultipleCanvases">{String(hasMultipleCanvases)}</div>
<div data-testid="isCurrentCanvasRestricted">{String(isCurrentCanvasRestricted)}</div>
<div data-testid="isFirstCanvas">{String(isFirstCanvas)}</div>
<div data-testid="isLastCanvas">{String(isLastCanvas)}</div>
<div data-testid="canNavigateNext">{String(canNavigateNext)}</div>
<div data-testid="canNavigatePrevious">{String(canNavigatePrevious)}</div>
<div data-testid="hasIiifImageService">{String(hasIiifImageService)}</div>
<div data-testid="isImageZoomable">{String(isImageZoomable)}</div>
<div data-testid="isWorkBornDigital">{String(isWorkBornDigital)}</div>
</div>
);
};
describe('ItemViewerContextV2 - Derived Canvas Data', () => {
it('should calculate currentCanvasIndex from canvas query param', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
query: { canvas: 3, manifest: 1, page: 1, shouldScrollToCanvas: true, query: '' },
currentCanvasIndex: 2, // canvas=3 to index 2
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('currentCanvasIndex')).toHaveTextContent('2');
});
it('should handle mainImageService with empty string fallback', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
currentCanvas: { id: 'canvas-1', imageServiceId: undefined },
mainImageService: { '@id': '' },
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('mainImageServiceId')).toHaveTextContent('none');
});
it('should detect multiple canvases correctly', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
transformedManifest: {
canvases: [
{ id: 'c1', label: 'Canvas 1' },
{ id: 'c2', label: 'Canvas 2' },
],
},
hasMultipleCanvases: true,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('hasMultipleCanvases')).toHaveTextContent('true');
});
it('should correctly identify restricted canvas', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
currentCanvas: {
id: 'canvas-1',
label: 'Canvas 1',
accessConditions: [{ type: 'RestrictedAccess' }],
},
isCurrentCanvasRestricted: true,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('isCurrentCanvasRestricted')).toHaveTextContent('true');
});
it('should correctly calculate navigation booleans for first canvas', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
currentCanvasIndex: 0,
transformedManifest: {
canvases: [
{ id: 'c1', label: 'Canvas 1' },
{ id: 'c2', label: 'Canvas 2' },
{ id: 'c3', label: 'Canvas 3' },
],
},
hasMultipleCanvases: true,
isFirstCanvas: true,
isLastCanvas: false,
canNavigateNext: true,
canNavigatePrevious: false,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('isFirstCanvas')).toHaveTextContent('true');
expect(screen.getByTestId('isLastCanvas')).toHaveTextContent('false');
expect(screen.getByTestId('canNavigateNext')).toHaveTextContent('true');
expect(screen.getByTestId('canNavigatePrevious')).toHaveTextContent('false');
});
it('should correctly calculate navigation booleans for last canvas', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
currentCanvasIndex: 2,
transformedManifest: {
canvases: [
{ id: 'c1', label: 'Canvas 1' },
{ id: 'c2', label: 'Canvas 2' },
{ id: 'c3', label: 'Canvas 3' },
],
},
hasMultipleCanvases: true,
isFirstCanvas: false,
isLastCanvas: true,
canNavigateNext: false,
canNavigatePrevious: true,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('isFirstCanvas')).toHaveTextContent('false');
expect(screen.getByTestId('isLastCanvas')).toHaveTextContent('true');
expect(screen.getByTestId('canNavigateNext')).toHaveTextContent('false');
expect(screen.getByTestId('canNavigatePrevious')).toHaveTextContent('true');
});
it('should detect IIIF image service correctly', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
currentCanvas: {
id: 'canvas-1',
label: 'Canvas 1',
imageServiceId: 'https://iiif.example.com/image/123',
},
hasIiifImageService: true,
isImageZoomable: true,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('hasIiifImageService')).toHaveTextContent('true');
expect(screen.getByTestId('isImageZoomable')).toHaveTextContent('true');
});
it('should detect born digital works', () => {
const contextValue: ItemViewerContextV2Props = {
...mockDefaultContext,
work: {
id: 'work-1',
title: 'Test Work',
production: [{ type: 'BornDigital' }],
},
isWorkBornDigital: true,
};
render(
<ItemViewerContextV2.Provider value={contextValue}>
<ContextValueDisplay />
</ItemViewerContextV2.Provider>
);
expect(screen.getByTestId('isWorkBornDigital')).toHaveTextContent('true');
});
});1.2 Mock Utilities with Types
1.3 Component Integration Tests
Manual Testing Checklist
Last updated