mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-08-11 13:48:37 +02:00
Add tests for sanitize
This commit is contained in:
parent
4465130677
commit
25a0252b09
194
frontend/src/components/tools/sanitize/SanitizeSettings.test.tsx
Normal file
194
frontend/src/components/tools/sanitize/SanitizeSettings.test.tsx
Normal file
@ -0,0 +1,194 @@
|
||||
import { describe, expect, test, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import SanitizeSettings from './SanitizeSettings';
|
||||
import { SanitizeParameters } from '../../../hooks/tools/sanitize/useSanitizeParameters';
|
||||
|
||||
// Mock useTranslation with predictable return values
|
||||
const mockT = vi.fn((key: string) => `mock-${key}`);
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({ t: mockT })
|
||||
}));
|
||||
|
||||
// Wrapper component to provide Mantine context
|
||||
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MantineProvider>{children}</MantineProvider>
|
||||
);
|
||||
|
||||
describe('SanitizeSettings', () => {
|
||||
const defaultParameters: SanitizeParameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false,
|
||||
};
|
||||
|
||||
const mockOnParameterChange = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should render all sanitization option checkboxes', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
// Should render one checkbox for each parameter
|
||||
const expectedCheckboxCount = Object.keys(defaultParameters).length;
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
expect(checkboxes).toHaveLength(expectedCheckboxCount);
|
||||
});
|
||||
|
||||
test('should show correct initial checkbox states based on parameters', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
const parameterValues = Object.values(defaultParameters);
|
||||
|
||||
parameterValues.forEach((value, index) => {
|
||||
if (value) {
|
||||
expect(checkboxes[index]).toBeChecked();
|
||||
} else {
|
||||
expect(checkboxes[index]).not.toBeChecked();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should call onParameterChange with correct parameters when checkboxes are clicked', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
|
||||
// Click the first checkbox (removeJavaScript - should toggle from true to false)
|
||||
fireEvent.click(checkboxes[0]);
|
||||
expect(mockOnParameterChange).toHaveBeenCalledWith('removeJavaScript', false);
|
||||
|
||||
// Click the third checkbox (removeXMPMetadata - should toggle from false to true)
|
||||
fireEvent.click(checkboxes[2]);
|
||||
expect(mockOnParameterChange).toHaveBeenCalledWith('removeXMPMetadata', true);
|
||||
});
|
||||
|
||||
test('should disable all checkboxes when disabled prop is true', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
disabled={true}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
checkboxes.forEach(checkbox => {
|
||||
expect(checkbox).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should enable all checkboxes when disabled prop is false or undefined', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
disabled={false}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
checkboxes.forEach(checkbox => {
|
||||
expect(checkbox).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle different parameter combinations', () => {
|
||||
const allEnabledParameters: SanitizeParameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: true,
|
||||
removeMetadata: true,
|
||||
removeLinks: true,
|
||||
removeFonts: true,
|
||||
};
|
||||
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={allEnabledParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
checkboxes.forEach(checkbox => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test('should call translation function with correct keys', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
// Verify that translation keys are being called (just check that it was called, not specific order)
|
||||
expect(mockT).toHaveBeenCalledWith('sanitize.options.title', 'Sanitization Options');
|
||||
expect(mockT).toHaveBeenCalledWith('sanitize.options.removeJavaScript', 'Remove JavaScript');
|
||||
expect(mockT).toHaveBeenCalledWith('sanitize.options.removeEmbeddedFiles', 'Remove Embedded Files');
|
||||
expect(mockT).toHaveBeenCalledWith('sanitize.options.note', expect.any(String));
|
||||
});
|
||||
|
||||
test('should not call onParameterChange when disabled', () => {
|
||||
render(
|
||||
<TestWrapper>
|
||||
<SanitizeSettings
|
||||
parameters={defaultParameters}
|
||||
onParameterChange={mockOnParameterChange}
|
||||
disabled={true}
|
||||
/>
|
||||
</TestWrapper>
|
||||
);
|
||||
|
||||
const checkboxes = screen.getAllByRole('checkbox');
|
||||
|
||||
// Verify checkboxes are disabled
|
||||
checkboxes.forEach(checkbox => {
|
||||
expect(checkbox).toBeDisabled();
|
||||
});
|
||||
|
||||
// Try to click a disabled checkbox - this might still fire the event in tests
|
||||
// but we can verify the checkbox state doesn't actually change
|
||||
const firstCheckbox = checkboxes[0] as HTMLInputElement;
|
||||
const initialChecked = firstCheckbox.checked;
|
||||
fireEvent.click(firstCheckbox);
|
||||
expect(firstCheckbox.checked).toBe(initialChecked);
|
||||
});
|
||||
});
|
236
frontend/src/hooks/tools/sanitize/useSanitizeOperation.test.ts
Normal file
236
frontend/src/hooks/tools/sanitize/useSanitizeOperation.test.ts
Normal file
@ -0,0 +1,236 @@
|
||||
import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useSanitizeOperation } from './useSanitizeOperation';
|
||||
|
||||
// Mock useTranslation
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, fallback?: string, options?: any) => {
|
||||
if (key === 'sanitize.error' && options?.error) {
|
||||
return `Sanitization failed: ${options.error}`;
|
||||
}
|
||||
if (key === 'error.noFilesSelected') {
|
||||
return 'No files selected';
|
||||
}
|
||||
if (key === 'sanitize.error.generic') {
|
||||
return 'Sanitization failed';
|
||||
}
|
||||
return fallback || key;
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
// Mock fetch
|
||||
const mockFetch = vi.fn();
|
||||
globalThis.fetch = mockFetch;
|
||||
|
||||
// Mock URL.createObjectURL and revokeObjectURL
|
||||
globalThis.URL.createObjectURL = vi.fn(() => 'mock-blob-url');
|
||||
globalThis.URL.revokeObjectURL = vi.fn();
|
||||
|
||||
describe('useSanitizeOperation', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should initialize with default state', () => {
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.errorMessage).toBe(null);
|
||||
expect(result.current.downloadUrl).toBe(null);
|
||||
expect(result.current.status).toBe(null);
|
||||
});
|
||||
|
||||
test('should execute sanitization operation successfully', async () => {
|
||||
const mockBlob = new Blob(['test'], { type: 'application/pdf' });
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
blob: () => Promise.resolve(mockBlob)
|
||||
};
|
||||
mockFetch.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
const parameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: false,
|
||||
removeXMPMetadata: true,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false
|
||||
};
|
||||
|
||||
const testFile = new File(['test'], 'test.pdf', { type: 'application/pdf' });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.executeOperation(parameters, [testFile]);
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith('/api/v1/security/sanitize-pdf', {
|
||||
method: 'POST',
|
||||
body: expect.any(FormData)
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.downloadUrl).toBe('mock-blob-url');
|
||||
expect(result.current.status).toBe('Sanitization completed successfully');
|
||||
expect(result.current.errorMessage).toBe(null);
|
||||
});
|
||||
|
||||
test('should handle API errors correctly', async () => {
|
||||
const mockResponse = {
|
||||
ok: false,
|
||||
text: () => Promise.resolve('Server error')
|
||||
};
|
||||
mockFetch.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
const parameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false
|
||||
};
|
||||
|
||||
const testFile = new File(['test'], 'test.pdf', { type: 'application/pdf' });
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.executeOperation(parameters, [testFile]);
|
||||
} catch (error) {
|
||||
// Expected to throw
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.errorMessage).toBe('Sanitization failed: Server error');
|
||||
expect(result.current.downloadUrl).toBe(null);
|
||||
expect(result.current.status).toBe(null);
|
||||
});
|
||||
|
||||
test('should handle no files selected error', async () => {
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
const parameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false
|
||||
};
|
||||
|
||||
let thrownError: Error | null = null;
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.executeOperation(parameters, []);
|
||||
} catch (error) {
|
||||
thrownError = error as Error;
|
||||
}
|
||||
});
|
||||
|
||||
// The error should be thrown
|
||||
expect(thrownError).toBeInstanceOf(Error);
|
||||
expect(thrownError!.message).toBe('No files selected');
|
||||
expect(mockFetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should send correct form data to API', async () => {
|
||||
const mockBlob = new Blob(['test'], { type: 'application/pdf' });
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
blob: () => Promise.resolve(mockBlob)
|
||||
};
|
||||
mockFetch.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
const parameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: false,
|
||||
removeXMPMetadata: true,
|
||||
removeMetadata: false,
|
||||
removeLinks: true,
|
||||
removeFonts: false
|
||||
};
|
||||
|
||||
const testFile = new File(['test'], 'test.pdf', { type: 'application/pdf' });
|
||||
|
||||
await act(async () => {
|
||||
await result.current.executeOperation(parameters, [testFile]);
|
||||
});
|
||||
|
||||
const [url, options] = mockFetch.mock.calls[0];
|
||||
expect(url).toBe('/api/v1/security/sanitize-pdf');
|
||||
expect(options.method).toBe('POST');
|
||||
|
||||
const formData = options.body as FormData;
|
||||
expect(formData.get('removeJavaScript')).toBe('true');
|
||||
expect(formData.get('removeEmbeddedFiles')).toBe('false');
|
||||
expect(formData.get('removeXMPMetadata')).toBe('true');
|
||||
expect(formData.get('removeMetadata')).toBe('false');
|
||||
expect(formData.get('removeLinks')).toBe('true');
|
||||
expect(formData.get('removeFonts')).toBe('false');
|
||||
expect(formData.get('fileInput')).toBe(testFile);
|
||||
});
|
||||
|
||||
test('should reset results correctly', () => {
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
act(() => {
|
||||
result.current.resetResults();
|
||||
});
|
||||
|
||||
expect(result.current.downloadUrl).toBe(null);
|
||||
expect(result.current.errorMessage).toBe(null);
|
||||
expect(result.current.status).toBe(null);
|
||||
expect(globalThis.URL.revokeObjectURL).not.toHaveBeenCalled(); // No URL to revoke initially
|
||||
});
|
||||
|
||||
test('should clear error message', async () => {
|
||||
// Mock a failed API response
|
||||
const mockResponse = {
|
||||
ok: false,
|
||||
text: () => Promise.resolve('API Error')
|
||||
};
|
||||
mockFetch.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const { result } = renderHook(() => useSanitizeOperation());
|
||||
|
||||
const parameters = {
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false
|
||||
};
|
||||
|
||||
const testFile = new File(['test'], 'test.pdf', { type: 'application/pdf' });
|
||||
|
||||
// Trigger an API error
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.executeOperation(parameters, [testFile]);
|
||||
} catch (error) {
|
||||
// Expected to throw
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.current.errorMessage).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
result.current.clearError();
|
||||
});
|
||||
|
||||
expect(result.current.errorMessage).toBe(null);
|
||||
});
|
||||
});
|
110
frontend/src/hooks/tools/sanitize/useSanitizeParameters.test.ts
Normal file
110
frontend/src/hooks/tools/sanitize/useSanitizeParameters.test.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useSanitizeParameters } from './useSanitizeParameters';
|
||||
|
||||
describe('useSanitizeParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
expect(result.current.parameters).toEqual({
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('should update individual parameters', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('removeXMPMetadata', true);
|
||||
});
|
||||
|
||||
expect(result.current.parameters.removeXMPMetadata).toBe(true);
|
||||
expect(result.current.parameters.removeJavaScript).toBe(true); // Other params unchanged
|
||||
expect(result.current.parameters.removeLinks).toBe(false); // Other params unchanged
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
result.current.updateParameter('removeXMPMetadata', true);
|
||||
result.current.updateParameter('removeJavaScript', false);
|
||||
});
|
||||
|
||||
expect(result.current.parameters.removeXMPMetadata).toBe(true);
|
||||
expect(result.current.parameters.removeJavaScript).toBe(false);
|
||||
|
||||
// Then reset
|
||||
act(() => {
|
||||
result.current.resetParameters();
|
||||
});
|
||||
|
||||
expect(result.current.parameters).toEqual({
|
||||
removeJavaScript: true,
|
||||
removeEmbeddedFiles: true,
|
||||
removeXMPMetadata: false,
|
||||
removeMetadata: false,
|
||||
removeLinks: false,
|
||||
removeFonts: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('sanitize-pdf');
|
||||
});
|
||||
|
||||
test('should validate parameters correctly', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
// Default state should be valid (has removeJavaScript and removeEmbeddedFiles enabled)
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
|
||||
// Turn off all parameters - should be invalid
|
||||
act(() => {
|
||||
result.current.updateParameter('removeJavaScript', false);
|
||||
result.current.updateParameter('removeEmbeddedFiles', false);
|
||||
});
|
||||
|
||||
expect(result.current.validateParameters()).toBe(false);
|
||||
|
||||
// Turn on one parameter - should be valid again
|
||||
act(() => {
|
||||
result.current.updateParameter('removeLinks', true);
|
||||
});
|
||||
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle all parameter types correctly', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
|
||||
const allParameters = [
|
||||
'removeJavaScript',
|
||||
'removeEmbeddedFiles',
|
||||
'removeXMPMetadata',
|
||||
'removeMetadata',
|
||||
'removeLinks',
|
||||
'removeFonts'
|
||||
] as const;
|
||||
|
||||
allParameters.forEach(param => {
|
||||
act(() => {
|
||||
result.current.updateParameter(param, true);
|
||||
});
|
||||
expect(result.current.parameters[param]).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(param, false);
|
||||
});
|
||||
expect(result.current.parameters[param]).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user