Merge remote-tracking branch 'origin/V2' into feature/v2/seperate-tool-hooks

This commit is contained in:
Reece Browne 2025-08-07 12:26:53 +01:00
commit 36adade6d7
15 changed files with 331 additions and 143 deletions

View File

@ -48,6 +48,7 @@ jobs:
"DarioGii"
"ConnorYoh"
"EthanHealy01"
"jbrunton96"
)
# Check if author is in the authorized list

View File

@ -29,6 +29,7 @@ jobs:
github.event.comment.user.login == 'reecebrowne' ||
github.event.comment.user.login == 'DarioGii' ||
github.event.comment.user.login == 'EthanHealy01' ||
github.event.comment.user.login == 'jbrunton96' ||
github.event.comment.user.login == 'ConnorYoh'
)
outputs:

View File

@ -53,7 +53,7 @@ jobs:
with:
gradle-version: 8.14
- name: Build with Gradle and spring security ${{ matrix.spring-security }}
run: ./gradlew clean build
run: ./gradlew clean build -PnoSpotless
env:
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.spring-security }}
- name: Check Test Reports Exist

View File

@ -203,9 +203,17 @@ subprojects {
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
dependsOn "spotlessApply"
if (!project.hasProperty("noSpotless")) {
dependsOn "spotlessApply"
}
}
gradle.taskGraph.whenReady { graph ->
if (project.hasProperty("noSpotless")) {
tasks.matching { it.name.startsWith("spotless") }.configureEach {
enabled = false
}
}
}
licenseReport {
projects = [project]
renderers = [new JsonReportRenderer()]

View File

@ -1,6 +1,7 @@
import React from 'react';
import { RainbowThemeProvider } from './components/shared/RainbowThemeProvider';
import { FileContextProvider } from './contexts/FileContext';
import { FilesModalProvider } from './contexts/FilesModalContext';
import HomePage from './pages/HomePage';
// Import global styles
@ -11,7 +12,9 @@ export default function App() {
return (
<RainbowThemeProvider>
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
<HomePage />
<FilesModalProvider>
<HomePage />
</FilesModalProvider>
</FileContextProvider>
</RainbowThemeProvider>
);

View File

@ -665,46 +665,35 @@ const FileEditor = ({
return (
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
<LoadingOverlay visible={false} />
<Dropzone
onDrop={handleFileUpload}
accept={["*/*"]}
multiple={true}
maxSize={2 * 1024 * 1024 * 1024}
style={{
height: '100vh',
border: 'none',
borderRadius: 0,
backgroundColor: 'transparent'
}}
activateOnClick={false}
activateOnDrag={true}
>
<Box pos="relative" h="100vh" style={{ overflow: 'auto' }}>
<LoadingOverlay visible={false} />
<Box p="md" pt="xl">
<Group mb="md">
{showBulkActions && !toolMode && (
<>
<Button onClick={selectAll} variant="light">Select All</Button>
<Button onClick={deselectAll} variant="light">Deselect All</Button>
<Button onClick={closeAllFiles} variant="light" color="orange">
Close All
</Button>
</>
)}
{/* Load from storage and upload buttons */}
{showUpload && (
<>
<Button
variant="outline"
color="blue"
onClick={() => setShowFilePickerModal(true)}
>
Load from Storage
</Button>
<Dropzone
onDrop={handleFileUpload}
accept={["*/*"]}
multiple={true}
maxSize={2 * 1024 * 1024 * 1024}
style={{ display: 'contents' }}
>
<Button variant="outline" color="green">
Upload Files
<Box p="md" pt="xl">
<Group mb="md">
{showBulkActions && !toolMode && (
<>
<Button onClick={selectAll} variant="light">Select All</Button>
<Button onClick={deselectAll} variant="light">Deselect All</Button>
<Button onClick={closeAllFiles} variant="light" color="orange">
Close All
</Button>
</Dropzone>
</>
)}
</Group>
</>
)}
</Group>
{files.length === 0 && !localLoading && !zipExtractionProgress.isExtracting ? (
@ -866,7 +855,8 @@ const FileEditor = ({
{error}
</Notification>
)}
</Box>
</Box>
</Dropzone>
);
};

View File

@ -0,0 +1,36 @@
import React from 'react';
import { Modal } from '@mantine/core';
import FileUploadSelector from './FileUploadSelector';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import { Tool } from '../../types/tool';
interface FileUploadModalProps {
selectedTool?: Tool | null;
}
const FileUploadModal: React.FC<FileUploadModalProps> = ({ selectedTool }) => {
const { isFilesModalOpen, closeFilesModal, onFileSelect, onFilesSelect } = useFilesModalContext();
return (
<Modal
opened={isFilesModalOpen}
onClose={closeFilesModal}
title="Upload Files"
size="xl"
centered
>
<FileUploadSelector
title="Upload Files"
subtitle="Choose files from storage or upload new files"
onFileSelect={onFileSelect}
onFilesSelect={onFilesSelect}
accept={["*/*"]}
supportedExtensions={selectedTool?.supportedFormats || ["pdf"]}
data-testid="file-upload-modal"
/>
</Modal>
);
};
export default FileUploadModal;

View File

@ -0,0 +1,30 @@
import React from 'react';
import { Container, Stack, Text, Button } from '@mantine/core';
import FolderIcon from '@mui/icons-material/FolderRounded';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
interface LandingPageProps {
title: string;
}
const LandingPage = ({ title }: LandingPageProps) => {
const { openFilesModal } = useFilesModalContext();
return (
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Stack align="center" gap="lg">
<Text size="xl" fw={500} c="dimmed">
{title}
</Text>
<Button
leftSection={<FolderIcon />}
size="lg"
onClick={openFilesModal}
>
Open Files
</Button>
</Stack>
</Container>
);
};
export default LandingPage;

View File

@ -11,6 +11,7 @@ import { useRainbowThemeContext } from "./RainbowThemeProvider";
import rainbowStyles from '../../styles/rainbow.module.css';
import AppConfigModal from './AppConfigModal';
import { useIsOverflowing } from '../../hooks/useIsOverflowing';
import { useFilesModalContext } from '../../contexts/FilesModalContext';
import './QuickAccessBar.css';
interface QuickAccessBarProps {
@ -30,6 +31,7 @@ interface ButtonConfig {
isRound?: boolean;
size?: 'sm' | 'md' | 'lg' | 'xl';
onClick: () => void;
type?: 'navigation' | 'modal' | 'action'; // navigation = main nav, modal = triggers modal, action = other actions
}
function NavHeader({
@ -111,11 +113,16 @@ const QuickAccessBar = ({
readerMode,
}: QuickAccessBarProps) => {
const { isRainbowMode } = useRainbowThemeContext();
const { openFilesModal, isFilesModalOpen } = useFilesModalContext();
const [configModalOpen, setConfigModalOpen] = useState(false);
const [activeButton, setActiveButton] = useState<string>('tools');
const scrollableRef = useRef<HTMLDivElement>(null);
const isOverflow = useIsOverflowing(scrollableRef);
const handleFilesButtonClick = () => {
openFilesModal();
};
const buttonConfigs: ButtonConfig[] = [
{
id: 'read',
@ -124,6 +131,7 @@ const QuickAccessBar = ({
tooltip: 'Read documents',
size: 'lg',
isRound: false,
type: 'navigation',
onClick: () => {
setActiveButton('read');
onReaderToggle();
@ -139,6 +147,7 @@ const QuickAccessBar = ({
tooltip: 'Sign your document',
size: 'lg',
isRound: false,
type: 'navigation',
onClick: () => setActiveButton('sign')
},
{
@ -148,6 +157,7 @@ const QuickAccessBar = ({
tooltip: 'Automate workflows',
size: 'lg',
isRound: false,
type: 'navigation',
onClick: () => setActiveButton('automate')
},
{
@ -157,7 +167,8 @@ const QuickAccessBar = ({
tooltip: 'Manage files',
isRound: true,
size: 'lg',
onClick: () => setActiveButton('files')
type: 'modal',
onClick: handleFilesButtonClick
},
{
id: 'activity',
@ -169,6 +180,7 @@ const QuickAccessBar = ({
tooltip: 'View activity and analytics',
isRound: true,
size: 'lg',
type: 'navigation',
onClick: () => setActiveButton('activity')
},
{
@ -177,6 +189,7 @@ const QuickAccessBar = ({
icon: <SettingsIcon sx={{ fontSize: "1rem" }} />,
tooltip: 'Configure settings',
size: 'lg',
type: 'modal',
onClick: () => {
setConfigModalOpen(true);
}
@ -190,8 +203,16 @@ const QuickAccessBar = ({
return config.isRound ? CIRCULAR_BORDER_RADIUS : ROUND_BORDER_RADIUS;
};
const isButtonActive = (config: ButtonConfig): boolean => {
return (
(config.type === 'navigation' && activeButton === config.id) ||
(config.type === 'modal' && config.id === 'files' && isFilesModalOpen) ||
(config.type === 'modal' && config.id === 'config' && configModalOpen)
);
};
const getButtonStyle = (config: ButtonConfig) => {
const isActive = activeButton === config.id;
const isActive = isButtonActive(config);
if (isActive) {
return {
@ -202,7 +223,7 @@ const QuickAccessBar = ({
};
}
// Inactive state - use consistent inactive colors
// Inactive state for all buttons
return {
backgroundColor: 'var(--icon-inactive-bg)',
color: 'var(--icon-inactive-color)',
@ -254,13 +275,14 @@ const QuickAccessBar = ({
variant="subtle"
onClick={config.onClick}
style={getButtonStyle(config)}
className={activeButton === config.id ? 'activeIconScale' : ''}
className={isButtonActive(config) ? 'activeIconScale' : ''}
data-testid={`${config.id}-button`}
>
<span className="iconContainer">
{config.icon}
</span>
</ActionIcon>
<span className={`button-text ${activeButton === config.id ? 'active' : 'inactive'}`}>
<span className={`button-text ${isButtonActive(config) ? 'active' : 'inactive'}`}>
{config.name}
</span>
</div>
@ -281,30 +303,29 @@ const QuickAccessBar = ({
<div className="spacer" />
{/* Config button at the bottom */}
<Tooltip label="Configure settings" position="right">
<div className="flex flex-col items-center gap-1">
<ActionIcon
size="lg"
variant="subtle"
onClick={() => {
setConfigModalOpen(true);
}}
style={{
backgroundColor: 'var(--icon-inactive-bg)',
color: 'var(--icon-inactive-color)',
border: 'none',
borderRadius: '8px',
}}
>
<span className="iconContainer">
<SettingsIcon sx={{ fontSize: "1rem" }} />
</span>
</ActionIcon>
<span className="config-button-text">
Config
</span>
</div>
</Tooltip>
{buttonConfigs
.filter(config => config.id === 'config')
.map(config => (
<Tooltip key={config.id} label={config.tooltip} position="right">
<div className="flex flex-col items-center gap-1">
<ActionIcon
size={config.size || 'lg'}
variant="subtle"
onClick={config.onClick}
style={getButtonStyle(config)}
className={isButtonActive(config) ? 'activeIconScale' : ''}
data-testid={`${config.id}-button`}
>
<span className="iconContainer">
{config.icon}
</span>
</ActionIcon>
<span className={`button-text ${isButtonActive(config) ? 'active' : 'inactive'}`}>
{config.name}
</span>
</div>
</Tooltip>
))}
</div>
</div>

View File

@ -198,7 +198,7 @@ const ConvertSettings = ({
</Text>
<GroupedFormatDropdown
name="convert-from-dropdown"
data-testid="from-format-dropdown"
data-testid="convert-from-dropdown"
value={parameters.fromExtension}
placeholder={t("convert.sourceFormatPlaceholder", "Source format")}
options={enhancedFromOptions}
@ -236,7 +236,7 @@ const ConvertSettings = ({
) : (
<GroupedFormatDropdown
name="convert-to-dropdown"
data-testid="to-format-dropdown"
data-testid="convert-to-dropdown"
value={parameters.toExtension}
placeholder={t("convert.targetFormatPlaceholder", "Target format")}
options={enhancedToOptions}

View File

@ -0,0 +1,30 @@
import React, { createContext, useContext } from 'react';
import { useFilesModal, UseFilesModalReturn } from '../hooks/useFilesModal';
import { useFileHandler } from '../hooks/useFileHandler';
interface FilesModalContextType extends UseFilesModalReturn {}
const FilesModalContext = createContext<FilesModalContextType | null>(null);
export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { addToActiveFiles, addMultipleFiles } = useFileHandler();
const filesModal = useFilesModal({
onFileSelect: addToActiveFiles,
onFilesSelect: addMultipleFiles,
});
return (
<FilesModalContext.Provider value={filesModal}>
{children}
</FilesModalContext.Provider>
);
};
export const useFilesModalContext = () => {
const context = useContext(FilesModalContext);
if (!context) {
throw new Error('useFilesModalContext must be used within FilesModalProvider');
}
return context;
};

View File

@ -0,0 +1,27 @@
import { useCallback } from 'react';
import { useFileContext } from '../contexts/FileContext';
export const useFileHandler = () => {
const { activeFiles, addFiles } = useFileContext();
const addToActiveFiles = useCallback(async (file: File) => {
const exists = activeFiles.some(f => f.name === file.name && f.size === file.size);
if (!exists) {
await addFiles([file]);
}
}, [activeFiles, addFiles]);
const addMultipleFiles = useCallback(async (files: File[]) => {
const newFiles = files.filter(file =>
!activeFiles.some(f => f.name === file.name && f.size === file.size)
);
if (newFiles.length > 0) {
await addFiles(newFiles);
}
}, [activeFiles, addFiles]);
return {
addToActiveFiles,
addMultipleFiles,
};
};

View File

@ -0,0 +1,57 @@
import { useState, useCallback } from 'react';
export interface UseFilesModalReturn {
isFilesModalOpen: boolean;
openFilesModal: () => void;
closeFilesModal: () => void;
onFileSelect?: (file: File) => void;
onFilesSelect?: (files: File[]) => void;
onModalClose?: () => void;
setOnModalClose: (callback: () => void) => void;
}
interface UseFilesModalProps {
onFileSelect?: (file: File) => void;
onFilesSelect?: (files: File[]) => void;
}
export const useFilesModal = ({
onFileSelect,
onFilesSelect
}: UseFilesModalProps = {}): UseFilesModalReturn => {
const [isFilesModalOpen, setIsFilesModalOpen] = useState(false);
const [onModalClose, setOnModalClose] = useState<(() => void) | undefined>();
const openFilesModal = useCallback(() => {
setIsFilesModalOpen(true);
}, []);
const closeFilesModal = useCallback(() => {
setIsFilesModalOpen(false);
onModalClose?.();
}, [onModalClose]);
const handleFileSelect = useCallback((file: File) => {
onFileSelect?.(file);
closeFilesModal();
}, [onFileSelect, closeFilesModal]);
const handleFilesSelect = useCallback((files: File[]) => {
onFilesSelect?.(files);
closeFilesModal();
}, [onFilesSelect, closeFilesModal]);
const setModalCloseCallback = useCallback((callback: () => void) => {
setOnModalClose(() => callback);
}, []);
return {
isFilesModalOpen,
openFilesModal,
closeFilesModal,
onFileSelect: handleFileSelect,
onFilesSelect: handleFilesSelect,
onModalClose,
setOnModalClose: setModalCloseCallback,
};
};

View File

@ -1,9 +1,10 @@
import React, { useState, useCallback, useEffect} from "react";
import React, { useState, useCallback, useEffect, useRef } from "react";
import { useTranslation } from 'react-i18next';
import { useFileContext } from "../contexts/FileContext";
import { FileSelectionProvider, useFileSelection } from "../contexts/FileSelectionContext";
import { useToolManagement } from "../hooks/useToolManagement";
import { Group, Box, Button, Container } from "@mantine/core";
import { useFileHandler } from "../hooks/useFileHandler";
import { Group, Box, Button } from "@mantine/core";
import { useRainbowThemeContext } from "../components/shared/RainbowThemeProvider";
import { PageEditorFunctions } from "../types/pageEditor";
import rainbowStyles from '../styles/rainbow.module.css';
@ -14,17 +15,19 @@ import FileEditor from "../components/fileEditor/FileEditor";
import PageEditor from "../components/pageEditor/PageEditor";
import PageEditorControls from "../components/pageEditor/PageEditorControls";
import Viewer from "../components/viewer/Viewer";
import FileUploadSelector from "../components/shared/FileUploadSelector";
import ToolRenderer from "../components/tools/ToolRenderer";
import QuickAccessBar from "../components/shared/QuickAccessBar";
import LandingPage from "../components/shared/LandingPage";
import FileUploadModal from "../components/shared/FileUploadModal";
function HomePageContent() {
const { t } = useTranslation();
const { isRainbowMode } = useRainbowThemeContext();
const fileContext = useFileContext();
const { activeFiles, currentView, currentMode, setCurrentView, addFiles } = fileContext;
const { activeFiles, currentView, setCurrentView } = fileContext;
const { setMaxFiles, setIsToolMode, setSelectedFiles } = useFileSelection();
const { addToActiveFiles } = useFileHandler();
const {
selectedToolKey,
@ -33,6 +36,7 @@ function HomePageContent() {
selectTool,
clearToolSelection,
} = useToolManagement();
const [sidebarsVisible, setSidebarsVisible] = useState(true);
const [leftPanelView, setLeftPanelView] = useState<'toolPicker' | 'toolContent'>('toolPicker');
const [readerMode, setReaderMode] = useState(false);
@ -77,12 +81,6 @@ function HomePageContent() {
setCurrentView(view as any);
}, [setCurrentView]);
const addToActiveFiles = useCallback(async (file: File) => {
const exists = activeFiles.some(f => f.name === file.name && f.size === file.size);
if (!exists) {
await addFiles([file]);
}
}, [activeFiles, addFiles]);
@ -183,26 +181,12 @@ function HomePageContent() {
}}
>
{!activeFiles[0] ? (
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<FileUploadSelector
title={currentView === "viewer"
? t("fileUpload.selectPdfToView", "Select a PDF to view")
: t("fileUpload.selectPdfToEdit", "Select a PDF to edit")
}
subtitle={t("fileUpload.chooseFromStorage", "Choose a file from storage or upload a new PDF")}
onFileSelect={(file) => {
addToActiveFiles(file);
}}
onFilesSelect={(files) => {
files.forEach(addToActiveFiles);
}}
accept={["*/*"]}
supportedExtensions={selectedTool?.supportedFormats || ["pdf"]}
loading={false}
showRecentFiles={true}
maxRecentFiles={8}
/>
</Container>
<LandingPage
title={currentView === "viewer"
? t("fileUpload.selectPdfToView", "Select a PDF to view")
: t("fileUpload.selectPdfToEdit", "Select a PDF to edit")
}
/>
) : currentView === "fileEditor" ? (
<FileEditor
toolMode={!!selectedToolKey}
@ -278,26 +262,15 @@ function HomePageContent() {
selectedToolKey={selectedToolKey}
/>
) : (
<Container size="lg" p="xl" h="100%" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<FileUploadSelector
title="File Management"
subtitle="Choose files from storage or upload new PDFs"
onFileSelect={(file) => {
addToActiveFiles(file);
}}
onFilesSelect={(files) => {
files.forEach(addToActiveFiles);
}}
accept={["*/*"]}
supportedExtensions={selectedTool?.supportedFormats || ["pdf"]}
loading={false}
showRecentFiles={true}
maxRecentFiles={8}
/>
</Container>
<LandingPage
title="File Management"
/>
)}
</Box>
</Box>
{/* Global Modals */}
<FileUploadModal selectedTool={selectedTool} />
</Group>
);
}

View File

@ -127,6 +127,27 @@ const getExpectedExtension = (toFormat: string): string => {
return extensionMap[toFormat] || '.pdf';
};
/**
* Helper function to upload files through the modal system
*/
async function uploadFileViaModal(page: Page, filePath: string) {
// Click the Files button in the QuickAccessBar to open the modal
await page.click('[data-testid="files-button"]');
// Wait for the modal to open
await page.waitForSelector('.mantine-Modal-overlay', { state: 'visible' }, { timeout: 5000 });
//await page.waitForSelector('[data-testid="file-upload-modal"]', { timeout: 5000 });
// Upload the file through the modal's file input
await page.setInputFiles('input[type="file"]', filePath);
// Wait for the file to be processed and the modal to close
await page.waitForSelector('[data-testid="file-upload-modal"]', { state: 'hidden' });
// Wait for the file thumbnail to appear in the main interface
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
}
/**
* Generic test function for any conversion
*/
@ -288,8 +309,8 @@ test.describe('Convert Tool E2E Tests', () => {
// Wait for the page to load
await page.waitForLoadState('networkidle');
// Wait for the file upload area to appear (shown when no active files)
await page.waitForSelector('[data-testid="file-dropzone"]', { timeout: 10000 });
// Wait for the QuickAccessBar to appear
await page.waitForSelector('[data-testid="files-button"]', { timeout: 10000 });
});
test.describe('Dynamic Conversion Tests', () => {
@ -302,8 +323,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -314,8 +334,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -326,8 +345,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -338,8 +356,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -350,8 +367,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -362,8 +378,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -374,8 +389,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -386,8 +400,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -398,8 +411,7 @@ test.describe('Convert Tool E2E Tests', () => {
test.skip(!isAvailable, `Endpoint ${conversion.endpoint} is not available`);
const testFile = getTestFileForFormat(conversion.fromFormat);
await page.setInputFiles('input[type="file"]', testFile);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, testFile);
await testConversion(page, conversion);
});
@ -410,8 +422,7 @@ test.describe('Convert Tool E2E Tests', () => {
// Test that disabled conversions don't appear in dropdowns when they shouldn't
test('should not show conversion button when no valid conversions available', async ({ page }) => {
// This test ensures the convert button is disabled when no valid conversion is possible
await page.setInputFiles('input[type="file"]', TEST_FILES.pdf);
await page.waitForSelector('[data-testid="file-thumbnail"]', { timeout: 10000 });
await uploadFileViaModal(page, TEST_FILES.pdf);
// Click the Convert tool button
await page.click('[data-testid="tool-convert"]');