Skip to content

feat(sandbox): create a dropdown selector for sandbox mode#6519

Merged
bijin-bruno merged 1 commit intomainfrom
feat/sandbox-mode-switcher
Dec 30, 2025
Merged

feat(sandbox): create a dropdown selector for sandbox mode#6519
bijin-bruno merged 1 commit intomainfrom
feat/sandbox-mode-switcher

Conversation

@bijin-bruno
Copy link
Collaborator

@bijin-bruno bijin-bruno commented Dec 26, 2025

Description

creates a dropdown selector for sandbox mode

Screen.Recording.2025-12-27.at.2.30.20.AM.mov

Contribution Checklist:

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

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

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • JS sandbox mode replaced with a dropdown: labeled options with icons, descriptions, “Recommended” badge, radio-style selection and keyboard support (Escape to close).
  • Style

    • Expanded styling for the sandbox dropdown, option badges, radio visuals, descriptions, and developer-mode warning.
  • Removed

    • Standalone Security Settings view/tab removed from the UI.
  • Chores

    • Security-settings tabs now behave like regular (replaceable) tabs.
  • Tests

    • E2E tests updated with new sandbox locators and simplified flows.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Replaces the standalone SecuritySettings modal with a dropdown-based JsSandboxMode component and new dropdown styles, removes the SecuritySettings module and its StyledWrapper, removes special-case rendering and tab non-replaceable behavior for security-settings, and updates tests and locators to the new dropdown flow.

Changes

Cohort / File(s) Summary
JsSandboxMode — styling
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
Adds polished import and comprehensive CSS for a sandbox dropdown: .sandbox-dropdown, headers, .sandbox-option variants (.safe-mode, .developer-mode, .active), custom radio visuals, .recommended-badge, descriptions, and .developer-mode-warning.
JsSandboxMode — behavior
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
Replaces inline icon toggles with a Dropdown-driven UI: SANDBOX_OPTIONS, local selectedMode synced to collection.securityConfig.jsSandboxMode, dropdown lifecycle (create/close/Escape), handleModeChange persisting via saveCollectionSecurityConfig, and option rendering (radios, icons, badges, warnings).
Removed SecuritySettings component & wrapper
packages/bruno-app/src/components/SecuritySettings/index.js, packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
Deletes the previous SecuritySettings React component and its StyledWrapper (radio-based modal and Save button removed).
Request tabs — render branches
packages/bruno-app/src/components/RequestTabPanel/index.js, packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js, packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
Removes rendering branches that handled security-settings; the tab type no longer short-circuits to the old StyledWrapper/close-confirm rendering path.
Tabs behavior
packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
Removes 'security-settings' from nonReplaceableTabTypes, making security-settings tabs replaceable like other tabs.
Toolbar integration
packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
Removes the external ToolHint wrapper around JsSandboxMode; JsSandboxMode now includes its own hint handling.
Tests — locators & flows
tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts, tests/utils/page/locators.ts, tests/utils/page/runner.ts
Adds buildSandboxLocators(page) and switches tests to locator helpers; simplifies sandbox flow to use Escape to close instead of Save/persist verification and removes duplicate locator export from runner.

Sequence Diagram

sequenceDiagram
  participant User
  participant UI as JsSandboxMode (Dropdown)
  participant API as saveCollectionSecurityConfig
  participant Toast as Toast Notification

  User->>UI: open dropdown
  User->>UI: select option
  UI->>UI: handleModeChange(selected)
  UI->>API: saveCollectionSecurityConfig(collectionUid, newMode)
  API-->>UI: success / error
  alt success
    UI->>UI: update selectedMode
    UI->>Toast: show success toast
  else error
    UI->>Toast: show error toast
  end
  UI-->>User: close dropdown (Escape or after save)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno

Poem

🏖️ A dropdown opens where modes convene,
Safe and daring in a tiny scene.
Click to change, a toast will sing,
Tabs reshuffled on a lighter wing.
Small styles cheer — the sandbox swings! 🐚

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: replacing inline sandbox mode controls with a dropdown UI component for selecting between Safe and Developer modes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b285b43 and dd34942.

📒 Files selected for processing (12)
  • packages/bruno-app/src/components/RequestTabPanel/index.js
  • packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
  • tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
  • tests/utils/page/locators.ts
  • tests/utils/page/runner.ts

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.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js (2)

90-97: Hardcoded color values should use theme props.

Lines 93-94 and 96 use hardcoded rgba and hex values. Per coding standards, colors in styled components should come from the theme prop for consistency and theming support.

🔎 Suggested approach

