From fa2720ecd84a1bf065ac7b08186066e135a74cfd Mon Sep 17 00:00:00 2001 From: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:48:26 -0500 Subject: [PATCH 1/3] fix(ui): prevent bulk upload of files with missing filenames --- .../elements/BulkUpload/FormsManager/index.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/ui/src/elements/BulkUpload/FormsManager/index.tsx b/packages/ui/src/elements/BulkUpload/FormsManager/index.tsx index d14a3ae8434..f1461d174be 100644 --- a/packages/ui/src/elements/BulkUpload/FormsManager/index.tsx +++ b/packages/ui/src/elements/BulkUpload/FormsManager/index.tsx @@ -333,6 +333,21 @@ export function FormsManagerProvider({ children }: FormsManagerProps) { for (let i = 0; i < currentForms.length; i++) { try { const form = currentForms[i] + const fileValue = form.formState?.file?.value + + // Skip upload if file is missing a filename + if ( + fileValue && + typeof fileValue === 'object' && + 'name' in fileValue && + (!fileValue.name || fileValue.name === '') + ) { + currentForms[i] = { + ...currentForms[i], + errorCount: 1, + } + continue + } setLoadingText(t('general:uploadingBulk', { current: i + 1, total: currentForms.length })) From b66f6bebbb355ef48ea30ce34be944f9e97a0efc Mon Sep 17 00:00:00 2001 From: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:13:59 -0500 Subject: [PATCH 2/3] test: adds e2e test --- test/uploads/e2e.spec.ts | 79 +++++++++++++++++++++++++++++++++++ test/uploads/payload-types.ts | 4 +- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/test/uploads/e2e.spec.ts b/test/uploads/e2e.spec.ts index 5b8f13baa0f..3975c870c75 100644 --- a/test/uploads/e2e.spec.ts +++ b/test/uploads/e2e.spec.ts @@ -1269,6 +1269,85 @@ describe('Uploads', () => { // should show add files dropzone view await expect(fieldBulkUploadDrawer.locator('.bulk-upload--add-files')).toBeVisible() }) + + test('should show error when bulk uploading files with missing filenames and allow retry after fixing', async () => { + await page.goto(uploadsOne.create) + + await page.setInputFiles( + '.file-field input[type="file"]', + path.resolve(dirname, './image.png'), + ) + const filename = page.locator('.file-field__filename') + await expect(filename).toHaveValue('image.png') + + const bulkUploadButton = page.locator('#field-hasManyUpload button', { + hasText: exactText('Create New'), + }) + await bulkUploadButton.click() + + const bulkUploadModal = page.locator('#hasManyUpload-bulk-upload-drawer-slug-1') + await expect(bulkUploadModal).toBeVisible() + + await bulkUploadModal + .locator('.dropzone input[type="file"]') + .setInputFiles([ + path.resolve(dirname, './image.png'), + path.resolve(dirname, './test-image.png'), + ]) + + await bulkUploadModal + .locator('.bulk-upload--file-manager .render-fields #field-prefix') + .fill('prefix-one') + + // Clear the filename from the first file + await bulkUploadModal.locator('.file-field__filename').clear() + + const nextImageChevronButton = bulkUploadModal.locator( + '.bulk-upload--actions-bar__controls button:nth-of-type(2)', + ) + await nextImageChevronButton.click() + + await bulkUploadModal + .locator('.bulk-upload--file-manager .render-fields #field-prefix') + .fill('prefix-two') + + const saveButton = bulkUploadModal.locator('.bulk-upload--actions-bar__saveButtons button') + await saveButton.click() + + // Should show error message for failed files + await expect(page.locator('.payload-toast-container')).toContainText('Failed to save 1 files') + await expect(page.locator('.payload-toast-container')).toContainText( + 'Successfully saved 1 files', + ) + + const errorCount = bulkUploadModal.locator('.file-selections .error-pill__count').first() + await expect(errorCount).toHaveText('1') + + await expect(bulkUploadModal).toBeVisible() + + // Navigate back to first file to fix it + const prevImageChevronButton = bulkUploadModal.locator( + '.bulk-upload--actions-bar__controls button:nth-of-type(1)', + ) + await prevImageChevronButton.click() + await expect(bulkUploadModal.locator('.file-field__filename')).toHaveValue('') + + // Add the filename back + await bulkUploadModal.locator('.file-field__filename').fill('fixed-filename.png') + + await saveButton.click() + + await expect(page.locator('.payload-toast-container')).toContainText( + 'Successfully saved 1 files', + ) + + await expect(bulkUploadModal).toBeHidden() + + const items = page.locator('#field-hasManyUpload .upload--has-many__dragItem') + await expect(items).toHaveCount(2) + + await saveDocAndAssert(page) + }) }) describe('remote url fetching', () => { diff --git a/test/uploads/payload-types.ts b/test/uploads/payload-types.ts index ae067f4983c..ee9dfa4e727 100644 --- a/test/uploads/payload-types.ts +++ b/test/uploads/payload-types.ts @@ -3787,6 +3787,6 @@ export interface Auth { declare module 'payload' { - // @ts-ignore + // @ts-ignore export interface GeneratedTypes extends Config {} -} +} \ No newline at end of file From be56e263827c32cd40d0d92f4d2b12108ddb88c9 Mon Sep 17 00:00:00 2001 From: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:44:51 -0500 Subject: [PATCH 3/3] test: test for required field name tooltip --- test/uploads/e2e.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/uploads/e2e.spec.ts b/test/uploads/e2e.spec.ts index 3975c870c75..c62d0dd0f75 100644 --- a/test/uploads/e2e.spec.ts +++ b/test/uploads/e2e.spec.ts @@ -1330,6 +1330,11 @@ describe('Uploads', () => { '.bulk-upload--actions-bar__controls button:nth-of-type(1)', ) await prevImageChevronButton.click() + + // Should show "A file name is required" error message + await expect(bulkUploadModal.locator('.field-error')).toContainText('A file name is required') + + // Filename field should be empty (as we cleared it) await expect(bulkUploadModal.locator('.file-field__filename')).toHaveValue('') // Add the filename back