Skip to content

Prototype/simplify request creation#6295

Merged
sid-bruno merged 24 commits intousebruno:mainfrom
chirag-bruno:prototype/simplify-request-creation
Dec 9, 2025
Merged

Prototype/simplify request creation#6295
sid-bruno merged 24 commits intousebruno:mainfrom
chirag-bruno:prototype/simplify-request-creation

Conversation

@chirag-bruno
Copy link
Collaborator

@chirag-bruno chirag-bruno commented Dec 3, 2025

Description

This PR addresses https://usebruno.atlassian.net/browse/BRU-2114.
This update enables users to create requests with a default name at the collection root level

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Summary by CodeRabbit

  • New Features

    • Create requests (HTTP, GraphQL, WebSocket, gRPC) inline from tabs with automatic "Untitled" naming, success/error toasts, and updated tab placement.
    • Paste cURL into the URL editor to auto-populate URL, method, headers, body (including GraphQL) and auth; URL field now shows a placeholder.
    • Improved unique untitled request name generation to avoid collisions.
  • Style

    • Added a small styled wrapper component for request creation UI.
  • Tests

    • Test helpers and specs updated to use a new createUntitledRequest helper and to assert against untitled naming and selectors.

✏️ Tip: You can customize this high-level summary in your review settings.

- 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
- 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
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.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Adds an inline CreateUntitledRequest dropdown to create HTTP/GraphQL/WebSocket/gRPC requests, curl-paste handlers in the URL editor to parse cURL into request fields, a generateUniqueRequestName utility (duplicated), and test helpers + tests updated to use the new untitled-request flow. (45 words)

Changes