Consider adding these warning colors to the theme and referencing them:

  .developer-mode-warning {
    margin: 0.5rem 0;
    padding: 0.25rem 0.5rem;
-   background-color: rgba(245, 158, 11, 0.1);
-   border: 1px solid rgba(245, 158, 11, 0.2);
+   background-color: ${(props) => props.theme.colors.warning.bg};
+   border: 1px solid ${(props) => props.theme.colors.warning.border};
    border-radius: 0.25rem;
-   color: #fbbf24;
+   color: ${(props) => props.theme.colors.warning.text};
  }

66-73: Hardcoded rgba in recommended-badge background.

Line 70 uses a hardcoded rgba(16, 185, 129, 0.2) for the background. For theming consistency, consider extracting this to the theme as well.

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

111-113: Close button lacks accessible attributes and cursor styling.

The IconX close button should have cursor: pointer styling (or use a <button> element) and accessible attributes for screen readers.

🔎 Proposed fix
  <div className="sandbox-header flex items-center justify-between">
-   JavaScript Sandbox <IconX size={16} strokeWidth={1.5} onClick={closeDropdown} />
+   JavaScript Sandbox
+   <button
+     type="button"
+     className="close-btn"
+     onClick={closeDropdown}
+     aria-label="Close dropdown"
+   >
+     <IconX size={16} strokeWidth={1.5} />
+   </button>
  </div>

Add corresponding styles in StyledWrapper.js:

.close-btn {
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  display: flex;
  align-items: center;
}

50-68: Consider closing the dropdown after successful mode change.

When the user selects a different mode and it saves successfully, the dropdown remains open. Typical UX would close the dropdown after selection.

🔎 Proposed fix
    )
      .then(() => {
        setSelectedMode(mode);
        toast.success('Sandbox mode updated successfully');
+       closeDropdown();
      })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84f572f and 2aca207.

📒 Files selected for processing (2)
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/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() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
🧬 Code graph analysis (1)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
  • saveCollectionSecurityConfig (2417-2431)
  • saveCollectionSecurityConfig (2417-2431)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js (1)
  • StyledWrapper (3-108)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-27)
⏰ 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 - Linux
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: Unit Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
🔇 Additional comments (5)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js (1)

28-107: Overall styling structure looks good.

The new dropdown styles are well-organized with clear class naming conventions. The use of theme props for most colors and the separation of concerns between layout and visual styling aligns with project standards.

packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (4)

9-25: SANDBOX_OPTIONS constant is well-structured.

Clean configuration object with clear keys, labels, descriptions, and icons. The recommended flag and warning field provide good UX hints.


70-96: Good accessibility implementation on options.

Using role="menuitemradio" and aria-checked attributes provides proper screen reader support for the radio-like selection behavior.


27-48: State management and dropdown lifecycle look correct.

The useEffect properly syncs local state with prop changes, and the dropdown ref pattern for programmatic control is appropriate.


108-113: The onKeyDown handler on StyledWrapper may not work reliably with Tippy's event scope.

Even though the Dropdown component uses appendTo: 'parent', Tippy isolates event bubbling from its rendered content. Keyboard events triggered inside the dropdown won't reliably bubble up to the StyledWrapper parent. For Escape key handling, either add a document-level listener (like ResponseExample does in the codebase) or pass onHide callback to the Tippy instance through the onCreate handler to close the dropdown when focus is lost.

@github-actions
Copy link

github-actions bot commented Dec 26, 2025

CLI Test Results

  1 files  ±0  140 suites  ±0   55s ⏱️ +6s
235 tests ±0  235 ✅ ±0  0 💤 ±0  0 ❌ ±0 
301 runs  ±0  300 ✅ ±0  1 💤 ±0  0 ❌ ±0 

Results for commit b285b43. ± Comparison against base commit 0848393.

♻️ This comment has been updated with latest results.

@bijin-bruno bijin-bruno force-pushed the feat/sandbox-mode-switcher branch from 2aca207 to c102acf Compare December 30, 2025 15:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

163-163: Minor formatting: extra spaces before template literal.

Remove the extra spaces between the colon and the template literal for consistency.

🔎 Proposed fix
-    background-color:   ${(props) => rgba(props.theme.colors.text.warning, 0.1)};
+    background-color: ${(props) => rgba(props.theme.colors.text.warning, 0.1)};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2aca207 and c102acf.

📒 Files selected for processing (8)
  • packages/bruno-app/src/components/RequestTabPanel/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
💤 Files with no reviewable changes (5)
  • packages/bruno-app/src/components/SecuritySettings/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
  • packages/bruno-app/src/components/RequestTabPanel/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
  • packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

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

