mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
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
This commit is contained in:
@@ -744,6 +744,7 @@ const CollectionItem = ({ item, collectionUid, collectionPathname, searchText })
|
|||||||
))}
|
))}
|
||||||
<div style={{ paddingLeft: 8 }}>
|
<div style={{ paddingLeft: 8 }}>
|
||||||
<MenuDropdown
|
<MenuDropdown
|
||||||
|
data-testid="add-request-cta-folder"
|
||||||
items={emptyFolderMenuItems}
|
items={emptyFolderMenuItems}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
appendTo={dropdownContainerRef?.current || document.body}
|
appendTo={dropdownContainerRef?.current || document.body}
|
||||||
|
|||||||
@@ -75,8 +75,9 @@ const Collection = ({ collection, searchText }) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isLoading = collection.isLoading;
|
const isLoading = collection.isLoading;
|
||||||
const collectionRef = useRef(null);
|
const collectionRef = useRef(null);
|
||||||
// Only count persisted items; transients don't affect empty state
|
// Only count persisted requests and folders; transients and file items
|
||||||
const itemCount = collection.items?.filter((i) => !i.isTransient).length || 0;
|
// (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 isCollectionFocused = useSelector(isTabForItemActive({ itemUid: collection.uid }));
|
||||||
const { hasCopiedItems } = useSelector((state) => state.app.clipboard);
|
const { hasCopiedItems } = useSelector((state) => state.app.clipboard);
|
||||||
@@ -533,6 +534,7 @@ const Collection = ({ collection, searchText }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div style={{ paddingLeft: 8 }}>
|
<div style={{ paddingLeft: 8 }}>
|
||||||
<MenuDropdown
|
<MenuDropdown
|
||||||
|
data-testid="add-request-cta"
|
||||||
items={emptyStateMenuItems}
|
items={emptyStateMenuItems}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
appendTo={dropdownContainerRef?.current || document.body}
|
appendTo={dropdownContainerRef?.current || document.body}
|
||||||
|
|||||||
143
tests/sidebar/empty-state-cta/empty-state-cta.spec.ts
Normal file
143
tests/sidebar/empty-state-cta/empty-state-cta.spec.ts
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { test, expect, Page } from '../../../playwright';
|
||||||
|
import { buildCommonLocators, closeAllCollections } from '../../utils/page';
|
||||||
|
|
||||||
|
test.describe.serial('Sidebar empty-state "+ Add request" CTA', () => {
|
||||||
|
let locators: ReturnType<typeof buildCommonLocators>;
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "bru-folder-with-js",
|
||||||
|
"type": "collection"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: bru-folder-with-js
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: bru-scripts
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// placeholder for the empty-state CTA test
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "bru-with-js",
|
||||||
|
"type": "collection"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: bru-with-js
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// placeholder for the empty-state CTA test
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "bru-with-request",
|
||||||
|
"type": "collection"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: bru-with-request
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
meta {
|
||||||
|
name: bru-echo
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: https://echo.usebruno.com
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "empty-bru",
|
||||||
|
"type": "collection"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: empty-bru
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
opencollection: "1.0.0"
|
||||||
|
info:
|
||||||
|
name: empty-yml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
opencollection: "1.0.0"
|
||||||
|
info:
|
||||||
|
name: yml-with-folder
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
info:
|
||||||
|
name: yml-scripts
|
||||||
|
seq: 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// placeholder for the empty-state CTA test
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// placeholder for the empty-state CTA test
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
opencollection: "1.0.0"
|
||||||
|
info:
|
||||||
|
name: yml-with-js
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
info:
|
||||||
|
name: yml-echo
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: https://echo.usebruno.com
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
opencollection: "1.0.0"
|
||||||
|
info:
|
||||||
|
name: yml-with-request
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user