Conversation
WalkthroughMoves environment and global-environment configuration from modal dialogs into tab-based UI, replacing modal visibility actions with Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as App UI
participant Tabs as Tabs system
participant Redux as Redux store
participant Env as Environment editor
User->>UI: Open Environments / Global Environments
UI->>Tabs: dispatch addTab({ type: environment-settings|global-environment-settings, ... })
Tabs->>Redux: create/activate tab
Redux->>UI: focusedTab state update
UI->>Env: render EnvironmentSettings (tab content)
User->>Env: Edit variables/name
Env->>Redux: dispatch setEnvironmentsDraft / setGlobalEnvironmentDraft
Redux->>Redux: store draft
User->>Env: Save (button or hotkey)
Env->>window: emit 'environment-save'
Env->>Redux: dispatch saveEnvironment / saveGlobalEnvironment
Redux->>Redux: persist and dispatch clearEnvironmentsDraft / clearGlobalEnvironmentDraft
User->>UI: Close tab
UI->>Redux: detect draft -> show ConfirmCloseEnvironment
alt Save then Close
UI->>Redux: dispatch save... then clear draft
else Discard
UI->>Redux: dispatch clear draft
end
UI->>Tabs: dispatch closeTab
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–75 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
168-170: IncorrectresetFormparameter structure.Formik's
resetFormexpects{ values: ... }shape. The current code passes{ originalEnvironmentVariables }which creates{ originalEnvironmentVariables: [...] }instead of{ values: [...] }.const handleReset = () => { - formik.resetForm({ originalEnvironmentVariables }); + formik.resetForm({ values: originalEnvironmentVariables }); };packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
263-278: Draft not cleared on reset.The
handleResetfunction resets the form and setsisModifiedto false, but doesn't dispatchclearGlobalEnvironmentsDraft(). This leaves stale draft data in Redux, which could cause issues when navigating away and back.const handleReset = () => { const originalVars = environment.variables || []; const resetValues = [ ...originalVars, { uid: uuid(), name: '', value: '', type: 'text', secret: false, enabled: true } ]; formik.resetForm({ values: resetValues }); setIsModified(false); + dispatch(clearGlobalEnvironmentsDraft()); };
🧹 Nitpick comments (18)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.js (1)
215-266: Duplicate input/actions styles in.creatingblock.The
.environment-name-inputand.inline-actionsstyles (lines 216-234) duplicate what's already defined in.rename-container(lines 194-212). Consider extracting these shared styles or relying on CSS inheritance from the parent selectors.packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js (1)
222-284: Consider extracting repeatedaddTabdispatch logic.The same
addTabdispatch pattern appears four times (lines 226-232, 242-248, 258-264, 275-281). Consider extracting to helper functions:const openEnvironmentSettingsTab = () => { dispatch( addTab({ uid: `${collection.uid}-environment-settings`, collectionUid: collection.uid, type: 'environment-settings' }) ); }; const openGlobalEnvironmentSettingsTab = () => { dispatch( addTab({ uid: `${collection.uid}-global-environment-settings`, collectionUid: collection.uid, type: 'global-environment-settings' }) ); };Then use as
onEnvironmentCreated={openEnvironmentSettingsTab}etc.packages/bruno-app/src/providers/ReduxStore/slices/tabs.js (1)
24-30: Singleton & non-preview behavior for env/security tabs looks rightMarking
security-settings,environment-settings, andglobal-environment-settingsasnonReplaceableTabTypescorrectly:
- Avoids opening duplicate tabs per
collectionUid(re-activates existing instead)- Makes these tabs non-preview by default
That matches the mental model of “one settings view per collection”. If you also want
collection-settings/collection-overviewto behave as singletons, they could be added to this list later, but that’s optional and not required by this change.Also applies to: 38-44, 63-66, 84-87
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js (1)
14-24: EnvironmentList props match new flow;originalEnvironmentVariablesnow looks unusedDropping
onClosefrom the props and from howEnvironmentDetailsis used is consistent with moving the close/navigation responsibility up to the tab system instead of the inner details view.However,
originalEnvironmentVariablesis still tracked locally and passed into<EnvironmentDetails ... originalEnvironmentVariables={originalEnvironmentVariables} />, while theEnvironmentDetailsin this path no longer accepts or uses that prop. If unsaved-changes logic has been moved fully intoEnvironmentVariables, consider removing theoriginalEnvironmentVariablesstate and prop here to avoid dead parameters and confusion.Also applies to: 96-100, 143-148
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)
1-192: Theme usage is solid; add explicit focus styles for better accessibilityNice use of
props.themefor all colors and borders here, which is consistent with the styled-components theming guidance (no raw CSS vars in this context). Based on learnings, this is the right pattern.One thing to tighten up:
input[type='text'](and buttons via the globalbuttonrule plus subclasses) remove the browser outline (outline: none !important) and don’t introduce a custom focus visual. That makes it hard to see where focus is when tabbing.Consider adding a clear
:focusstyle (e.g., border or background change using theme tokens) instead of, or in addition to, removing the default outline, so keyboard users still get a visible focus indicator.packages/bruno-app/src/components/GlobalEnvironmentSettingsTab/index.js (1)
1-8: Wrapper is fine; clarify whethercollectionshould be part of the API
GlobalEnvironmentSettingsTabis a thin wrapper overWorkspaceEnvironmentsand currently doesn’t take any props, which is fine for now.Since
RequestTabPanelis calling it as<GlobalEnvironmentSettingsTab collection={collection} />, consider either:
- Updating this component to accept and (if needed later) use
collection, or- Dropping the
collectionprop at the call site so the API surface matches the implementation.Right now the extra prop is harmless but can be misleading when scanning the code.
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (2)
21-21: Unused proporiginalEnvironmentVariables.The
originalEnvironmentVariablesprop is destructured but never referenced in the component body. Remove it if no longer needed.-const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables, collection }) => { +const EnvironmentVariables = ({ environment, setIsModified, collection }) => {
125-150: Optimize draft comparison to avoid unnecessary Redux dispatches.Using
JSON.stringifyfor deep comparison works but is expensive for large variable sets. More importantly, the effect dispatches on every formik.values change, and the comparison logic could be simplified.Consider memoizing the comparison or debouncing the draft sync to reduce Redux overhead during rapid typing.
packages/bruno-app/src/providers/Hotkeys/index.js (2)
43-58: Missing save handling for environment-settings tabs.The save hotkey handles
folder-settingsandcollection-settingsbut doesn't handleenvironment-settingsorglobal-environment-settingstab types. Users might expect Ctrl+S to save environment changes when viewing those tabs.Consider adding cases for the new tab types:
} else if (activeTab.type === 'collection-settings') { dispatch(saveCollectionSettings(collection.uid)); + } else if (activeTab.type === 'environment-settings') { + // Handle environment save via appropriate action + } else if (activeTab.type === 'global-environment-settings') { + // Handle global environment save via appropriate action }
66-66: Unnecessary dependency in useEffect array.
saveRequestis an imported action creator and doesn't change between renders. Remove it from the dependency array.- }, [activeTabUid, tabs, saveRequest, collections, dispatch]); + }, [activeTabUid, tabs, collections, dispatch]);packages/bruno-app/src/components/EnvironmentSettingsTab/index.js (1)
30-32: Redundant null check.Since
environmentsdefaults to[]on line 30, the!environmentscheck on line 32 is redundant.- if (!environments || !environments.length) { + if (!environments.length) {packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js (2)
62-65: Redundant condition in environment change check.If
hasSelectedEnvironmentChangedis true (viaisEqual), thenselectedEnvironment.uid !== _selectedEnvironment?.uidis implicitly covered. The second condition is only useful ifisEqualreturned false but UIDs differ, which would be a data inconsistency.- if (hasSelectedEnvironmentChanged || selectedEnvironment.uid !== _selectedEnvironment?.uid) { + if (hasSelectedEnvironmentChanged) { setSelectedEnvironment(_selectedEnvironment); }
89-117: Duplicate click-outside handler pattern.These two useEffects share identical logic for handling outside clicks. Consider extracting to a custom hook like
useClickOutside(ref, callback, isActive).packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/index.js (2)
23-49: Redundant length check and duplicate validation logic.Line 28-30: If
name.trim() === ''is false (line 24-26), thenname.length >= 1is guaranteed, making the check redundant.Also,
validateEnvironmentNameis duplicated inEnvironmentList/index.js. Consider extracting to a shared utility.const validateEnvironmentName = (name) => { if (!name || name.trim() === '') { return 'Name is required'; } - if (name.length < 1) { - return 'Must be at least 1 character'; - } - if (name.length > 255) { return 'Must be 255 characters or less'; }
93-102: Potential UX issue with blur validation.
handleNameBlurvalidates and shows an error but doesn't auto-save or auto-cancel. Users might be confused when the error persists but clicking elsewhere doesn't resolve the state. Consider either auto-canceling on blur with invalid input or triggering save on blur with valid input.packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
127-153: Performance consideration with JSON serialization on every change.This effect runs on every
formik.valueschange and performs multipleJSON.stringifyoperations. For large environment variable lists, this could impact performance.Consider debouncing the draft sync or using a more efficient comparison (e.g., shallow compare of array length + deep compare only when lengths match).
104-124: Draft restoration effect intentionally omits formik from deps.The effect correctly avoids including
formikin the dependency array to prevent infinite loops. However, this pattern is fragile. Adding a brief comment explaining this design decision would help future maintainers.// Restore draft values on mount or environment switch +// Note: formik intentionally omitted from deps to prevent infinite loops React.useEffect(() => {
220-223: Race condition risk with setTimeout + setFieldValue.Using
setTimeoutto append a new row afterhandleChangecompletes could cause race conditions if the user types quickly. Consider using Formik'ssetValuesto batch the update atomically.if (isLastRow) { - const newVariable = { - uid: uuid(), - name: '', - value: '', - type: 'text', - secret: false, - enabled: true - }; - setTimeout(() => { - formik.setFieldValue(formik.values.length, newVariable, false); - }, 0); + const newVariable = { + uid: uuid(), + name: '', + value: '', + type: 'text', + secret: false, + enabled: true + }; + formik.setValues([...formik.values, newVariable]); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (40)
packages/bruno-app/src/components/CollectionSettings/Overview/Info/index.js(2 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/index.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/EnvironmentSettingsTab/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js(6 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js(2 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironmentSettingsTab/index.js(1 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/CopyEnvironment/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/CreateEnvironment/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/DeleteEnvironment/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/DeleteEnvironment/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/ConfirmSwitchEnv.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/RenameEnvironment/index.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/index.js(0 hunks)packages/bruno-app/src/components/RequestTabPanel/index.js(2 hunks)packages/bruno-app/src/components/RequestTabs/RequestTab/ConfirmEnvironmentClose/index.js(1 hunks)packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js(2 hunks)packages/bruno-app/src/components/RequestTabs/RequestTab/index.js(5 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.js(2 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(5 hunks)packages/bruno-app/src/providers/Hotkeys/index.js(5 hunks)packages/bruno-app/src/providers/ReduxStore/slices/app.js(0 hunks)packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js(2 hunks)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js(3 hunks)packages/bruno-app/src/providers/ReduxStore/slices/tabs.js(1 hunks)
💤 Files with no reviewable changes (16)
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/ConfirmSwitchEnv.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/DeleteEnvironment/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/StyledWrapper.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/DeleteEnvironment/StyledWrapper.js
- packages/bruno-app/src/providers/ReduxStore/slices/app.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/CopyEnvironment/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/CreateEnvironment/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/RenameEnvironment/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/index.js
- packages/bruno-app/src/components/GlobalEnvironments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/index.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()notfunc ()
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/EnvironmentSettingsTab/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/providers/ReduxStore/slices/tabs.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.jspackages/bruno-app/src/components/GlobalEnvironmentSettingsTab/index.jspackages/bruno-app/src/components/RequestTabs/RequestTab/ConfirmEnvironmentClose/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.jspackages/bruno-app/src/components/Environments/EnvironmentSelector/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.jspackages/bruno-app/src/providers/Hotkeys/index.jspackages/bruno-app/src/components/RequestTabs/RequestTab/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/index.jspackages/bruno-app/src/components/CollectionSettings/Overview/Info/index.jspackages/bruno-app/src/components/RequestTabPanel/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.jspackages/bruno-app/src/providers/ReduxStore/slices/global-environments.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.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/EnvironmentSettingsTab/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/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/EnvironmentSettingsTab/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/EnvironmentSettingsTab/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/EnvironmentSettingsTab/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
🧬 Code graph analysis (9)
packages/bruno-app/src/components/EnvironmentSettingsTab/StyledWrapper.js (2)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
StyledWrapper(3-131)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.js (1)
StyledWrapper(3-277)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (2)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
EnvironmentVariables(18-395)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
EnvironmentVariables(21-395)
packages/bruno-app/src/components/RequestTabs/RequestTab/ConfirmEnvironmentClose/index.js (2)
packages/bruno-app/src/components/Modal/index.js (1)
Modal(60-163)packages/bruno-app/src/components/Environments/Common/ImportEnvironmentModal/index.js (1)
isGlobal(18-18)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (1)
packages/bruno-app/src/utils/collections/index.js (7)
collection(582-582)collection(1160-1160)collection(1162-1162)collection(1590-1590)collection(1600-1600)findCollectionByUid(80-82)findCollectionByUid(80-82)
packages/bruno-app/src/components/EnvironmentSettingsTab/index.js (2)
packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js (1)
environments(32-32)packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js (1)
EnvironmentList(15-440)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.js (2)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.js (1)
StyledWrapper(3-277)packages/bruno-app/src/components/EnvironmentSettingsTab/StyledWrapper.js (1)
StyledWrapper(3-50)
packages/bruno-app/src/providers/Hotkeys/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/tabs.js (3)
activeTab(151-151)activeTab(158-158)tabs(197-197)packages/bruno-app/src/components/GlobalSearchModal/index.js (3)
tabs(31-31)collections(30-30)dispatch(28-28)packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (1)
dispatch(30-30)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (3)
packages/bruno-app/src/components/RequestTabs/RequestTab/ConfirmEnvironmentClose/index.js (1)
ConfirmEnvironmentClose(5-41)packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
saveEnvironment(1739-1777)saveEnvironment(1739-1777)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js (2)
saveGlobalEnvironment(186-220)saveGlobalEnvironment(186-220)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js (1)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js (1)
EnvironmentList(15-440)
🪛 Biome (2.1.2)
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
[error] 63-63: Do not add then to an object.
(lint/suspicious/noThenProperty)
⏰ 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 - macOS
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - Windows
🔇 Additional comments (29)
packages/bruno-app/src/components/EnvironmentSettingsTab/StyledWrapper.js (1)
1-52: LGTM!Clean styled-component wrapper that correctly uses theme props for all color definitions. The structure with
.empty-stateand.shared-buttonclasses is well-organized and follows project conventions.packages/bruno-app/src/components/CollectionSettings/Overview/Info/index.js (2)
56-62: LGTM!Clean migration to tab-based navigation. The tab UID pattern
${collection.uid}-environment-settingsis consistent and the dispatch structure is correct.
70-78: LGTM!Consistent pattern for global environment settings tab creation.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
21-21: LGTM!Removal of
onCloseprop aligns with the tab-based navigation pattern. The component no longer needs modal-closing semantics.packages/bruno-app/src/components/RequestTabs/RequestTab/ConfirmEnvironmentClose/index.js (1)
1-43: LGTM!Well-structured confirmation dialog with appropriate modal configuration (escape/outside-click disabled for data safety). Button layout follows standard UX patterns with destructive action isolated on the left.
packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js (1)
74-94: LGTM!Clean refactor of
handleSettingsClickfrom modal-based to tab-based navigation. The conditional logic correctly handles both collection and global environment settings.packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/StyledWrapper.js (1)
81-98: No issues found. TheindentBordertheme value already contains the complete border definition.The
indentBorderproperty in both light and dark themes is defined as a full border string (e.g.,'solid 1px #f9f9f9'), not just a color value. Line 86 correctly uses it without adding redundant prefixes:border: ${(props) => props.theme.sidebar.collection.item.indentBorder};This renders as expected since the theme value already includes width, style, and color.
Likely an incorrect or invalid review comment.
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.js (1)
35-41: Copy updates correctly reflect global environment scopeThe duplicate-name error, success toast, and modal title now consistently talk about “Global environment”, which matches the use of the
globalEnvironmentsslice; behavior is unchanged and looks good.Also applies to: 65-67
packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js (1)
3-4: Environment tab icons/labels integrate cleanly with SpecialTabThe added cases for
environment-settingsandglobal-environment-settingsmirror the existing pattern for other special tabs, and the choice ofIconDatabase/IconWorldwith “Environments” / “Global Environments” labels fits the new tab types without changing any external API.Also applies to: 56-71
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js (1)
9-13: Confirm there are no remaining callers relying ononClosefrom EnvironmentDetails/Variables
EnvironmentDetailsand its nestedEnvironmentVariablesno longer receive or propagate anonCloseprop, which means inner components can’t directly close the environment settings view anymore. That’s consistent with the tab-based model where navigation/closing is handled at the tab level, but if any legacy modal-based usages of this component remain, they may now fail to close as expected.It’s worth double-checking all imports/usages of this
EnvironmentDetailsvariant to ensure nothing still depends on anonClose-driven close path.Also applies to: 53-55
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
101-122: Potential stale closure issue in draft restoration effect.The effect references
formik.setValuesbutformikisn't in the dependency array. While this typically works due to formik's stable reference, omitting it from dependencies can lead to stale closures if the form instance changes. Consider addingformik.setValuesto the dependency array or use a ref to track the setter.Also,
globalEnvironmentsDraft?.variablesin the dependency array will cause the effect to re-run on every variable change, potentially causing unwanted re-renders when editing.
240-260: LGTM on save flow.The save handler properly validates before dispatching, clones data to avoid mutation, and handles success/error states with appropriate toasts.
13-17: No action required—the component is correctly using global environment Redux actions.The file path indicates the component hierarchy for workspace environment management, not collection-scoped environments. The implementation confirms this: it imports and dispatches
saveGlobalEnvironment,setGlobalEnvironmentsDraft, andclearGlobalEnvironmentsDraftconsistently, and selects fromstate.globalEnvironments. This is the correct pattern for global environment variables.A separate
EnvironmentVariablescomponent exists atpackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jsfor collection-scoped environments.Likely an incorrect or invalid review comment.
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (3)
4-7: LGTM on imports.Clean separation of collection and global environment actions.
157-159: Draft detection relies on Redux state correctly.The
hasEnvironmentDraftandhasGlobalEnvironmentDraftvariables correctly check both tab type and draft presence. TheuseSelectorcall forglobalEnvironmentsDraftis appropriately placed.
302-305: LGTM on SpecialTab rendering.The new environment tab types are correctly integrated into the conditional rendering chain with appropriate close handlers and draft indicators.
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (2)
697-709: LGTM on new draft reducers.The
setEnvironmentsDraftandclearEnvironmentsDraftreducers follow the established pattern for managing draft state in collections. The shape{ environmentUid, variables }mirrors the global environments draft structure, ensuring consistency.
3388-3389: LGTM on exports.Actions correctly exported for use in components.
packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js (3)
6-10: LGTM on initial state update.Adding
globalEnvironmentsDraft: nullto the initial state properly initializes the draft field.
77-84: LGTM on draft reducers.Clean implementation following Redux Toolkit patterns. The draft shape
{ environmentUid, variables }is consistent with the collections slice implementation.
95-97: LGTM on exports.New actions correctly exported alongside existing ones.
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
1-133: LGTM on styled component.The
StyledWrapperfollows project conventions:
- All colors derived from
props.theme(no hardcoded values)- Flex-based layout with Tailwind-compatible structure
- Consistent with peer
StyledWrapper.jsfiles inEnvironmentSettingsTabandEnvironmentList- Interactive states (hover, focus) appropriately themed
Based on learnings, the theme prop usage for colors is correct.
packages/bruno-app/src/providers/Hotkeys/index.js (1)
115-121: Tab creation for environment settings looks good.The hotkey now correctly dispatches
addTabto open environment settings in a tab instead of a modal, aligning with the new tab-based navigation architecture.packages/bruno-app/src/components/EnvironmentSettingsTab/index.js (2)
46-57: Component structure looks good.The EnvironmentList integration with props for selection, modification state, and export modal control follows a clean pattern.
58-64: Align prop names between modal components for consistency.
ImportEnvironmentModalusestypeprop whileExportEnvironmentModalusesenvironmentTypeprop. Both should use the same name to maintain consistency across related components.packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js (1)
326-389: Environment list rendering and inline editing implementation is solid.The filtering, selection, renaming flow, and activation logic are well-structured with proper event handling (stopPropagation, preventDefault on mouseDown for buttons).
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/index.js (1)
114-181: Component structure and modal integration look good.The inline rename UI with save/cancel buttons, keyboard handling (Enter/Escape), and modal triggers for copy/delete are well-implemented.
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (2)
61-72: Static analysis false positive - Yup schema is valid.The Biome warning about
noThenPropertyis a false positive. This is Yup's conditional schema API (.when().then()), not adding athenproperty to a plain object.
284-393: Table rendering and row handling implementation is well-structured.The conditional rendering for last empty row, checkbox visibility, read-only value handling with tooltip, and action buttons are cleanly implemented.
packages/bruno-app/src/components/EnvironmentSettingsTab/EnvironmentList/index.js
Outdated
Show resolved
Hide resolved
| onSaveAndClose={() => { | ||
| const draft = collection.environmentsDraft; | ||
| if (draft?.environmentUid && draft?.variables) { | ||
| dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid)) | ||
| .then(() => { | ||
| dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); | ||
| dispatch(closeTabs({ tabUids: [tab.uid] })); | ||
| setShowConfirmEnvironmentClose(false); | ||
| toast.success('Environment saved'); | ||
| }) | ||
| .catch((err) => { | ||
| console.log('err', err); | ||
| toast.error('Failed to save environment'); | ||
| }); | ||
| } | ||
| }} |
There was a problem hiding this comment.
Missing null check in save handler could silently fail.
The onSaveAndClose callback only executes the save if draft?.environmentUid && draft?.variables are truthy. If either is falsy, the modal remains open with no feedback to the user.
Consider showing an error toast or closing without save when draft is incomplete.
onSaveAndClose={() => {
const draft = collection.environmentsDraft;
if (draft?.environmentUid && draft?.variables) {
dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid))
.then(() => {
dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid }));
dispatch(closeTabs({ tabUids: [tab.uid] }));
setShowConfirmEnvironmentClose(false);
toast.success('Environment saved');
})
.catch((err) => {
console.log('err', err);
toast.error('Failed to save environment');
});
+ } else {
+ toast.error('No draft to save');
+ setShowConfirmEnvironmentClose(false);
}
}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onSaveAndClose={() => { | |
| const draft = collection.environmentsDraft; | |
| if (draft?.environmentUid && draft?.variables) { | |
| dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid)) | |
| .then(() => { | |
| dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); | |
| dispatch(closeTabs({ tabUids: [tab.uid] })); | |
| setShowConfirmEnvironmentClose(false); | |
| toast.success('Environment saved'); | |
| }) | |
| .catch((err) => { | |
| console.log('err', err); | |
| toast.error('Failed to save environment'); | |
| }); | |
| } | |
| }} | |
| onSaveAndClose={() => { | |
| const draft = collection.environmentsDraft; | |
| if (draft?.environmentUid && draft?.variables) { | |
| dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid)) | |
| .then(() => { | |
| dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); | |
| dispatch(closeTabs({ tabUids: [tab.uid] })); | |
| setShowConfirmEnvironmentClose(false); | |
| toast.success('Environment saved'); | |
| }) | |
| .catch((err) => { | |
| console.log('err', err); | |
| toast.error('Failed to save environment'); | |
| }); | |
| } else { | |
| toast.error('No draft to save'); | |
| setShowConfirmEnvironmentClose(false); | |
| } | |
| }} |
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/RequestTabs/RequestTab/index.js around
lines 251-266, the onSaveAndClose handler only runs when draft?.environmentUid
&& draft?.variables are truthy, leaving the modal open with no feedback if the
draft is incomplete; add an else branch that handles the missing draft case by
calling toast.error with a clear message (e.g., "Incomplete environment draft —
cannot save"), optionally log the missing fields for debugging, and keep the
modal open (do not proceed to closeTabs) so the user can correct the draft;
ensure this branch does not attempt to dispatch saveEnvironment.
| onSaveAndClose={() => { | ||
| const draft = globalEnvironmentsDraft; | ||
| if (draft?.environmentUid && draft?.variables) { | ||
| dispatch(saveGlobalEnvironment({ variables: draft.variables, environmentUid: draft.environmentUid })) | ||
| .then(() => { | ||
| dispatch(clearGlobalEnvironmentsDraft()); | ||
| dispatch(closeTabs({ tabUids: [tab.uid] })); | ||
| setShowConfirmGlobalEnvironmentClose(false); | ||
| toast.success('Global environment saved'); | ||
| }) | ||
| .catch((err) => { | ||
| console.log('err', err); | ||
| toast.error('Failed to save global environment'); | ||
| }); | ||
| } | ||
| }} | ||
| /> |
There was a problem hiding this comment.
Same issue: missing fallback for incomplete global environment draft.
Mirror the same handling pattern as collection environments for consistency.
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/RequestTabs/RequestTab/index.js around
lines 278-294, the onSaveAndClose handler attempts to save only when draft has
environmentUid and variables but lacks a fallback for incomplete drafts; add an
else branch that mirrors the collection-environment handling: when draft is
missing required fields, avoid calling save, dispatch
clearGlobalEnvironmentsDraft(), dispatch closeTabs({ tabUids: [tab.uid] }),
ensure setShowConfirmGlobalEnvironmentClose is set to the proper state (hide
confirmation), and show a user-facing toast (e.g., info or warning) indicating
there was nothing to save or the draft was incomplete.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
packages/bruno-app/src/components/RequestTabPanel/index.js (1)
205-211: Verifycollectionprop usage inGlobalEnvironmentSettings.Past review flagged that
GlobalEnvironmentSettingsmay not use thecollectionprop being passed at line 210. If the component doesn't accept or use this prop, it should be removed to maintain API consistency.Run the following script to verify prop usage:
#!/bin/bash # Check if GlobalEnvironmentSettings component accepts collection prop # Check component definition cat packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.js | head -50 # Search for collection prop usage in the component rg -n "collection" packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/ -C 3
🧹 Nitpick comments (21)
tests/request/newlines/newlines-persistence.spec.ts (1)
5-78: Consider usingtest.stepfor better report readability.The test could benefit from wrapping logical sections in
test.stepcalls (e.g., "Create collection and accept sandbox", "Create request and add data", "Verify persistence after restart"). This makes Playwright reports easier to navigate and understand.Example structure:
test('should persist request with newlines across app restarts', async ({ createTmpDir, launchElectronApp }) => { const userDataPath = await createTmpDir('newlines-persistence-userdata'); const collectionPath = await createTmpDir('newlines-persistence-collection'); - // Create collection and request - const app1 = await launchElectronApp({ userDataPath }); - const page = await app1.firstWindow(); - - await page.getByTestId('collections-header-add-menu').click(); - ... + await test.step('Create collection and accept sandbox', async () => { + const app1 = await launchElectronApp({ userDataPath }); + const page = await app1.firstWindow(); + await page.getByTestId('collections-header-add-menu').click(); + ... + await openCollectionAndAcceptSandbox(page, 'newlines-persistence', 'safe'); + }); + + await test.step('Create request and add data with newlines', async () => { + ... + }); + + await test.step('Verify persistence after restart', async () => { + ... + });As per coding guidelines, test.step usage is promoted for improved report readability.
tests/global-environments/non-string-values.spec.ts (3)
57-58: Consider more robust selectors for environment variable inputs.Selecting inputs by
valueattribute (e.g.,input[value="numericVar"]) can be fragile if the value isn't consistently set or if the DOM structure changes. Consider usingdata-testidattributes or a combination of text content and structural selectors for better stability.Also applies to: 112-112
26-27: Extract repeated envTab locator to reduce duplication.The
envTablocator is defined three times with identical logic. Consider extracting it to a const at the test function level to follow DRY principles.Example:
// At the top of the test function const getEnvTab = () => page.locator('.request-tab').filter({ hasText: 'Global Environments' });Then use
getEnvTab()instead of redefining the locator each time.Also applies to: 53-54, 125-126
37-38: Tab close pattern is repeated.The pattern of hovering over the tab and clicking the close icon appears twice. If this pattern is used across multiple tests, consider extracting it to a helper function in the utils module.
Also applies to: 126-127
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
175-210: handleRemoveVar dependency concern.The
useCallbackdepends onformik.values, which changes on every edit. This effectively disables memoization. Consider using a ref or restructuring to avoid the dependency.Apply this change to improve memoization:
- const handleRemoveVar = useCallback((id) => { - const currentValues = formik.values; + const handleRemoveVar = useCallback((id) => { + const currentValues = formik.values;Or use
formik.setValueswith a function form to avoid the dependency:const handleRemoveVar = useCallback((id) => { - const currentValues = formik.values; - - if (!currentValues || currentValues.length === 0) { + formik.setValues((currentValues) => { + if (!currentValues || currentValues.length === 0) { - return; + return currentValues; - } + } - - const lastRow = currentValues[currentValues.length - 1]; - const isLastEmptyRow = lastRow?.uid === id && (!lastRow.name || lastRow.name.trim() === ''); - - if (isLastEmptyRow) { - return; - } - - const filteredValues = currentValues.filter((variable) => variable.uid !== id); - - const hasEmptyLastRow = filteredValues.length > 0 - && (!filteredValues[filteredValues.length - 1].name - || filteredValues[filteredValues.length - 1].name.trim() === ''); - - const newValues = hasEmptyLastRow - ? filteredValues - : [ - ...filteredValues, - { - uid: uuid(), - name: '', - value: '', - type: 'text', - secret: false, - enabled: true - } - ]; - - formik.setValues(newValues); - }, [formik.values]); + + const lastRow = currentValues[currentValues.length - 1]; + const isLastEmptyRow = lastRow?.uid === id && (!lastRow.name || lastRow.name.trim() === ''); + + if (isLastEmptyRow) { + return currentValues; + } + + const filteredValues = currentValues.filter((variable) => variable.uid !== id); + + const hasEmptyLastRow = filteredValues.length > 0 + && (!filteredValues[filteredValues.length - 1].name + || filteredValues[filteredValues.length - 1].name.trim() === ''); + + return hasEmptyLastRow + ? filteredValues + : [ + ...filteredValues, + { + uid: uuid(), + name: '', + value: '', + type: 'text', + secret: false, + enabled: true + } + ]; + }); + }, []);packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
178-214: handleRemoveVar dependency concern.Similar to the global variant, the
useCallbackdepends onformik.values, which defeats memoization. Consider using the function form offormik.setValuesto eliminate this dependency.Use the function form of
setValuesto avoid depending onformik.values:const handleRemoveVar = useCallback((id) => { - const currentValues = formik.values; - - if (!currentValues || currentValues.length === 0) { - return; - } - - const lastRow = currentValues[currentValues.length - 1]; - const isLastEmptyRow = lastRow?.uid === id && (!lastRow.name || lastRow.name.trim() === ''); - - if (isLastEmptyRow) { - return; - } - - const filteredValues = currentValues.filter((variable) => variable.uid !== id); - - const hasEmptyLastRow - = filteredValues.length > 0 - && (!filteredValues[filteredValues.length - 1].name - || filteredValues[filteredValues.length - 1].name.trim() === ''); - - const newValues = hasEmptyLastRow - ? filteredValues - : [ - ...filteredValues, - { - uid: uuid(), - name: '', - value: '', - type: 'text', - secret: false, - enabled: true - } - ]; - - formik.setValues(newValues); - }, [formik.values]); + formik.setValues((currentValues) => { + if (!currentValues || currentValues.length === 0) { + return currentValues; + } + + const lastRow = currentValues[currentValues.length - 1]; + const isLastEmptyRow = lastRow?.uid === id && (!lastRow.name || lastRow.name.trim() === ''); + + if (isLastEmptyRow) { + return currentValues; + } + + const filteredValues = currentValues.filter((variable) => variable.uid !== id); + + const hasEmptyLastRow + = filteredValues.length > 0 + && (!filteredValues[filteredValues.length - 1].name + || filteredValues[filteredValues.length - 1].name.trim() === ''); + + return hasEmptyLastRow + ? filteredValues + : [ + ...filteredValues, + { + uid: uuid(), + name: '', + value: '', + type: 'text', + secret: false, + enabled: true + } + ]; + }); + }, []);tests/environments/import-environment/collection-env-import.spec.ts (1)
55-66: Consider wrapping verification intest.stepfor better reporting.The tab verification and assertions work correctly, but wrapping this logic in a
test.stepwould improve test report readability and align with the Playwright best practices promoted in the coding guidelines.Apply this pattern:
+ await test.step('Verify imported environment variables in tab', async () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(envTab).toBeVisible(); await expect(page.locator('input[name="0.name"]')).toHaveValue('host'); await expect(page.locator('input[name="1.name"]')).toHaveValue('userId'); await expect(page.locator('input[name="2.name"]')).toHaveValue('apiKey'); await expect(page.locator('input[name="3.name"]')).toHaveValue('postTitle'); await expect(page.locator('input[name="4.name"]')).toHaveValue('postBody'); await expect(page.locator('input[name="5.name"]')).toHaveValue('secretApiToken'); await expect(page.locator('input[name="5.secret"]')).toBeChecked(); await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click(); + });tests/environments/import-environment/global-env-import.spec.ts (1)
50-61: Consider wrapping verification intest.stepfor better reporting.Similar to the collection environment import test, wrapping this tab verification logic in a
test.stepwould improve report readability and maintainability.Apply this pattern:
+ await test.step('Verify imported global environment variables in tab', async () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' }); await expect(envTab).toBeVisible(); await expect(page.locator('input[name="0.name"]')).toHaveValue('host'); await expect(page.locator('input[name="1.name"]')).toHaveValue('userId'); await expect(page.locator('input[name="2.name"]')).toHaveValue('apiKey'); await expect(page.locator('input[name="3.name"]')).toHaveValue('postTitle'); await expect(page.locator('input[name="4.name"]')).toHaveValue('postBody'); await expect(page.locator('input[name="5.name"]')).toHaveValue('secretApiToken'); await expect(page.locator('input[name="5.secret"]')).toBeChecked(); await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click(); + });tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts (1)
31-38: Consider extracting tab verification into a helper function.The tab verification and close pattern appears twice in this test (lines 31-38 and 51-59). Extracting this into a reusable helper would reduce duplication and improve maintainability, especially since this pattern is also used across multiple test files.
Example helper:
async function verifyAndCloseEnvironmentsTab(page, expectedRows: string[]) { const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(envTab).toBeVisible(); for (const rowName of expectedRows) { await expect(page.getByRole('row', { name: rowName })).toBeVisible(); } await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click(); }tests/environments/global-env-config-selection/global-env-config-selection.spec.ts (1)
24-31: Consider wrapping verification intest.stepfor better reporting.The tab verification logic would benefit from a
test.stepwrapper to improve test report readability, consistent with Playwright best practices.+ await test.step('Verify global environment tab shows active environment', async () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' }); await expect(envTab).toBeVisible(); const activeEnvItem = page.locator('.environment-item.active'); await expect(activeEnvItem).toContainText(currentEnvName); await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click(); + });tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts (1)
24-31: Consider extracting repeated verification pattern.Similar to the persist test, this verification pattern is duplicated (lines 24-31 and 45-51). Consider extracting into a shared helper function to reduce duplication across test files.
Additionally, wrapping in
test.stepwould improve report readability:+ await test.step('Verify environment variables are set', async () => { const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); await expect(envTab).toBeVisible(); await expect(page.getByRole('row', { name: 'token' }).getByRole('cell').nth(1)).toBeVisible(); await expect(page.getByRole('row', { name: 'secret' }).getByRole('cell').nth(2)).toBeVisible(); await envTab.hover(); await envTab.getByTestId('request-tab-close-icon').click(); + });tests/environments/api-setEnvVar/multiple-persist-vars.spec.ts (2)
13-15: ReducewaitForTimeoutusage.The test uses
waitForTimeout(200)multiple times, which the coding guidelines discourage. Consider replacing these with explicit locator waits or expectations.Replace:
- await page.waitForTimeout(200); - await page.locator('#configure-env').click(); - await page.waitForTimeout(200); + await page.locator('#configure-env').click(); + const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); + await expect(envTab).toBeVisible();Based on coding guidelines: Try to reduce usage of
page.waitForTimeout();unless absolutely necessary.
76-77: Consider replacingwaitForTimeoutwith locator-based waits.Similar to the cleanup section, these timeouts could be replaced with explicit visibility checks for more reliable test execution.
- await page.waitForTimeout(200); - await page.locator('#configure-env').click(); - await page.waitForTimeout(200); + await page.locator('#configure-env').click(); + const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' }); + await expect(envTab).toBeVisible();tests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.ts (1)
99-99: ReplacewaitForTimeoutwith explicit wait.Instead of a fixed timeout, consider waiting for a specific UI element to be ready.
- await test.step('Verify both environments are available in selector', async () => { - await page.waitForTimeout(500); - await page.getByTestId('environment-selector-trigger').click(); + await test.step('Verify both environments are available in selector', async () => { + await page.getByTestId('environment-selector-trigger').click(); + await expect(page.locator('.dropdown-item').filter({ hasText: /^local$/ })).toBeVisible({ timeout: 10000 });Based on coding guidelines.
tests/environments/update-global-environment-via-script/global-env-update-via-script.spec.ts (1)
35-56: Consider extracting repeated row locator pattern.The pattern of locating a row by input value and reading CodeMirror content is repeated four times. Extracting this to a helper function would improve maintainability.
Example helper:
const getEnvVariableValue = async (page: Page, variableName: string): Promise<string> => { const row = page.locator('tbody tr').filter({ has: page.locator(`input[value="${variableName}"]`) }); return await row.locator('.CodeMirror-line').first().textContent(); };Then use:
const value = await getEnvVariableValue(page, 'existingEnvEnabled'); await expect(value).toContain('newExistingEnvEnabledValue');tests/environments/export-environment/collection-env-export/collection-env-export.spec.ts (2)
45-45: Consider centralizing the export button selector.The selector
button[title="Export environment"]appears multiple times. Consider defining it once at the top of the test suite or in a locator helper.const EXPORT_BUTTON = 'button[title="Export environment"]'; // Then use: await page.locator(EXPORT_BUTTON).click();This improves maintainability if the selector changes.
Also applies to: 102-102, 170-170, 227-227, 282-282, 337-337, 396-396
39-40: Repeated envTab locator pattern.The pattern of defining and verifying the Environments tab is repeated across multiple tests. Consider extracting to a helper function in the test utilities.
Example:
const verifyEnvironmentsTab = async (page: Page, type: 'collection' | 'global' = 'collection') => { const tabLabel = type === 'collection' ? 'Environments' : 'Global Environments'; const envTab = page.locator('.request-tab').filter({ hasText: tabLabel }); await expect(envTab).toBeVisible(); return envTab; };Also applies to: 96-97, 164-165, 222-223, 277-278, 332-333, 390-391
tests/environments/export-environment/global-env-export/global-env-export.spec.ts (1)
300-300: Remove unnecessarywaitForTimeout.The
waitForTimeout(200)appears unnecessary here since the success message visibility check immediately follows.await test.step('Execute export and verify success', async () => { // Export the environments await page.getByRole('button', { name: 'Export 2 Environments' }).click(); - await page.waitForTimeout(200); // Verify success message await expect(page.getByText('Environment(s) exported successfully', { exact: false }).first()).toBeVisible(); });The
expect().toBeVisible()already includes an implicit wait.packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js (1)
30-30: Optional: Simplify with default parameter.The environments extraction could be simplified using a default parameter.
-const EnvironmentSettings = ({ collection }) => { +const EnvironmentSettings = ({ collection = {} }) => { const [isModified, setIsModified] = useState(false); const [selectedEnvironment, setSelectedEnvironment] = useState(null); const [tab, setTab] = useState('default'); const [showExportModal, setShowExportModal] = useState(false); - const environments = collection?.environments || []; + const environments = collection.environments || [];packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js (2)
23-49: Minor: Redundant length validation.Lines 28-30 check
if (name.length < 1)which is redundant since lines 24-26 already checkif (!name || name.trim() === ''). An empty string will have length 0, so this condition will never be reached independently.if (!name || name.trim() === '') { return 'Name is required'; } - if (name.length < 1) { - return 'Must be at least 1 character'; - } - if (name.length > 255) { return 'Must be 255 characters or less'; }
51-59: Consider adding a comment for the setTimeout delay.The 50ms setTimeout is used to ensure the input is rendered before focusing. A brief comment would clarify this timing for future maintainers.
const handleRenameClick = () => { setIsRenaming(true); setNewName(environment.name); setNameError(''); + // Wait for input to render before focusing setTimeout(() => { inputRef.current?.focus(); inputRef.current?.select(); }, 50); };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (40)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/ConfirmSwitchEnv.js(0 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js(0 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(2 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js(6 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/ManageSecrets/index.js(0 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/RenameEnvironment/index.js(0 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js(1 hunks)packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.js(1 hunks)packages/bruno-app/src/components/RequestTabPanel/index.js(2 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(9 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/WorkspaceHome/index.js(1 hunks)packages/bruno-app/src/providers/Hotkeys/index.js(5 hunks)packages/bruno-app/src/utils/common/regex.js(1 hunks)tests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.ts(2 hunks)tests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.ts(2 hunks)tests/environments/api-setEnvVar/multiple-persist-vars.spec.ts(2 hunks)tests/environments/collection-env-config-selection/collection-env-config-selection.spec.ts(1 hunks)tests/environments/create-environment/collection-env-create.spec.ts(0 hunks)tests/environments/create-environment/global-env-create.spec.ts(0 hunks)tests/environments/export-environment/collection-env-export/collection-env-export.spec.ts(7 hunks)tests/environments/export-environment/global-env-export/global-env-export.spec.ts(7 hunks)tests/environments/global-env-config-selection/global-env-config-selection.spec.ts(1 hunks)tests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.ts(3 hunks)tests/environments/import-environment/bruno-env-import/global-env-import/global-env-import.spec.ts(5 hunks)tests/environments/import-environment/collection-env-import.spec.ts(1 hunks)tests/environments/import-environment/global-env-import.spec.ts(1 hunks)tests/environments/multiline-variables/write-multiline-variable.spec.ts(3 hunks)tests/environments/update-global-environment-via-script/global-env-update-via-script.spec.ts(1 hunks)tests/global-environments/non-string-values.spec.ts(7 hunks)tests/import/insomnia/import-insomnia-v4-environments.spec.ts(1 hunks)tests/import/insomnia/import-insomnia-v5-environments.spec.ts(1 hunks)tests/request/newlines/newlines-persistence.spec.ts(1 hunks)tests/utils/page/actions.ts(2 hunks)tests/utils/page/locators.ts(1 hunks)
💤 Files with no reviewable changes (6)
- tests/environments/create-environment/global-env-create.spec.ts
- tests/environments/create-environment/collection-env-create.spec.ts
- packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/RenameEnvironment/index.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/ConfirmSwitchEnv.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/ManageSecrets/index.js
✅ Files skipped from review due to trivial changes (2)
- packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js
- packages/bruno-app/src/utils/common/regex.js
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/bruno-app/src/providers/Hotkeys/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()notfunc ()
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/environments/import-environment/collection-env-import.spec.tstests/environments/global-env-config-selection/global-env-config-selection.spec.tstests/environments/api-setEnvVar/multiple-persist-vars.spec.tspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.jstests/request/newlines/newlines-persistence.spec.tstests/import/insomnia/import-insomnia-v5-environments.spec.tstests/environments/collection-env-config-selection/collection-env-config-selection.spec.tstests/environments/import-environment/global-env-import.spec.tspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/index.jstests/import/insomnia/import-insomnia-v4-environments.spec.tspackages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.jstests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.tstests/environments/import-environment/bruno-env-import/global-env-import/global-env-import.spec.tstests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.tstests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.tspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.jstests/environments/export-environment/collection-env-export/collection-env-export.spec.tspackages/bruno-app/src/components/RequestTabPanel/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jstests/utils/page/locators.tspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.jstests/utils/page/actions.tstests/global-environments/non-string-values.spec.tstests/environments/update-global-environment-via-script/global-env-update-via-script.spec.tstests/environments/multiline-variables/write-multiline-variable.spec.tspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jstests/environments/export-environment/global-env-export/global-env-export.spec.tspackages/bruno-app/src/components/Environments/EnvironmentSettings/index.js
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 existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture 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/environments/import-environment/collection-env-import.spec.tstests/environments/global-env-config-selection/global-env-config-selection.spec.tstests/environments/api-setEnvVar/multiple-persist-vars.spec.tstests/request/newlines/newlines-persistence.spec.tstests/import/insomnia/import-insomnia-v5-environments.spec.tstests/environments/collection-env-config-selection/collection-env-config-selection.spec.tstests/environments/import-environment/global-env-import.spec.tstests/import/insomnia/import-insomnia-v4-environments.spec.tstests/environments/api-setEnvVar/api-setEnvVar-with-persist.spec.tstests/environments/import-environment/bruno-env-import/global-env-import/global-env-import.spec.tstests/environments/api-setEnvVar/api-setEnvVar-without-persist.spec.tstests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.tstests/environments/export-environment/collection-env-export/collection-env-export.spec.tstests/utils/page/locators.tstests/utils/page/actions.tstests/global-environments/non-string-values.spec.tstests/environments/update-global-environment-via-script/global-env-update-via-script.spec.tstests/environments/multiline-variables/write-multiline-variable.spec.tstests/environments/export-environment/global-env-export/global-env-export.spec.ts
🧠 Learnings (7)
📚 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/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/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/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/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/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
📚 Learning: 2025-12-16T07:16:08.934Z
Learnt from: sanish-bruno
Repo: usebruno/bruno PR: 6090
File: tests/scripting/hooks/init-user-data/ui-state-snapshot.json:1-8
Timestamp: 2025-12-16T07:16:08.934Z
Learning: For e2e tests in the bruno repository: Collections that are shared between CLI and UI tests (comprehensive test suites testing core functionality) should be placed in `packages/bruno-tests/` to avoid duplication. The `tests/**/fixtures/collection` pattern should be used for test-specific collections that test particular UI behaviors or are specific to a single test file.
Applied to files:
tests/request/newlines/newlines-persistence.spec.tstests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.ts
📚 Learning: 2025-12-02T20:48:05.670Z
Learnt from: NikHillAgar
Repo: usebruno/bruno PR: 6279
File: packages/bruno-app/src/components/RequestPane/PromptVariables/PromptVariablesModal/index.js:79-86
Timestamp: 2025-12-02T20:48:05.670Z
Learning: In React 19+, ref callbacks support returning cleanup functions. When a ref callback returns a function, React will call it when the element unmounts or the ref changes, similar to useEffect cleanup. This is documented at https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback
Applied to files:
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.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: Use consistent patterns and helper utilities where they improve clarity. Prefer shared test utilities over copy-pasted setup code, but only when it actually reduces complexity
Applied to files:
tests/global-environments/non-string-values.spec.ts
📚 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/environments/update-global-environment-via-script/global-env-update-via-script.spec.ts
🧬 Code graph analysis (4)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.js (2)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
StyledWrapper(3-131)packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js (1)
StyledWrapper(3-50)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (9)
EnvironmentVariables(18-439)useTheme(20-20)useSelector(21-21)hasDraftForThisEnv(24-24)handleRemoveVar(178-214)handleNameBlur(235-237)handleNameKeyDown(239-244)handleSave(246-292)handleSaveRef(311-311)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/index.js (2)
handleNameBlur(92-101)handleNameKeyDown(103-111)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js (3)
environment(33-33)environment(42-42)environment(61-61)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js (5)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js (2)
dispatch(25-25)inputRef(34-34)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.js (2)
dispatch(19-19)inputRef(20-20)packages/bruno-electron/src/utils/filesystem.js (1)
validateName(229-244)packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
renameEnvironment(1689-1714)renameEnvironment(1689-1714)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
StyledWrapper(3-132)
tests/utils/page/actions.ts (2)
packages/bruno-electron/src/app/workspace-watcher.js (1)
type(41-41)packages/bruno-electron/src/utils/workspace-config.js (1)
type(171-171)
🪛 Biome (2.1.2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
[error] 63-63: Do not add then to an object.
(lint/suspicious/noThenProperty)
🔇 Additional comments (52)
tests/request/newlines/newlines-persistence.spec.ts (1)
19-20: Good consolidation of sandbox acceptance flow.Moving the sandbox acceptance to immediately after collection creation and removing the duplicate call (per the AI summary) is cleaner. This aligns well with the tab-based UI changes in the PR.
packages/bruno-app/src/components/WorkspaceHome/index.js (1)
135-135: LGTM! Label change improves clarity.The update from "Environments" to "Global Environments" better distinguishes this from collection-level environments. The tab
idremains unchanged, avoiding breaking changes.tests/global-environments/non-string-values.spec.ts (1)
2-2: LGTM! Good use of shared helper utility.The migration to
addEnvironmentVariablesimproves test maintainability and follows the pattern of preferring shared utilities over repeated setup code.Also applies to: 29-33
packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.js (1)
1-8: LGTM!Clean wrapper component that follows coding guidelines. The component correctly serves as the entry point for global environment settings in the new tab-based UI.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
1-134: LGTM!StyledWrapper correctly implements the inline rename UI with proper theme-based styling. Color management uses theme props as expected, and the layout structure supports both normal and renaming states effectively.
tests/environments/multiline-variables/write-multiline-variable.spec.ts (4)
14-14: Double-click interaction aligns with updated request opening pattern.The change from single click to double-click for opening requests is consistent with the tab-based UI flow introduced in this PR.
31-36: Tab-based environment interaction with direct row creation.The test correctly verifies the Environments tab visibility and directly creates a variable row via the empty row input, which is cleaner than the previous approach. Good use of locator variables.
53-56: CodeMirror-based value entry is appropriate for multiline JSON.The approach of locating the variable row, finding CodeMirror within it, and using
keyboard.insertTextcorrectly handles multiline JSON input.
58-61: Test-id based save and close actions.The migration to test-id based interactions (
save-env,request-tab-close-icon) improves test reliability and aligns with the tab-based environment settings flow.packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (9)
1-1: useCallback and useRef imports are appropriate.These hooks support the draft-aware, event-driven save pattern introduced in this component.
13-17: Global environment actions imported correctly.The imports for
saveGlobalEnvironment,setGlobalEnvironmentsDraft, andclearGlobalEnvironmentsDraftalign with the new draft management system.
24-32: Draft state and environment tracking setup.The implementation correctly tracks the current environment's draft state and uses refs to detect environment switches for draft restoration.
41-56: Initial values use useMemo with correct dependencies.The memoization correctly depends on
environment.uidandenvironment.variables, ensuring the form reinitializes only when the environment changes or its saved variables change.
101-122: Draft restoration logic on mount and environment switch.The effect correctly distinguishes between mount and environment change, restoring draft values only when appropriate. The trailing empty row is consistently appended.
124-150: Draft synchronization with Redux.The effect properly compares current and saved values, dispatching draft actions only when changes occur. The comparison avoids unnecessary dispatches by checking existing draft JSON.
231-240: Simplified name blur and keydown handlers.These handlers appropriately mark the field as touched for validation. The Enter key behavior is clear.
242-288: Comprehensive save validation and error handling.The save handler correctly validates changes, checks for validation errors, dispatches the save action, and handles success/error cases with appropriate toast messages.
307-320: Event-driven save pattern with useRef.The
handleSaveRefpattern correctly maintains a stable event listener while allowing the handler to access the latest closure. This is the appropriate approach for window events in React.packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (9)
1-1: useCallback and useRef imports are appropriate.These hooks support the draft-aware, event-driven save pattern for collection environments.
13-16: Collection-specific actions imported correctly.The imports for
saveEnvironment,setEnvironmentsDraft, andclearEnvironmentsDraftalign with the collection environment draft system.
23-28: Collection draft state tracking.The component correctly accesses
collection.environmentsDraftand tracks draft state for the current environment.
61-72: Yup validation schema is correct; ignore static analysis false positive.The Biome linter incorrectly flags the
.then()method as suspicious. In Yup schemas,.when()accepts a configuration object withthenandotherwiseproperties—this is the documented API.
103-124: Draft restoration on mount and environment switch.The logic correctly restores draft values when mounting or switching environments, maintaining the trailing empty row.
126-153: Draft sync with collection-specific actions.The effect properly synchronizes draft state with Redux using collection UID, avoiding unnecessary dispatches through JSON comparison.
246-291: Save handler with collection-specific dispatch.The handler correctly validates, dispatches
saveEnvironmentwith collection UID, and handles success/error states appropriately.
311-324: Event-driven save with stable listener reference.The
handleSaveRefpattern ensures the window event listener remains stable while accessing the latest handler closure.
346-346: test-id on table row improves test reliability.Adding
data-testidwith the variable name makes it easier to target specific rows in tests, as seen in the multiline-variables test.packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/StyledWrapper.js (1)
84-84: Error tooltip alignment improved.Changing
left: 20pxaligns the error tooltip with the header's horizontal padding, creating consistent visual alignment with the content.tests/import/insomnia/import-insomnia-v4-environments.spec.ts (1)
181-184: Tab-based close interaction aligns with new UI pattern.The test correctly closes the Environments tab using the hover + click close icon pattern, replacing the previous modal-based close action. Good use of locator variable and test-id.
tests/utils/page/locators.ts (1)
42-42: Test-id based tab close improves test reliability.Switching from class-based selector (
.close-icon) to test-id (request-tab-close-icon) makes tests more resilient to styling changes and aligns with best practices.tests/import/insomnia/import-insomnia-v5-environments.spec.ts (1)
205-209: LGTM!The tab-based close flow is well-implemented using proper Playwright patterns: locator variable, filter, hover, and testId-based close action. Good use of
test.stepfor report readability.packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js (1)
1-52: LGTM!The styled component follows all coding guidelines and learnings: uses theme props for colors, applies proper layout structure, and maintains consistent spacing. The empty-state and shared-button classes provide reusable styling with proper transitions and hover states.
Based on learnings, styled components are correctly used for defining component and children styles with theme-based color management.
packages/bruno-app/src/components/RequestTabPanel/index.js (1)
37-38: LGTM!Import statements follow coding standards with proper formatting and organization.
tests/environments/collection-env-config-selection/collection-env-config-selection.spec.ts (1)
31-38: LGTM! Clean tab-based navigation implementation.The transition from modal-based to tab-based environment configuration is implemented correctly. The locator for the Environments tab is well-defined, and the close mechanism using hover + testId is appropriate.
tests/environments/api-setEnvVar/multiple-persist-vars.spec.ts (2)
17-30: LGTM! Cleanup flow updated correctly.The cleanup logic now properly closes the Environments tab instead of a modal. The implementation aligns with the new tab-based navigation pattern.
79-88: Good addition of tab visibility check.Adding the explicit visibility assertion before interacting with environment variables improves test reliability.
tests/environments/import-environment/bruno-env-import/collection-env-import/collection-env-import.spec.ts (2)
42-49: LGTM! Import verification correctly updated.The test now properly verifies the Environments tab visibility after import and uses the tab-based close mechanism.
115-124: Good implementation of tab-based verification.The switch to prod environment and subsequent verification using the tab-based approach is correctly implemented.
tests/environments/update-global-environment-via-script/global-env-update-via-script.spec.ts (2)
25-32: LGTM! Tab-based navigation correctly implemented.The test step now opens and verifies the Global Environments tab instead of a modal.
58-62: LGTM! Tab close logic updated correctly.The test properly closes the Global Environments tab using the new hover + click approach.
tests/environments/import-environment/bruno-env-import/global-env-import/global-env-import.spec.ts (2)
57-65: LGTM! Import verification correctly updated.The test now properly verifies the Global Environments tab visibility and uses the tab-based close mechanism.
118-119: Good addition of tab visibility checks.The explicit visibility assertions improve test reliability and align with the new tab-based UI pattern.
Also applies to: 140-148
tests/environments/export-environment/collection-env-export/collection-env-export.spec.ts (1)
39-45: LGTM! Tab-based navigation implemented correctly.The test now verifies the Environments tab instead of a modal and uses the appropriate export button selector.
tests/utils/page/actions.ts (3)
391-407: Excellent type-aware environment creation.The updated
createEnvironmentfunction properly handles both collection and global environment types with appropriate input selectors and post-creation verification. The addition of verification steps improves test reliability.
424-461: Well-structured environment variable helpers.The new
addEnvironmentVariableandaddEnvironmentVariablesfunctions provide clean, reusable utilities for populating environment variables. Good use of test.step for better test reporting.
480-487: LGTM!closeEnvironmentPanelcorrectly updated.The function now properly closes the environment tab using the hover + testId click pattern. The type parameter allows it to handle both collection and global environment tabs.
tests/environments/export-environment/global-env-export/global-env-export.spec.ts (1)
43-49: LGTM! Global environment export tests updated correctly.All test steps now properly verify the Global Environments tab visibility and use appropriate selectors for the tab-based UI flow.
Also applies to: 100-106, 168-174, 226-231, 283-288, 338-343, 398-403
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)
1-190: LGTM - Comprehensive theme-driven styling.The styling changes properly use theme props for all colors and follow the styled-components pattern. The flex layout, table structure, button variants, and input styling all align with the project's design system.
Based on learnings, styled components are correctly used for styling both wrapper and children, with theme props managing all colors.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js (1)
1-277: LGTM - Well-structured sidebar and list styling.The styling comprehensively covers the environment list sidebar, search functionality, item states (active, renaming, creating), inline actions, and error messages. All colors properly use theme props, and the layout follows a clean flex-based structure.
Based on learnings, styled components correctly define both wrapper and children styles with theme-based colors.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js (1)
12-181: LGTM - Well-implemented inline rename functionality.The inline rename implementation is solid with:
- Comprehensive validation covering required, length, regex, and duplicates
- Proper state management for rename flow
- Good UX patterns (Enter to save, Escape to cancel, blur handling)
- Correct use of
onMouseDownpreventDefault to prevent blur on button clicks- Toast notifications for user feedback
- Clean conditional rendering between rename and display modes
packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js (2)
9-22: The.shared-buttonclass is already defined inStyledWrapper.js(line 35) with complete styling. No issues found.Likely an incorrect or invalid review comment.
24-24: TheonCloseprop removal is correctly handled. The component now accepts onlycollection, and the single parent usage inRequestTabPanelproperly reflects this change without passingonClose.Likely an incorrect or invalid review comment.
| await page.locator('button[title="Delete"]').first().click(); | ||
| const confirmButton = page.getByRole('button', { name: 'Delete' }); | ||
| if (await confirmButton.isVisible()) { | ||
| await confirmButton.click(); | ||
| await page.getByText('×').click(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Generic delete button selector may be brittle.
The selector button[title="Delete"] could match multiple delete buttons on the page. Consider scoping it to the environment list or using a more specific selector.
- await page.locator('button[title="Delete"]').first().click();
+ const envList = page.locator('.environment-list'); // or appropriate container
+ await envList.locator('button[title="Delete"]').first().click();Or verify that only one such button exists before clicking:
const deleteButtons = page.locator('button[title="Delete"]');
await expect(deleteButtons).toHaveCount(1);
await deleteButtons.click();🤖 Prompt for AI Agents
In
tests/environments/import-environment/bruno-env-import/global-env-import/global-env-import.spec.ts
around lines 26 to 31, the test uses a generic selector button[title="Delete"]
which may match multiple delete buttons and be brittle; update the test to scope
the selector to the specific environment list or container (e.g., locate the
environment row/card first and then find the Delete button within it) or
validate uniqueness before clicking by querying the locator count and asserting
it equals 1, then perform the click on that scoped/single locator so the test
targets the intended delete button reliably.
| await page.locator('button[title="Delete"]').first().click(); | ||
| const confirmButton = page.getByText('Delete', { exact: true }); | ||
| if (await confirmButton.isVisible()) { | ||
| await confirmButton.click(); | ||
| await page.getByText('×').click(); | ||
| } |
There was a problem hiding this comment.
Same delete button selector concern.
This has the same potential brittleness as the earlier delete button usage. Consider applying the same scoping suggestion.
bijin-bruno
left a comment
There was a problem hiding this comment.
- Unable to scroll and view save button when we have too many variables and the table size is more than view height
...nts/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/constants.js
Show resolved
Hide resolved
There was a problem hiding this comment.
What is the alternative for this info display?
There was a problem hiding this comment.
I think we can keep this file tp components/Environments/ConfirmCloseEnvironment/index.js
packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js
Outdated
Show resolved
Hide resolved
tests/environments/import-environment/global-env-import.spec.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
tests/global-environments/non-string-values.spec.ts (1)
75-75: Remove unnecessary.nth(0)now that selectors are scoped to rows.Since the selectors are now scoped to
numericRowandbooleanRowrespectively, the.nth(0)is redundant. Each row should only contain one disabled-info-icon, making the index unnecessary and improving readability.Apply this refactor:
- const infoIcon = numericRow.locator('[id$="-disabled-info-icon"]').nth(0); + const infoIcon = numericRow.locator('[id$="-disabled-info-icon"]');And for the boolean variable:
- const infoIcon = booleanRow.locator('[id$="-disabled-info-icon"]').nth(0); + const infoIcon = booleanRow.locator('[id$="-disabled-info-icon"]');Also applies to: 102-102
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (2)
251-266: Missing feedback when draft is incomplete (flagged previously).As noted in the earlier review, if
draft?.environmentUidordraft?.variablesis missing, the save handler does nothing and the modal remains open without user feedback.Add an else branch to handle incomplete drafts:
onSaveAndClose={() => { const draft = collection.environmentsDraft; if (draft?.environmentUid && draft?.variables) { dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid)) .then(() => { dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); dispatch(closeTabs({ tabUids: [tab.uid] })); setShowConfirmEnvironmentClose(false); toast.success('Environment saved'); }) .catch((err) => { console.log('err', err); toast.error('Failed to save environment'); }); + } else { + toast.error('Incomplete environment draft — cannot save'); + setShowConfirmEnvironmentClose(false); } }}
278-294: Missing feedback when global draft is incomplete (flagged previously).As noted in the earlier review, if the global environment draft is missing required fields, the save handler silently does nothing and the modal remains open.
Add an else branch to mirror the collection environment handling:
onSaveAndClose={() => { const draft = globalEnvironmentDraft; if (draft?.environmentUid && draft?.variables) { dispatch(saveGlobalEnvironment({ variables: draft.variables, environmentUid: draft.environmentUid })) .then(() => { dispatch(clearGlobalEnvironmentDraft()); dispatch(closeTabs({ tabUids: [tab.uid] })); setShowConfirmGlobalEnvironmentClose(false); toast.success('Global environment saved'); }) .catch((err) => { console.log('err', err); toast.error('Failed to save global environment'); }); + } else { + toast.error('Incomplete global environment draft — cannot save'); + setShowConfirmGlobalEnvironmentClose(false); } }}
🧹 Nitpick comments (12)
packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js (1)
5-5: Consider adding JSDoc for component documentation.A brief JSDoc comment would help other developers understand the component's purpose and usage, especially in the context of the tab-based environment flow.
As per coding guidelines, add JSDoc to provide more details:
+/** + * Confirmation modal displayed when attempting to close an environment tab with unsaved changes. + * Provides options to save, discard, or cancel the close action. + * + * @param {Function} onCancel - Called when user clicks Cancel + * @param {Function} onCloseWithoutSave - Called when user clicks Don't Save + * @param {Function} onSaveAndClose - Called when user clicks Save + * @param {boolean} isGlobal - Whether this is for global or collection environment + */ const ConfirmCloseEnvironment = ({ onCancel, onCloseWithoutSave, onSaveAndClose, isGlobal }) => {tests/global-environments/non-string-values.spec.ts (1)
26-26: Consider extracting the repeated envTab locator.The locator pattern
.request-tabwith filter{ hasText: 'Global Environments' }appears three times in the test. Extracting it to a variable at the test scope would improve maintainability and reduce duplication.Example refactor:
test('should seed non-string globals via request and verify read-only + tooltip', async ({ pageWithUserData: page }) => { + const getEnvTab = () => page.locator('.request-tab').filter({ hasText: 'Global Environments' }); + await openCollectionAndAcceptSandbox(page, 'global-env-non-string', 'safe'); await test.step('Create a new global environment with a string variable', async () => { // ... - const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' }); + const envTab = getEnvTab();Also applies to: 53-53, 125-125
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js (1)
276-276: Avoid fragile color string concatenation.Appending
15to the color value assumes it's in hex format. If the theme ever usesrgb(),hsl(), or other formats, this will break.Consider using a CSS color-mix or rgba transformation helper from your theme/utils, or apply opacity via a separate
opacityproperty if the theme supports it.packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (1)
262-262: Consider structured error logging.Console.log is used for error handling in the catch blocks. While this follows the existing pattern in the file (lines 209, 237, 407), consider using a structured logging service for better observability in production.
Also applies to: 289-289
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (5)
21-21: Remove unusedoriginalEnvironmentVariablesprop.This parameter is destructured but never used in the component. Remove it to keep the API clean.
-const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables, collection }) => { +const EnvironmentVariables = ({ environment, setIsModified, collection }) => {
61-77: Yup$isLastRowcontext is never provided — this validation logic is dead code.The
when('$isLastRow', ...)requires a validation context to be passed (e.g., viavalidateAtorvalidatewith{ context: { isLastRow } }). Since formik doesn't receive this context, the conditional always falls through tootherwise. The customvalidatefunction at lines 78-96 handles this logic correctly, making this Yup schema redundant.Simplify by removing the non-functional conditional:
validationSchema: Yup.array().of(Yup.object({ enabled: Yup.boolean(), - name: Yup.string() - .when('$isLastRow', { - is: true, - then: (schema) => schema.optional(), - otherwise: (schema) => schema - .required('Name cannot be empty') - .matches(variableNameRegex, - 'Name contains invalid characters. Must only contain alphanumeric characters, "-", "_", "." and cannot start with a digit.') - .trim() - }), + name: Yup.string(), secret: Yup.boolean(), type: Yup.string(), uid: Yup.string(), value: Yup.mixed().nullable() })),
101-122: Missingformik.setValuesin effect dependency array.The effect calls
formik.setValuesbut doesn't include it in dependencies. While formik'ssetValuesis typically stable, the linter will flag this. Consider suppressing with a comment or using a ref pattern for clarity:+ const setValuesRef = useRef(formik.setValues); + setValuesRef.current = formik.setValues; React.useEffect(() => { // ... if ((isMount || envChanged) && hasDraftForThisEnv && globalEnvironmentDraft?.variables) { - formik.setValues([ + setValuesRef.current([ ...globalEnvironmentDraft.variables, // ... ]); } - }, [environment.uid, hasDraftForThisEnv, globalEnvironmentDraft?.variables]); + }, [environment.uid, hasDraftForThisEnv, globalEnvironmentDraft?.variables]);Alternatively, add an eslint-disable comment if the current behavior is intentional.
225-227:setTimeoutwith stale closure risk.
formik.values.lengthis captured when setTimeout is scheduled. If rapid inputs occur, the index could be stale. Consider using a callback pattern or formik'sarrayHelpersinstead:- setTimeout(() => { - formik.setFieldValue(formik.values.length, newVariable, false); - }, 0); + const currentLength = formik.values.length; + setTimeout(() => { + formik.setFieldValue(currentLength, newVariable, false); + }, 0);This makes the capture explicit, though the underlying race condition remains. If dynamic arrays become more complex, consider
FieldArray.
152-173: Inline component recreated on every render.
ErrorMessageis defined inside the component body, causing React to treat it as a new component type on each render. For small tables this is fine, but consider extracting it outside the component or memoizing if performance becomes a concern.packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
33-38: Consider memoizing the collection cloning.The collection is cloned on every render, which could be expensive for large collections. Since this depends on
collectionandglobalEnvironmentVariables, wrap this inuseMemo.- let _collection = collection ? cloneDeep(collection) : {}; - - const globalEnvironmentVariables = getGlobalEnvironmentVariables({ globalEnvironments, activeGlobalEnvironmentUid }); - if (_collection) { - _collection.globalEnvironmentVariables = globalEnvironmentVariables; - } + const globalEnvironmentVariables = getGlobalEnvironmentVariables({ globalEnvironments, activeGlobalEnvironmentUid }); + + const _collection = useMemo(() => { + const cloned = collection ? cloneDeep(collection) : {}; + if (cloned) { + cloned.globalEnvironmentVariables = globalEnvironmentVariables; + } + return cloned; + }, [collection, globalEnvironmentVariables]);
279-281: Consider if setTimeout is necessary.The
setTimeout(0)is used to defer the new row addition, likely to avoid state update conflicts. If you encounter issues, consider usingformik.setValueswith a callback or ensure this pattern is actually needed.
296-342: LGTM - Consider extracting validation logic.The save flow is correct and handles errors appropriately. For maintainability, you could extract the validation logic (lines 306-314) into a separate helper function, but this is optional.
Example:
const validateVariables = (variables) => { return variables.some((variable) => { if (!variable.name || variable.name.trim() === '') return true; if (!variableNameRegex.test(variable.name)) return true; return false; }); }; const handleSave = () => { // ... filtering logic ... if (validateVariables(variablesToSave)) { toast.error('Please fix validation errors before saving'); return; } // ... rest of save logic ... };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(4 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/RequestTabPanel/index.js(3 hunks)packages/bruno-app/src/components/RequestTabs/RequestTab/index.js(5 hunks)packages/bruno-app/src/components/RequestTabs/index.js(0 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(9 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.js(1 hunks)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js(3 hunks)tests/environments/import-environment/global-env-import.spec.ts(1 hunks)tests/global-environments/non-string-values.spec.ts(7 hunks)
💤 Files with no reviewable changes (1)
- packages/bruno-app/src/components/RequestTabs/index.js
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js
- tests/environments/import-environment/global-env-import.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()notfunc ()
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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.jspackages/bruno-app/src/components/RequestTabPanel/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jstests/global-environments/non-string-values.spec.tspackages/bruno-app/src/components/RequestTabs/RequestTab/index.js
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 existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture 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/global-environments/non-string-values.spec.ts
🧠 Learnings (5)
📚 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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js
📚 Learning: 2025-12-02T20:48:05.670Z
Learnt from: NikHillAgar
Repo: usebruno/bruno PR: 6279
File: packages/bruno-app/src/components/RequestPane/PromptVariables/PromptVariablesModal/index.js:79-86
Timestamp: 2025-12-02T20:48:05.670Z
Learning: In React 19+, ref callbacks support returning cleanup functions. When a ref callback returns a function, React will call it when the element unmounts or the ref changes, similar to useEffect cleanup. This is documented at https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback
Applied to files:
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.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 **/*.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/global-environments/non-string-values.spec.ts
🧬 Code graph analysis (2)
packages/bruno-app/src/components/RequestTabPanel/index.js (4)
packages/bruno-app/src/components/RequestTabs/index.js (1)
activeTabUid(23-23)packages/bruno-app/src/hooks/useTabPaneBoundaries/index.js (1)
focusedTab(11-11)packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.js (1)
GlobalEnvironmentSettings(4-6)packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js (1)
EnvironmentSettings(24-67)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (6)
packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js (1)
tab(27-27)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (2)
useSelector(24-24)dispatch(22-22)packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js (1)
ConfirmCloseEnvironment(5-41)packages/bruno-app/src/components/RequestTabs/index.js (1)
dispatch(16-16)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js (2)
saveGlobalEnvironment(186-220)saveGlobalEnvironment(186-220)packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js (1)
SpecialTab(5-86)
🪛 Biome (2.1.2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
[error] 113-113: Do not add then to an object.
(lint/suspicious/noThenProperty)
🔇 Additional comments (25)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/StyledWrapper.js (1)
6-6: LGTM! Overflow constraints align with tab-based layout.The
overflow: hiddenadditions on both the wrapper and container properly constrain the layout boundaries for the tab-based environment UI. The nested.environments-listretainsoverflow-y: auto(line 106) for internal scrolling, which is the correct hierarchy.Also applies to: 14-14
packages/bruno-app/src/components/Environments/EnvironmentSettings/StyledWrapper.js (3)
1-2: LGTM!Standard styled-components import is correct.
4-34: LGTM!Container and empty-state styling is well-structured. Proper use of theme props for all colors, clean flexbox layout for centering, and good visual hierarchy with SVG opacity.
36-50: LGTM!Shared button styling is clean and consistent. Proper theme prop usage, smooth hover transitions, and appropriate cursor behavior.
packages/bruno-app/src/components/RequestTabPanel/index.js (4)
37-38: LGTM!Imports correctly reference the new environment settings components from the
Environmentsfolder.
150-156: LGTM!Good placement of the global-environment-settings check before the
collectionUidvalidation. SinceGlobalEnvironmentSettingsdoesn't require a collection context, this ordering avoids unnecessary guards and correctly short-circuits to the component.Past review concern addressed:
collectionprop is no longer passed.
158-160: LGTM!Guard correctly positioned after global-environment-settings (which doesn't require
collectionUid) and before collection-dependent tab types.
209-211: LGTM!Correctly placed after the collection existence guard (line 162). The
collectionprop is properly passed as required by theEnvironmentSettingscomponent. Follows the established pattern used by other collection-dependent tabs likeCollectionSettingsandSecuritySettings.packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js (1)
1-3: Imports look good.Clean and minimal imports. All used in the component.
tests/global-environments/non-string-values.spec.ts (1)
1-130: LGTM! Well-structured test with good practices.The test follows Playwright best practices effectively:
- Excellent use of
test.stepfor clear test flow- Multiple assertions throughout
- No usage of
test.only,page.pause(), or unnecessarypage.waitForTimeout()- Good use of locator variables
- Successfully migrated from modal-based to tab-based UI interactions
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/StyledWrapper.js (1)
7-7: No changes needed; scrolling is properly handled by child components.The child components (EnvironmentList, EnvironmentDetails) implement their own scroll containers with
overflow-y: auto, so theoverflow: hiddenon the main wrapper is the correct pattern for this flex-based layout.Likely an incorrect or invalid review comment.
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (6)
4-7: LGTM: Imports correctly support environment tab functionality.The new imports are properly structured and necessary for the environment-settings and global-environment-settings tab features.
Also applies to: 9-9, 16-16, 27-27
38-39: LGTM: State declarations follow existing patterns.The confirmation dialog state variables are consistent with the established naming convention for collection and folder confirmations.
157-159: LGTM: Draft detection logic is consistent.The environment draft flags mirror the existing collection and folder draft detection patterns, and the useSelector usage follows Redux best practices.
161-179: LGTM: Close handlers properly guard against unnecessary confirmations.Both handlers correctly check for draft existence before showing the confirmation dialog, matching the established pattern for collection and folder settings.
181-181: LGTM: Tab type list updated correctly.The new environment-related tab types are properly included in the special tab routing logic.
302-305: LGTM: Environment tab rendering is properly integrated.The SpecialTab rendering for both environment types correctly passes the appropriate close handlers and draft flags, following the established pattern for other special tab types.
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (5)
124-150: Good optimization to avoid redundant draft dispatches.The comparison against
existingDraftJsonbefore dispatching prevents unnecessary Redux updates. The logic correctly handles both setting and clearing draft state.
175-210: LGTM.The callback correctly handles edge cases: prevents removing the trailing empty row and ensures one always exists after removal.
307-320: Well-implemented event listener with ref pattern.Using
handleSaveRefto avoid recreating the listener while still accessing the latesthandleSaveis the correct approach. Cleanup is properly handled.
322-438: LGTM.Clean table rendering with proper conditional logic for the trailing empty row. Event handlers and props are correctly wired.
242-288: LGTM.Save logic correctly validates before dispatch, handles success/error states, and resets form with new values including the trailing empty row.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
153-174: Verify draft restoration doesn't cause unnecessary re-renders.The draft restoration effect depends on
environmentsDraft?.variables, which changes when the sync effect (lines 177-203) dispatchessetEnvironmentsDraft. Ensure this doesn't cause cascading effects or unnecessary restorations, especially during rapid typing.The guards (
isMount,envChanged) should prevent most issues, but monitor for edge cases.
361-374: LGTM - Good ref pattern for event handlers.Using a ref to hold the latest
handleSaveensures the event listener always invokes the current version without needing to re-register the listener. The cleanup is properly handled.
437-450: LGTM - Good UX for read-only non-string values.The
readOnlyprop prevents editing of non-string values (set via scripts), and the info icon with tooltip clearly communicates this to users. Well done.
packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js
Show resolved
Hide resolved
...onments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
Outdated
Show resolved
Hide resolved
...s/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
Show resolved
Hide resolved
| .rename-container { | ||
| display: flex; | ||
| align-items: center; | ||
| flex: 1; | ||
|
|
||
| .environment-name-input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: ${(props) => props.theme.text}; | ||
| font-size: 13px; | ||
| padding: 2px 4px; | ||
|
|
||
| &::placeholder { | ||
| color: ${(props) => props.theme.colors.text.muted}; | ||
| } | ||
| } | ||
|
|
||
| .inline-actions { | ||
| display: flex; | ||
| gap: 2px; | ||
| margin-left: 4px; | ||
| } | ||
| } | ||
|
|
||
| &.creating { | ||
| .environment-name-input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: ${(props) => props.theme.text}; | ||
| font-size: 13px; | ||
| padding: 2px 4px; | ||
|
|
||
| &::placeholder { | ||
| color: ${(props) => props.theme.colors.text.muted}; | ||
| } | ||
| } | ||
|
|
||
| .inline-actions { | ||
| display: flex; | ||
| gap: 2px; | ||
| margin-left: 4px; | ||
| } | ||
|
|
||
| .inline-action-btn { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| width: 22px; | ||
| height: 22px; | ||
| padding: 0; | ||
| background: transparent; | ||
| border: none; | ||
| border-radius: 4px; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
|
|
||
| &.save { | ||
| color: ${(props) => props.theme.textLink}; | ||
|
|
||
| &:hover { | ||
| background: ${(props) => props.theme.listItem.hoverBg}; | ||
| } | ||
| } | ||
|
|
||
| &.cancel { | ||
| color: ${(props) => props.theme.colors.text.muted}; | ||
|
|
||
| span:hover { | ||
| text-decoration: underline; | ||
| &:hover { | ||
| background: ${(props) => props.theme.listItem.hoverBg}; | ||
| color: ${(props) => props.theme.text}; | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Eliminate duplicate styles for rename and create modes.
The .environment-name-input (lines 196-208 vs 218-230) and .inline-actions (lines 210-214 vs 232-236) are duplicated between .renaming and .creating contexts. Additionally, .inline-action-btn (lines 238-267) is only defined inside .creating but likely needed for both modes.
Apply this diff to consolidate the shared styles:
&.renaming,
&.creating {
cursor: default;
padding: 4px 4px 4px 8px;
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
&:hover {
background: ${(props) => props.theme.workspace.button.bg};
}
- }
-
- .rename-container {
- display: flex;
- align-items: center;
- flex: 1;
.environment-name-input {
flex: 1;
background: transparent;
border: none;
outline: none;
color: ${(props) => props.theme.text};
font-size: 13px;
padding: 2px 4px;
&::placeholder {
color: ${(props) => props.theme.colors.text.muted};
}
}
.inline-actions {
display: flex;
gap: 2px;
margin-left: 4px;
}
- }
-
- &.creating {
- .environment-name-input {
- flex: 1;
- background: transparent;
- border: none;
- outline: none;
- color: ${(props) => props.theme.text};
- font-size: 13px;
- padding: 2px 4px;
-
- &::placeholder {
- color: ${(props) => props.theme.colors.text.muted};
- }
- }
-
- .inline-actions {
- display: flex;
- gap: 2px;
- margin-left: 4px;
- }
.inline-action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
&.save {
color: ${(props) => props.theme.textLink};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
}
}
&.cancel {
color: ${(props) => props.theme.colors.text.muted};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
color: ${(props) => props.theme.text};
}
}
}
+ }
+
+ .rename-container {
+ display: flex;
+ align-items: center;
+ flex: 1;
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
lines 191-268: consolidate duplicated styles by extracting the shared
.environment-name-input and .inline-actions rules from inside both the
.rename-container and the &.creating blocks into a single shared selector (e.g.,
define .environment-name-input and .inline-actions once at the parent scope used
by both states), remove the duplicated blocks, and move the .inline-action-btn
rules currently only inside &.creating into the shared scope so both renaming
and creating modes use the same button styles; keep all theme prop references
unchanged and ensure specificity/ordering remains the same so hover and modifier
classes still apply.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (2)
255-270: Missing feedback when draft is incomplete.The
onSaveAndClosehandler only executes whendraft?.environmentUid && draft?.variablesare truthy. If either is falsy, clicking "Save" does nothing and the modal stays open without user feedback.🔎 Suggested fix
onSaveAndClose={() => { const draft = collection.environmentsDraft; if (draft?.environmentUid && draft?.variables) { dispatch(saveEnvironment(draft.variables, draft.environmentUid, collection.uid)) .then(() => { dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); dispatch(closeTabs({ tabUids: [tab.uid] })); setShowConfirmEnvironmentClose(false); toast.success('Environment saved'); }) .catch((err) => { console.log('err', err); toast.error('Failed to save environment'); }); + } else { + toast.error('No environment changes to save'); + setShowConfirmEnvironmentClose(false); } }}
282-298: Same issue: missing fallback for incomplete global environment draft.Mirror the same handling pattern as collection environments for consistency.
🔎 Suggested fix
onSaveAndClose={() => { const draft = globalEnvironmentDraft; if (draft?.environmentUid && draft?.variables) { dispatch(saveGlobalEnvironment({ variables: draft.variables, environmentUid: draft.environmentUid })) .then(() => { dispatch(clearGlobalEnvironmentDraft()); dispatch(closeTabs({ tabUids: [tab.uid] })); setShowConfirmGlobalEnvironmentClose(false); toast.success('Global environment saved'); }) .catch((err) => { console.log('err', err); toast.error('Failed to save global environment'); }); + } else { + toast.error('No global environment changes to save'); + setShowConfirmGlobalEnvironmentClose(false); } }}
🧹 Nitpick comments (1)
tests/utils/page/actions.ts (1)
399-410: Consider splitting verification logic from environment creation.The function now does more than just "create" - it also opens the selector, switches tabs, selects the environment, and verifies selection. While thorough, this verification adds latency to every test using
createEnvironment.Consider:
- Extracting lines 400-410 into a separate
verifyEnvironmentCreatedhelper- Or making the post-creation verification optional via a parameter
- This would allow tests that don't need full verification to run faster
Potential refactor approach
const createEnvironment = async ( page: Page, environmentName: string, - type: EnvironmentType = 'collection' + type: EnvironmentType = 'collection', + options: { verify?: boolean } = { verify: true } ) => { await test.step(`Create ${type} environment "${environmentName}"`, async () => { await openEnvironmentSelector(page, type); await page.locator('button[id="create-env"]').click(); const nameInput = type === 'collection' ? page.locator('input[name="name"]') : page.locator('#environment-name'); await expect(nameInput).toBeVisible(); await nameInput.fill(environmentName); await page.getByRole('button', { name: 'Create' }).click(); - const tabLabel = type === 'collection' ? 'Environments' : 'Global Environments'; - await expect(page.locator('.request-tab').filter({ hasText: tabLabel })).toBeVisible(); - - const locators = buildCommonLocators(page); - await locators.environment.selector().click(); - if (type === 'global') { - await locators.environment.globalTab().click(); - } - await locators.environment.envOption(environmentName).click(); - await expect(page.locator('.current-environment')).toContainText(environmentName); + if (options.verify) { + const tabLabel = type === 'collection' ? 'Environments' : 'Global Environments'; + await expect(page.locator('.request-tab').filter({ hasText: tabLabel })).toBeVisible(); + + const locators = buildCommonLocators(page); + await locators.environment.selector().click(); + if (type === 'global') { + await locators.environment.globalTab().click(); + } + await locators.environment.envOption(environmentName).click(); + await expect(page.locator('.current-environment')).toContainText(environmentName); + } }); };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
packages/bruno-app/src/components/RequestTabPanel/index.js(4 hunks)packages/bruno-app/src/components/RequestTabs/RequestTab/index.js(5 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.js(2 hunks)packages/bruno-app/src/components/WorkspaceHome/index.js(1 hunks)packages/bruno-app/src/providers/ReduxStore/slices/app.js(0 hunks)packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js(2 hunks)tests/request/newlines/newlines-persistence.spec.ts(1 hunks)tests/utils/page/actions.ts(2 hunks)tests/utils/page/locators.ts(1 hunks)
💤 Files with no reviewable changes (1)
- packages/bruno-app/src/providers/ReduxStore/slices/app.js
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/CreateEnvironment/index.js
- tests/request/newlines/newlines-persistence.spec.ts
- tests/utils/page/locators.ts
- packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
- packages/bruno-app/src/components/WorkspaceHome/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()notfunc ()
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/RequestTabs/RequestTab/index.jstests/utils/page/actions.tspackages/bruno-app/src/components/RequestTabPanel/index.js
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 existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture 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/actions.ts
🧠 Learnings (1)
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-app/src/components/RequestTabs/RequestTab/index.jspackages/bruno-app/src/components/RequestTabPanel/index.js
🧬 Code graph analysis (2)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (3)
packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js (1)
ConfirmCloseEnvironment(5-41)packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
saveEnvironment(1740-1778)saveEnvironment(1740-1778)packages/bruno-app/src/providers/ReduxStore/slices/global-environments.js (2)
saveGlobalEnvironment(186-220)saveGlobalEnvironment(186-220)
packages/bruno-app/src/components/RequestTabPanel/index.js (7)
packages/bruno-app/src/components/RequestTabs/index.js (1)
activeTabUid(23-23)packages/bruno-app/src/pages/Bruno/index.js (1)
activeTabUid(57-57)packages/bruno-app/src/providers/Hotkeys/index.js (1)
activeTabUid(27-27)packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js (2)
activeTabUid(42-42)focusedTab(56-56)packages/bruno-app/src/hooks/useTabPaneBoundaries/index.js (1)
focusedTab(11-11)packages/bruno-app/src/components/Environments/GlobalEnvironmentSettings/index.js (1)
GlobalEnvironmentSettings(4-6)packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js (1)
EnvironmentSettings(24-67)
⏰ 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 - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
🔇 Additional comments (13)
packages/bruno-app/src/components/RequestTabPanel/index.js (3)
37-38: LGTM!Imports are correctly added and both components are used in the render branches below.
174-184: LGTM!The conditional ordering is correct. Global environment tabs are handled before the
collectionUidvalidation since they don't require a collection context. This prevents false "error occurred" messages.
233-235: LGTM!Correctly placed after collection validation. The
collectionprop is appropriately passed toEnvironmentSettings.packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (6)
4-7: LGTM!Imports are correctly added for the new environment save/clear actions.
38-39: LGTM!State variables follow the established pattern for close confirmation modals.
161-163: LGTM!Draft detection follows the established pattern for collection and folder drafts.
165-183: LGTM!Close handlers follow the established pattern for collection and folder settings.
306-309: LGTM!SpecialTab branches for environment tabs follow the established pattern and correctly pass the
hasDraftprop.
185-185: LGTM!Tab type array correctly extended to include the new environment tab types.
tests/utils/page/actions.ts (4)
353-353: LGTM - Clean type definition.The
EnvironmentTypeunion type is well-defined and properly exported for use across test files.
393-398: LGTM - Conditional locator selection handles both environment types.The conditional logic correctly selects the appropriate input locator based on environment type (
input[name="name"]for collection,#environment-namefor global).
482-489: LGTM - Tab-based closing aligns with modal-to-tabs migration.The implementation correctly:
- Determines the tab label based on environment type
- Locates the tab using text filter
- Hovers before clicking (ensures close icon visibility)
- Uses testId for the close action
This change properly supports the PR's shift from modal dialogs to tab-based navigation.
756-756: LGTM - Proper type export for test reusability.Exporting
EnvironmentTypeallows test files to use type-safe parameters when calling these utility functions.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (4)
107-151: Redundant validation: schema's$isLastRowcontext is never passed.The
validationSchemauseswhen('$isLastRow', ...)but Formik isn't configured withvalidationContextto provide this value. The schema validation will always use theotherwisebranch. Since the customvalidatefunction (lines 129-149) correctly handles the last-row logic, the schema's conditional is effectively dead code.Consider simplifying by removing the
whenclause from the schema and relying solely on the customvalidatefunction, or remove the custom validate and properly pass context to the schema.🔎 Simplified schema (relying on custom validate):
validationSchema: Yup.array().of( Yup.object({ enabled: Yup.boolean(), - name: Yup.string() - .when('$isLastRow', { - is: true, - then: (schema) => schema.optional(), - otherwise: (schema) => - schema - .required('Name cannot be empty') - .matches( - variableNameRegex, - 'Name contains invalid characters. Must only contain alphanumeric characters, "-", "_", "." and cannot start with a digit.' - ) - .trim() - }), + name: Yup.string().trim(), secret: Yup.boolean(), type: Yup.string(), uid: Yup.string(), value: Yup.mixed().nullable() }) ),Note: The static analysis hint about
thenon line 113 is a false positive—Yup's.when()usesthenas a configuration property, not a Promise method.
214-235: Consider extractingErrorMessageoutside the component.This inner component is recreated on every render. While the impact is minimal for this simple presentational component, extracting it outside and passing
formikas a prop would be cleaner and slightly more performant.
275-292: Potential stale closure insetTimeout.Line 289 captures
formik.values.lengthat call time, but thesetTimeoutexecutes asynchronously. If the user types rapidly, the captured length might be stale. In practice,setFieldValuewith an out-of-bounds index appends correctly, so this works, but using a functional update pattern would be clearer.🔎 Alternative using current values:
if (isLastRow) { const newVariable = { uid: uuid(), name: '', value: '', type: 'text', secret: false, enabled: true }; setTimeout(() => { - formik.setFieldValue(formik.values.length, newVariable, false); + formik.setValues((currentValues) => [...currentValues, newVariable]); }, 0); }
305-351: Consider usingformik.validateForm()instead of duplicating validation.Lines 315-327 duplicate the validation logic that already exists in the custom
validatefunction. You could simplify by callingformik.validateForm()and checking if errors exist:const errors = await formik.validateForm(); if (Object.keys(errors).length > 0) { toast.error('Please fix validation errors before saving'); return; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(4 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.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. 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()notfunc ()
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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
🧠 Learnings (4)
📚 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/Environments/EnvironmentSettings/EnvironmentList/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/Environments/EnvironmentSettings/EnvironmentList/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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.jspackages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
🧬 Code graph analysis (1)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js (25)
collection(113-113)collection(148-148)collection(154-154)collection(162-162)collection(168-168)collection(175-175)collection(182-182)collection(214-214)collection(223-223)environment(243-243)environment(255-255)environment(268-268)vars(2185-2185)vars(2194-2194)vars(2214-2214)vars(2224-2224)vars(2245-2245)vars(2249-2249)vars(2416-2416)vars(2425-2425)vars(2447-2447)vars(2457-2457)vars(2479-2479)vars(2483-2483)window(2751-2751)packages/bruno-app/src/utils/common/regex.js (2)
variableNameRegex(7-7)variableNameRegex(7-7)packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
saveEnvironment(1740-1778)saveEnvironment(1740-1778)
🪛 Biome (2.1.2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
[error] 113-113: Do not add then to an object.
(lint/suspicious/noThenProperty)
⏰ 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: SSL Tests - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
🔇 Additional comments (14)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (7)
1-20: LGTM!Imports are well-organized and appropriate for the draft-based editing functionality being introduced.
21-38: LGTM!Component setup with draft tracking refs and collection cloning is solid. The
hasDraftForThisEnvcheck correctly identifies when a draft exists for the current environment.
40-86: LGTM!The memoized sensitive field detection is efficient and correctly identifies non-secret variables used in sensitive contexts. Good security practice.
187-212: Good improvement: debounced draft sync.The 300ms debounce with proper cleanup addresses the previous performance concern about running on every keystroke. The additional check comparing
currentValuesJson !== existingDraftJsonbefore dispatching is a nice optimization to prevent redundant Redux updates.
370-383: LGTM!The
handleSaveRefpattern correctly maintains a stable reference to the latesthandleSavewhile avoiding stale closures in the window event listener. Proper cleanup on unmount.
399-487: LGTM!The table rendering correctly handles the trailing empty row pattern—hiding checkboxes and delete buttons for the placeholder row while maintaining a clean UX. The read-only treatment for non-string values with an explanatory tooltip is a nice touch.
492-501: LGTM!Clean button layout with appropriate test IDs for automation. The inline Save/Reset pattern fits well with the tab-based editing paradigm.
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js (7)
3-9: LGTM!The wrapper structure correctly establishes a full-height flex container with relative positioning for the overlay pattern. Theme usage follows project standards.
10-26: LGTM!The container and overlay structure is well-designed. The confirm-switch-overlay uses proper absolute positioning with z-index layering, and the border declaration is complete and correct.
28-68: LGTM!The sidebar layout and header structure are well-organized with appropriate fixed width and flexbox behavior. Hover states and transitions are properly implemented using theme colors.
70-101: LGTM!The search container implementation is correct with proper icon positioning and input styling. The border property has the complete declaration.
103-107: LGTM!The environments-list container is properly configured for scrollable content with flex growth and vertical overflow handling.
109-179: LGTM!The environment item base styling and action controls are well-implemented. The opacity-based show/hide pattern for actions on hover and the active state styling are appropriate.
249-256: LGTM!The error message styling uses appropriate danger colors with a semi-transparent background for visual distinction. The compact sizing fits well within the list context.
...s/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/bruno-app/src/components/RequestTabPanel/index.js (1)
174-180: LGTM: Correct ordering for workspace-level global environment settings.The enhanced guard at line 174 is good defensive programming. The early return for
global-environment-settings(lines 178-180) correctly bypasses collection validation since global environments are workspace-level, not collection-scoped.This intentional ordering distinguishes global settings from collection-scoped
environment-settings(line 233), which properly validates collection existence first.Optional: Add explanatory comment
Consider adding a brief comment before line 178 to document the ordering rationale:
+ // Global environment settings are workspace-level, no collection validation needed if (focusedTab.type === 'global-environment-settings') { return <GlobalEnvironmentSettings />; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-app/src/components/RequestTabPanel/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()notfunc ()
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/RequestTabPanel/index.js
🧠 Learnings (1)
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-app/src/components/RequestTabPanel/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). (5)
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
🔇 Additional comments (3)
packages/bruno-app/src/components/RequestTabPanel/index.js (3)
37-38: LGTM: Clean imports for new environment tab types.The imports follow the project conventions and are required for the new tab rendering paths below.
182-184: LGTM: Enhanced guard properly validates tab properties.Checking both
focusedTab.uidandfocusedTab.collectionUidprevents downstream errors. Correctly placed after the global-environment-settings path (which doesn't require collection context) and before collection-specific logic.
233-235: LGTM: Environment settings correctly placed and scoped.The render path for
environment-settingsis properly positioned after collection validation (lines 186-188) since these settings are collection-scoped. Passing thecollectionprop is appropriate and consistent with the component's requirements.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js (1)
181-269: Critical: Fix broken CSS nesting and eliminate duplication.There are two critical structural issues here:
Broken nesting: The
&.renaming, &.creatingblock closes at line 190, but.rename-container(line 192) and the second&.creatingblock (line 218) are siblings at the.environment-itemlevel. This breaks scoping—.rename-containerwill apply regardless of state, and you have two separate&.creatingblocks.Massive duplication:
.environment-name-inputand.inline-actionsare defined identically in both.rename-container(lines 197-215) and the second&.creatingblock (lines 219-237). Additionally,.inline-action-btn(lines 239-268) only exists in&.creatingbut is likely needed for rename actions too.🔎 Apply this diff to fix nesting and consolidate shared styles:
&.renaming, &.creating { cursor: default; padding: 4px 4px 4px 8px; background: ${(props) => props.theme.sidebar.collection.item.hoverBg}; &:hover { background: ${(props) => props.theme.workspace.button.bg}; } - } - - .rename-container { - display: flex; - align-items: center; - flex: 1; .environment-name-input { flex: 1; background: transparent; border: none; outline: none; color: ${(props) => props.theme.text}; font-size: 13px; padding: 2px 4px; &::placeholder { color: ${(props) => props.theme.colors.text.muted}; } } .inline-actions { display: flex; gap: 2px; margin-left: 4px; } - } - - &.creating { - .environment-name-input { - flex: 1; - background: transparent; - border: none; - outline: none; - color: ${(props) => props.theme.text}; - font-size: 13px; - padding: 2px 4px; - - &::placeholder { - color: ${(props) => props.theme.colors.text.muted}; - } - } - - .inline-actions { - display: flex; - gap: 2px; - margin-left: 4px; - } .inline-action-btn { display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; background: transparent; border: none; border-radius: 4px; cursor: pointer; transition: all 0.15s ease; &.save { color: ${(props) => props.theme.textLink}; &:hover { background: ${(props) => props.theme.listItem.hoverBg}; } } &.cancel { color: ${(props) => props.theme.colors.text.muted}; &:hover { background: ${(props) => props.theme.listItem.hoverBg}; color: ${(props) => props.theme.text}; } } } + } + + .rename-container { + display: flex; + align-items: center; + flex: 1; } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.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. 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()notfunc ()
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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
🧠 Learnings (4)
📚 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/Environments/EnvironmentSettings/EnvironmentList/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/Environments/EnvironmentSettings/EnvironmentList/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/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.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: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Windows
🔇 Additional comments (3)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js (3)
4-26: LGTM on root layout structure.The flex-based layout with absolute overlay positioning is well-structured. Theme properties are used correctly for colors and borders.
110-180: LGTM on environment item base styles.The item layout, text overflow handling, and action visibility states are well-implemented. Theme properties and transitions are used appropriately.
272-279: LGTM on error message styling.The error state styling appropriately uses theme danger colors with semi-transparent background. The compact size and border radius are suitable for inline error display.
| position: absolute; | ||
| left: 20px; | ||
| top: 50%; | ||
| transform: translateY(-100%); | ||
| color: ${(props) => props.theme.colors.text.muted}; | ||
| pointer-events: none; | ||
| } |
There was a problem hiding this comment.
Fix search icon vertical positioning.
Line 79 uses translateY(-100%) which moves the icon above the input field rather than centering it. For proper vertical centering with top: 50%, use translateY(-50%).
🔎 Apply this diff:
.search-icon {
position: absolute;
left: 20px;
top: 50%;
- transform: translateY(-100%);
+ transform: translateY(-50%);
color: ${(props) => props.theme.colors.text.muted};
pointer-events: none;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| position: absolute; | |
| left: 20px; | |
| top: 50%; | |
| transform: translateY(-100%); | |
| color: ${(props) => props.theme.colors.text.muted}; | |
| pointer-events: none; | |
| } | |
| position: absolute; | |
| left: 20px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| color: ${(props) => props.theme.colors.text.muted}; | |
| pointer-events: none; | |
| } |
🤖 Prompt for AI Agents
In
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/StyledWrapper.js
around lines 76 to 82, the search icon is vertically misaligned because
translateY(-100%) is used with top: 50%; change the transform to
translateY(-50%) so the icon is vertically centered relative to the input;
update that single property and run the UI to verify the icon centers correctly.
|
|
||
| const ConfirmCloseEnvironment = ({ onCancel, onCloseWithoutSave, onSaveAndClose, isGlobal }) => { | ||
| return ( | ||
| <Modal |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (5)
104-151: Consider removing redundant validation logic.Both the Yup
validationSchema(lines 107-128) and the customvalidatefunction (lines 129-149) enforce the same name validation rules. This duplication creates maintenance overhead and risks inconsistencies if one is updated without the other.🔎 Simplify by relying solely on Yup schema
The Yup schema already handles conditional validation based on
isLastRow. Consider removing the customvalidatefunction and using only the schema, or consolidate the logic if the custom function provides additional checks not shown in the schema.const formik = useFormik({ enableReinitialize: true, initialValues: initialValues, validationSchema: Yup.array().of( Yup.object({ enabled: Yup.boolean(), name: Yup.string() .when('$isLastRow', { is: true, then: (schema) => schema.optional(), otherwise: (schema) => schema .required('Name cannot be empty') .matches( variableNameRegex, 'Name contains invalid characters. Must only contain alphanumeric characters, "-", "_", "." and cannot start with a digit.' ) .trim() }), secret: Yup.boolean(), type: Yup.string(), uid: Yup.string(), value: Yup.mixed().nullable() }) ), - validate: (values) => { - const errors = {}; - values.forEach((variable, index) => { - const isLastRow = index === values.length - 1; - const isEmptyRow = !variable.name || variable.name.trim() === ''; - - if (isLastRow && isEmptyRow) { - return; - } - - if (!variable.name || variable.name.trim() === '') { - if (!errors[index]) errors[index] = {}; - errors[index].name = 'Name cannot be empty'; - } else if (!variableNameRegex.test(variable.name)) { - if (!errors[index]) errors[index] = {}; - errors[index].name - = 'Name contains invalid characters. Must only contain alphanumeric characters, "-", "_", "." and cannot start with a digit.'; - } - }); - return Object.keys(errors).length > 0 ? errors : {}; - }, onSubmit: () => {} });Note: If the custom
validatefunction is needed for runtime-specific checks, document why both are necessary.
176-212: Consider consolidating the two similar effects.Two effects process
formik.valueswith similar filtering and JSON comparison logic. The first (lines 180-185) updatesisModifiedimmediately, while the second (lines 187-212) debounces draft synchronization. While functional, this creates code duplication and unnecessary re-computation.🔎 Potential consolidation approach
Consider combining into a single debounced effect that updates both
isModified(via a state update outside the timeout) and draft persistence (inside the timeout):useEffect(() => { const currentValues = formik.values.filter((variable) => variable.name && variable.name.trim() !== ''); const currentValuesJson = JSON.stringify(currentValues); const hasActualChanges = currentValuesJson !== savedValuesJson; // Immediate UI feedback setIsModified(hasActualChanges); // Debounced draft sync const timeoutId = setTimeout(() => { const existingDraftVariables = hasDraftForThisEnv ? environmentsDraft?.variables : null; const existingDraftJson = existingDraftVariables ? JSON.stringify(existingDraftVariables) : null; if (hasActualChanges) { if (currentValuesJson !== existingDraftJson) { dispatch(setEnvironmentsDraft({ collectionUid: collection.uid, environmentUid: environment.uid, variables: currentValues })); } } else if (hasDraftForThisEnv) { dispatch(clearEnvironmentsDraft({ collectionUid: collection.uid })); } }, 300); return () => clearTimeout(timeoutId); }, [formik.values, savedValuesJson, environment.uid, collection.uid, dispatch, hasDraftForThisEnv, environmentsDraft?.variables, setIsModified]);Alternatively, if immediate
isModifiedfeedback is critical, document why the separation is needed.
275-291: setTimeout for trailing row addition is fragile.Using
setTimeoutwith a 0ms delay (line 288) to add a new trailing row can introduce timing issues and race conditions, especially if the user types quickly or if multiple state updates occur concurrently.Consider alternatives:
- Use
formik.setFieldValuesynchronously if possible- Use a proper state management approach that doesn't rely on deferred execution
- If the setTimeout is necessary to work around Formik's batching, add a comment explaining why
If testing confirms this works reliably across scenarios, document the reasoning to help future maintainers understand the timing constraint.
305-351: Replace manual validation with formik's built-in validation.Lines 315-328 manually validate variables, duplicating the validation logic already defined in formik's schema and custom
validatefunction. This creates maintenance overhead and risks divergence.🔎 Use formik.validateForm() instead
const handleSave = () => { const variablesToSave = formik.values.filter((variable) => variable.name && variable.name.trim() !== ''); const savedValues = environment.variables || []; const hasChanges = JSON.stringify(variablesToSave) !== JSON.stringify(savedValues); if (!hasChanges) { toast.error('No changes to save'); return; } - const hasValidationErrors = variablesToSave.some((variable) => { - if (!variable.name || variable.name.trim() === '') { - return true; - } - if (!variableNameRegex.test(variable.name)) { - return true; - } - return false; - }); - - if (hasValidationErrors) { + const errors = await formik.validateForm(); + if (Object.keys(errors).length > 0) { toast.error('Please fix validation errors before saving'); return; } dispatch(saveEnvironment(cloneDeep(variablesToSave), environment.uid, collection.uid)) .then(() => { toast.success('Changes saved successfully'); const newValues = [ ...variablesToSave, { uid: uuid(), name: '', value: '', type: 'text', secret: false, enabled: true } ]; formik.resetForm({ values: newValues }); setIsModified(false); }) .catch((error) => { console.error(error); toast.error('An error occurred while saving the changes'); }); };Note: If you use this approach, make
handleSaveasync or handle the validateForm promise appropriately.
370-383: Custom window event pattern for save is functional but non-idiomatic.The component listens for a custom
'environment-save'window event, using a ref to maintain the latesthandleSave. While this works, it couples the component to a global event system. Consider using React patterns likeuseImperativeHandlewith a forwarded ref if you refactor component communication in the future.For now, this is functional and may be consistent with patterns elsewhere in the codebase.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(4 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js(10 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js
- packages/bruno-app/src/components/Environments/EnvironmentSettings/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()notfunc ()
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/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
🧠 Learnings (5)
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.jspackages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
📚 Learning: 2025-12-02T20:48:05.670Z
Learnt from: NikHillAgar
Repo: usebruno/bruno PR: 6279
File: packages/bruno-app/src/components/RequestPane/PromptVariables/PromptVariablesModal/index.js:79-86
Timestamp: 2025-12-02T20:48:05.670Z
Learning: In React 19+, ref callbacks support returning cleanup functions. When a ref callback returns a function, React will call it when the element unmounts or the ref changes, similar to useEffect cleanup. This is documented at https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback
Applied to files:
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.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/index.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
📚 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
🪛 Biome (2.1.2)
packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
[error] 113-113: Do not add then to an object.
(lint/suspicious/noThenProperty)
⏰ 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: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: CLI Tests
🔇 Additional comments (5)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js (1)
142-142: LGTM! Minor padding adjustment for improved layout.The 2px horizontal padding prevents buttons from being flush against the container edges in the new tab-based UI.
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (1)
307-320: Nice pattern for global save event handling.Using a ref to store the latest
handleSavefunction prevents stale closures in the window event listener. This is a clean way to integrate with the global save flow.packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js (3)
1-21: LGTM! Component signature aligns with tab-based editing.The updated signature removing
onCloseandoriginalEnvironmentVariableswhile addingsetIsModifiedcorrectly reflects the shift from modal to tab-based editing. Draft-related imports support the new draft persistence flow.
153-174: LGTM! Draft restoration logic handles mount and environment switches correctly.The use of refs to track previous environment UID and mount state is appropriate. The effect correctly restores draft values when switching environments or on initial mount, and properly maintains the trailing empty row.
385-504: LGTM! UI rendering is well-structured with good UX considerations.The table rendering with inline editing, conditional tooltips for read-only non-string values, and sensitive field warnings provides a clear user experience. The trailing empty row pattern is handled cleanly.
| import { Tooltip } from 'react-tooltip'; | ||
| import { getGlobalEnvironmentVariables } from 'utils/collections'; | ||
|
|
||
| const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables, collection }) => { |
There was a problem hiding this comment.
Remove unused prop originalEnvironmentVariables.
The originalEnvironmentVariables prop is declared but never referenced in the component. This is likely leftover from the previous modal-based implementation.
🔎 Apply this diff to remove the unused prop:
-const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables, collection }) => {
+const EnvironmentVariables = ({ environment, setIsModified, collection }) => {🤖 Prompt for AI Agents
In
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
around line 21, the component signature includes an unused prop
originalEnvironmentVariables; remove originalEnvironmentVariables from the
function parameter list and from any related prop-type/default export/type
definitions or callers if present so the component only accepts and uses
environment, setIsModified and collection; run linter/tests to ensure no
remaining references to originalEnvironmentVariables remain.
| // Restore draft values on mount or environment switch | ||
| React.useEffect(() => { | ||
| const isMount = !mountedRef.current; | ||
| const envChanged = prevEnvUidRef.current !== null && prevEnvUidRef.current !== environment.uid; | ||
|
|
||
| prevEnvUidRef.current = environment.uid; | ||
| mountedRef.current = true; | ||
|
|
||
| if ((isMount || envChanged) && hasDraftForThisEnv && globalEnvironmentDraft?.variables) { | ||
| formik.setValues([ | ||
| ...globalEnvironmentDraft.variables, | ||
| { | ||
| uid: uuid(), | ||
| name: '', | ||
| value: '', | ||
| type: 'text', | ||
| secret: false, | ||
| enabled: true | ||
| } | ||
| ]); | ||
| } | ||
| }, [environment.uid, hasDraftForThisEnv, globalEnvironmentDraft?.variables]); |
There was a problem hiding this comment.
Include formik in the dependency array.
The formik object's helper methods are referentially unstable across renders, meaning formik.setValues is a true dependency. While this may cause additional effect runs, this is the correct behavior. Alternatively, if extra runs are undesirable, extract and memoize only the setValues method to break the dependency cycle.
🤖 Prompt for AI Agents
In
packages/bruno-app/src/components/WorkspaceHome/WorkspaceEnvironments/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
around lines 101 to 122, the effect depends on formik.setValues but formik is
not listed in the dependency array; add formik (or at minimum formik.setValues)
to the dependency array so the effect re-runs when the formik helpers change, or
alternatively extract and memoize formik.setValues outside the effect and use
that memoized setter in the dependency array to avoid unnecessary runs.
There was a problem hiding this comment.
We should create a common table for both collection and global environments and also reuse the same for the workspace environment.
Manage Secrets info modal is also missing.
@naman-bruno To be done in a follow-up PR

Description
Contribution Checklist:
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
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.