Files
picpeak/frontend/src/components/common/__tests__/ProtectedImage.test.tsx
T
paul 1b4b497fdf
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m42s
Test and Lint / frontend-test (push) Has been cancelled
Version and Release / version-bump (push) Has been cancelled
Version and Release / trigger-drone (push) Has been cancelled
chore: clean up codebase for production readiness
- Remove all console.log/debug statements from production code
- Add NODE_ENV checks for development-only logging
- Remove test scripts (test-feedback, test-image-security, test-backup-*, test-restore)
- Remove one-time fix scripts (fix-temp-photos, fix-migration-state, mark-migration-applied)
- Remove sensitive files (.env.backup, ADMIN_CREDENTIALS.txt)
- Update package.json to remove references to deleted scripts
- Replace console statements with logger utility in backend
- Secure error boundaries to not expose stack traces in production

This makes the codebase production-ready with no debug output or test scripts.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 23:19:30 +02:00

236 lines
6.0 KiB
TypeScript

import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ProtectedImage } from '../ProtectedImage';
// Mock canvas and image APIs
const mockCanvas = {
getContext: jest.fn(() => ({
clearRect: jest.fn(),
drawImage: jest.fn(),
getImageData: jest.fn(() => ({
data: new Uint8ClampedArray(4).fill(255)
})),
putImageData: jest.fn(),
fillRect: jest.fn(),
fillText: jest.fn(),
strokeText: jest.fn(),
measureText: jest.fn(() => ({ width: 100 }))
})),
width: 100,
height: 100,
style: {},
addEventListener: jest.fn(),
removeEventListener: jest.fn()
};
// Mock HTMLCanvasElement
Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', {
value: () => mockCanvas.getContext()
});
// Mock Image constructor
global.Image = class {
onload: (() => void) | null = null;
onerror: (() => void) | null = null;
src = '';
naturalWidth = 100;
naturalHeight = 100;
width = 100;
height = 100;
crossOrigin = '';
constructor() {
// Simulate image loading
setTimeout(() => {
if (this.onload) this.onload();
}, 10);
}
} as any;
describe('ProtectedImage', () => {
const defaultProps = {
src: '/test-image.jpg',
alt: 'Test image'
};
beforeEach(() => {
jest.clearAllMocks();
});
it('renders loading state initially', () => {
render(<ProtectedImage {...defaultProps} />);
expect(screen.getByRole('img', { name: /loading test image/i })).toBeInTheDocument();
});
it('renders canvas after image loads', async () => {
render(<ProtectedImage {...defaultProps} />);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
});
it('applies protection level classes and events', async () => {
const onViolation = jest.fn();
render(
<ProtectedImage
{...defaultProps}
protectionLevel="enhanced"
onProtectionViolation={onViolation}
/>
);
await waitFor(() => {
const canvas = screen.getByRole('img', { name: 'Test image' });
expect(canvas).toBeInTheDocument();
});
// Test context menu blocking
const canvas = screen.getByRole('img', { name: 'Test image' });
fireEvent.contextMenu(canvas);
expect(onViolation).toHaveBeenCalledWith('canvas_context_menu');
});
it('applies watermark text when specified', async () => {
render(
<ProtectedImage
{...defaultProps}
watermarkText="Test Watermark"
protectionLevel="standard"
/>
);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
// Verify canvas context methods were called for watermark
expect(mockCanvas.getContext().fillText).toHaveBeenCalled();
});
it('handles fragment grid rendering', async () => {
render(
<ProtectedImage
{...defaultProps}
fragmentGrid={true}
gridSize={4}
protectionLevel="enhanced"
/>
);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
// Verify multiple drawImage calls for fragments
expect(mockCanvas.getContext().drawImage).toHaveBeenCalled();
});
it('blocks interactions in maximum protection mode', async () => {
const onViolation = jest.fn();
render(
<ProtectedImage
{...defaultProps}
protectionLevel="maximum"
onProtectionViolation={onViolation}
/>
);
await waitFor(() => {
const canvas = screen.getByRole('img', { name: 'Test image' });
expect(canvas).toBeInTheDocument();
// Test click blocking
fireEvent.click(canvas);
expect(onViolation).toHaveBeenCalledWith('canvas_interaction_blocked');
});
});
it('handles image loading errors gracefully', async () => {
// Mock image error
global.Image = class {
onload: (() => void) | null = null;
onerror: (() => void) | null = null;
src = '';
constructor() {
setTimeout(() => {
if (this.onerror) this.onerror();
}, 10);
}
} as any;
const onViolation = jest.fn();
render(
<ProtectedImage
{...defaultProps}
onProtectionViolation={onViolation}
fallbackSrc="/fallback.jpg"
/>
);
await waitFor(() => {
expect(screen.getByText('Image unavailable')).toBeInTheDocument();
});
expect(onViolation).toHaveBeenCalledWith('image_load_error');
});
it('applies invisible watermark for enhanced protection', async () => {
render(
<ProtectedImage
{...defaultProps}
watermarkText="Hidden"
invisibleWatermark={true}
protectionLevel="enhanced"
/>
);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
// Verify getImageData and putImageData called for steganography
expect(mockCanvas.getContext().getImageData).toHaveBeenCalled();
expect(mockCanvas.getContext().putImageData).toHaveBeenCalled();
});
it('scrambles fragments when enabled', async () => {
render(
<ProtectedImage
{...defaultProps}
fragmentGrid={true}
scrambleFragments={true}
protectionLevel="maximum"
/>
);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
// Fragment scrambling should result in multiple drawImage calls
expect(mockCanvas.getContext().drawImage).toHaveBeenCalled();
});
it('adds random noise in maximum protection', async () => {
render(
<ProtectedImage
{...defaultProps}
protectionLevel="maximum"
/>
);
await waitFor(() => {
expect(screen.getByRole('img', { name: 'Test image' })).toBeInTheDocument();
});
// Noise injection requires getImageData and putImageData
expect(mockCanvas.getContext().getImageData).toHaveBeenCalled();
expect(mockCanvas.getContext().putImageData).toHaveBeenCalled();
});
});