mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20a6a1c954 | ||
|
|
2c84587387 |
@@ -353,6 +353,8 @@
|
||||
"autoUnzipFileLimitTooltip": "Only unzip if the ZIP contains this many files or fewer. Set higher to extract larger ZIPs.",
|
||||
"defaultToolPickerMode": "Default tool picker mode",
|
||||
"defaultToolPickerModeDescription": "Choose whether the tool picker opens in fullscreen or sidebar by default",
|
||||
"rememberInputs": "Remember tool inputs",
|
||||
"rememberInputsDescription": "Store the last settings you used for each tool on this device",
|
||||
"mode": {
|
||||
"fullscreen": "Fullscreen",
|
||||
"sidebar": "Sidebar"
|
||||
|
||||
@@ -323,6 +323,8 @@
|
||||
"autoUnzipFileLimitTooltip": "Only unzip if the ZIP contains this many files or fewer. Set higher to extract larger ZIPs.",
|
||||
"defaultToolPickerMode": "Default tool picker mode",
|
||||
"defaultToolPickerModeDescription": "Choose whether the tool picker opens in fullscreen or sidebar by default",
|
||||
"rememberInputs": "Remember tool inputs",
|
||||
"rememberInputsDescription": "Store the last settings you used for each tool on this device",
|
||||
"mode": {
|
||||
"fullscreen": "Fullscreen",
|
||||
"sidebar": "Sidebar"
|
||||
|
||||
@@ -114,6 +114,23 @@ const GeneralSection: React.FC<GeneralSectionProps> = ({ hideTitle = false }) =>
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<Text fw={500} size="sm">
|
||||
{t('settings.general.rememberInputs', 'Remember tool inputs')}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
{t(
|
||||
'settings.general.rememberInputsDescription',
|
||||
'Store the last settings you used for each tool on this device'
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
<Switch
|
||||
checked={preferences.rememberFormInputs}
|
||||
onChange={(event) => updatePreference('rememberFormInputs', event.currentTarget.checked)}
|
||||
/>
|
||||
</div>
|
||||
<Tooltip
|
||||
label={t('settings.general.autoUnzipTooltip', 'Automatically extract ZIP files returned from API operations. Disable to keep ZIP files intact. This does not affect automation workflows.')}
|
||||
multiline
|
||||
|
||||
@@ -2,10 +2,13 @@ import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useAddPasswordParameters, defaultParameters, AddPasswordParametersHook } from '@app/hooks/tools/addPassword/useAddPasswordParameters';
|
||||
import { defaultParameters as defaultChangePermissionsParameters, ChangePermissionsParameters } from '@app/hooks/tools/changePermissions/useChangePermissionsParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderAddPasswordHook = () => renderHook(() => useAddPasswordParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useAddPasswordParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
@@ -15,7 +18,7 @@ describe('useAddPasswordParameters', () => {
|
||||
{ paramName: 'ownerPassword' as const, value: 'owner-password' },
|
||||
{ paramName: 'keyLength' as const, value: 256 }
|
||||
])('should update parameter $paramName', ({ paramName, value }) => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(paramName, value);
|
||||
@@ -28,7 +31,7 @@ describe('useAddPasswordParameters', () => {
|
||||
{ paramName: 'preventAssembly' as const },
|
||||
{ paramName: 'preventPrinting' as const }
|
||||
])('should update boolean permission parameter $paramName', ({ paramName }) => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.permissions.updateParameter(paramName, true);
|
||||
@@ -38,7 +41,7 @@ describe('useAddPasswordParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
@@ -60,7 +63,7 @@ describe('useAddPasswordParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('add-password');
|
||||
});
|
||||
@@ -112,7 +115,7 @@ describe('useAddPasswordParameters', () => {
|
||||
}
|
||||
}
|
||||
])('should validate parameters correctly $description', ({ setup }) => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
// Default state should be valid
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
@@ -126,7 +129,7 @@ describe('useAddPasswordParameters', () => {
|
||||
});
|
||||
|
||||
test.each(Object.keys(defaultChangePermissionsParameters) as Array<keyof ChangePermissionsParameters>)('should handle boolean restriction parameter %s', (param) => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.resetParameters();
|
||||
@@ -137,7 +140,7 @@ describe('useAddPasswordParameters', () => {
|
||||
});
|
||||
|
||||
test('should handle mixed parameter types in updateParameter', () => {
|
||||
const { result } = renderHook(() => useAddPasswordParameters());
|
||||
const { result } = renderAddPasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('password', 'test-string');
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useAdjustPageScaleParameters, defaultParameters, PageSize, AdjustPageScaleParametersHook } from '@app/hooks/tools/adjustPageScale/useAdjustPageScaleParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderAdjustPageScaleHook = () => renderHook(() => useAdjustPageScaleParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useAdjustPageScaleParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
expect(result.current.parameters.scaleFactor).toBe(1.0);
|
||||
@@ -19,7 +22,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
{ paramName: 'pageSize' as const, value: PageSize.LETTER },
|
||||
{ paramName: 'pageSize' as const, value: PageSize.LEGAL },
|
||||
])('should update parameter $paramName to $value', ({ paramName, value }) => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(paramName, value);
|
||||
@@ -29,7 +32,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
@@ -49,7 +52,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('scale-pages');
|
||||
});
|
||||
@@ -96,7 +99,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
expected: false
|
||||
}
|
||||
])('should validate parameters correctly $description', ({ setup, expected }) => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
act(() => {
|
||||
setup(result.current);
|
||||
@@ -106,7 +109,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
});
|
||||
|
||||
test('should handle all PageSize enum values', () => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
Object.values(PageSize).forEach(pageSize => {
|
||||
act(() => {
|
||||
@@ -119,7 +122,7 @@ describe('useAdjustPageScaleParameters', () => {
|
||||
});
|
||||
|
||||
test('should handle scale factor edge cases', () => {
|
||||
const { result } = renderHook(() => useAdjustPageScaleParameters());
|
||||
const { result } = renderAdjustPageScaleHook();
|
||||
|
||||
// Test very small valid scale factor
|
||||
act(() => {
|
||||
|
||||
@@ -2,10 +2,13 @@ import { renderHook, act } from '@testing-library/react';
|
||||
import { defaultParameters, useChangeMetadataParameters } from '@app/hooks/tools/changeMetadata/useChangeMetadataParameters';
|
||||
import { TrappedStatus } from '@app/types/metadata';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderChangeMetadataHook = () => renderHook(() => useChangeMetadataParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useChangeMetadataParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
@@ -23,7 +26,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
{ paramName: 'trapped', value: TrappedStatus.TRUE },
|
||||
{ paramName: 'deleteAll', value: true },
|
||||
] as const)('should update $paramName parameter', ({ paramName, value }) => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(paramName, value);
|
||||
@@ -48,7 +51,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
{ description: 'no meaningful content', updates: {}, expected: false },
|
||||
{ description: 'whitespace only', updates: { title: ' ', author: ' ' }, expected: false },
|
||||
])('should validate correctly when $description', ({ updates, expected }) => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
@@ -64,7 +67,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
{ description: 'valid modification date', updates: { title: 'Test', modificationDate: new Date('2025/01/17 14:30:00') }, expected: true },
|
||||
{ description: 'empty dates are valid', updates: { title: 'Test', creationDate: null, modificationDate: null }, expected: true },
|
||||
])('should validate dates correctly with $description', ({ updates, expected }) => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
@@ -78,7 +81,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
|
||||
describe('custom metadata', () => {
|
||||
test('should add custom metadata with sequential IDs', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
result.current.addCustomMetadata();
|
||||
@@ -93,7 +96,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
});
|
||||
|
||||
test('should remove custom metadata by ID', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
result.current.addCustomMetadata();
|
||||
@@ -109,7 +112,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
});
|
||||
|
||||
test('should update custom metadata by ID', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
result.current.addCustomMetadata();
|
||||
@@ -129,7 +132,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
});
|
||||
|
||||
test('should validate with custom metadata', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
act(() => {
|
||||
result.current.addCustomMetadata();
|
||||
@@ -145,7 +148,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
});
|
||||
|
||||
test('should generate unique IDs for multiple custom entries', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
act(() => {
|
||||
@@ -161,7 +164,7 @@ describe('useChangeMetadataParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useChangeMetadataParameters());
|
||||
const { result } = renderChangeMetadataHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('update-metadata');
|
||||
});
|
||||
|
||||
+9
-6
@@ -1,16 +1,19 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useChangePermissionsParameters, defaultParameters, ChangePermissionsParameters } from '@app/hooks/tools/changePermissions/useChangePermissionsParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderChangePermissionsHook = () => renderHook(() => useChangePermissionsParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useChangePermissionsParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
|
||||
test('should update individual boolean parameters', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('preventAssembly', true);
|
||||
@@ -28,7 +31,7 @@ describe('useChangePermissionsParameters', () => {
|
||||
});
|
||||
|
||||
test('should update all permission parameters', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
const permissionKeys = Object.keys(defaultParameters) as Array<keyof ChangePermissionsParameters>;
|
||||
|
||||
@@ -56,7 +59,7 @@ describe('useChangePermissionsParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
@@ -78,13 +81,13 @@ describe('useChangePermissionsParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('add-password');
|
||||
});
|
||||
|
||||
test('should always validate as true', () => {
|
||||
const { result } = renderHook(() => useChangePermissionsParameters());
|
||||
const { result } = renderChangePermissionsHook();
|
||||
|
||||
// Default state should be valid
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
|
||||
@@ -6,13 +6,16 @@ import { describe, test, expect } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useConvertParameters } from '@app/hooks/tools/convert/useConvertParameters';
|
||||
import { FIT_OPTIONS } from '@app/constants/convertConstants';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderConvertHook = () => renderHook(() => useConvertParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useConvertParameters', () => {
|
||||
|
||||
describe('Parameter Management', () => {
|
||||
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
expect(result.current.parameters.fromExtension).toBe('');
|
||||
expect(result.current.parameters.toExtension).toBe('');
|
||||
@@ -28,7 +31,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should update individual parameters', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'pdf');
|
||||
@@ -39,7 +42,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should update nested image options', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('imageOptions', {
|
||||
@@ -61,7 +64,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should update nested HTML options', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('htmlOptions', {
|
||||
@@ -73,7 +76,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should update nested email options', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('emailOptions', {
|
||||
@@ -91,7 +94,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should update nested PDF/A options', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('pdfaOptions', {
|
||||
@@ -103,7 +106,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'pdf');
|
||||
@@ -124,7 +127,7 @@ describe('useConvertParameters', () => {
|
||||
describe('Parameter Validation', () => {
|
||||
|
||||
test('should validate parameters correctly', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
// No parameters - should be invalid
|
||||
expect(result.current.validateParameters()).toBe(false);
|
||||
@@ -143,7 +146,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should validate unsupported conversions', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'pdf');
|
||||
@@ -158,7 +161,7 @@ describe('useConvertParameters', () => {
|
||||
describe('Endpoint Generation', () => {
|
||||
|
||||
test('should generate correct endpoint names', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'pdf');
|
||||
@@ -170,7 +173,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should generate correct endpoint URLs', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'pdf');
|
||||
@@ -182,7 +185,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should return empty strings for invalid conversions', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('fromExtension', 'invalid');
|
||||
@@ -197,7 +200,7 @@ describe('useConvertParameters', () => {
|
||||
describe('Available Extensions', () => {
|
||||
|
||||
test('should return available extensions for valid source format', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const availableExtensions = result.current.getAvailableToExtensions('pdf');
|
||||
|
||||
@@ -207,7 +210,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should return empty array for invalid source format', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const availableExtensions = result.current.getAvailableToExtensions('invalid');
|
||||
|
||||
@@ -219,7 +222,7 @@ describe('useConvertParameters', () => {
|
||||
});
|
||||
|
||||
test('should return empty array for empty source format', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const availableExtensions = result.current.getAvailableToExtensions('');
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ export const useConvertParameters = (): ConvertParametersHook => {
|
||||
defaultParameters,
|
||||
endpointName: getEndpointName,
|
||||
validateFn: validateParameters,
|
||||
storageKey: 'convert',
|
||||
}), []);
|
||||
|
||||
const baseHook = useBaseParameters(config);
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useConvertParameters } from '@app/hooks/tools/convert/useConvertParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderConvertHook = () => renderHook(() => useConvertParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
|
||||
describe('Single File Detection', () => {
|
||||
|
||||
test('should detect single file extension and set auto-target', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const pdfFile = [{ name: 'document.pdf' }];
|
||||
|
||||
@@ -27,7 +30,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle unknown file types with file-to-pdf fallback', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const unknownFile = [{ name: 'document.xyz' }, { name: 'image.jpggg' }];
|
||||
|
||||
@@ -41,7 +44,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle files without extensions', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const noExtFile = [{ name: 'document' }];
|
||||
|
||||
@@ -59,7 +62,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Multiple Identical Files', () => {
|
||||
|
||||
test('should detect multiple PDF files and set auto-target', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const pdfFiles = [
|
||||
{ name: 'doc1.pdf' },
|
||||
@@ -78,7 +81,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle multiple unknown file types with fallback', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const unknownFiles = [
|
||||
{ name: 'file1.xyz' },
|
||||
@@ -98,7 +101,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Smart Detection - All Images', () => {
|
||||
|
||||
test('should detect all image files and enable smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const imageFiles = [
|
||||
{ name: 'photo1.jpg' },
|
||||
@@ -117,7 +120,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle mixed case image extensions', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const imageFiles = [
|
||||
{ name: 'photo1.JPG' },
|
||||
@@ -136,7 +139,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Smart Detection - All Web Files', () => {
|
||||
|
||||
test('should detect all web files and enable web smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const webFiles = [
|
||||
{ name: 'page1.html' },
|
||||
@@ -154,7 +157,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle mixed case web extensions', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const webFiles = [
|
||||
{ name: 'page1.HTML' },
|
||||
@@ -170,7 +173,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should detect multiple web files and enable web smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const zipFiles = [
|
||||
{ name: 'site1.zip' },
|
||||
@@ -191,7 +194,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Smart Detection - Mixed File Types', () => {
|
||||
|
||||
test('should detect mixed file types and enable smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const mixedFiles = [
|
||||
{ name: 'document.pdf' },
|
||||
@@ -210,7 +213,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should detect mixed images and documents as mixed type', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const mixedFiles = [
|
||||
{ name: 'photo.jpg' },
|
||||
@@ -227,7 +230,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle mixed with unknown file types', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const mixedFiles = [
|
||||
{ name: 'document.pdf' },
|
||||
@@ -247,7 +250,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Smart Detection Endpoint Resolution', () => {
|
||||
|
||||
test('should return correct endpoint for image smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const imageFiles = [
|
||||
{ name: 'photo1.jpg' },
|
||||
@@ -263,7 +266,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint for web smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const webFiles = [
|
||||
{ name: 'page1.html' },
|
||||
@@ -279,7 +282,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint for mixed smart detection', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const mixedFiles = [
|
||||
{ name: 'document.pdf' },
|
||||
@@ -298,7 +301,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Auto-Target Selection Logic', () => {
|
||||
|
||||
test('should select single available target automatically', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
// Markdown has only one conversion target (PDF)
|
||||
const mdFile = [{ name: 'readme.md' }];
|
||||
@@ -312,7 +315,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should not auto-select when multiple targets available', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
// PDF has multiple conversion targets, so no auto-selection
|
||||
const pdfFile = [{ name: 'document.pdf' }];
|
||||
@@ -330,7 +333,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
describe('Edge Cases', () => {
|
||||
|
||||
test('should handle empty file names', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const emptyFiles = [{ name: '' }];
|
||||
|
||||
@@ -343,7 +346,7 @@ describe('useConvertParameters - Auto Detection & Smart Conversion', () => {
|
||||
});
|
||||
|
||||
test('should handle malformed file objects', () => {
|
||||
const { result } = renderHook(() => useConvertParameters());
|
||||
const { result } = renderConvertHook();
|
||||
|
||||
const malformedFiles: Array<{name: string}> = [
|
||||
{ name: 'valid.pdf' },
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useMergeParameters, defaultParameters } from '@app/hooks/tools/merge/useMergeParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderMergeHook = () => renderHook(() => useMergeParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useMergeParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useMergeParameters());
|
||||
const { result } = renderMergeHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
@@ -15,7 +18,7 @@ describe('useMergeParameters', () => {
|
||||
{ paramName: 'generateTableOfContents' as const, value: true },
|
||||
{ paramName: 'generateTableOfContents' as const, value: false }
|
||||
])('should update parameter $paramName to $value', ({ paramName, value }) => {
|
||||
const { result } = renderHook(() => useMergeParameters());
|
||||
const { result } = renderMergeHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(paramName, value);
|
||||
@@ -25,7 +28,7 @@ describe('useMergeParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useMergeParameters());
|
||||
const { result } = renderMergeHook();
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
@@ -45,7 +48,7 @@ describe('useMergeParameters', () => {
|
||||
});
|
||||
|
||||
test('should validate parameters correctly - always returns true', () => {
|
||||
const { result } = renderHook(() => useMergeParameters());
|
||||
const { result } = renderMergeHook();
|
||||
|
||||
// Default state should be valid
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useRedactParameters, defaultParameters } from '@app/hooks/tools/redact/useRedactParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderRedactHook = () => renderHook(() => useRedactParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useRedactParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
@@ -18,7 +21,7 @@ describe('useRedactParameters', () => {
|
||||
{ paramName: 'customPadding' as const, value: 0.5 },
|
||||
{ paramName: 'convertPDFToImage' as const, value: false }
|
||||
])('should update parameter $paramName', ({ paramName, value }) => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter(paramName, value);
|
||||
@@ -28,7 +31,7 @@ describe('useRedactParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
// Modify some parameters
|
||||
act(() => {
|
||||
@@ -52,7 +55,7 @@ describe('useRedactParameters', () => {
|
||||
{ description: 'not validate when wordsToRedact contains only empty strings in automatic mode', wordsToRedact: ['', ' ', ''], expected: false },
|
||||
{ description: 'validate when wordsToRedact contains at least one non-empty word in automatic mode', wordsToRedact: ['', 'valid', ' '], expected: true },
|
||||
])('should $description', ({ wordsToRedact, expected }) => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('mode', 'automatic');
|
||||
@@ -63,7 +66,7 @@ describe('useRedactParameters', () => {
|
||||
});
|
||||
|
||||
test('should not validate in manual mode (not implemented)', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('mode', 'manual');
|
||||
@@ -75,7 +78,7 @@ describe('useRedactParameters', () => {
|
||||
|
||||
describe('endpoint handling', () => {
|
||||
test('should return correct endpoint for automatic mode', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('mode', 'automatic');
|
||||
@@ -85,7 +88,7 @@ describe('useRedactParameters', () => {
|
||||
});
|
||||
|
||||
test('should throw error for manual mode (not implemented)', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('mode', 'manual');
|
||||
@@ -96,7 +99,7 @@ describe('useRedactParameters', () => {
|
||||
});
|
||||
|
||||
test('should maintain parameter state across updates', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('redactColor', '#FF0000');
|
||||
@@ -117,7 +120,7 @@ describe('useRedactParameters', () => {
|
||||
});
|
||||
|
||||
test('should handle array parameter updates correctly', () => {
|
||||
const { result } = renderHook(() => useRedactParameters());
|
||||
const { result } = renderRedactHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('wordsToRedact', ['initial']);
|
||||
|
||||
@@ -43,6 +43,7 @@ export const useRedactParameters = (): RedactParametersHook => {
|
||||
}
|
||||
// Manual mode validation would go here when implemented
|
||||
return false;
|
||||
}
|
||||
},
|
||||
storageKey: 'redact'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useRemovePasswordParameters, defaultParameters } from '@app/hooks/tools/removePassword/useRemovePasswordParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderRemovePasswordHook = () => renderHook(() => useRemovePasswordParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useRemovePasswordParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useRemovePasswordParameters());
|
||||
const { result } = renderRemovePasswordHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
|
||||
test('should update password parameter', () => {
|
||||
const { result } = renderHook(() => useRemovePasswordParameters());
|
||||
const { result } = renderRemovePasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('password', 'test-password');
|
||||
@@ -20,7 +23,7 @@ describe('useRemovePasswordParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useRemovePasswordParameters());
|
||||
const { result } = renderRemovePasswordHook();
|
||||
|
||||
// First, change the password
|
||||
act(() => {
|
||||
@@ -38,7 +41,7 @@ describe('useRemovePasswordParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useRemovePasswordParameters());
|
||||
const { result } = renderRemovePasswordHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('remove-password');
|
||||
});
|
||||
@@ -70,7 +73,7 @@ describe('useRemovePasswordParameters', () => {
|
||||
expectedValid: true
|
||||
}
|
||||
])('should validate parameters correctly $description', ({ password, expectedValid }) => {
|
||||
const { result } = renderHook(() => useRemovePasswordParameters());
|
||||
const { result } = renderRemovePasswordHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('password', password);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useRotateParameters, defaultParameters, normalizeAngle } from '@app/hooks/tools/rotate/useRotateParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderRotateHook = () => renderHook(() => useRotateParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useRotateParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
expect(result.current.parameters).toEqual(defaultParameters);
|
||||
expect(result.current.parameters.angle).toBe(0);
|
||||
@@ -12,7 +15,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should validate parameters correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
// Default should be valid
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
@@ -31,7 +34,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should rotate clockwise correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
act(() => {
|
||||
result.current.rotateClockwise();
|
||||
@@ -58,7 +61,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should rotate anticlockwise correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
act(() => {
|
||||
result.current.rotateAnticlockwise();
|
||||
@@ -85,7 +88,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should normalize angles correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
expect(result.current.normalizeAngle(360)).toBe(0);
|
||||
expect(result.current.normalizeAngle(450)).toBe(90);
|
||||
@@ -94,7 +97,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
// Set some rotation
|
||||
act(() => {
|
||||
@@ -116,7 +119,7 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should update parameters', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('angle', 450);
|
||||
@@ -132,13 +135,13 @@ describe('useRotateParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('rotate-pdf');
|
||||
});
|
||||
|
||||
test('should detect rotation state correctly', () => {
|
||||
const { result } = renderHook(() => useRotateParameters());
|
||||
const { result } = renderRotateHook();
|
||||
|
||||
// Initially no rotation
|
||||
expect(result.current.hasRotation).toBe(false);
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { defaultParameters, useSanitizeParameters } from '@app/hooks/tools/sanitize/useSanitizeParameters';
|
||||
import { PreferencesTestWrapper } from '@testing/preferencesTestWrapper';
|
||||
|
||||
const renderSanitizeHook = () => renderHook(() => useSanitizeParameters(), { wrapper: PreferencesTestWrapper });
|
||||
|
||||
describe('useSanitizeParameters', () => {
|
||||
test('should initialize with default parameters', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
expect(result.current.parameters).toStrictEqual(defaultParameters);
|
||||
});
|
||||
|
||||
test('should update individual parameters', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
act(() => {
|
||||
result.current.updateParameter('removeXMPMetadata', true);
|
||||
@@ -23,7 +26,7 @@ describe('useSanitizeParameters', () => {
|
||||
});
|
||||
|
||||
test('should reset parameters to defaults', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
// First, change some parameters
|
||||
act(() => {
|
||||
@@ -43,13 +46,13 @@ describe('useSanitizeParameters', () => {
|
||||
});
|
||||
|
||||
test('should return correct endpoint name', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
expect(result.current.getEndpointName()).toBe('sanitize-pdf');
|
||||
});
|
||||
|
||||
test('should validate parameters correctly', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
// Default state should be valid (has removeJavaScript and removeEmbeddedFiles enabled)
|
||||
expect(result.current.validateParameters()).toBe(true);
|
||||
@@ -71,7 +74,7 @@ describe('useSanitizeParameters', () => {
|
||||
});
|
||||
|
||||
test('should handle all parameter types correctly', () => {
|
||||
const { result } = renderHook(() => useSanitizeParameters());
|
||||
const { result } = renderSanitizeHook();
|
||||
|
||||
const allParameters = Object.keys(defaultParameters) as (keyof typeof defaultParameters)[];
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useState, useCallback, Dispatch, SetStateAction } from 'react';
|
||||
import { useState, useCallback, Dispatch, SetStateAction, useRef, useEffect } from 'react';
|
||||
import { usePreferences } from '@app/contexts/PreferencesContext';
|
||||
import { loadToolParameters, saveToolParameters } from '@app/services/toolParameterStorage';
|
||||
|
||||
export interface BaseParametersHook<T> {
|
||||
parameters: T;
|
||||
@@ -13,10 +15,31 @@ export interface BaseParametersConfig<T> {
|
||||
defaultParameters: T;
|
||||
endpointName: string | ((params: T) => string);
|
||||
validateFn?: (params: T) => boolean;
|
||||
storageKey?: string;
|
||||
}
|
||||
|
||||
export function useBaseParameters<T>(config: BaseParametersConfig<T>): BaseParametersHook<T> {
|
||||
const [parameters, setParameters] = useState<T>(config.defaultParameters);
|
||||
const { preferences } = usePreferences();
|
||||
const storageKey = config.storageKey ?? (typeof config.endpointName === 'string' ? config.endpointName : undefined);
|
||||
|
||||
const shouldPersist = Boolean(storageKey && preferences.rememberFormInputs);
|
||||
const hasHydratedFromStorage = useRef(false);
|
||||
|
||||
const mergeWithDefaults = useCallback((values?: Partial<T>): T => ({
|
||||
...(config.defaultParameters as T),
|
||||
...(values ?? {}),
|
||||
}), [config.defaultParameters]);
|
||||
|
||||
const [parameters, setParameters] = useState<T>(() => {
|
||||
if (shouldPersist && storageKey) {
|
||||
const stored = loadToolParameters<T>(storageKey);
|
||||
if (stored) {
|
||||
hasHydratedFromStorage.current = true;
|
||||
return mergeWithDefaults(stored);
|
||||
}
|
||||
}
|
||||
return mergeWithDefaults();
|
||||
});
|
||||
|
||||
const updateParameter = useCallback(<K extends keyof T>(parameter: K, value: T[K]) => {
|
||||
setParameters(prev => ({
|
||||
@@ -33,6 +56,34 @@ export function useBaseParameters<T>(config: BaseParametersConfig<T>): BaseParam
|
||||
return config.validateFn ? config.validateFn(parameters) : true;
|
||||
}, [parameters, config.validateFn]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!storageKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preferences.rememberFormInputs) {
|
||||
hasHydratedFromStorage.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasHydratedFromStorage.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stored = loadToolParameters<T>(storageKey);
|
||||
if (stored) {
|
||||
setParameters(mergeWithDefaults(stored));
|
||||
}
|
||||
hasHydratedFromStorage.current = true;
|
||||
}, [mergeWithDefaults, preferences.rememberFormInputs, storageKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!storageKey || !preferences.rememberFormInputs) {
|
||||
return;
|
||||
}
|
||||
saveToolParameters(storageKey, parameters);
|
||||
}, [parameters, preferences.rememberFormInputs, storageKey]);
|
||||
|
||||
const endpointName = config.endpointName;
|
||||
let getEndpointName: () => string;
|
||||
if (typeof endpointName === "string") {
|
||||
|
||||
@@ -57,5 +57,6 @@ export const useSplitParameters = (): SplitParametersHook => {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
storageKey: 'split',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface UserPreferences {
|
||||
toolPanelModePromptSeen: boolean;
|
||||
showLegacyToolDescriptions: boolean;
|
||||
hasCompletedOnboarding: boolean;
|
||||
rememberFormInputs: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_PREFERENCES: UserPreferences = {
|
||||
@@ -19,6 +20,7 @@ export const DEFAULT_PREFERENCES: UserPreferences = {
|
||||
toolPanelModePromptSeen: false,
|
||||
showLegacyToolDescriptions: false,
|
||||
hasCompletedOnboarding: false,
|
||||
rememberFormInputs: false,
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'stirlingpdf_preferences';
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
const STORAGE_KEY = 'stirlingpdf_tool_parameters';
|
||||
|
||||
type SerializableValue = string | number | boolean | null | SerializableValue[] | { [key: string]: SerializableValue };
|
||||
|
||||
const isFile = (value: unknown): value is File => typeof File !== 'undefined' && value instanceof File;
|
||||
const isBlob = (value: unknown): value is Blob => typeof Blob !== 'undefined' && value instanceof Blob;
|
||||
|
||||
function sanitizeValue(value: unknown): SerializableValue | undefined {
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const valueType = typeof value;
|
||||
|
||||
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') {
|
||||
if (valueType === 'number' && Number.isNaN(value)) {
|
||||
return null;
|
||||
}
|
||||
return value as SerializableValue;
|
||||
}
|
||||
|
||||
if (valueType === 'undefined' || valueType === 'function' || valueType === 'symbol' || valueType === 'bigint') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const sanitizedArray = value
|
||||
.map((item) => sanitizeValue(item))
|
||||
.filter((item) => item !== undefined) as SerializableValue[];
|
||||
return sanitizedArray;
|
||||
}
|
||||
|
||||
if (isFile(value) || isBlob(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (valueType === 'object' && value !== null) {
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
const sanitizedEntries: Record<string, SerializableValue> = {};
|
||||
|
||||
for (const [key, entryValue] of entries) {
|
||||
const sanitized = sanitizeValue(entryValue);
|
||||
if (sanitized !== undefined) {
|
||||
sanitizedEntries[key] = sanitized;
|
||||
}
|
||||
}
|
||||
|
||||
return sanitizedEntries;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readStorage(): Record<string, SerializableValue> {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return {};
|
||||
}
|
||||
const parsed = JSON.parse(raw);
|
||||
if (typeof parsed === 'object' && parsed !== null) {
|
||||
return parsed as Record<string, SerializableValue>;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[toolParameterStorage] Failed to read stored parameters', error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function writeStorage(map: Record<string, SerializableValue>): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(map));
|
||||
} catch (error) {
|
||||
console.error('[toolParameterStorage] Failed to write stored parameters', error);
|
||||
}
|
||||
}
|
||||
|
||||
export function loadToolParameters<T>(toolKey: string): Partial<T> | null {
|
||||
const storage = readStorage();
|
||||
const stored = storage[toolKey];
|
||||
|
||||
if (!stored || typeof stored !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stored as Partial<T>;
|
||||
}
|
||||
|
||||
export function saveToolParameters<T>(toolKey: string, parameters: T): void {
|
||||
const sanitized = sanitizeValue(parameters);
|
||||
if (sanitized === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const storage = readStorage();
|
||||
storage[toolKey] = sanitized;
|
||||
writeStorage(storage);
|
||||
}
|
||||
|
||||
export function clearToolParameters(toolKey: string): void {
|
||||
const storage = readStorage();
|
||||
if (Object.prototype.hasOwnProperty.call(storage, toolKey)) {
|
||||
delete storage[toolKey];
|
||||
writeStorage(storage);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearAllToolParameters(): void {
|
||||
try {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
} catch (error) {
|
||||
console.error('[toolParameterStorage] Failed to clear stored parameters', error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import { PreferencesProvider } from '@app/contexts/PreferencesContext';
|
||||
|
||||
export const PreferencesTestWrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PreferencesProvider>{children}</PreferencesProvider>
|
||||
);
|
||||
@@ -4,7 +4,8 @@
|
||||
"paths": {
|
||||
"@app/*": [
|
||||
"src/core/*"
|
||||
]
|
||||
],
|
||||
"@testing/*": ["src/testing/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
],
|
||||
"@desktop/*": ["src/desktop/*"],
|
||||
"@proprietary/*": ["src/proprietary/*"],
|
||||
"@core/*": ["src/core/*"]
|
||||
"@core/*": ["src/core/*"],
|
||||
"@testing/*": ["src/testing/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"src/proprietary/*",
|
||||
"src/core/*"
|
||||
],
|
||||
"@testing/*": ["src/testing/*"],
|
||||
"@desktop/*": ["src/desktop/*"],
|
||||
"@proprietary/*": ["src/proprietary/*"],
|
||||
"@core/*": ["src/core/*"]
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"src/proprietary/*",
|
||||
"src/core/*"
|
||||
],
|
||||
"@core/*": ["src/core/*"]
|
||||
"@core/*": ["src/core/*"],
|
||||
"@testing/*": ["src/testing/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
||||
Reference in New Issue
Block a user