From 351b294c3f0f02d58ccc8406ff5495310a1612fd Mon Sep 17 00:00:00 2001 From: gopu-bruno Date: Thu, 14 May 2026 21:46:33 +0530 Subject: [PATCH] fix: show "+ Add request" CTA in empty .bru collection sidebar (#8000) * fix: show empty collection Add request CTA when only files exist * test: add .bru parity to empty-state CTA spec --- .../Collection/CollectionItem/index.js | 1 + .../Sidebar/Collections/Collection/index.js | 6 +- .../empty-state-cta/empty-state-cta.spec.ts | 143 ++++++++++++++++++ .../collections/bru-folder-with-js/bruno.json | 5 + .../bru-folder-with-js/collection.bru | 3 + .../bru-folder-with-js/scripts/folder.bru | 4 + .../bru-folder-with-js/scripts/helper.js | 1 + .../collections/bru-with-js/bruno.json | 5 + .../collections/bru-with-js/collection.bru | 3 + .../collections/bru-with-js/helper.js | 1 + .../collections/bru-with-request/bruno.json | 5 + .../bru-with-request/collection.bru | 3 + .../collections/bru-with-request/echo.bru | 9 ++ .../fixtures/collections/empty-bru/bruno.json | 5 + .../collections/empty-bru/collection.bru | 3 + .../collections/empty-yml/opencollection.yml | 3 + .../yml-with-folder/opencollection.yml | 3 + .../yml-with-folder/scripts/folder.yml | 3 + .../yml-with-folder/scripts/helper.js | 1 + .../collections/yml-with-js/helper.js | 1 + .../yml-with-js/opencollection.yml | 3 + .../collections/yml-with-request/echo.yml | 8 + .../yml-with-request/opencollection.yml | 3 + .../init-user-data/preferences.json | 18 +++ 24 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 tests/sidebar/empty-state-cta/empty-state-cta.spec.ts create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/bruno.json create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/collection.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/folder.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/helper.js create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/bruno.json create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/collection.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/helper.js create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/bruno.json create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/collection.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/echo.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/bruno.json create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/collection.bru create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/empty-yml/opencollection.yml create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/opencollection.yml create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/folder.yml create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/helper.js create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/helper.js create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/opencollection.yml create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/echo.yml create mode 100644 tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/opencollection.yml create mode 100644 tests/sidebar/empty-state-cta/init-user-data/preferences.json diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index 9394bea69..acca2e68f 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -744,6 +744,7 @@ const CollectionItem = ({ item, collectionUid, collectionPathname, searchText }) ))}
{ const dispatch = useDispatch(); const isLoading = collection.isLoading; const collectionRef = useRef(null); - // Only count persisted items; transients don't affect empty state - const itemCount = collection.items?.filter((i) => !i.isTransient).length || 0; + // Only count persisted requests and folders; transients and file items + // (bruno.json, .js scripts) don't affect empty state + const itemCount = collection.items?.filter((i) => !i.isTransient && (isItemARequest(i) || isItemAFolder(i))).length || 0; const isCollectionFocused = useSelector(isTabForItemActive({ itemUid: collection.uid })); const { hasCopiedItems } = useSelector((state) => state.app.clipboard); @@ -533,6 +534,7 @@ const Collection = ({ collection, searchText }) => {
{ + let locators: ReturnType; + + test.beforeAll(async ({ pageWithUserData: page }) => { + locators = buildCommonLocators(page); + }); + + test.afterAll(async ({ pageWithUserData: page }) => { + await closeAllCollections(page); + }); + + // Scope an assertion to a single collection — pageWithUserData reuses one app + // across the describe block, and multiple expanded collections would otherwise + // make `getByTestId('add-request-cta')` match more than one element. + const collectionScope = (page: Page, name: string) => page.locator(`#collection-${name}`); + + const expandCollection = async (name: string) => { + const collection = locators.sidebar.collection(name); + await collection.waitFor({ state: 'visible' }); + await collection.click(); + }; + + // Empty collection — CTA should appear + + test('should show CTA for an empty .bru collection', async ({ pageWithUserData: page }) => { + await test.step('Expand empty-bru collection', async () => { + await expandCollection('empty-bru'); + }); + + await test.step('Verify CTA is visible at collection root', async () => { + await expect(collectionScope(page, 'empty-bru').getByTestId('add-request-cta')).toBeVisible(); + }); + }); + + test('should show CTA for an empty .yml collection', async ({ pageWithUserData: page }) => { + await test.step('Expand empty-yml collection', async () => { + await expandCollection('empty-yml'); + }); + + await test.step('Verify CTA is visible at collection root', async () => { + await expect(collectionScope(page, 'empty-yml').getByTestId('add-request-cta')).toBeVisible(); + }); + }); + + // Collection containing only a .js script — CTA should still appear + + test('should show CTA for a .bru collection containing only a .js script', async ({ pageWithUserData: page }) => { + await test.step('Expand bru-with-js collection', async () => { + await expandCollection('bru-with-js'); + }); + + await test.step('Verify CTA is visible at collection root', async () => { + await expect(collectionScope(page, 'bru-with-js').getByTestId('add-request-cta')).toBeVisible(); + }); + }); + + test('should show CTA for a .yml collection containing only a .js script', async ({ pageWithUserData: page }) => { + await test.step('Expand yml-with-js collection', async () => { + await expandCollection('yml-with-js'); + }); + + await test.step('Verify CTA is visible at collection root', async () => { + await expect(collectionScope(page, 'yml-with-js').getByTestId('add-request-cta')).toBeVisible(); + }); + }); + + // Collection has user content — root CTA should be hidden + + test('should hide CTA when .bru collection contains a request', async ({ pageWithUserData: page }) => { + await test.step('Expand bru-with-request collection', async () => { + await expandCollection('bru-with-request'); + await expect(locators.sidebar.request('bru-echo')).toBeVisible(); + }); + + await test.step('Verify CTA is not rendered at collection root', async () => { + await expect(collectionScope(page, 'bru-with-request').getByTestId('add-request-cta')).toHaveCount(0); + }); + }); + + test('should hide CTA when .yml collection contains a request', async ({ pageWithUserData: page }) => { + await test.step('Expand yml-with-request collection', async () => { + await expandCollection('yml-with-request'); + await expect(locators.sidebar.request('yml-echo')).toBeVisible(); + }); + + await test.step('Verify CTA is not rendered at collection root', async () => { + await expect(collectionScope(page, 'yml-with-request').getByTestId('add-request-cta')).toHaveCount(0); + }); + }); + + test('should hide root CTA when .bru collection contains a folder', async ({ pageWithUserData: page }) => { + await test.step('Expand bru-folder-with-js collection', async () => { + await expandCollection('bru-folder-with-js'); + await expect(locators.sidebar.folder('bru-scripts')).toBeVisible(); + }); + + await test.step('Verify CTA is not rendered at collection root', async () => { + await expect(collectionScope(page, 'bru-folder-with-js').getByTestId('add-request-cta')).toHaveCount(0); + }); + }); + + test('should hide root CTA when .yml collection contains a folder', async ({ pageWithUserData: page }) => { + await test.step('Expand yml-with-folder collection', async () => { + await expandCollection('yml-with-folder'); + await expect(locators.sidebar.folder('yml-scripts')).toBeVisible(); + }); + + await test.step('Verify CTA is not rendered at collection root', async () => { + await expect(collectionScope(page, 'yml-with-folder').getByTestId('add-request-cta')).toHaveCount(0); + }); + }); + + // Folder containing only a .js script — folder CTA should appear + + test('should show folder CTA when a .bru folder contains only a .js script', async ({ pageWithUserData: page }) => { + await test.step('Expand bru-folder-with-js collection and the bru-scripts folder', async () => { + await expandCollection('bru-folder-with-js'); + const folder = locators.sidebar.folder('bru-scripts'); + await folder.waitFor({ state: 'visible' }); + await folder.click(); + }); + + await test.step('Verify folder-level CTA is visible', async () => { + await expect(collectionScope(page, 'bru-folder-with-js').getByTestId('add-request-cta-folder')).toBeVisible(); + }); + }); + + test('should show folder CTA when a .yml folder contains only a .js script', async ({ pageWithUserData: page }) => { + await test.step('Expand yml-with-folder collection and the yml-scripts folder', async () => { + await expandCollection('yml-with-folder'); + const folder = locators.sidebar.folder('yml-scripts'); + await folder.waitFor({ state: 'visible' }); + await folder.click(); + }); + + await test.step('Verify folder-level CTA is visible', async () => { + await expect(collectionScope(page, 'yml-with-folder').getByTestId('add-request-cta-folder')).toBeVisible(); + }); + }); +}); diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/bruno.json b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/bruno.json new file mode 100644 index 000000000..6b9d08a47 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "bru-folder-with-js", + "type": "collection" +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/collection.bru b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/collection.bru new file mode 100644 index 000000000..fd6cf0938 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/collection.bru @@ -0,0 +1,3 @@ +meta { + name: bru-folder-with-js +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/folder.bru b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/folder.bru new file mode 100644 index 000000000..ff08ad7e9 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/folder.bru @@ -0,0 +1,4 @@ +meta { + name: bru-scripts + seq: 1 +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/helper.js b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/helper.js new file mode 100644 index 000000000..c62d994a5 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-folder-with-js/scripts/helper.js @@ -0,0 +1 @@ +// placeholder for the empty-state CTA test diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/bruno.json b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/bruno.json new file mode 100644 index 000000000..895beeadc --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "bru-with-js", + "type": "collection" +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/collection.bru b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/collection.bru new file mode 100644 index 000000000..11fbbedb9 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/collection.bru @@ -0,0 +1,3 @@ +meta { + name: bru-with-js +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/helper.js b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/helper.js new file mode 100644 index 000000000..c62d994a5 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-js/helper.js @@ -0,0 +1 @@ +// placeholder for the empty-state CTA test diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/bruno.json b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/bruno.json new file mode 100644 index 000000000..d90b00757 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "bru-with-request", + "type": "collection" +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/collection.bru b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/collection.bru new file mode 100644 index 000000000..519ec32f2 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/collection.bru @@ -0,0 +1,3 @@ +meta { + name: bru-with-request +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/echo.bru b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/echo.bru new file mode 100644 index 000000000..44ccc6ff9 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/bru-with-request/echo.bru @@ -0,0 +1,9 @@ +meta { + name: bru-echo + type: http + seq: 1 +} + +get { + url: https://echo.usebruno.com +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/bruno.json b/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/bruno.json new file mode 100644 index 000000000..6b3431e54 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "empty-bru", + "type": "collection" +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/collection.bru b/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/collection.bru new file mode 100644 index 000000000..06e4392c1 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/empty-bru/collection.bru @@ -0,0 +1,3 @@ +meta { + name: empty-bru +} diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/empty-yml/opencollection.yml b/tests/sidebar/empty-state-cta/fixtures/collections/empty-yml/opencollection.yml new file mode 100644 index 000000000..17b8e4890 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/empty-yml/opencollection.yml @@ -0,0 +1,3 @@ +opencollection: "1.0.0" +info: + name: empty-yml diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/opencollection.yml b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/opencollection.yml new file mode 100644 index 000000000..7cc4e5318 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/opencollection.yml @@ -0,0 +1,3 @@ +opencollection: "1.0.0" +info: + name: yml-with-folder diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/folder.yml b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/folder.yml new file mode 100644 index 000000000..c4db2154f --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/folder.yml @@ -0,0 +1,3 @@ +info: + name: yml-scripts + seq: 1 diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/helper.js b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/helper.js new file mode 100644 index 000000000..c62d994a5 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-folder/scripts/helper.js @@ -0,0 +1 @@ +// placeholder for the empty-state CTA test diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/helper.js b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/helper.js new file mode 100644 index 000000000..c62d994a5 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/helper.js @@ -0,0 +1 @@ +// placeholder for the empty-state CTA test diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/opencollection.yml b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/opencollection.yml new file mode 100644 index 000000000..7e0808251 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-js/opencollection.yml @@ -0,0 +1,3 @@ +opencollection: "1.0.0" +info: + name: yml-with-js diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/echo.yml b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/echo.yml new file mode 100644 index 000000000..71b5041d5 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/echo.yml @@ -0,0 +1,8 @@ +info: + name: yml-echo + type: http + seq: 1 + +http: + method: GET + url: https://echo.usebruno.com diff --git a/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/opencollection.yml b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/opencollection.yml new file mode 100644 index 000000000..905eb62d4 --- /dev/null +++ b/tests/sidebar/empty-state-cta/fixtures/collections/yml-with-request/opencollection.yml @@ -0,0 +1,3 @@ +opencollection: "1.0.0" +info: + name: yml-with-request diff --git a/tests/sidebar/empty-state-cta/init-user-data/preferences.json b/tests/sidebar/empty-state-cta/init-user-data/preferences.json new file mode 100644 index 000000000..9cce3d367 --- /dev/null +++ b/tests/sidebar/empty-state-cta/init-user-data/preferences.json @@ -0,0 +1,18 @@ +{ + "lastOpenedCollections": [ + "{{collectionPath}}/empty-bru", + "{{collectionPath}}/empty-yml", + "{{collectionPath}}/bru-with-js", + "{{collectionPath}}/yml-with-js", + "{{collectionPath}}/bru-with-request", + "{{collectionPath}}/yml-with-request", + "{{collectionPath}}/bru-folder-with-js", + "{{collectionPath}}/yml-with-folder" + ], + "preferences": { + "onboarding": { + "hasLaunchedBefore": true, + "hasSeenWelcomeModal": true + } + } +}