Prototype/simplify request creation (#6295)

* feat: add dropdown for quick request creation in tab bar

- Create reusable CreateUntitledRequest component with customizable trigger
- Add generateUniqueRequestName utility for unique request naming
- Replace modal-based request creation with dropdown in tab bar
- Support HTTP, GraphQL, WebSocket, and gRPC request types
- Generate unique names (Untitled, Untitled1, etc.) automatically
- Create requests at collection root level

* Update request creation and collection components

* Fix dropdown positioning and styling when appended to document.body

- Change appendTo from 'parent' to document.body for absolute positioning
- Add comprehensive styling via onShow handler to ensure proper width, padding, text color, and opacity
- Add global styles as fallback for dropdown elements
- Ensure dropdown overlaps parent without expanding it

* Update RequestTabs and Collection components

* Add curl paste detection and parsing for HTTP requests

* Fix generateUniqueRequestName to check filesystem for existing files

* feat: add placeholder text to HTTP request URL input

Add helpful placeholder text 'Enter URL or paste a cURL request' to the HTTP request URL input field. This guides users on how to use the input field, indicating they can either enter a URL directly or paste a cURL command which will be automatically parsed.

* Simplify request creation in collection menu

* fix: fixed issues with cURL paste for GraphQL requests in the URL input bar

* fix: added icons to create request dropdown

* fix: fixed the icon | text gap in dropdown

* fix: removed unnecessary updates on the Dropdown Component

* added onCreate to Dropdown to remove unwanted diffs

* fix: simplified the generateUniqueRequestName function. ai writes complex code

* chore: formatting and removed unnecessary diffs

* Update packages/bruno-app/src/components/RequestPane/QueryUrl/index.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: format

* Fix failing E2E tests by updating to new request creation flow

- Replace #create-new-tab selector with new dropdown flow using createUntitledRequest helper
- Update generateUniqueRequestName to handle .bru, .yml, and .yaml file extensions
- Add createUntitledRequest helper function with optional URL and tag parameters
- Update all failing tests to use the new helper function
- Fix selectors from .collection-item-name to .item-name where needed
- All 13 previously failing tests now pass

* chore: removed unused import

---------

Co-authored-by: Sid <siddharth@usebruno.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Chirag Chandrashekhar
2025-12-09 19:08:52 +05:30
committed by GitHub
parent 03e8f2d67d
commit f6363389d0
11 changed files with 689 additions and 120 deletions

View File

@@ -89,6 +89,68 @@ type CreateRequestOptions = {
inFolder?: boolean;
};
type CreateUntitledRequestOptions = {
requestType?: 'HTTP' | 'GraphQL' | 'WebSocket' | 'gRPC';
requestName?: string;
url?: string;
tag?: string;
};
/**
* Create an untitled request using the new dropdown flow (from tabs area)
* @param page - The page object
* @param options - Optional settings (requestType, url, tag)
* @returns void
*/
const createUntitledRequest = async (
page: Page,
options: CreateUntitledRequestOptions = {}
) => {
const { requestType = 'HTTP', url, tag } = options;
await test.step(`Create untitled ${requestType} request${url ? ' with URL' : ''}${tag ? ' with tag' : ''}`, async () => {
// Click the + icon to open the dropdown
const createButton = page.locator('.short-tab').locator('svg').first();
await createButton.waitFor({ state: 'visible' });
await createButton.click();
// Select the request type from dropdown
await page.locator('.tippy-box .dropdown-item').filter({ hasText: requestType }).waitFor({ state: 'visible' });
await page.locator('.tippy-box .dropdown-item').filter({ hasText: requestType }).click();
// Wait for the request tab to be active
await page.locator('.request-tab.active').waitFor({ state: 'visible' });
await page.waitForTimeout(300);
// Fill URL if provided
if (url) {
await page.locator('#request-url .CodeMirror').click();
await page.locator('#request-url textarea').fill(url);
await page.locator('#send-request').getByTitle('Save Request').click();
await page.waitForTimeout(200);
}
// Add tag if provided
if (tag) {
await page.getByRole('tab', { name: 'Settings' }).click();
await page.waitForTimeout(200);
const tagInput = await page.getByTestId('tag-input').getByRole('textbox');
await tagInput.fill(tag);
await tagInput.press('Enter');
await page.waitForTimeout(200);
await expect(page.locator('.tag-item', { hasText: tag })).toBeVisible();
await page.keyboard.press('Meta+s');
await page.waitForTimeout(200);
}
// Wait for toast message to ensure request creation is complete
// This helps prevent race conditions when creating multiple requests
await expect(page.getByText('New request created!')).toBeVisible({ timeout: 10000 }).catch(() => {
// Toast might have already disappeared, that's okay
});
});
};
/**
* Create a request in a collection or folder
* @param page - The page object
@@ -521,6 +583,7 @@ export {
openCollectionAndAcceptSandbox,
createCollection,
createRequest,
createUntitledRequest,
deleteRequest,
importCollection,
removeCollection,
@@ -539,4 +602,4 @@ export {
expectResponseContains
};
export type { SandboxMode, EnvironmentType, EnvironmentVariable, CreateCollectionOptions, ImportCollectionOptions, CreateRequestOptions };
export type { SandboxMode, EnvironmentType, EnvironmentVariable, CreateCollectionOptions, ImportCollectionOptions, CreateRequestOptions, CreateUntitledRequestOptions };