Cohort / File(s) Summary
CreateUntitledRequest component
packages/bruno-app/src/components/CreateUntitledRequest/StyledWrapper.js, packages/bruno-app/src/components/CreateUntitledRequest/index.js
New styled wrapper and dropdown UI that creates untitled requests. Generates sanitized unique names, dispatches Redux create actions for HTTP/GraphQL/WebSocket/gRPC, shows success/error toasts, and calls optional onRequestCreated.
URL Editor (curl paste)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
Adds handleGraphqlPaste and handleHttpPaste using getRequestFromCurlCommand to parse cURL and populate URL, method, headers, body (multiple modes), GraphQL query/variables, and auth; binds paste handler by item.type and adds a URL placeholder.
RequestTabs integration
packages/bruno-app/src/components/RequestTabs/index.js
Replaces legacy create-new-tab button/modal with the inline CreateUntitledRequest component in the tab area and removes the old createNewTab flow.
Collections utility
packages/bruno-app/src/utils/collections/index.js
Adds generateUniqueRequestName(collection, baseName = 'Untitled', itemUid = null) to compute numeric suffixes for untitled request names. Note: function appears duplicated in the file.
Tests & Test helpers
tests/utils/page/actions.ts, tests/collection/*, tests/request/*, tests/response/*, tests/...
Adds CreateUntitledRequestOptions type and createUntitledRequest(page, options) helper; updates many tests to use the new helper, updated selectors (untitled item names, CodeMirror URL selector), and Save Request action.

Sequence Diagram

sequenceDiagram
    actor User
    participant Browser as Browser UI
    participant Dropdown as CreateUntitledRequest (Dropdown)
    participant Utils as generateUniqueRequestName
    participant Redux as Redux Store / Actions
    participant Toast as Toast Notification

    User->>Browser: open tabs area, click dropdown
    Browser->>Dropdown: onSelect(requestType)
    Dropdown->>Utils: generateUniqueRequestName(collection, "Untitled")
    Utils-->>Dropdown: uniqueName
    Dropdown->>Redux: dispatch new<RequestType>Request({ name: uniqueName, filename, type-specific payload, collectionUid, itemUid })
    Redux-->>Dropdown: success / error
    alt success
        Dropdown->>Toast: show success
        Dropdown->>Browser: call onRequestCreated callback (if provided)
    else failure
        Dropdown->>Toast: show error message
    end
    Dropdown->>Browser: hide dropdown
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Areas requiring attention:
    • Duplicate generateUniqueRequestName definitions — deduplicate and confirm single authoritative implementation.
    • Validate Redux payload shapes for newHttpRequest/newGrpcRequest/newWsRequest and ensure created item metadata (filename/type) match expectations.
    • RequestPane curl handlers — verify getRequestFromCurlCommand coverage, body-mode mapping (formUrlEncoded/multipartForm placeholders), and auth handling edge cases.
    • Tests & helpers — confirm selectors (#request-url .CodeMirror, .item-name /^Untitled/) are stable and timing/wait logic is robust.

Possibly related PRs

  • feature/autoSave (#582) #6211 — touches packages/bruno-app/src/components/RequestPane/QueryUrl/index.js; may conflict with onSave/on-paste handling changes.

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

✨ A dropdown clicks, an Untitled is born,
Curl lines parsed at the edge of the morn.
Names tick upward, tests find their beat,
Redux dispatches, and toasts cheer the feat. 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objective: introducing a simplified request creation flow via an untitled request dropdown component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea21ae4 and 1d88c8d.

📒 Files selected for processing (1)
  • packages/bruno-app/src/utils/collections/index.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/utils/collections/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Unit Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
packages/bruno-app/src/utils/collections/index.js (2)

1644-1647: Remove unnecessary async keyword.

The function doesn't contain any await expressions, making the async declaration superfluous. This adds overhead by wrapping the return value in a Promise unnecessarily.

-export const generateUniqueRequestName = async (collection, baseName = 'Untitled', itemUid = null) => {
+export const generateUniqueRequestName = (collection, baseName = 'Untitled', itemUid = null) => {
   if (!collection) {
     return baseName;
   }

1649-1652: Move require to top-level import and escape regex special characters.

Using require() inside functions is non-idiomatic for ES modules and incurs repeated resolution overhead. Additionally, if baseName contains regex special characters (e.g., parentheses, dots), the pattern will fail.

 import {cloneDeep, isEqual, sortBy, filter, map, isString, findIndex, find, each, get } from 'lodash';
+import trim from 'lodash/trim';
 // ... other imports

Then update the function:

-  const trim = require('lodash/trim');
   const parentItem = itemUid ? findItemInCollection(collection, itemUid) : null;
   const parentItems = parentItem ? (parentItem.items || []) : (collection.items || []);
-  const baseNamePattern = new RegExp(`^${baseName}(\\d+)?$`);
+  const escapedBaseName = baseName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+  const baseNamePattern = new RegExp(`^${escapedBaseName}(\\d+)?$`);
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (3)

95-107: Inconsistent UX: error toast shown for non-cURL regular paste.

When pasting a regular URL (not a cURL command) into a GraphQL request, this shows an error toast and then allows the paste to proceed. This could confuse users pasting normal URLs.

Consider silently allowing non-cURL pastes without the error:

   const curlCommandRegex = /^\s*curl\s/i;
   if (!curlCommandRegex.test(pastedData)) {
-    toast.error('Invalid cURL command');
     return;
   }

178-186: Fix inconsistent indentation.

Lines 181-185 have extra indentation that doesn't match the surrounding code structure, making the control flow harder to follow.

   const curlCommandRegex = /^\s*curl\s/i;
   if (!curlCommandRegex.test(pastedData)) {
     // Not a curl command, allow normal paste behavior
-      return;
-    }
-
-    // Prevent the default paste behavior
-    event.preventDefault();
+    return;
+  }
+
+  // Prevent the default paste behavior
+  event.preventDefault();

283-291: TODOs for formUrlEncoded and multipartForm remain unimplemented.

The cURL import silently ignores body content for these modes, which may confuse users expecting full import support.

Consider at minimum showing a toast warning when these body modes are detected but not fully imported. Would you like me to help implement proper param setting for these modes, or open an issue to track this?

packages/bruno-app/src/components/CreateUntitledRequest/index.js (2)

22-116: Unify async handling and reduce duplication across create handlers

All four handleCreate*Request functions follow the same pattern: hide dropdown, compute a unique name, dispatch an async action, then show a success/error toast and optionally call onRequestCreated. Right now they:

  • Are marked async but use .then/.catch instead of await
  • Duplicate the same success/error handling logic 4 times

Consider refactoring to a shared helper that takes the payload/action creator as input and uses try/catch with await for consistency:

-const handleCreateHttpRequest = async () => {
-  dropdownTippyRef.current?.hide();
-  const uniqueName = await generateUniqueRequestName(collection, 'Untitled', itemUid);
-  const filename = sanitizeName(uniqueName);
-
-  dispatch(
-    newHttpRequest({
-      requestName: uniqueName,
-      filename: filename,
-      requestType: 'http-request',
-      requestUrl: '',
-      requestMethod: 'GET',
-      collectionUid: collection.uid,
-      itemUid: itemUid
-    })
-  )
-    .then(() => {
-      toast.success('New request created!');
-      onRequestCreated?.();
-    })
-    .catch((err) => toast.error(err ? err.message : 'An error occurred while adding the request'));
-};
+const createRequest = async (actionPayloadBuilder) => {
+  dropdownTippyRef.current?.hide();
+  const uniqueName = await generateUniqueRequestName(collection, 'Untitled', itemUid);
+  const filename = sanitizeName(uniqueName);
+
+  try {
+    await dispatch(actionPayloadBuilder({ uniqueName, filename }));
+    toast.success('New request created!');
+    onRequestCreated?.();
+  } catch (err) {
+    toast.error(err ? err.message : 'An error occurred while adding the request');
+  }
+};
+
+const handleCreateHttpRequest = () =>
+  createRequest(({ uniqueName, filename }) =>
+    newHttpRequest({
+      requestName: uniqueName,
+      filename,
+      requestType: 'http-request',
+      requestUrl: '',
+      requestMethod: 'GET',
+      collectionUid: collection.uid,
+      itemUid
+    })
+  );

You can apply the same pattern to the GraphQL, WebSocket, and gRPC handlers to keep their differences (payload shape) focused and avoid repeating the toast logic.


10-171: Add a brief JSDoc for the CreateUntitledRequest abstraction

Given this component is now the central entry point for creating new requests of multiple types at the collection root, a short JSDoc on the component describing props (collectionUid, itemUid, onRequestCreated, placement) and behavior would make its purpose clearer to future readers.

Example:

/**
 * Renders a dropdown button that creates new untitled requests (HTTP, GraphQL, WebSocket, gRPC)
 * at the given collection (and optional parent item) using sane defaults.
 *
 * @param {Object} props
 * @param {string} props.collectionUid - Target collection UID.
 * @param {string|null} [props.itemUid=null] - Optional parent item UID within the collection.
 * @param {Function} [props.onRequestCreated] - Callback invoked after a request is successfully created.
 * @param {('top'|'bottom'|'left'|'right')} [props.placement='bottom'] - Dropdown placement.
 */
