From 53b75d083fb535a09fb8755b096c992cb2f91d07 Mon Sep 17 00:00:00 2001 From: Pooja Date: Fri, 27 Mar 2026 16:41:36 +0530 Subject: [PATCH] fix: multipart form upload icon visibility (#7571) --- .../RequestPane/MultipartFormParams/index.js | 20 ++--- .../index.js | 20 ++--- .../multipart-form-upload-icon.spec.ts | 73 +++++++++++++++++++ 3 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 tests/request/multipart-form/multipart-form-upload-icon.spec.ts diff --git a/packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js b/packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js index 829146e7e..4d887d00c 100644 --- a/packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js +++ b/packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js @@ -133,11 +133,9 @@ const MultipartFormParams = ({ item, collection }) => { name: 'Value', placeholder: 'Value', width: '35%', - render: ({ row, value, onChange, isLastEmptyRow }) => { + render: ({ row, value, onChange }) => { const isFile = row.type === 'file'; const fileName = isFile ? getFileName(value) : null; - const hasTextValue = !isFile && value && value.length > 0; - if (fileName) { return (
@@ -171,15 +169,13 @@ const MultipartFormParams = ({ item, collection }) => { placeholder={!value ? 'Value' : ''} />
- {!hasTextValue && !isLastEmptyRow && ( - - )} + ); } diff --git a/packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js b/packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js index 5afd01459..647973319 100644 --- a/packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js +++ b/packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js @@ -181,11 +181,9 @@ const ResponseExampleMultipartFormParams = ({ item, collection, exampleUid, edit placeholder: 'Value', width: '40%', readOnly: !editMode, - render: ({ row, value, onChange, isLastEmptyRow }) => { + render: ({ row, value, onChange }) => { const isFile = row.type === 'file'; const fileName = isFile ? getFileName(value) : null; - const hasTextValue = !isFile && value && value.length > 0; - if (fileName) { return (
@@ -220,15 +218,13 @@ const ResponseExampleMultipartFormParams = ({ item, collection, exampleUid, edit placeholder={!value ? 'Value' : ''} />
- {!hasTextValue && !isLastEmptyRow && ( - - )} + ); } diff --git a/tests/request/multipart-form/multipart-form-upload-icon.spec.ts b/tests/request/multipart-form/multipart-form-upload-icon.spec.ts new file mode 100644 index 000000000..780c87a1c --- /dev/null +++ b/tests/request/multipart-form/multipart-form-upload-icon.spec.ts @@ -0,0 +1,73 @@ +import { test, expect } from '../../../playwright'; +import { closeAllCollections, createCollection, createRequest, openCollection, openRequest, selectRequestPaneTab } from '../../utils/page'; +import { buildCommonLocators } from '../../utils/page/locators'; + +test.describe.serial('Multipart Form - Upload Icon Visibility', () => { + test.afterAll(async ({ page }) => { + await closeAllCollections(page); + }); + + test.beforeAll(async ({ page, createTmpDir }) => { + await test.step('Create collection and request', async () => { + await createCollection(page, 'multipart-upload-icon', await createTmpDir('multipart-upload-icon')); + await createRequest(page, 'test-multipart', '', { + url: 'https://httpbin.org/post', + method: 'POST', + inFolder: false + }); + }); + + await test.step('Open the request', async () => { + await openCollection(page, 'multipart-upload-icon'); + await openRequest(page, 'multipart-upload-icon', 'test-multipart', { persist: true }); + }); + + await test.step('Switch body mode to Multipart Form', async () => { + await selectRequestPaneTab(page, 'Body'); + const locators = buildCommonLocators(page); + await locators.request.bodyModeSelector().click(); + await page.locator('.dropdown-item').filter({ hasText: 'Multipart Form' }).click(); + }); + }); + + test('upload icon should be visible on the empty last row', async ({ page }) => { + await test.step('Verify upload icon is visible on the empty row', async () => { + const rows = page.locator('table tbody tr'); + await expect(rows).toHaveCount(1); + const uploadBtn = rows.first().locator('.upload-btn'); + await expect(uploadBtn).toBeVisible(); + }); + }); + + test('upload icon should be visible after entering a key', async ({ page }) => { + await test.step('Enter a key in the empty row', async () => { + const row = page.locator('table tbody tr').first(); + const nameCell = row.locator('td').nth(1); + await nameCell.locator('input').fill('myfield'); + // Press Tab to commit the value + await page.keyboard.press('Tab'); + }); + + await test.step('Verify upload icon is visible on the row with the key', async () => { + // Wait for the new empty row to appear (should now have 2 rows) + await expect(page.locator('table tbody tr')).toHaveCount(2); + // The first row has our key, check its upload button + const uploadBtn = page.locator('table tbody tr').first().locator('.upload-btn'); + await expect(uploadBtn).toBeVisible(); + }); + }); + + test('upload icon should remain visible after entering a value', async ({ page }) => { + await test.step('Enter a value in the first row', async () => { + const firstRow = page.locator('table tbody tr').first(); + const editor = firstRow.locator('.value-cell .CodeMirror'); + await editor.click({ position: { x: 10, y: 10 } }); + await page.keyboard.type('some text value'); + }); + + await test.step('Verify upload icon is still visible with text value', async () => { + const uploadBtn = page.locator('table tbody tr').first().locator('.upload-btn'); + await expect(uploadBtn).toBeVisible(); + }); + }); +});