diff --git a/frontend/src/components/tools/removePassword/RemovePasswordSettings.test.tsx b/frontend/src/components/tools/removePassword/RemovePasswordSettings.test.tsx new file mode 100644 index 000000000..9aac9cd31 --- /dev/null +++ b/frontend/src/components/tools/removePassword/RemovePasswordSettings.test.tsx @@ -0,0 +1,127 @@ +import { describe, expect, test, vi, beforeEach } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { MantineProvider } from '@mantine/core'; +import RemovePasswordSettings from './RemovePasswordSettings'; +import { defaultParameters } from '../../../hooks/tools/removePassword/useRemovePasswordParameters'; + +// 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 }) => ( + {children} +); + +describe('RemovePasswordSettings', () => { + const mockOnParameterChange = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + test('should render password input field', () => { + render( + + + + ); + + expect(screen.getByText('mock-removePassword.password.label')).toBeInTheDocument(); + }); + + test('should call onParameterChange when password is entered', () => { + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder'); + fireEvent.change(passwordInput, { target: { value: 'test-password' } }); + + expect(mockOnParameterChange).toHaveBeenCalledWith('password', 'test-password'); + }); + + test('should display current password value', () => { + const parametersWithPassword = { ...defaultParameters, password: 'current-password' }; + + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder') as HTMLInputElement; + expect(passwordInput.value).toBe('current-password'); + }); + + test('should disable password input when disabled prop is true', () => { + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder'); + expect(passwordInput).toBeDisabled(); + }); + + test('should enable password input when disabled prop is false', () => { + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder'); + expect(passwordInput).not.toBeDisabled(); + }); + + test('should show password input as required', () => { + render( + + + + ); + + const passwordInput = screen.getByPlaceholderText('mock-removePassword.password.placeholder'); + expect(passwordInput).toHaveAttribute('required'); + }); + + test('should call translation function with correct keys', () => { + render( + + + + ); + + expect(mockT).toHaveBeenCalledWith('removePassword.password.label', 'Current Password'); + expect(mockT).toHaveBeenCalledWith('removePassword.password.placeholder', 'Enter current password'); + }); +}); diff --git a/frontend/src/hooks/tools/removePassword/useRemovePasswordOperation.test.ts b/frontend/src/hooks/tools/removePassword/useRemovePasswordOperation.test.ts new file mode 100644 index 000000000..66cd240fc --- /dev/null +++ b/frontend/src/hooks/tools/removePassword/useRemovePasswordOperation.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, test, vi, beforeEach } from 'vitest'; +import { renderHook } from '@testing-library/react'; +import { useRemovePasswordOperation } from './useRemovePasswordOperation'; +import type { RemovePasswordParameters } from './useRemovePasswordParameters'; + +// Mock the useToolOperation hook +vi.mock('../shared/useToolOperation', () => ({ + useToolOperation: vi.fn() +})); + +// Mock the translation hook +const mockT = vi.fn((key: string) => `translated-${key}`); +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ t: mockT }) +})); + +// Mock the error handler +vi.mock('../../../utils/toolErrorHandler', () => ({ + createStandardErrorHandler: vi.fn(() => 'error-handler-function') +})); + +// Import the mocked function +import { ToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation'; + +describe('useRemovePasswordOperation', () => { + const mockUseToolOperation = vi.mocked(useToolOperation); + + const getToolConfig = (): ToolOperationConfig => mockUseToolOperation.mock.calls[0][0]; + + const mockToolOperationReturn: ToolOperationHook = { + files: [], + thumbnails: [], + downloadUrl: null, + downloadFilename: '', + isLoading: false, + errorMessage: null, + status: '', + isGeneratingThumbnails: false, + progress: null, + executeOperation: vi.fn(), + resetResults: vi.fn(), + clearError: vi.fn(), + cancelOperation: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + mockUseToolOperation.mockReturnValue(mockToolOperationReturn); + }); + + test.each([ + { + description: 'with valid password', + password: 'test-password' + }, + { + description: 'with complex password', + password: 'C0mpl3x@P@ssw0rd!' + }, + { + description: 'with single character password', + password: 'a' + } + ])('should create form data correctly $description', ({ password }) => { + renderHook(() => useRemovePasswordOperation()); + + const callArgs = getToolConfig(); + const buildFormData = callArgs.buildFormData; + + const testParameters: RemovePasswordParameters = { + password + }; + + const testFile = new File(['test content'], 'test.pdf', { type: 'application/pdf' }); + const formData = buildFormData(testParameters, testFile as any); + + // Verify the form data contains the file + expect(formData.get('fileInput')).toBe(testFile); + + // Verify password parameter + expect(formData.get('password')).toBe(password); + }); + + test('should use correct translation for error messages', () => { + renderHook(() => useRemovePasswordOperation()); + + expect(mockT).toHaveBeenCalledWith( + 'removePassword.error.failed', + 'An error occurred while removing the password from the PDF.' + ); + }); + + test.each([ + { property: 'multiFileEndpoint' as const, expectedValue: false }, + { property: 'endpoint' as const, expectedValue: '/api/v1/security/remove-password' }, + { property: 'filePrefix' as const, expectedValue: 'translated-removePassword.filenamePrefix_' }, + { property: 'operationType' as const, expectedValue: 'removePassword' } + ])('should configure $property correctly', ({ property, expectedValue }) => { + renderHook(() => useRemovePasswordOperation()); + + const callArgs = getToolConfig(); + expect(callArgs[property]).toBe(expectedValue); + }); +}); diff --git a/frontend/src/hooks/tools/removePassword/useRemovePasswordParameters.test.ts b/frontend/src/hooks/tools/removePassword/useRemovePasswordParameters.test.ts new file mode 100644 index 000000000..f7310847b --- /dev/null +++ b/frontend/src/hooks/tools/removePassword/useRemovePasswordParameters.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, test } from 'vitest'; +import { renderHook, act } from '@testing-library/react'; +import { useRemovePasswordParameters, defaultParameters } from './useRemovePasswordParameters'; + +describe('useRemovePasswordParameters', () => { + test('should initialize with default parameters', () => { + const { result } = renderHook(() => useRemovePasswordParameters()); + + expect(result.current.parameters).toStrictEqual(defaultParameters); + }); + + test('should update password parameter', () => { + const { result } = renderHook(() => useRemovePasswordParameters()); + + act(() => { + result.current.updateParameter('password', 'test-password'); + }); + + expect(result.current.parameters.password).toBe('test-password'); + }); + + test('should reset parameters to defaults', () => { + const { result } = renderHook(() => useRemovePasswordParameters()); + + // First, change the password + act(() => { + result.current.updateParameter('password', 'test-password'); + }); + + expect(result.current.parameters.password).toBe('test-password'); + + // Then reset + act(() => { + result.current.resetParameters(); + }); + + expect(result.current.parameters).toStrictEqual(defaultParameters); + }); + + test('should return correct endpoint name', () => { + const { result } = renderHook(() => useRemovePasswordParameters()); + + expect(result.current.getEndpointName()).toBe('remove-password'); + }); + + test.each([ + { + description: 'with valid password', + password: 'valid-password', + expectedValid: true + }, + { + description: 'with empty password', + password: '', + expectedValid: false + }, + { + description: 'with whitespace only password', + password: ' \t ', + expectedValid: true + }, + { + description: 'with password containing special characters', + password: 'p@ssw0rd!', + expectedValid: true + }, + { + description: 'with single character password', + password: 'a', + expectedValid: true + } + ])('should validate parameters correctly $description', ({ password, expectedValid }) => { + const { result } = renderHook(() => useRemovePasswordParameters()); + + act(() => { + result.current.updateParameter('password', password); + }); + + expect(result.current.validateParameters()).toBe(expectedValid); + }); +});