const CreateUntitledRequest = ({ collectionUid, itemUid = null, onRequestCreated, placement = 'bottom' }) => {

This aligns with your guideline to document abstractions without adding noise.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38ba53b and fc8c249.

📒 Files selected for processing (6)
  • packages/bruno-app/src/components/CreateUntitledRequest/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/CreateUntitledRequest/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (3 hunks)
  • packages/bruno-app/src/components/RequestTabs/index.js (2 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js (1 hunks)
  • packages/bruno-app/src/utils/collections/index.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-app/src/components/CreateUntitledRequest/StyledWrapper.js
  • packages/bruno-app/src/components/RequestTabs/index.js
  • packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
  • packages/bruno-app/src/utils/collections/index.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js
  • packages/bruno-app/src/components/CreateUntitledRequest/index.js
🧠 Learnings (1)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles

Applied to files:

  • packages/bruno-app/src/components/CreateUntitledRequest/StyledWrapper.js
🧬 Code graph analysis (3)
packages/bruno-app/src/components/RequestTabs/index.js (1)
packages/bruno-app/src/components/CreateUntitledRequest/index.js (1)
  • CreateUntitledRequest (10-171)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (2)
packages/bruno-app/src/utils/curl/curl-to-json.js (1)
  • request (126-126)
packages/bruno-app/src/utils/curl/index.js (3)
  • request (37-37)
  • getRequestFromCurlCommand (6-103)
  • getRequestFromCurlCommand (6-103)
packages/bruno-app/src/utils/collections/index.js (2)
packages/bruno-cli/src/utils/collection.js (1)
  • findItemInCollection (252-256)
packages/bruno-electron/src/utils/collection.js (1)
  • findItemInCollection (166-170)
🪛 Biome (2.1.2)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js

[error] 151-151: variables is assigned to itself.

This is where is assigned.

Self assignments have no effect and can be removed.

(lint/correctness/noSelfAssign)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: Unit Tests
🔇 Additional comments (6)
packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js (1)

617-617: LGTM!

Minor indentation adjustment. No functional impact.

packages/bruno-app/src/utils/collections/index.js (1)

1674-1682: LGTM!

The suffix calculation logic correctly handles the edge cases: returns base name when no matches exist, and increments from the highest existing suffix otherwise.

packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (1)

401-406: LGTM!

Clean integration of the paste handlers with appropriate conditional binding based on request type. The placeholder text clearly communicates the cURL paste capability.

packages/bruno-app/src/components/CreateUntitledRequest/StyledWrapper.js (1)

1-8: LGTM!

Minimal styled wrapper appropriate for the dropdown positioning context. Follows project conventions for using styled-components as wrappers.

packages/bruno-app/src/components/RequestTabs/index.js (1)

144-153: LGTM!

Clean integration of CreateUntitledRequest in the tab bar. The conditional render on activeCollection prevents errors when no collection is active, and itemUid={null} correctly indicates root-level request creation.

packages/bruno-app/src/components/CreateUntitledRequest/index.js (1)

10-21: Collection lookup and null guard look good

Using useSelector to pull collections and early‑returning null when the target collection is missing is a reasonable defensive pattern for this component.

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (1)

166-166: Missing item.type in dependency array.

The callback checks item.type on line 96, but the dependency array only includes item.uid. Add item.type to avoid stale closures if the type changes.

-  }, [dispatch, item.uid, collection.uid]);
+  }, [dispatch, item.uid, item.type, collection.uid]);
🧹 Nitpick comments (2)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (2)

283-291: Empty conditional branches with TODO comments.

These empty branches set the body mode but don't populate the params, which may confuse users when their form data doesn't appear. Consider logging a warning or showing a toast indicating partial import.

         } else if (bodyMode === 'formUrlEncoded' && request.body.formUrlEncoded) {
-          // For formUrlEncoded, we need to set each param individually
-          // This is a limitation - we'd need to clear existing params first
-          // For now, we'll set the body mode and the user can manually adjust
-          // TODO: Implement proper formUrlEncoded param setting
+          // TODO: Implement proper formUrlEncoded param setting
+          console.warn('formUrlEncoded body detected but params not fully imported');
         } else if (bodyMode === 'multipartForm' && request.body.multipartForm) {
-          // For multipartForm, similar limitation
-          // TODO: Implement proper multipartForm param setting
+          // TODO: Implement proper multipartForm param setting
+          console.warn('multipartForm body detected but params not fully imported');
         }

Would you like me to open an issue to track the implementation of formUrlEncoded and multipartForm param setting?


95-372: Significant code duplication between paste handlers.

Both handleGraphqlPaste and handleHttpPaste share nearly identical logic for URL updates, method updates, and header updates. Consider extracting shared logic into a helper function to reduce maintenance burden.

const applyParsedRequest = (request) => {
  dispatch(requestUrlChanged({ itemUid: item.uid, collectionUid: collection.uid, url: request.url }));
  if (request.method) {
    dispatch(updateRequestMethod({ method: request.method.toUpperCase(), itemUid: item.uid, collectionUid: collection.uid }));
  }
  if (request.headers?.length > 0) {
    dispatch(setRequestHeaders({ collectionUid: collection.uid, itemUid: item.uid, headers: request.headers }));
  }
};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc8c249 and c48165a.

📒 Files selected for processing (1)
  • packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions. () => {} is good
No space between function name and parentheses. func() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
🧬 Code graph analysis (1)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (1)
packages/bruno-app/src/utils/curl/index.js (3)
  • request (37-37)
  • getRequestFromCurlCommand (6-103)
  • getRequestFromCurlCommand (6-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: CLI Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (2)
packages/bruno-app/src/components/RequestPane/QueryUrl/index.js (2)

1-16: LGTM!

Imports are appropriately organized and all appear to be used by the new paste handling functionality.


401-406: LGTM!

Placeholder text improves discoverability of the cURL paste feature. The conditional onPaste binding correctly routes to the appropriate handler based on request type.

- 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
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
packages/bruno-app/src/utils/collections/index.js (1)

1633-1681: Simplify generateUniqueRequestName and align implementation with docs

The logic looks correct for the current Untitled use case, but a few cleanups would make this helper more robust and consistent:

  • The function is async but only does synchronous work; dropping async avoids returning a Promise unnecessarily.
  • Inside this ES‑module style file, prefer a top‑level import for trim over require('lodash/trim') for consistency and bundler friendliness.
  • The JSDoc mentions checking the filesystem, but the current implementation only inspects collection.items; either update the comment or add the filesystem behavior if still needed.
  • If you ever pass arbitrary baseName values, consider escaping regex metacharacters in baseNamePattern to avoid surprising matches.

A possible refactor:

-export const generateUniqueRequestName = async (collection, baseName = 'Untitled', itemUid = null) => {
+export const generateUniqueRequestName = (collection, baseName = 'Untitled', itemUid = null) => {
   if (!collection) {
     return baseName;
   }
 
-  const trim = require('lodash/trim');
+  // using lodash.trim via top-level import keeps this helper tree-shakable and consistent with other lodash usage
   const parentItem = itemUid ? findItemInCollection(collection, itemUid) : null;
   const parentItems = parentItem ? (parentItem.items || []) : (collection.items || []);
-  const baseNamePattern = new RegExp(`^${baseName}(\\d+)?$`);
+  const baseNamePattern = new RegExp(`^${baseName}(\\d+)?$`);

And add a top‑level import alongside the other lodash utilities:

import trim from 'lodash/trim';
tests/request/encoding/curl-encoding.spec.ts (1)

2-60: Nice use of createUntitledRequest; consider extracting the Untitled locator

Switching to createUntitledRequest makes these tests much closer to the real user flow and easier to maintain; the subsequent steps to open the generated request and assert code generation look solid.

You might optionally extract the repeated locator for the Untitled request into a variable to improve readability and keep selectors consistent across both tests, e.g.:

const untitledRequest = page.locator('.item-name').filter({ hasText: /^Untitled/ }).first();
await untitledRequest.click();

As per coding guidelines, extracting locator variables where reused keeps Playwright tests easier to read and maintain.

Also applies to: 62-105

tests/collection/create/create-collection.spec.ts (1)

27-34: Tighten URL textarea selector after using createUntitledRequest

The switch to createUntitledRequest looks good here. One small robustness tweak: after clicking #request-url .CodeMirror, using a global page.locator('textarea') can become fragile if another textarea appears in the UI.

You can scope the selector to the URL editor (matching what the helper does) and keep the flow the same:

-    await page.locator('#request-url .CodeMirror').click();
-    await page.locator('textarea').fill('http://localhost:8081');
+    await page.locator('#request-url .CodeMirror').click();
+    await page.locator('#request-url textarea').fill('http://localhost:8081');

Optionally, you could also pass url into createUntitledRequest to avoid duplicating this step, but that’s not required for correctness.

As per coding guidelines, tighter locators help keep Playwright tests stable over time.

tests/collection/moving-requests/tag-persistence.spec.ts (1)

2-69: Avoid the fixed waitForTimeout(1000) before creating untitled requests

The refactor to use createUntitledRequest with tag: 'smoke' and to work purely with the three Untitled requests in the sidebar is solid and reads much better than the manual flow.

The one questionable bit is the explicit await page.waitForTimeout(1000); after saving the collection, especially since each createUntitledRequest already waits for the new-request toast and the later untitledRequests locator asserts toHaveCount(3). If there isn’t a known race here, you can likely drop or replace this with a more targeted wait (e.g., an expect on whatever element indicates the collection is ready) to keep the test fast and less brittle:

-    await page.getByRole('button', { name: 'Save' }).click();
-    await page.waitForTimeout(1000);
+    await page.getByRole('button', { name: 'Save' }).click();
+    // Rely on subsequent `createUntitledRequest` + `toHaveCount(3)` to synchronize instead of a fixed timeout

As per coding guidelines, we try to avoid page.waitForTimeout in Playwright tests unless there’s no reliable locator-based condition to wait on.

tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts (1)

2-62: Stabilize URL and Untitled locators in cross-collection drag-and-drop tests

The move to createUntitledRequest makes both drag‑and‑drop tests much cleaner, and the assertions around presence/absence of Untitled requests in source/target collections look correct.

Two small improvements for robustness/readability:

  • As in the collection‑creation test, after clicking #request-url .CodeMirror, scope the textarea to the URL editor to avoid accidentally hitting another textarea:
-    await page.locator('#request-url .CodeMirror').click();
-    await page.locator('textarea').fill('https://echo.usebruno.com');
+    await page.locator('#request-url .CodeMirror').click();
+    await page.locator('#request-url textarea').fill('https://echo.usebruno.com');
  • You reuse the .item-name + /^Untitled/ selector multiple times; consider assigning it to a local const (or two, one per collection container) for clarity and to keep selectors consistent.

As per coding guidelines, tighter locators and shared locator variables help keep Playwright tests less flaky.

Also applies to: 64-124

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bed0b7 and ea21ae4.

📒 Files selected for processing (7)
  • packages/bruno-app/src/utils/collections/index.js (2 hunks)
  • tests/collection/create/create-collection.spec.ts (2 hunks)
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts (7 hunks)
  • tests/collection/moving-requests/tag-persistence.spec.ts (2 hunks)
  • tests/request/encoding/curl-encoding.spec.ts (3 hunks)
  • tests/response/large-response-crash-prevention.spec.ts (2 hunks)
  • tests/utils/page/actions.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions. () => {} is good
No space between function name and parentheses. func() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • tests/response/large-response-crash-prevention.spec.ts
  • tests/collection/create/create-collection.spec.ts
  • tests/utils/page/actions.ts
  • packages/bruno-app/src/utils/collections/index.js
  • tests/request/encoding/curl-encoding.spec.ts
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts
  • tests/collection/moving-requests/tag-persistence.spec.ts
tests/**/**.*

⚙️ CodeRabbit configuration file

tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:

  • Follow best practices for Playwright code and e2e automation

  • Try to reduce usage of page.waitForTimeout(); in code unless absolutely necessary and the locator cannot be found using existing expect() playwright calls

  • Avoid using page.pause() in code

  • Use locator variables for locators

  • Avoid using test.only

  • Use multiple assertions

  • Promote the use of test.step as much as possible so the generated reports are easier to read

  • Ensure that the fixtures like the collections are nested inside the fixtures folder

    Fixture Example*: Here's an example of possible fixture and test pair

    .
    ├── fixtures
    │   └── collection
    │       ├── base.bru
    │       ├── bruno.json
    │       ├── collection.bru
    │       ├── ws-test-request-with-headers.bru
    │       ├── ws-test-request-with-subproto.bru
    │       └── ws-test-request.bru
    ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection
    ├── headers.spec.ts
    ├── persistence.spec.ts
    ├── variable-interpolation
    │   ├── fixtures
    │   │   └── collection
    │   │       ├── environments
    │   │       ├── bruno.json
    │   │       └── ws-interpolation-test.bru
    │   ├── init-user-data
    │   └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection
    └── subproto.spec.ts
    

Files:

  • tests/response/large-response-crash-prevention.spec.ts
  • tests/collection/create/create-collection.spec.ts
  • tests/utils/page/actions.ts
  • tests/request/encoding/curl-encoding.spec.ts
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts
  • tests/collection/moving-requests/tag-persistence.spec.ts
🧠 Learnings (1)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use 2 spaces for indentation. No tabs, just spaces

Applied to files:

  • packages/bruno-app/src/utils/collections/index.js
🧬 Code graph analysis (2)
tests/response/large-response-crash-prevention.spec.ts (1)
tests/utils/page/actions.ts (1)
  • createUntitledRequest (586-586)
packages/bruno-app/src/utils/collections/index.js (1)
packages/bruno-cli/src/utils/collection.js (1)
  • findItemInCollection (336-340)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: CLI Tests
  • GitHub Check: Unit Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (2)
tests/response/large-response-crash-prevention.spec.ts (1)

2-39: LGTM on using createUntitledRequest for the large-response scenario

Moving the request-creation steps into createUntitledRequest makes this test much leaner and focused purely on the large-response behavior; the subsequent send and assertions are unchanged and still clear.

tests/utils/page/actions.ts (1)

92-152: Refine createUntitledRequest to reduce timeouts and unused options

This helper is a nice abstraction over the new dropdown flow and the use of test.step makes traces readable. A couple of tweaks would improve it further:

  • CreateUntitledRequestOptions.requestName is currently unused; either wire it into a rename flow or drop it from the type to avoid confusion for callers.
  • Several waitForTimeout calls are probably redundant given the preceding waitFor/expect checks and can be removed or replaced with locator-based waits to reduce flakiness and overall test time.

For example, you can rely on the existing waits and assertions:

-    // Wait for the request tab to be active
-    await page.locator('.request-tab.active').waitFor({ state: 'visible' });
-    await page.waitForTimeout(300);
+    // Wait for the request tab to be active
+    await page.locator('.request-tab.active').waitFor({ state: 'visible' });
@@
-    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);
-    }
+    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();
+      // rely on downstream assertions (and the toast) instead of a fixed delay
+    }
@@
-    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);
-    }
+    if (tag) {
+      await page.getByRole('tab', { name: 'Settings' }).click();
+      const tagInput = await page.getByTestId('tag-input').getByRole('textbox');
+      await tagInput.fill(tag);
+      await tagInput.press('Enter');
+      await expect(page.locator('.tag-item', { hasText: tag })).toBeVisible();
+      await page.keyboard.press('Meta+s');
+    }

And if requestName isn’t going to be implemented immediately, simplifying the type keeps the API honest:

-type CreateUntitledRequestOptions = {
-  requestType?: 'HTTP' | 'GraphQL' | 'WebSocket' | 'gRPC';
-  requestName?: string;
-  url?: string;
-  tag?: string;
-};
+type CreateUntitledRequestOptions = {
+  requestType?: 'HTTP' | 'GraphQL' | 'WebSocket' | 'gRPC';
+  url?: string;
+  tag?: string;
+};

[ suggest_recommended_refactor ]
As per coding guidelines for tests, minimizing page.waitForTimeout and unused surface area makes the helper more reliable and easier to use.

Also applies to: 586-603, 605-605

import path from 'utils/common/path';
import { isRequestTagsIncluded } from '@usebruno/common';

import { writeFileSync } from 'node:fs';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid and unused import

@sid-bruno sid-bruno merged commit f636338 into usebruno:main Dec 9, 2025
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 23, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants