diff --git a/web/src/client/components/session-view-drag-drop.test.ts b/web/src/client/components/session-view-drag-drop.test.ts index 1fc6aa3c..702ea50c 100644 --- a/web/src/client/components/session-view-drag-drop.test.ts +++ b/web/src/client/components/session-view-drag-drop.test.ts @@ -227,7 +227,7 @@ describe('SessionView Drag & Drop and Paste', () => { expect(mockFilePicker.uploadFile).not.toHaveBeenCalled(); }); - it('should handle multiple files and pick the first one', async () => { + it('should handle multiple files and upload all of them', async () => { const textFile = new File(['text'], 'test.txt', { type: 'text/plain' }); const jsonFile = new File(['{}'], 'test.json', { type: 'application/json' }); const pdfFile = new File(['pdf'], 'test.pdf', { type: 'application/pdf' }); @@ -272,12 +272,15 @@ describe('SessionView Drag & Drop and Paste', () => { element.dispatchEvent(dropEvent); - // Wait for async operations + // Wait for all async operations to complete await vi.waitFor(() => { - expect(mockFilePicker.uploadFile).toHaveBeenCalledWith(textFile); + expect(mockFilePicker.uploadFile).toHaveBeenCalledTimes(3); }); - expect(mockFilePicker.uploadFile).toHaveBeenCalledTimes(1); + // Verify all files were uploaded in order + expect(mockFilePicker.uploadFile).toHaveBeenNthCalledWith(1, textFile); + expect(mockFilePicker.uploadFile).toHaveBeenNthCalledWith(2, jsonFile); + expect(mockFilePicker.uploadFile).toHaveBeenNthCalledWith(3, pdfFile); }); }); @@ -416,6 +419,50 @@ describe('SessionView Drag & Drop and Paste', () => { ); }); }); + + it('should handle multiple files pasted from clipboard', async () => { + const textFile = new File(['text content'], 'text.txt', { type: 'text/plain' }); + const imageFile = new File(['image content'], 'image.png', { type: 'image/png' }); + + const mockClipboardItems = [ + { + kind: 'file', + type: 'text/plain', + getAsFile: () => textFile, + }, + { + kind: 'file', + type: 'image/png', + getAsFile: () => imageFile, + }, + ]; + + const pasteEvent = new ClipboardEvent('paste', { + bubbles: true, + clipboardData: new DataTransfer(), + }); + + Object.defineProperty(pasteEvent.clipboardData, 'items', { + value: mockClipboardItems, + writable: false, + }); + + const mockFilePicker = { + uploadFile: vi.fn().mockResolvedValue(undefined), + }; + element.querySelector = vi.fn(() => mockFilePicker); + + document.dispatchEvent(pasteEvent); + + // Wait for all uploads to complete + await vi.waitFor(() => { + expect(mockFilePicker.uploadFile).toHaveBeenCalledTimes(2); + }); + + // Verify both files were uploaded + expect(mockFilePicker.uploadFile).toHaveBeenNthCalledWith(1, textFile); + expect(mockFilePicker.uploadFile).toHaveBeenNthCalledWith(2, imageFile); + }); }); describe('Event Listener Management', () => { diff --git a/web/src/client/components/session-view.ts b/web/src/client/components/session-view.ts index 73bf6bab..83e3d99a 100644 --- a/web/src/client/components/session-view.ts +++ b/web/src/client/components/session-view.ts @@ -809,7 +809,7 @@ export class SessionView extends LitElement { } } - private handleDrop(e: DragEvent) { + private async handleDrop(e: DragEvent) { e.preventDefault(); e.stopPropagation(); this.isDragOver = false; @@ -821,12 +821,19 @@ export class SessionView extends LitElement { return; } - // Upload the first file (or we could upload all of them) - this.uploadFile(files[0]); + // Upload all files sequentially + for (const file of files) { + try { + await this.uploadFile(file); + logger.log(`Successfully uploaded file: ${file.name}`); + } catch (error) { + logger.error(`Failed to upload file: ${file.name}`, error); + } + } } // Paste handler - private handlePaste(e: ClipboardEvent) { + private async handlePaste(e: ClipboardEvent) { // Only handle paste if session view is focused and no modal is open if (this.showFileBrowser || this.showImagePicker || this.showMobileInput) { return; @@ -841,12 +848,17 @@ export class SessionView extends LitElement { e.preventDefault(); // Prevent default paste behavior for files - const fileItem = fileItems[0]; - const file = fileItem.getAsFile(); - - if (file) { - logger.log('File pasted from clipboard'); - this.uploadFile(file); + // Upload all pasted files + for (const fileItem of fileItems) { + const file = fileItem.getAsFile(); + if (file) { + try { + await this.uploadFile(file); + logger.log(`Successfully pasted and uploaded file: ${file.name}`); + } catch (error) { + logger.error(`Failed to upload pasted file: ${file?.name}`, error); + } + } } }