mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
Add Remove Password UI into V2 (#4214)
# Description of Changes - Add UI for Remove Password tool - Fix more translation warnings that were being thrown in the console - Add an encrypted PDF thumbnail and refactor thumbnail generation code
This commit is contained in:
@@ -49,25 +49,6 @@ describe('useAddPasswordOperation', () => {
|
||||
mockUseToolOperation.mockReturnValue(mockToolOperationReturn);
|
||||
});
|
||||
|
||||
test('should configure useToolOperation with correct parameters', () => {
|
||||
renderHook(() => useAddPasswordOperation());
|
||||
|
||||
expect(mockUseToolOperation).toHaveBeenCalledWith({
|
||||
operationType: 'addPassword',
|
||||
endpoint: '/api/v1/security/add-password',
|
||||
buildFormData: expect.any(Function),
|
||||
filePrefix: 'translated-addPassword.filenamePrefix_',
|
||||
multiFileEndpoint: false,
|
||||
getErrorMessage: 'error-handler-function'
|
||||
});
|
||||
});
|
||||
|
||||
test('should return the result from useToolOperation', () => {
|
||||
const { result } = renderHook(() => useAddPasswordOperation());
|
||||
|
||||
expect(result.current).toBe(mockToolOperationReturn);
|
||||
});
|
||||
|
||||
test.each([
|
||||
{
|
||||
description: 'with all parameters filled',
|
||||
|
||||
@@ -48,25 +48,6 @@ describe('useChangePermissionsOperation', () => {
|
||||
mockUseToolOperation.mockReturnValue(mockToolOperationReturn);
|
||||
});
|
||||
|
||||
test('should configure useToolOperation with correct parameters', () => {
|
||||
renderHook(() => useChangePermissionsOperation());
|
||||
|
||||
expect(mockUseToolOperation).toHaveBeenCalledWith({
|
||||
operationType: 'changePermissions',
|
||||
endpoint: '/api/v1/security/add-password',
|
||||
buildFormData: expect.any(Function),
|
||||
filePrefix: 'permissions_',
|
||||
multiFileEndpoint: false,
|
||||
getErrorMessage: 'error-handler-function'
|
||||
});
|
||||
});
|
||||
|
||||
test('should return the result from useToolOperation', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsOperation());
|
||||
|
||||
expect(result.current).toBe(mockToolOperationReturn);
|
||||
});
|
||||
|
||||
test.each([
|
||||
{
|
||||
preventAssembly: false,
|
||||
|
||||
@@ -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<RemovePasswordParameters> => mockUseToolOperation.mock.calls[0][0];
|
||||
|
||||
const mockToolOperationReturn: ToolOperationHook<unknown> = {
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useToolOperation } from '../shared/useToolOperation';
|
||||
import { createStandardErrorHandler } from '../../../utils/toolErrorHandler';
|
||||
import { RemovePasswordParameters } from './useRemovePasswordParameters';
|
||||
|
||||
export const useRemovePasswordOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const buildFormData = (parameters: RemovePasswordParameters, file: File): FormData => {
|
||||
const formData = new FormData();
|
||||
formData.append("fileInput", file);
|
||||
formData.append("password", parameters.password);
|
||||
return formData;
|
||||
};
|
||||
|
||||
return useToolOperation<RemovePasswordParameters>({
|
||||
operationType: 'removePassword',
|
||||
endpoint: '/api/v1/security/remove-password',
|
||||
buildFormData,
|
||||
filePrefix: t('removePassword.filenamePrefix', 'decrypted') + '_',
|
||||
multiFileEndpoint: false,
|
||||
getErrorMessage: createStandardErrorHandler(t('removePassword.error.failed', 'An error occurred while removing the password from the PDF.'))
|
||||
});
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface RemovePasswordParameters {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface RemovePasswordParametersHook {
|
||||
parameters: RemovePasswordParameters;
|
||||
updateParameter: <K extends keyof RemovePasswordParameters>(parameter: K, value: RemovePasswordParameters[K]) => void;
|
||||
resetParameters: () => void;
|
||||
validateParameters: () => boolean;
|
||||
getEndpointName: () => string;
|
||||
}
|
||||
|
||||
export const defaultParameters: RemovePasswordParameters = {
|
||||
password: '',
|
||||
};
|
||||
|
||||
export const useRemovePasswordParameters = (): RemovePasswordParametersHook => {
|
||||
const [parameters, setParameters] = useState<RemovePasswordParameters>(defaultParameters);
|
||||
|
||||
const updateParameter = <K extends keyof RemovePasswordParameters>(parameter: K, value: RemovePasswordParameters[K]) => {
|
||||
setParameters(prev => ({
|
||||
...prev,
|
||||
[parameter]: value,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const resetParameters = () => {
|
||||
setParameters(defaultParameters);
|
||||
};
|
||||
|
||||
const validateParameters = () => {
|
||||
return parameters.password !== '';
|
||||
};
|
||||
|
||||
const getEndpointName = () => {
|
||||
return 'remove-password';
|
||||
};
|
||||
|
||||
return {
|
||||
parameters,
|
||||
updateParameter,
|
||||
resetParameters,
|
||||
validateParameters,
|
||||
getEndpointName,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user