/* Created By : Suhas. YS Created On : 02/09/2025 Note this extension is not a product feature of Virtuoso and is not officially supported Extensions use javascript which may or may not be compatible with systems under test (SUTs) We welcome you to use this extension or adapt it for your needs */ class StudioFileUploader { constructor() { this.maxFileSize = 50 * 1024 * 1024; // 50MB limit this.supportedTypes = ['application/pdf']; // PDF only this.supportedExtensions = ['.pdf']; // PDF extension only } async uploadToFileChooser(fileUrl) { try { console.log('Starting Studio PDF upload for:', fileUrl); const file = await this.fetchFileFromUrl(fileUrl); console.log('PDF fetched successfully:', file.name, file.size, 'bytes', 'Type:', file.type); if (!this.isValidFileType(file)) { throw new Error(`Only PDF files are supported. File type: ${file.type}`); } const fileChooser = await this.findFileChooser(); if (!fileChooser) { throw new Error('FileChooser component not found'); } console.log('Found FileChooser component:', fileChooser.id); const strategies = [ () => this.tryReactHandlers(fileChooser, file), ]; let success = false; for (let i = 0; i < strategies.length; i++) { try { console.log(`Trying strategy ${i + 1}/${strategies.length}...`); success = await strategies[i](); if (success) { console.log(`Strategy ${i + 1} successful!`); break; } } catch (error) { console.warn(`Strategy ${i + 1} failed:`, error.message); } } if (success) { await this.waitForUploadCompletion(fileChooser, file); console.log('PDF upload completed successfully'); return { success: true, message: `PDF upload completed successfully for: ${file.name}`, file: { name: file.name, size: file.size, type: file.type, lastModified: new Date(file.lastModified) } }; } else { throw new Error('All upload strategies failed'); } } catch (error) { console.error(' Studio PDF upload failed:', error); throw new Error(`PDF upload failed: ${error.message}`); } } async fetchFileFromUrl(url, timeout = 30000) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal, mode: 'cors' }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const blob = await response.blob(); const fileName = this.extractFileName(url); const mimeType = this.detectMimeType(fileName, blob.type); return new File([blob], fileName, { type: mimeType, lastModified: Date.now() }); } catch (error) { if (error.name === 'AbortError') { throw new Error(`File fetch timeout after ${timeout}ms`); } throw new Error(`Failed to fetch file from URL: ${error.message}`); } finally { clearTimeout(timeoutId); } } extractFileName(url) { try { const pathname = new URL(url).pathname; let filename = pathname.split('/').pop().split('?')[0]; if (!filename || !filename.toLowerCase().endsWith('.pdf')) { filename = `document_${Date.now()}.pdf`; } return decodeURIComponent(filename); } catch (error) { throw new Error(`Invalid URL format: ${error.message}`); } } detectMimeType(fileName, blobType) { try { const ext = fileName.toLowerCase().split('.').pop(); if (ext === 'pdf') { return 'application/pdf'; } return blobType && blobType.includes('pdf') ? 'application/pdf' : 'application/pdf'; } catch (error) { throw new Error(`Failed to detect MIME type: ${error.message}`); } } isValidFileType(file) { try { const ext = '.' + file.name.toLowerCase().split('.').pop(); const isPdfType = file.type === 'application/pdf'; const isPdfExtension = ext === '.pdf'; return isPdfType || isPdfExtension; } catch (error) { throw new Error(`File type validation failed: ${error.message}`); } } async findFileChooser() { const selectors = [ '#\\31 756803255675_1058', // Specific ID '[data-testing-id*="documentItem.blobFile"]', // Testing ID '.FileChooser_root__VaWr4', // Class name '.BlobFileChooser_withFile__sYe+H' // Alternative class ]; for (const selector of selectors) { try { const element = document.querySelector(selector); if (element) return element; } catch (error) { console.warn('Selector failed:', selector, error); } } throw new Error('FileChooser component not found with any of the available selectors'); } async tryReactHandlers(fileChooser, file) { try { const reactKey = Object.keys(fileChooser).find(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactFiber') || key.startsWith('__reactProps') ); if (!reactKey) { throw new Error('React instance not found on element'); } const reactInstance = fileChooser[reactKey]; let props = reactInstance?.memoizedProps || reactInstance?.props; if (!props && reactInstance?.return) { props = reactInstance.return.memoizedProps || reactInstance.return.props; } if (props && typeof props.onDrop === 'function') { const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); const dropEvent = { preventDefault: () => {}, stopPropagation: () => {}, dataTransfer: dataTransfer, target: fileChooser, currentTarget: fileChooser, type: 'drop' }; console.log('Calling React onDrop handler directly'); await props.onDrop(dropEvent); return true; } if (props && typeof props.onChange === 'function') { const changeEvent = { target: { files: [file] }, currentTarget: { files: [file] } }; await props.onChange(changeEvent); return true; } throw new Error('No suitable React handlers found (onDrop or onChange)'); } catch (error) { throw new Error(`React handler method failed: ${error.message}`); } } async waitForUploadCompletion(fileChooser, file, maxWait = 5000) { try { const startTime = Date.now(); while (Date.now() - startTime < maxWait) { const fileNameElement = fileChooser.querySelector('.BlobFileEntity_name__KDXma'); if (fileNameElement && fileNameElement.textContent.includes(file.name.split('.')[0])) { console.log('PDF upload verified - file name updated in UI'); return true; } const loadingElement = document.querySelector('.LoadingBackdrop_loadingText__8HiCr'); if (loadingElement && loadingElement.textContent.includes('Creating')) { console.log('PDF upload in progress...'); } await new Promise(resolve => setTimeout(resolve, 500)); } console.warn('PDF upload verification timed out'); throw new Error(`Upload verification timed out after ${maxWait}ms`); } catch (error) { throw new Error(`Upload completion verification failed: ${error.message}`); } } debugReactComponent(element) { try { const reactKey = Object.keys(element).find(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactFiber') ); if (reactKey) { const reactInstance = element[reactKey]; console.log('React Debug Info:', { type: reactInstance.type, props: Object.keys(reactInstance.memoizedProps || {}), state: reactInstance.memoizedState, elementType: reactInstance.elementType }); } } catch (error) { console.warn('Debug React component failed:', error.message); } } } const Uploader = new StudioFileUploader(); Uploader.uploadToFileChooser(file) .then(result => { console.log(result.message); console.log('PDF details:', result.file); done(result); }) .catch(error => { console.error('Upload failed:', error.message); doneError(error); });