Files:

  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/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 Component CSS might also change layout but Tailwind classes shouldn't define colors

Applied to files:

  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/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/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/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: CLI Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Windows
🔇 Additional comments (6)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (1)

175-175: Change is correct and complete.

The removal of 'security-settings' from the special tab types array is safe. No remaining references to 'security-settings' exist in the codebase, confirming the entire UI pathway has been properly eliminated.

packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (5)

1-8: Imports look good.

All necessary dependencies are imported for the new dropdown functionality.


9-25: Well-structured configuration.

The SANDBOX_OPTIONS constant is clear, maintainable, and provides all necessary metadata for rendering the dropdown options.


27-34: State management is appropriate.

The component properly initializes and syncs local state with the collection's security config.


36-48: Dropdown lifecycle handlers look good.

The dropdown creation callback and keyboard handling (Escape key) are properly implemented.


101-121: Render logic is clean.

The triggerIcon conditional rendering and Dropdown structure are well-implemented.

Comment on lines +50 to +68
const handleModeChange = (mode) => {
if (!collection?.uid || mode === selectedMode) {
return;
}

const viewSecuritySettings = () => {
dispatch(
addTab({
uid: uuid(),
collectionUid: collection.uid,
type: 'security-settings'
saveCollectionSecurityConfig(collection.uid, {
jsSandboxMode: mode
})
)
.then(() => {
setSelectedMode(mode);
toast.success('Sandbox mode updated successfully');
})
.catch((err) => {
console.error(err);
toast.error('Failed to update sandbox mode');
});
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing dropdown closure and loading state.

Two issues:

  1. The dropdown should close after successfully saving the mode. Add closeDropdown() in the success handler.
  2. No loading/disabled state during the async save operation. Users can rapidly click multiple options, potentially causing race conditions or duplicate requests.
🔎 Proposed fixes

Add closeDropdown after successful save:

      .then(() => {
        setSelectedMode(mode);
        toast.success('Sandbox mode updated successfully');
+       closeDropdown();
      })

Add loading state (requires additional state variable):

Add a loading state at the component level:

  const [selectedMode, setSelectedMode] = useState(collection?.securityConfig?.jsSandboxMode || 'safe');
+ const [isSaving, setIsSaving] = useState(false);

Then update handleModeChange:

  const handleModeChange = (mode) => {
-   if (!collection?.uid || mode === selectedMode) {
+   if (!collection?.uid || mode === selectedMode || isSaving) {
      return;
    }

+   setIsSaving(true);
    dispatch(
      saveCollectionSecurityConfig(collection.uid, {
        jsSandboxMode: mode
      })
    )
      .then(() => {
        setSelectedMode(mode);
        toast.success('Sandbox mode updated successfully');
        closeDropdown();
      })
      .catch((err) => {
        console.error(err);
        toast.error('Failed to update sandbox mode');
      })
+     .finally(() => {
+       setIsSaving(false);
+     });
  };

And disable options during save in renderOption (line 78):

-       className={`sandbox-option ${option.key}-mode ${isActive ? 'active' : ''}`}
+       className={`sandbox-option ${option.key}-mode ${isActive ? 'active' : ''}`}
+       disabled={isSaving}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
around lines 50 to 68, the success handler doesn’t close the dropdown and
there’s no loading/disabled state so users can click multiple options during the
async save; fix by adding a component-level loading state (e.g., const
[isSaving, setIsSaving] = useState(false)), early-return if isSaving or mode ===
selectedMode, setIsSaving(true) before dispatch, call closeDropdown() and
setSelectedMode(mode) in the .then success handler, and ensure
setIsSaving(false) in a .finally block (or after both success and catch); also
pass isSaving to renderOption (around line 78) and use it to disable option
buttons while saving to prevent duplicate requests and race conditions.

@bijin-bruno bijin-bruno force-pushed the feat/sandbox-mode-switcher branch from c102acf to b285b43 Compare December 30, 2025 16:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
tests/utils/page/runner.ts (1)

110-113: Clarify the intent of Promise.race with empty catches.

The Promise.race with empty catch blocks works but obscures intent. Consider documenting why both radio buttons are checked, or simplify by waiting for just one element that's always present.

💡 Alternative: Wait for the container or a single reliable element
-    // Wait for security settings form to be visible - wait for either radio button
-    await Promise.race([
-      sandboxLocators.safeModeRadio().waitFor({ state: 'visible', timeout: 10000 }).catch(() => {}),
-      sandboxLocators.developerModeRadio().waitFor({ state: 'visible', timeout: 10000 }).catch(() => {})
-    ]);
+    // Wait for security settings form to be visible
+    await sandboxLocators.jsSandboxHeading().waitFor({ state: 'visible', timeout: 10000 });
tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts (1)

6-27: Enhance test structure with test.step and verify default state.

The test should explicitly verify the default "safe" mode before switching to developer mode. Additionally, using test.step will improve test report readability.

Based on coding guidelines for e2e tests.

💡 Suggested structure with test.step
  test('should set jsSandboxMode to safe by default when creating a new collection', async ({ page, createTmpDir }) => {
    const collectionName = 'test-sandbox-collection';

-    await createCollection(page, collectionName, await createTmpDir());
-    const sandboxLocators = buildSandboxLocators(page);
-
-    // Verify sandbox mode is set to safe by default
-    await expect(sandboxLocators.sandboxModeSelector()).toBeVisible();
-
-    // Click on sandbox mode selector to open security settings
-    await sandboxLocators.sandboxModeSelector().click();
-
-    // Change to developer mode
-    const developerRadio = sandboxLocators.developerModeRadio();
-    await developerRadio.check();
-
-    // For safe mode, check if developer mode is currently selected
-    const safeModeChecked = await sandboxLocators.safeModeRadio().isChecked().catch(() => false);
-    await expect(safeModeChecked).toBe(false);
-
-    await page.keyboard.press('Escape');
+    await test.step('Create collection and verify default sandbox mode', async () => {
+      await createCollection(page, collectionName, await createTmpDir());
+      const sandboxLocators = buildSandboxLocators(page);
+      await expect(sandboxLocators.sandboxModeSelector()).toBeVisible();
+    });
+
+    await test.step('Open sandbox settings and verify safe mode is default', async () => {
+      const sandboxLocators = buildSandboxLocators(page);
+      await sandboxLocators.sandboxModeSelector().click();
+      await expect(sandboxLocators.safeModeRadio()).toBeChecked();
+    });
+
+    await test.step('Change to developer mode', async () => {
+      const sandboxLocators = buildSandboxLocators(page);
+      const developerRadio = sandboxLocators.developerModeRadio();
+      await developerRadio.check();
+      await expect(developerRadio).toBeChecked();
+      await expect(sandboxLocators.safeModeRadio()).not.toBeChecked();
+      await page.keyboard.press('Escape');
+    });
  });
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (1)

107-115: Consider using ActionIcon for consistency.

Other toolbar items (Runner, Variables, Settings) use the ActionIcon component, but this trigger uses nested divs. Using ActionIcon would provide consistent styling, better keyboard accessibility, and align with the established pattern in CollectionToolBar.

🔎 Proposed refactor using ActionIcon
+import ActionIcon from 'ui/ActionIcon';

...

  const triggerIcon = (
-   <div>
-     <ToolHint text={`${selectedMode === 'developer' ? 'Developer Mode' : 'Safe Mode'}`} toolhintId="JavascriptSandboxToolhintId" place="bottom">
-       <div className={`sandbox-icon ${selectedMode === 'developer' ? 'developer-mode' : 'safe-mode'}`} data-testid="sandbox-mode-selector">
-         {selectedMode === 'developer' ? <IconCode size={14} strokeWidth={2} /> : <IconShieldCheck size={14} strokeWidth={2} />}
-       </div>
-     </ToolHint>
-   </div>
+   <ToolHint text={selectedMode === 'developer' ? 'Developer Mode' : 'Safe Mode'} toolhintId="JavascriptSandboxToolhintId" place="bottom">
+     <ActionIcon size="sm" aria-label={selectedMode === 'developer' ? 'Developer Mode' : 'Safe Mode'} data-testid="sandbox-mode-selector">
+       {selectedMode === 'developer' ? <IconCode size={16} strokeWidth={1.5} /> : <IconShieldCheck size={16} strokeWidth={1.5} />}
+     </ActionIcon>
+   </ToolHint>
  );

Note: Adjust icon size and styling in StyledWrapper if needed to maintain current appearance.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c102acf and b285b43.

📒 Files selected for processing (12)
  • packages/bruno-app/src/components/RequestTabPanel/index.js
  • packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
  • packages/bruno-app/src/components/SecuritySettings/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
  • tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
  • tests/utils/page/locators.ts
  • tests/utils/page/runner.ts
💤 Files with no reviewable changes (5)
  • packages/bruno-app/src/components/RequestTabPanel/index.js
  • packages/bruno-app/src/components/SecuritySettings/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
  • packages/bruno-app/src/components/SecuritySettings/StyledWrapper.js
  • packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/StyledWrapper.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

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

Files:

  • tests/utils/page/locators.ts
  • packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
  • tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
  • packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js
  • tests/utils/page/runner.ts
tests/**/**.*

⚙️ CodeRabbit configuration file

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

  • Follow best practices for Playwright code and e2e automation

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

  • Avoid using page.pause() in code

  • Use locator variables for locators

  • Avoid using test.only

  • Use multiple assertions

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

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

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

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

Files:

  • tests/utils/page/locators.ts
  • tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
  • tests/utils/page/runner.ts
🧠 Learnings (3)
📚 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/CollectionToolBar/index.js
  • packages/bruno-app/src/components/RequestTabs/RequestTab/index.js
  • packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/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/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
📚 Learning: 2025-12-16T07:16:23.647Z
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:23.647Z
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/collection/default-sandbox-mode/default-sandbox-mode.spec.ts
🧬 Code graph analysis (2)
packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js (1)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (1)
  • JsSandboxMode (28-127)
packages/bruno-app/src/components/SecuritySettings/JsSandboxMode/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2)
  • saveCollectionSecurityConfig (2417-2431)
  • saveCollectionSecurityConfig (2417-2431)
packages/bruno-app/src/components/ToolHint/index.js (1)
  • ToolHint (5-57)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (7)
packages/bruno-app/src/components/RequestTabs/RequestTab/index.js (1)

175-175: The removal of 'security-settings' from the tab type array is safe and complete.

No remaining references to 'security-settings' exist in the codebase, confirming that this tab type has been fully deprecated as part of the UI refactoring. The change is correct and requires no further action.

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

205-216: LGTM! Well-structured locator builder.

The new buildSandboxLocators helper follows the established pattern in this file and provides clear, testable locators for the sandbox UI components.

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

124-124: Verify that Escape key properly persists the sandbox mode selection.

The change from clicking a Save button to pressing Escape suggests the UI now auto-saves selections. Ensure this behavior is intentional and that the sandbox mode selection is properly persisted when the modal is dismissed via Escape.

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

68-69: LGTM! Clean refactor.

The comment clearly documents the change, and removing the wrapper aligns correctly with the refactored JsSandboxMode component that now handles ToolHint internally.

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

10-26: LGTM! Well-structured configuration.

The SANDBOX_OPTIONS constant is clearly defined with all necessary metadata for rendering the dropdown options. The structure supports both visual and behavioral requirements.


33-43: LGTM! Correct state synchronization pattern.

The effect properly syncs local state with prop changes, and the dropdown lifecycle management with refs is a standard pattern.


45-49: Escape key handling appears sound with current Dropdown configuration.

The Dropdown component defaults to appendTo: 'parent' (not document.body), which keeps the portaled content within the DOM tree as a descendant of StyledWrapper. This allows keyboard events to bubble from the dropdown to the parent's onKeyDown handler, so pressing Escape should correctly trigger closeDropdown().

No changes needed unless testing reveals the Escape key doesn't work.

Comment on lines +22 to +24
// For safe mode, check if developer mode is currently selected
const safeModeChecked = await sandboxLocators.safeModeRadio().isChecked().catch(() => false);
await expect(safeModeChecked).toBe(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix misleading comment and add missing assertion.

The comment "For safe mode, check if developer mode is currently selected" is confusing. You're actually verifying that developer mode is selected (by checking safe mode is NOT selected). Also, there's no assertion that developer mode was successfully checked on line 20.

🔎 Suggested improvements
-    // For safe mode, check if developer mode is currently selected
-    const safeModeChecked = await sandboxLocators.safeModeRadio().isChecked().catch(() => false);
-    await expect(safeModeChecked).toBe(false);
+    // Verify developer mode is selected
+    await expect(developerRadio).toBeChecked();
+    await expect(sandboxLocators.safeModeRadio()).not.toBeChecked();

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In tests/collection/default-sandbox-mode/default-sandbox-mode.spec.ts around
lines 22 to 24, the inline comment is misleading and there is a missing
assertion: update the comment to state that we're verifying developer mode is
selected (since we check that safe mode is NOT checked), and add a corresponding
assertion that developer mode was checked on the earlier line (line ~20) —
capture developerModeChecked from
sandboxLocators.developerModeRadio().isChecked().catch(() => false) and add
await expect(developerModeChecked).toBe(true); to explicitly assert developer
mode is selected.

@bijin-bruno bijin-bruno force-pushed the feat/sandbox-mode-switcher branch from b285b43 to dd34942 Compare December 30, 2025 17:31
@bijin-bruno bijin-bruno merged commit 8fa8ae5 into main Dec 30, 2025
7 of 8 checks passed
@bijin-bruno bijin-bruno deleted the feat/sandbox-mode-switcher branch January 3, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant