Skip to content

Improve tables design#6330

Merged
helloanoop merged 8 commits intousebruno:mainfrom
naman-bruno:improve/tables
Dec 10, 2025
Merged

Improve tables design#6330
helloanoop merged 8 commits intousebruno:mainfrom
naman-bruno:improve/tables

Conversation

@naman-bruno
Copy link
Collaborator

@naman-bruno naman-bruno commented Dec 6, 2025

Description

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.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • A reusable EditableTable component added and rolled out for headers, vars, params, assertions, and form parts with add-row, bulk-edit, and reorder support.
  • Improvements

    • Unified bulk/set handlers for collections, folders, and requests; stable UID handling and validation.
    • Improved multipart file controls (browse/clear, path handling) and several styling/layout refinements.
  • Tests

    • Tests updated to use existing rows and more precise selectors.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

Walkthrough

Adds a reusable EditableTable component and migrates many header/param/var/assertion/multipart UIs to it; introduces bulk-set Redux actions and UID-preserving logic; updates styling and tests to match trailing-empty-row behavior. (47 words)

Changes

Cohort / File(s) Summary
EditableTable component
packages/bruno-app/src/components/EditableTable/index.js, packages/bruno-app/src/components/EditableTable/StyledWrapper.js
New EditableTable + StyledWrapper: configurable columns, inline editors, trailing empty-row add, optional checkboxes, per-row delete, drag-reorder, per-row validation, UID handling, and theme-aware styling.
Collection settings
packages/bruno-app/src/components/CollectionSettings/Headers/index.js, packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js
Replaced custom header/var tables with EditableTable; unified onChange handlers dispatching setCollectionHeaders/setCollectionVars; memoized handlers and validation wiring.
Folder settings
packages/bruno-app/src/components/FolderSettings/Headers/index.js, packages/bruno-app/src/components/FolderSettings/Vars/VarsTable/index.js
Migrated headers/vars UI to EditableTable; consolidated updates via setFolderHeaders/setFolderVars; removed per-row add/update/delete flows.
Request pane
packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js, packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js, packages/bruno-app/src/components/RequestPane/Assertions/index.js, packages/bruno-app/src/components/RequestPane/QueryParams/index.js, packages/bruno-app/src/components/RequestPane/FormUrlEncodedParams/index.js, packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js, packages/bruno-app/src/components/RequestPane/MultipartFormParams/StyledWrapper.js
Replaced many manual tables with EditableTable; added assertion operator parsing and unary handling; multipart params handle file browse, path normalization, and clearing; reordering wired to move actions; editors integrated per column.
Redux bulk actions & UID handling
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
Added bulk-set actions (setFormUrlEncodedParams, setMultipartFormParams, setFolderVars, setCollectionVars, setRequestAssertions, setRequestVars); UID generation updated to preserve provided uids (`uid
Environment / Workspace styling
packages/bruno-app/src/components/Environments/.../EnvironmentVariables/StyledWrapper.js, packages/bruno-app/src/components/GlobalEnvironments/.../EnvironmentVariables/StyledWrapper.js, packages/bruno-app/src/components/WorkspaceHome/.../EnvironmentVariables/StyledWrapper.js, packages/bruno-app/src/components/WorkspaceHome/.../EnvironmentDetails/StyledWrapper.js, packages/bruno-app/src/components/WorkspaceHome/.../EnvironmentDetails/index.js, packages/bruno-app/src/components/WorkspaceHome/.../EnvironmentVariables/index.js
Styling tweaks: vertical-align and padding, checkbox alignment, column width tweaks, extracted StyledWrapper for EnvironmentDetails, removed "Enabled" header label.
Tests & locators
tests/collection/draft/*.spec.ts, tests/request/newlines/*.spec.ts, tests/variable-tooltip/*.spec.ts, tests/collection/create/*.spec.ts, tests/collection/moving-requests/*.spec.ts, tests/collection/moving-tabs/*.spec.ts, tests/utils/page/locators.ts
Tests updated to edit existing rows instead of adding new rows; selectors scoped to first row or #request-url textarea; getTableCell indexing shifted (index+1) to match table cell layout changes.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser UI
  participant EditableTable as EditableTable component
  participant Redux as Redux store (collections slice)
  participant FS as File system (file picker)

  Browser->>EditableTable: render(columns, rows, defaultRow, getRowError)
  Browser->>EditableTable: user edits cell / bulk edit / delete / reorder
  EditableTable->>Redux: onChange(updatedRows) or onReorder(updatedUids)
  Redux-->>EditableTable: updated state (rows with preserved uid)
  EditableTable-->>Browser: re-render rows
  Note right of EditableTable: multipart file flow
  EditableTable->>Browser: request file browse
  Browser->>FS: open file picker
  FS-->>Browser: selected file paths
  Browser->>EditableTable: provide file paths
  EditableTable->>Redux: setMultipartFormParams(updatedRowsWithFiles)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus areas:
    • EditableTable: trailing empty-row lifecycle, UID uniqueness, drag/reorder behavior
    • Redux bulk-set reducers: draft initialization, immutability, uid preservation
    • Multipart file path normalization and Windows path handling
    • Assertion operator parsing and unary-operator UI behavior
    • Test locator changes (getTableCell index shift) and updated selectors

Possibly related PRs

Suggested reviewers

  • lohit-bruno
  • bijin-bruno
  • helloanoop

Poem

Rows that grow when needed, editors inline,
UIDs preserved in every saved line,
Files find home, assertions learn their art,
Redux listens closely, keeping each part,
Tests now point true — the table's fresh design. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Improve tables design' is vague and overly broad, lacking specificity about the actual changes made. Consider a more descriptive title that reflects the core change, such as 'Refactor table components to use EditableTable' or 'Consolidate table editing with EditableTable component'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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

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: 6

🧹 Nitpick comments (12)
packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1)

22-44: Excessive !important declarations.

Multiple !important rules in thead styling (lines 23, 25, 26, 31, 35, 36) suggest possible specificity conflicts. Consider reviewing CSS cascade to reduce reliance on !important, which can make future maintenance harder.

packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js (1)

32-60: Consider memoizing the columns array.

The columns array contains render functions that depend on storedTheme, onSave, collection, and varType. It's recreated each render. Wrapping with useMemo would prevent unnecessary re-renders in EditableTable.

-  const columns = [
+  const columns = useMemo(() => [
     {
       key: 'name',
       ...
     },
     {
       key: 'value',
       ...
     }
-  ];
+  ], [storedTheme, onSave, collection, varType]);
packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js (1)

45-84: Consider memoizing columns.

Similar to VarsTable, the columns array with render functions is recreated each render. Memoizing with useMemo would optimize re-renders.

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

34-69: Consider memoizing columns.

Same optimization opportunity as other EditableTable usages in this PR.


98-103: Consider adding reorderable support for consistency.

RequestHeaders component includes reorderable={true} with drag support, but this collection-level Headers component doesn't. If header ordering matters at the collection level, consider adding the same capability.

       <EditableTable
         columns={columns}
         rows={headers}
         onChange={handleHeadersChange}
         defaultRow={defaultRow}
+        reorderable={true}
+        onReorder={handleHeaderDrag}
       />
packages/bruno-app/src/components/RequestPane/Assertions/index.js (1)

86-108: Unused parameters in render function.

The value and isLastEmptyRow parameters are destructured but never used in the operator column's render function.

-      render: ({ row, value, onChange, isLastEmptyRow }) => {
+      render: ({ row, onChange }) => {
packages/bruno-app/src/components/RequestPane/QueryParams/index.js (1)

167-179: No-op onChange for path params is intentional but worth documenting.

The onChange={() => {}} is correct here since path param value changes are handled via handlePathParamChange in the column render function, and the table doesn't allow adding/deleting rows. Consider adding a brief comment to clarify this isn't an oversight.

         {pathParams && pathParams.length > 0 ? (
           <EditableTable
             columns={pathColumns}
             rows={pathParams}
-            onChange={() => {}}
+            onChange={() => {}} // Path param changes handled via render callback
             defaultRow={{}}
             showCheckbox={false}
             showDelete={false}
             showAddRow={false}
           />
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (1)

76-84: Unused onChange parameter in handleValueChange.

The onChange parameter is passed but never used since the function dispatches directly via handleParamsChange.

-  const handleValueChange = useCallback((row, newValue, onChange) => {
+  const handleValueChange = useCallback((row, newValue) => {

Also update the call site at line 144:

-                onChange={(newValue) => handleValueChange(row, newValue, onChange)}
+                onChange={(newValue) => handleValueChange(row, newValue)}
packages/bruno-app/src/components/RequestPane/FormUrlEncodedParams/index.js (1)

66-70: Missing enabled field in defaultRow.

The EditableTable component defaults checkboxKey to 'enabled', but defaultRow doesn't include it. This could cause inconsistent state when new rows are created. The Redux reducer setFormUrlEncodedParams defaults enabled to true, so it works, but adding it here improves clarity.

 const defaultRow = {
   name: '',
   value: '',
-  description: ''
+  description: '',
+  enabled: true
 };
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (1)

79-83: Missing enabled field in defaultRow.

Same issue as FormUrlEncodedParams - defaultRow should include enabled: true for consistency with the EditableTable checkbox behavior.

 const defaultRow = {
   name: '',
   value: '',
+  enabled: true,
   ...(varType === 'response' ? { local: false } : {})
 };
packages/bruno-app/src/components/EditableTable/index.js (2)

108-116: Filter logic could exclude valid rows with falsy key values.

The condition keyVal && (typeof keyVal !== 'string' || keyVal.trim() !== '') filters out rows where keyVal is any falsy value (0, false, empty string). If a numeric key field could legitimately be 0, this would incorrectly filter it out.

Given the current usage context (string-based keys like "name"), this is unlikely to cause issues, but worth noting for future use cases.

-const result = updatedRows.filter((row, i) => {
-  if (showAddRow && i === updatedRows.length - 1) return false;
-  if (!showAddRow) return true;
-  const keyVal = keyColumn ? (keyColumn.getValue ? keyColumn.getValue(row) : row[keyColumn.key]) : '';
-  return keyVal && (typeof keyVal !== 'string' || keyVal.trim() !== '');
-});
+const result = updatedRows.filter((row, i) => {
+  if (showAddRow && i === updatedRows.length - 1) return false;
+  if (!showAddRow) return true;
+  const keyVal = keyColumn ? (keyColumn.getValue ? keyColumn.getValue(row) : row[keyColumn.key]) : '';
+  if (keyVal === null || keyVal === undefined) return false;
+  if (typeof keyVal === 'string') return keyVal.trim() !== '';
+  return true;
+});

26-34: createEmptyRow spreads defaultRow after setting checkboxKey.

If defaultRow contains a property matching checkboxKey (typically enabled), it will override the [checkboxKey]: true set on line 31. This is likely fine if defaultRow always sets enabled: true, but could cause unexpected behavior if a consumer passes enabled: false in defaultRow.

Consider spreading defaultRow first to allow explicit override:

 const createEmptyRow = useCallback(() => {
   const newUid = uuid();
   emptyRowUidRef.current = newUid;
   return {
+    ...defaultRow,
     uid: newUid,
-    [checkboxKey]: true,
-    ...defaultRow
+    [checkboxKey]: true
   };
 }, [defaultRow, checkboxKey]);
📜 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 4a8d787 and 379b6b9.

📒 Files selected for processing (14)
  • packages/bruno-app/src/components/CollectionSettings/Headers/index.js (4 hunks)
  • packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js (1 hunks)
  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/EditableTable/index.js (1 hunks)
  • packages/bruno-app/src/components/FolderSettings/Headers/index.js (4 hunks)
  • packages/bruno-app/src/components/FolderSettings/Vars/VarsTable/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/Assertions/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/FormUrlEncodedParams/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/QueryParams/index.js (4 hunks)
  • packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js (3 hunks)
  • packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (1 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (13 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/EditableTable/StyledWrapper.js
  • packages/bruno-app/src/components/EditableTable/index.js
  • packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js
  • packages/bruno-app/src/components/CollectionSettings/Headers/index.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js
  • packages/bruno-app/src/components/RequestPane/Assertions/index.js
  • packages/bruno-app/src/components/RequestPane/QueryParams/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
  • packages/bruno-app/src/components/FolderSettings/Headers/index.js
  • packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js
  • packages/bruno-app/src/components/RequestPane/FormUrlEncodedParams/index.js
  • packages/bruno-app/src/components/FolderSettings/Vars/VarsTable/index.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/StyledWrapper.js
  • packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js
🧠 Learnings (3)
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles

Applied to files:

  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors

Applied to files:

  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component

Applied to files:

  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js
🧬 Code graph analysis (6)
packages/bruno-app/src/components/EditableTable/index.js (1)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (3)
  • defaultRow (79-83)
  • columns (47-77)
  • getRowError (38-45)
packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js (5)
packages/bruno-app/src/components/RequestPane/QueryParams/index.js (2)
  • dispatch (18-18)
  • onSave (26-26)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (4)
  • dispatch (14-14)
  • columns (47-77)
  • onSave (17-17)
  • defaultRow (79-83)
packages/bruno-app/src/components/EditableTable/index.js (2)
  • isLastEmptyRow (76-79)
  • EditableTable (7-304)
packages/bruno-app/src/utils/codemirror/autocompleteConstants.js (2)
  • MimeTypes (1-56)
  • MimeTypes (1-56)
packages/bruno-app/src/components/BulkEditor/index.js (1)
  • BulkEditor (7-38)
packages/bruno-app/src/components/CollectionSettings/Headers/index.js (3)
packages/bruno-app/src/components/FolderSettings/Headers/index.js (8)
  • headerAutoCompleteList (14-14)
  • Headers (16-121)
  • dispatch (17-17)
  • headers (19-21)
  • handleHeadersChange (28-34)
  • handleSave (36-36)
  • columns (38-74)
  • defaultRow (76-80)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
  • saveCollectionSettings (2203-2242)
  • saveCollectionSettings (2203-2242)
packages/bruno-app/src/components/EditableTable/index.js (1)
  • EditableTable (7-304)
packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js (5)
packages/bruno-app/src/components/FolderSettings/Vars/VarsTable/index.js (8)
  • VarsTable (13-85)
  • dispatch (14-14)
  • useTheme (15-15)
  • onSave (17-17)
  • handleVarsChange (19-26)
  • getRowError (28-35)
  • columns (37-66)
  • defaultRow (68-72)
packages/bruno-app/src/components/CollectionSettings/Headers/index.js (4)
  • dispatch (17-17)
  • useTheme (18-18)
  • columns (34-69)
  • defaultRow (71-75)
packages/bruno-js/src/bru.js (1)
  • variableNameRegex (7-7)
packages/bruno-app/src/components/EditableTable/index.js (1)
  • EditableTable (7-304)
packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1)
  • StyledWrapper (3-137)
packages/bruno-app/src/components/RequestPane/FormUrlEncodedParams/index.js (2)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (5)
  • dispatch (14-14)
  • onSave (17-17)
  • handleRun (18-18)
  • columns (47-77)
  • defaultRow (79-83)
packages/bruno-app/src/components/EditableTable/index.js (2)
  • isLastEmptyRow (76-79)
  • EditableTable (7-304)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (2)
packages/bruno-js/src/bru.js (1)
  • variableNameRegex (7-7)
packages/bruno-app/src/components/EditableTable/index.js (2)
  • isLastEmptyRow (76-79)
  • EditableTable (7-304)
⏰ 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: Playwright E2E Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (22)
packages/bruno-app/src/components/CollectionSettings/Vars/VarsTable/index.js (2)

62-66: Clean conditional property spread.

Good use of the spread operator for conditionally adding local property based on varType.


68-78: LGTM - Clean EditableTable integration.

Proper wiring of columns, rows, onChange, defaultRow, and getRowError to the EditableTable component. Aligns well with the pattern used in related files.

packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js (3)

25-39: Well-structured memoized handlers.

Both handleHeadersChange and handleHeaderDrag are properly memoized with correct dependency arrays. Clean separation of concerns between bulk updates and reordering.


106-121: LGTM - Clean EditableTable with reorder support.

Proper integration of EditableTable with reorderable={true} and onReorder handler. The defensive headers || [] fallback is a good safeguard.


86-90: Verify description field usage in headers data model.

The defaultRow includes a description field, but confirm whether the columns array defines a column for it. If not displayed in the UI, verify this field is intentionally preserved for API data consistency or backend persistence, and check if it's included in header payloads sent to the server.

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

19-21: LGTM - Clean conditional header retrieval.

Proper use of optional chaining with collection.draft?.root to determine the correct headers source.


28-30: Well-memoized change handler.

Proper use of useCallback with correct dependencies for handleHeadersChange.

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

110-134: LGTM on value column rendering logic.

The conditional rendering for unary operators (disabled input) vs binary operators (SingleLineEditor) is clean and handles the operator parsing correctly.


137-141: operator key in defaultRow may be unused.

The defaultRow includes an operator key, but columns only defines name and value as column keys. If EditableTable only processes columns by their key, this property won't be set on new rows. Verify whether operator is needed here or if it's vestigial from a prior implementation.

packages/bruno-app/src/components/FolderSettings/Vars/VarsTable/index.js (2)

28-35: Validation only checks name field, as expected.

The getRowError properly validates only the name field and allows empty names (for new rows). Clean implementation.


37-66: LGTM on columns configuration.

The conditional column header with InfoTip for non-request var types is a nice UX touch. The MultiLineEditor integration looks correct.

packages/bruno-app/src/components/FolderSettings/Headers/index.js (3)

76-80: description field in defaultRow has no corresponding column.

The defaultRow includes a description key, but there's no column defined for it. Verify if EditableTable preserves extra properties from defaultRow when creating new rows, or if this is dead configuration.


45-55: Good: Header name sanitization.

Stripping \r\n from header names (line 50) prevents invalid multi-line header names. Solid defensive coding.


103-108: LGTM on EditableTable integration.

The component correctly passes headers as rows and uses the centralized handleHeadersChange for updates.

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

101-112: Path param value changes bypass the generic onChange prop.

The handlePathParamChange is called directly in the render function (line 106). This works but creates a subtle inconsistency with how query params are handled. The current approach is acceptable since path params are read-only except for values.


29-36: LGTM on query params handler.

Correctly adds type: 'query' to all params before dispatching. This ensures the type is preserved through bulk updates.

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

86-99: LGTM on getFileName helper.

Good handling of null/empty arrays, platform-specific path separators, and multiple file count display.


114-163: Well-structured conditional rendering for file vs text values.

The value cell properly handles file display with clear button, text editing with MultiLineEditor, and conditionally shows the upload button. Clean separation of concerns.

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

1-84: LGTM overall - clean migration to EditableTable.

The migration from legacy table components to the unified EditableTable is well-structured. The useCallback hooks properly stabilize dependencies, and the Redux integration via setFormUrlEncodedParams and moveFormUrlEncodedParam is correct.

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

38-45: Validation callback is well implemented.

The getRowError callback correctly validates variable names using variableNameRegex, returns null for empty names (allowing placeholder rows), and provides a clear error message. Good use of useCallback with empty deps since no external state is referenced.

packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)

1297-1315: New bulk set actions are well-structured.

The setFormUrlEncodedParams, setRequestAssertions, setRequestVars, setFolderVars, and setCollectionVars reducers follow a consistent pattern: validate inputs, create draft if needed, and map with UID preservation. Good defensive defaults throughout.

Also applies to: 1749-1767, 1883-1907, 2214-2236, 2448-2471

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

1-304: Well-designed reusable component.

The EditableTable component provides good encapsulation of common table behaviors: row management, checkbox toggling, drag-and-drop reordering, and cell rendering. The use of useMemo and useCallback optimizes re-renders appropriately. The empty row management logic handles edge cases like stale UIDs.

@naman-bruno naman-bruno changed the title init Improve tables design Dec 6, 2025
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

♻️ Duplicate comments (1)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)

1401-1420: setMultipartFormParams missing description field in mapping.

This was flagged in a previous review and appears unaddressed. Other form param actions (e.g., setFormUrlEncodedParams at line 1311) include description. This could cause data loss:

-item.draft.request.body.multipartForm = map(params, ({ uid, name = '', value = '', contentType = '', type = 'text', enabled = true }) => ({
+item.draft.request.body.multipartForm = map(params, ({ uid, name = '', value = '', contentType = '', type = 'text', description = '', enabled = true }) => ({
   uid: uid || uuid(),
   name,
   value,
   contentType,
   type,
+  description,
   enabled
 }));
🧹 Nitpick comments (3)
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (2)

69-77: Potential stale closure on params in handleClearFile.

Same pattern as the fixed handleBrowseFiles - if the user makes changes before this callback runs, params could be outdated. Consider reading fresh state from item:

 const handleClearFile = useCallback((row) => {
+  const currentParams = item.draft
+    ? get(item, 'draft.request.body.multipartForm')
+    : get(item, 'request.body.multipartForm');
-  const updatedParams = params.map((p) => {
+  const updatedParams = (currentParams || []).map((p) => {
     if (p.uid === row.uid) {
       return { ...p, type: 'text', value: '' };
     }
     return p;
   });
   handleParamsChange(updatedParams);
-}, [params, handleParamsChange]);
+}, [item, handleParamsChange]);

79-87: Same stale closure concern and unused onChange parameter.

The params closure risk applies here too. Additionally, onChange is passed but never used - remove it to avoid confusion:

-const handleValueChange = useCallback((row, newValue, onChange) => {
-  const updatedParams = params.map((p) => {
+const handleValueChange = useCallback((row, newValue) => {
+  const currentParams = item.draft
+    ? get(item, 'draft.request.body.multipartForm')
+    : get(item, 'request.body.multipartForm');
+  const updatedParams = (currentParams || []).map((p) => {
     if (p.uid === row.uid) {
       return { ...p, type: 'text', value: newValue };
     }
     return p;
   });
   handleParamsChange(updatedParams);
-}, [params, handleParamsChange]);
+}, [item, handleParamsChange]);

Update the caller at line 147 accordingly:

-onChange={(newValue) => handleValueChange(row, newValue, onChange)}
+onChange={(newValue) => handleValueChange(row, newValue)}
packages/bruno-app/src/components/EditableTable/index.js (1)

81-116: Consider simplifying the filter logic.

The filtering at lines 108-113 is complex. It filters out empty rows to pass to onChange, but the conditions are hard to follow. Consider extracting to a named function for clarity:

const filterEmptyTrailingRows = (rows) => {
  const keyColumn = columns.find((col) => col.isKeyField);
  return rows.filter((row, i) => {
    if (showAddRow && i === rows.length - 1) return false;
    if (!keyColumn) return true;
    const keyVal = keyColumn.getValue ? keyColumn.getValue(row) : row[keyColumn.key];
    return keyVal && (typeof keyVal !== 'string' || keyVal.trim() !== '');
  });
};
📜 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 379b6b9 and 5febefe.

📒 Files selected for processing (3)
  • packages/bruno-app/src/components/EditableTable/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (1 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (13 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/MultipartFormParams/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
  • packages/bruno-app/src/components/EditableTable/index.js
🧬 Code graph analysis (2)
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (2)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
  • browseFiles (2195-2201)
  • browseFiles (2195-2201)
packages/bruno-app/src/utils/common/platform.js (2)
  • isWindowsOS (22-27)
  • isWindowsOS (22-27)
packages/bruno-app/src/components/EditableTable/index.js (1)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (2)
  • columns (47-77)
  • getRowError (38-45)
⏰ 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: Playwright E2E Tests
  • GitHub Check: CLI Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
🔇 Additional comments (14)
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (2)

89-102: LGTM!

getFileName handles both single and multiple files correctly, with proper Windows/Unix path separator detection. Edge cases (null, empty array, empty strings) are well covered.


104-185: Clean column configuration and EditableTable integration.

The columns setup with custom render functions for file handling is well-structured. The conditional rendering for file display vs. text editor is clear.

Also applies to: 187-206

packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (6)

1300-1318: LGTM!

setFormUrlEncodedParams correctly maps all fields including description and preserves UIDs when provided.


1752-1770: LGTM!

setRequestAssertions correctly includes the operator field and follows the established pattern.


1886-1910: LGTM!

setRequestVars correctly handles the conditional local field for response-type variables.


2217-2239: LGTM!

setFolderVars properly initializes folder.draft from folder.root and handles both request/response types.


2451-2474: LGTM!

setCollectionVars follows the same consistent pattern as other bulk-set actions.


1231-1240: LGTM!

setFolderHeaders now correctly initializes folder.draft before modifying headers, addressing the previous review concern about bypassing the draft workflow.

packages/bruno-app/src/components/EditableTable/index.js (6)

7-20: Well-designed prop interface.

Good defaults and clear prop names. The flexibility to customize checkbox behavior, deletion, and reordering makes this component reusable across the codebase.


36-66: Solid trailing empty row management.

The logic correctly handles edge cases: empty initial state, preventing duplicate empty rows, and maintaining UID stability. The check at line 57 prevents UID collisions if a row somehow matches the empty row UID.


144-156: Drag-drop reorder excludes trailing empty row correctly.

Good handling at line 148 to slice off the empty row before reordering. The updateReorderedItem callback shape aligns with the Redux actions expecting UID arrays.


193-208: Error tooltip implementation looks correct.

Using data-tooltip-id on the anchor and id on the Tooltip follows react-tooltip v5+ API. The error is only shown for non-empty rows.


251-280: Drag handle visibility tied to hover state.

The conditional rendering based on hoveredRow === rowIndex means the grip icon only appears on hover. This is good UX but note: if users need visual affordance that rows are reorderable, consider showing a subtle indicator always.


236-298: Solid row rendering with conditional drag behavior.

The canDrag check properly prevents dragging empty rows and respects the reorderable prop. Event handlers are correctly conditionally applied.

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: 1

🧹 Nitpick comments (9)
packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)

60-64: Checkbox alignment tweak is a solid simplification

Centering via vertical-align: middle and resetting margin is cleaner and more predictable than positional nudging; this should align with the new table styling well.

If you later see browser-specific misalignment, consider also normalizing line-height on the checkbox container rather than the input itself, but it’s not necessary for this PR.

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

99-99: Consider refactoring CSS variable to theme prop for consistency.

Line 99 uses a CSS variable for color, while the rest of the file uses theme props. Based on learnings, styled components should use theme props for colors.

Apply this diff for consistency:

-    color: var(--color-tab-inactive);
+    color: ${(props) => props.theme.tabs.inactive.color};

Note: Verify the correct theme path exists for inactive tab color.

packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)

1-133: Theme usage and layout in StyledWrapper look solid.

The wrapper correctly uses props.theme for all colors/typography and mirrors the workspace rename/error patterns, so it should integrate cleanly with the existing UI. There is some duplication with WorkspaceHome/StyledWrapper (e.g., .inline-actions, .inline-action-btn, error tooltip), which could be factored into a shared helper later if this pattern spreads further.

packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)

21-27: Environment variables table styling aligns with EditableTable patterns.

Updating td padding/vertical-align, header color/font-size, and input/checkbox alignment makes this table visually consistent with the new EditableTable styles and keeps colors theme-driven. If more tables converge on this look, consider sharing more of this styling with the generic EditableTable StyledWrapper to avoid drift.

Also applies to: 41-45, 103-109, 119-126

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

12-52: Consolidate assertion operator helpers to avoid duplication.

unaryOperators, the full operators list inside parseAssertionOperator, and the operator options in AssertionOperator all encode essentially the same sets. Keeping these in one shared module (e.g., assertionOperators.js) would reduce drift when adding/removing operators and make tests easier to maintain.


142-147: EditableTable wiring for assertions looks appropriate; consider cleaning up the extra operator field.

Passing columns, rows={assertions || []}, defaultRow (with value: 'eq '), and reorderable/onReorder into EditableTable matches the intended API and should work well with drag-reorder and empty-row handling. The persisted operator is now encoded inside value ("operator value"), so the separate operator: 'eq' field in defaultRow appears unused; you might be able to drop or repurpose it to avoid confusion about the source of truth.

Also applies to: 150-157

packages/bruno-app/src/components/EditableTable/index.js (3)

36-79: Clarify or enforce the requirement for an isKeyField column.

rowsWithEmpty, isEmptyRow, isLastEmptyRow, and the final result filter all assume there is a column with isKeyField: true. If no such column exists, keyColumn is undefined, and the filter will effectively drop all rows when showAddRow is true. At minimum, it’s worth documenting that EditableTable requires exactly one key column; alternatively, you could add a runtime guard (e.g., console.warn or throwing in dev) when !columns.some(c => c.isKeyField).


81-116: Last empty row drops non-key edits; consider whether that UX is acceptable.

handleValueChange only promotes the last row into a “real” row when the key field changes from empty to non-empty. Edits to other columns on the last row are applied to updatedRows but then filtered out (the last row is removed from result and recreated from defaultRow on the next render). That means typing into a non-key field of the add-row is effectively discarded.

If you want to allow users to fill non-key fields first, you could:

  • Treat a row as “non-empty” when any of a configurable set of columns has data, or
  • Skip the i === updatedRows.length - 1 filter when !isEmptyRow(row) so partial edits on the last row are preserved.

As-is, it’s a reasonable tradeoff if the intended flow is “enter key column first,” but it’s worth confirming this matches UX expectations.


24-25: Remove unused dragStart state to reduce noise.

dragStart is set in handleDragStart and cleared in handleDragEnd, but never read. You can safely drop this state (and its setter) to avoid unnecessary state updates during drag-and-drop.

Also applies to: 132-136, 158-161

📜 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 5febefe and 50a84ce.

📒 Files selected for processing (11)
  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/EditableTable/index.js (1 hunks)
  • packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (2 hunks)
  • packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (2 hunks)
  • packages/bruno-app/src/components/RequestPane/Assertions/index.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (4 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/index.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js
🧰 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/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/index.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js
  • packages/bruno-app/src/components/EditableTable/index.js
  • packages/bruno-app/src/components/RequestPane/Assertions/index.js
  • packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
🧠 Learnings (3)
📚 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 **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles

Applied to files:

  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js
  • packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
📚 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 **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors

Applied to files:

  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js
  • packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
📚 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 **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component

Applied to files:

  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js
  • packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
  • packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
🧬 Code graph analysis (3)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
packages/bruno-app/src/components/WorkspaceHome/StyledWrapper.js (1)
  • StyledWrapper (3-133)
packages/bruno-app/src/components/EditableTable/index.js (3)
packages/bruno-app/src/components/FolderSettings/Headers/index.js (2)
  • defaultRow (76-80)
  • columns (38-74)
packages/bruno-app/src/components/RequestPane/Vars/VarsTable/index.js (3)
  • defaultRow (79-83)
  • columns (47-77)
  • getRowError (38-45)
packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1)
  • StyledWrapper (3-144)
packages/bruno-app/src/components/RequestPane/Assertions/index.js (3)
packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js (8)
  • unaryOperators (78-91)
  • unaryOperators (117-130)
  • parseAssertionOperator (39-114)
  • parseAssertionOperator (146-146)
  • operators (47-76)
  • operator (93-93)
  • value (94-94)
  • isUnaryOperator (116-133)
packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js (1)
  • AssertionOperator (36-92)
packages/bruno-app/src/components/EditableTable/index.js (2)
  • isLastEmptyRow (76-79)
  • EditableTable (7-304)
🔇 Additional comments (7)
packages/bruno-app/src/components/WorkspaceHome/index.js (2)

222-223: LGTM! Header sizing refinement.

The reduction in text size (text-xltext-sm) and icon size (24 → 18) creates a more compact workspace header, consistent with the broader UI modernization described in the PR summary.


305-305: LGTM! Tab padding refinement.

The vertical padding adjustment (py-2pt-2 pb-1.5) subtly reduces tab height while maintaining consistent spacing with the updated header styling.

packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)

10-15: Table cell vertical alignment change looks good

Using vertical-align: middle on td will help keep text and controls visually centered in rows and is consistent with the broader table cleanup. No issues.

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

111-111: LGTM! Clean styling improvement.

Adding medium font-weight to active tabs improves visual hierarchy and user feedback.

packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (2)

10-27: Vertical centering for table cells is a good improvement

Adding vertical-align: middle to the table cell rule will help keep header and body content visually aligned when rows get taller, without affecting logic or theming. Looks good as part of the broader table standardization.


60-64: Checkbox alignment tweak is appropriate and non-breaking

Centering the checkbox vertically and resetting its margin to 0 should remove the previous “off by a pixel” feel and align it better with adjacent text/controls inside the table cells. This is a safe, localized styling improvement.

packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js (1)

10-10: StyledWrapper import looks correct and consistent.

Import path and usage of the extracted StyledWrapper match the local file structure; no behavior concerns here.

Comment on lines +87 to 113
key: 'operator',
name: 'Operator',
width: '120px',
render: ({ row, rowIndex, isLastEmptyRow }) => {
const { operator } = parseAssertionOperator(row.value);
const assertionValue = parseAssertionOperator(row.value).value;

const handleOperatorChange = (newOperator) => {
const updatedAssertions = assertions.map((assertion, idx) => {
if (idx === rowIndex) {
return {
...assertion,
value: isUnaryOperator(newOperator) ? newOperator : `${newOperator} ${assertionValue}`
};
}
return assertion;
});
handleAssertionsChange(updatedAssertions);
};

return (
<AssertionOperator
operator={operator}
onChange={handleOperatorChange}
/>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use EditableTable’s onChange for operator updates and avoid indexing into assertions.

Right now, the Operator column:

  • Parses row.value twice
  • Rebuilds updatedAssertions from the external assertions array using rowIndex
  • Dispatches setRequestAssertions directly

This has a couple of downsides:

  • Changing the operator on the trailing “empty” row is effectively a no-op (there’s no corresponding entry in assertions), so the select will snap back to the default and the choice is lost.
  • It bypasses EditableTable’s onChange/handleValueChange machinery, which is what manages the empty row lifecycle and any future validation.

You can simplify and make this more robust by using the value and onChange provided by the column render, and parsing only once, e.g.:

-      render: ({ row, rowIndex, isLastEmptyRow }) => {
-        const { operator } = parseAssertionOperator(row.value);
-        const assertionValue = parseAssertionOperator(row.value).value;
-
-        const handleOperatorChange = (newOperator) => {
-          const updatedAssertions = assertions.map((assertion, idx) => {
-            if (idx === rowIndex) {
-              return {
-                ...assertion,
-                value: isUnaryOperator(newOperator) ? newOperator : `${newOperator} ${assertionValue}`
-              };
-            }
-            return assertion;
-          });
-          handleAssertionsChange(updatedAssertions);
-        };
+      render: ({ value, onChange }) => {
+        const { operator, value: assertionValue } = parseAssertionOperator(value);
+
+        const handleOperatorChange = (newOperator) => {
+          const nextValue = isUnaryOperator(newOperator)
+            ? newOperator
+            : `${newOperator} ${assertionValue}`;
+
+          onChange(nextValue);
+        };
-        return (
-          <AssertionOperator
-            operator={operator}
-            onChange={handleOperatorChange}
-          />
-        );
+        return (
+          <AssertionOperator
+            operator={operator}
+            onChange={handleOperatorChange}
+          />
+        );

This keeps all row mutation logic inside EditableTable, avoids relying on rowIndex/assertions, and fixes the “last empty row operator change is ignored” behavior.

Also applies to: 116-137

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: 2

♻️ Duplicate comments (2)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)

1404-1423: setMultipartFormParams missing description field in mapping.

Other form param actions (e.g., setFormUrlEncodedParams at line 1314) include description in the mapping. This omission causes data loss when updating multipart form params.

-item.draft.request.body.multipartForm = map(params, ({ uid, name = '', value = '', contentType = '', type = 'text', enabled = true }) => ({
+item.draft.request.body.multipartForm = map(params, ({ uid, name = '', value = '', contentType = '', type = 'text', description = '', enabled = true }) => ({
   uid: uid || uuid(),
   name,
   value,
   contentType,
   type,
+  description,
   enabled
 }));
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (1)

64-66: Silent failure on file browse error.

When browseFiles() fails, the error is only logged to console. Users won't know the operation failed.

Consider adding a toast notification:

 .catch((error) => {
   console.error(error);
+  // Add user-facing feedback, e.g.:
+  // toast.error('Failed to select files');
 });
🧹 Nitpick comments (5)
packages/bruno-app/src/components/RequestPane/Assertions/index.js (2)

91-93: Redundant parsing of row.value.

parseAssertionOperator(row.value) is called twice in succession (lines 92 and 93). Destructure both values in one call:

-        const { operator } = parseAssertionOperator(row.value);
-        const assertionValue = parseAssertionOperator(row.value).value;
+        const { operator, value: assertionValue } = parseAssertionOperator(row.value);

151-155: operator field in defaultRow appears unused.

The assertion data model stores the operator as a prefix in the value field (e.g., "eq someValue"). The standalone operator property in defaultRow isn't persisted and may cause confusion.

 const defaultRow = {
   name: '',
-  value: 'eq ',
-  operator: 'eq'
+  value: 'eq '
 };
packages/bruno-app/src/components/EditableTable/index.js (2)

109-118: hasAnyValue may incorrectly filter rows with intentional default values.

If defaultRow contains meaningful defaults (e.g., type: 'text'), comparing val !== defaultVal will treat rows with those defaults as "having a value" and persist them prematurely.

Consider checking only the key field or columns explicitly marked as significant:

 const hasAnyValue = (row) => {
-  for (const col of columns) {
-    const val = col.getValue ? col.getValue(row) : row[col.key];
-    const defaultVal = defaultRow[col.key];
-    if (val && val !== defaultVal && (typeof val !== 'string' || val.trim() !== '')) {
-      return true;
-    }
-  }
-  return false;
+  const keyColumn = columns.find((col) => col.isKeyField);
+  if (!keyColumn) return false;
+  const val = keyColumn.getValue ? keyColumn.getValue(row) : row[keyColumn.key];
+  return val && (typeof val !== 'string' || val.trim() !== '');
 };

265-282: Drag handle lacks keyboard accessibility.

Drag-and-drop reordering is mouse-only. For accessibility, consider adding keyboard-based reorder (e.g., up/down arrow keys on focused rows). This is a nice-to-have for future improvement.

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

194-199: defaultRow missing description field.

For consistency with the data model and other form param components, include description:

 const defaultRow = {
   name: '',
   value: '',
   contentType: '',
-  type: 'text'
+  type: 'text',
+  description: ''
 };
📜 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 50a84ce and 554a21f.

📒 Files selected for processing (6)
  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/EditableTable/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/Assertions/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (4 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (13 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/bruno-app/src/components/EditableTable/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
🧰 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/Assertions/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
  • packages/bruno-app/src/components/EditableTable/index.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js
🧬 Code graph analysis (2)
packages/bruno-app/src/components/RequestPane/Assertions/index.js (2)
packages/bruno-app/src/components/RequestPane/Assertions/AssertionRow/index.js (9)
  • unaryOperators (78-91)
  • unaryOperators (117-130)
  • parseAssertionOperator (39-114)
  • parseAssertionOperator (146-146)
  • operators (47-76)
  • operator (93-93)
  • value (94-94)
  • isUnaryOperator (116-133)
  • useTheme (144-144)
packages/bruno-app/src/components/RequestPane/Assertions/AssertionOperator/index.js (1)
  • AssertionOperator (36-92)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (4)
packages/bruno-app/src/utils/collections/index.js (13)
  • item (1037-1037)
  • item (1261-1261)
  • collection (582-582)
  • collection (1160-1160)
  • collection (1162-1162)
  • collection (1590-1590)
  • collection (1600-1600)
  • findCollectionByUid (80-82)
  • findCollectionByUid (80-82)
  • findItemInCollection (117-121)
  • findItemInCollection (117-121)
  • isItemARequest (827-829)
  • isItemARequest (827-829)
packages/bruno-app/src/utils/common/index.js (2)
  • uuid (15-21)
  • uuid (15-21)
packages/bruno-cli/src/utils/collection.js (2)
  • findItemInCollection (336-340)
  • type (544-544)
packages/bruno-app/src/utils/tabs/index.js (2)
  • isItemARequest (3-5)
  • isItemARequest (3-5)
⏰ 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: Playwright E2E Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (7)
packages/bruno-app/src/components/RequestPane/Assertions/index.js (1)

128-147: LGTM!

Value column correctly handles unary operators with a disabled input and binary operators with the SingleLineEditor, reconstructing the prefixed value on change.

packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (2)

1755-1773: operator field in setRequestAssertions may be inconsistent with data model.

The assertion parsing logic in Assertions/index.js extracts the operator from the value field prefix (e.g., "eq someValue"). Storing operator as a separate field here could cause schema drift if not used consistently.

Verify if assertions should store operator separately or if this field should be removed from the mapping.


1231-1243: LGTM!

setFolderHeaders now correctly initializes folder.draft and folder.draft.request before assignment, addressing the previous draft/save workflow issue.

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

156-168: LGTM!

Drag-drop logic correctly excludes the trailing empty row from reordering and emits the updated UID order.


206-217: LGTM!

react-tooltip v5 API correctly used with data-tooltip-id on the anchor and id on the Tooltip.

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

96-109: LGTM!

getFileName handles edge cases well—null values, empty arrays, single vs. multiple files, and OS-specific path separators.


124-173: LGTM!

Value column handles file vs. text mode elegantly, with appropriate UI affordances for each state. The upload button visibility logic prevents accidental overwrites.

Comment on lines +1901 to +1907
const mappedVars = map(vars, ({ uid, name = '', value = '', enabled = true, local = false }) => ({
uid: uid || uuid(),
name,
value,
enabled,
...(type === 'response' ? { local } : {})
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

local field excluded for request vars, inconsistent with addVar.

addVar (lines 1809-1815) includes local: varData.local === true for request-type vars, but setRequestVars conditionally omits it. This could cause data loss.

 const mappedVars = map(vars, ({ uid, name = '', value = '', enabled = true, local = false }) => ({
   uid: uid || uuid(),
   name,
   value,
   enabled,
-  ...(type === 'response' ? { local } : {})
+  local
 }));

Alternatively, verify if local should indeed be excluded for request vars.

🤖 Prompt for AI Agents
In packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
around lines 1901-1907, the mappedVars creation omits the local field for
request-type vars causing inconsistency with addVar; change the mapping to
always include local (e.g. local: local === true) instead of conditionally only
for type === 'response' so request vars preserve the local flag just like addVar
does.

Comment on lines +2230 to +2236
const mappedVars = map(vars, ({ uid, name = '', value = '', enabled = true, local = false }) => ({
uid: uid || uuid(),
name,
value,
enabled,
...(type === 'response' ? { local } : {})
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same local field inconsistency in setFolderVars.

Same pattern as setRequestVars—verify if local should be included for all var types consistently.

🤖 Prompt for AI Agents
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js around
lines 2230-2236: the mappedVars construction conditionally adds the local field
only when type === 'response', causing an inconsistency with other var setters;
make it consistent by always including the local property (defaulting to false)
for every var entry (remove the conditional spread) or alternatively mirror the
exact behavior used in setRequestVars—ensure the chosen behavior is applied to
setFolderVars so local is handled consistently across setters.

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 (2)
tests/request/newlines/newlines-persistence.spec.ts (1)

32-51: Add assertions to verify rows exist before interaction.

The test now uses .first() to select pre-existing rows for params, headers, and vars without verifying they exist. This could lead to test failures if the tables don't have default rows.

Consider adding visibility assertions before interaction:

  await page.getByRole('tab', { name: 'Params' }).click();
  const paramRow = page.locator('table tbody tr').first();
+ await expect(paramRow).toBeVisible();
  await getTableCell(paramRow, 0).locator('input[type="text"]').fill('queryParamKey');

  await page.getByRole('tab', { name: 'Headers' }).click();
  const headerRow = page.locator('table tbody tr').first();
+ await expect(headerRow).toBeVisible();
  await getTableCell(headerRow, 0).locator('.CodeMirror').click();
tests/variable-tooltip/variable-tooltip.spec.ts (1)

70-77: Improved selector scoping.

The new pattern using headerTable -> headerRow -> editor is cleaner and more specific than selecting table rows globally. This reduces the risk of selecting unintended elements if multiple tables are present.

However, the same concern applies here: the test assumes the first row exists in the header table. Consider adding:

await expect(headerRow).toBeVisible();

before interacting with its editors.

📜 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 554a21f and 2f0c1bb.

📒 Files selected for processing (5)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (13 hunks)
  • tests/collection/draft/draft-indicator.spec.ts (2 hunks)
  • tests/collection/draft/draft-values-in-requests.spec.ts (1 hunks)
  • tests/request/newlines/newlines-persistence.spec.ts (1 hunks)
  • tests/variable-tooltip/variable-tooltip.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
🧰 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/collection/draft/draft-values-in-requests.spec.ts
  • tests/collection/draft/draft-indicator.spec.ts
  • tests/request/newlines/newlines-persistence.spec.ts
  • tests/variable-tooltip/variable-tooltip.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/collection/draft/draft-values-in-requests.spec.ts
  • tests/collection/draft/draft-indicator.spec.ts
  • tests/request/newlines/newlines-persistence.spec.ts
  • tests/variable-tooltip/variable-tooltip.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 **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created

Applied to files:

  • tests/collection/draft/draft-indicator.spec.ts
🧬 Code graph analysis (1)
tests/request/newlines/newlines-persistence.spec.ts (1)
tests/utils/page/locators.ts (1)
  • getTableCell (126-126)
⏰ 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: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: Unit Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
🔇 Additional comments (3)
tests/collection/draft/draft-indicator.spec.ts (2)

200-202: LGTM!

The simplified selector varRow.locator('.CodeMirror') is appropriate here since the var value cell should contain only one CodeMirror editor. The variable naming is also more descriptive.


314-316: LGTM!

Consistent selector pattern with the collection vars test above. The naming clearly distinguishes this as the folder-level var value editor.

tests/collection/draft/draft-values-in-requests.spec.ts (1)

50-59: Verify that EditableTable pre-populates an editable row.

The test assumes a first row exists in the folder header table without explicitly creating it (lines 50-51). If the EditableTable component doesn't pre-populate an empty row on initialization, this test will fail when attempting to interact with folderHeaderRow. Confirm that the FolderSettings/Headers component's EditableTable initializes with at least one empty row, or add explicit row creation before these interactions.

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 (3)
tests/collection/create/create-collection.spec.ts (1)

32-32: Good locator scoping; factor out a reusable locator variable

Scoping the textarea under #request-url is a solid improvement and should be more robust than the old generic textarea selector. To cut duplication and better follow the “use locator variables” guideline, consider extracting this into a local locator:

   // Set the URL
-  await page.locator('#request-url .CodeMirror').click();
-  await page.locator('#request-url').locator('textarea').fill('http://localhost:8081');
+  const requestUrlTextarea = page.locator('#request-url textarea');
+  await page.locator('#request-url .CodeMirror').click();
+  await requestUrlTextarea.fill('http://localhost:8081');
@@
   // Send a request
-  await page.locator('#request-url .CodeMirror').click();
-  await page.locator('#request-url').locator('textarea').fill('/ping');
+  await page.locator('#request-url .CodeMirror').click();
+  await requestUrlTextarea.fill('/ping');

If you want to double‑check best practices for locator reuse in Playwright 1.51.x, please confirm against the current Playwright docs.

Also applies to: 37-37

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

17-21: Scoped URL textarea locator is solid; consider DRYing via a locator variable

The new page.locator('#request-url').locator('textarea') is a clear improvement over a generic textarea and should be more stable. To better follow the guideline of using locator variables and to avoid repeating the #request-url selector, consider extracting a locator per test and reusing it for both click and fill:

@@
-    // Set the URL
-    await page.locator('#request-url .CodeMirror').click();
-    await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com');
+    // Set the URL
+    const requestUrl = page.locator('#request-url');
+    await requestUrl.locator('.CodeMirror').click();
+    await requestUrl.locator('textarea').fill('https://echo.usebruno.com');
@@
-    // Set the URL
-    await page.locator('#request-url .CodeMirror').click();
-    await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com');
+    // Set the URL
+    const sourceRequestUrl = page.locator('#request-url');
+    await sourceRequestUrl.locator('.CodeMirror').click();
+    await sourceRequestUrl.locator('textarea').fill('https://echo.usebruno.com');
@@
-    // Set the URL
-    await page.locator('#request-url .CodeMirror').click();
-    await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com');
+    // Set the URL
+    const targetRequestUrl = page.locator('#request-url');
+    await targetRequestUrl.locator('.CodeMirror').click();
+    await targetRequestUrl.locator('textarea').fill('https://echo.usebruno.com');

Also applies to: 74-78, 88-92

tests/collection/moving-tabs/move-tabs.spec.ts (1)

53-55: Selector refinement looks good; consider using a locator variable for reuse

Scoping the textarea fill to #request-url is an improvement over a broad textarea selector. To align with the Playwright guideline of using locator variables and to avoid duplicating the selector across both tests, you could capture the locator once in each test and reuse it:

@@
-    await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com');
+    const requestUrlTextarea = page.locator('#request-url textarea');
+    await requestUrlTextarea.fill('https://echo.usebruno.com');
@@
-    await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com');
+    const requestUrlTextarea = page.locator('#request-url textarea');
+    await requestUrlTextarea.fill('https://echo.usebruno.com');

This keeps the selector DRY and makes future selector changes easier in both tests.

Also applies to: 143-146

📜 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 2f0c1bb and 21c67c9.

📒 Files selected for processing (3)
  • tests/collection/create/create-collection.spec.ts (1 hunks)
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts (3 hunks)
  • tests/collection/moving-tabs/move-tabs.spec.ts (2 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/collection/moving-tabs/move-tabs.spec.ts
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts
  • tests/collection/create/create-collection.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/collection/moving-tabs/move-tabs.spec.ts
  • tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts
  • tests/collection/create/create-collection.spec.ts
⏰ 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). (3)
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: Playwright E2E Tests

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 (1)
tests/utils/page/locators.ts (1)

126-126: Add type annotations for parameters.

The row and index parameters lack type annotations. Consider adding them for better type safety:

-export const getTableCell = (row, index) => row.locator('td').nth(index + 1);
+export const getTableCell = (row: any, index: number) => row.locator('td').nth(index + 1);

(Replace any with the appropriate Playwright Locator type if available in your imports.)

📜 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 21c67c9 and ce3a32b.

📒 Files selected for processing (4)
  • tests/collection/moving-tabs/move-tabs.spec.ts (2 hunks)
  • tests/request/newlines/newlines-persistence.spec.ts (2 hunks)
  • tests/utils/page/locators.ts (1 hunks)
  • tests/variable-tooltip/variable-tooltip.spec.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/collection/moving-tabs/move-tabs.spec.ts
  • tests/request/newlines/newlines-persistence.spec.ts
  • tests/variable-tooltip/variable-tooltip.spec.ts
🧰 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/utils/page/locators.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/utils/page/locators.ts
⏰ 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: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - Windows
🔇 Additional comments (1)
tests/utils/page/locators.ts (1)

126-126: Document the offset and verify all call sites.

The +1 offset changes this helper's behavior—getTableCell(row, 0) now returns the second cell instead of the first. While this may align with an EditableTable component refactor, the offset needs clear documentation (JSDoc or inline comment explaining why the leading column is skipped).

Additionally, add type annotations to the row and index parameters in this TypeScript file for consistency with type safety guidelines.

Verify all call sites of getTableCell across the test suite handle the new offset correctly.

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