Skip to content

feat: revamp Runner UI with Timings and Filters sections#7505

Merged
bijin-bruno merged 9 commits intousebruno:mainfrom
gopu-bruno:feat/runner-ui-revamp
Mar 27, 2026
Merged

feat: revamp Runner UI with Timings and Filters sections#7505
bijin-bruno merged 9 commits intousebruno:mainfrom
gopu-bruno:feat/runner-ui-revamp

Conversation

@gopu-bruno
Copy link
Copy Markdown
Collaborator

@gopu-bruno gopu-bruno commented Mar 17, 2026

Description

Revamps the Collection Runner UI (main Runner tab and Run Collection modal) with theme-based styling and two separate sections.

Part of : JIRA, JIRA

Screenshots

image image

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

    • Timings: configurable delay between requests (including in collection modal).
    • Filters: tag include/exclude controls always available and applied to runs.
  • Improvements

    • Tag area redesigned to flexible two-column layout; headings now "Include tags" / "Exclude tags".
    • Updated radios, inputs, and section styling for improved focus and hierarchy.
    • Shortened tag input placeholder text.
  • Tests

    • Added UI tests covering runner configuration, selection behavior, delay input, and run button states.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Removes tag-enable radio toggles and configure-mode branching; always shows include/exclude tag lists, threads tags and selectedRequestItems/delay through run actions, disables requests by tag/transport in the configuration panel, adds a numeric Delay input, updates multiple styles, and adds Playwright tests and locators for the runner configuration UI.

Changes

Cohort / File(s) Summary
RunnerResults (core UI & orchestration)
packages/bruno-app/src/components/RunnerResults/index.jsx, packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
Removed configureMode branching and runnerTagsEnabled gating; Run now always persists selectedRequestItems/tags/delay and starts runner with tags; layout reworked (Timings/Filters), icon/title adjustments, and large CSS additions for textbox/radio/runner-section/CodeMirror.
RunConfigurationPanel (selection & disabling by tags)
packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx, packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/StyledWrapper.jsx
Adds tags prop, computes isDisabled per item (unsupported transports or excluded by tag rules), prevents dragging/selecting disabled items, updates select-all/deselect/reset behaviors to consider only enabled items, persists ordering on tag changes, and adds test-friendly data-testid attributes.
RunnerTags (UI simplification)
packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx
Removed radio-driven toggles and conditional editor rendering; always renders include/exclude lists with same add/remove/validation/dispatch behavior; changed layout to flexible columns and renamed headings to "Include tags" / "Exclude tags".
RunCollectionItem (modal) & StyledWrapper
packages/bruno-app/src/components/Sidebar/.../RunCollectionItem/index.js, packages/bruno-app/src/components/Sidebar/.../RunCollectionItem/StyledWrapper.js
Adds local delay numeric input (passes delay ? Number(delay) : null to runCollectionFolder), inserts Timings UI and divider, applies textbox/radio styles used elsewhere.
TagList & TagList styling
packages/bruno-app/src/components/TagList/index.js, packages/bruno-app/src/components/TagList/StyledWrapper.js
Minor placeholder text change and adjusted .tag-remove hover/focus styling (no color-danger/scale on hover).
Tests & test utils
tests/runner/runner-configuration.spec.ts, tests/utils/page/runner.ts
Adds Playwright spec for Runner Configuration Panel and new runner locators + helper openRunnerTab to drive the UI (config panel locators, delay input, select/reset/run assertions).

Sequence Diagram(s)

sequenceDiagram
    participant UI as "RunnerResults UI"
    participant Panel as "RunConfigurationPanel"
    participant Store as "Redux (runnerConfiguration)"
    participant Runner as "Runner service / IPC"

    UI->>Panel: render(tags, collection, savedConfig)
    Panel->>Store: update selectedItems (on user select/reset/tags change)
    Store-->>Panel: persisted runnerConfiguration
    UI->>Store: updateRunnerConfiguration(selectedRequestItems, delay, tags)
    Store->>Runner: runCollection/runCollectionFolder(payload with tags, selectedRequestItems, delay)
    Runner-->>UI: runner started / status updates
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

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

Poem

🏁 Delay knobs set, filters hum in rows,
Disabled items dim where excluded tags go,
Selected orders persist, the runner takes flight,
Timings and filters aligned in plain sight,
Small refactors, neat UI — code ready for night.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main UI revamp by introducing two new sections (Timings and Filters) visible across multiple components and the Runner interface restructuring.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
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.

🧹 Nitpick comments (4)
packages/bruno-app/src/components/RunnerResults/index.jsx (1)

284-294: Add data-testid for Playwright testing.

Same as the delay input in RunCollectionItem - testable elements should have data-testid attributes per coding guidelines.

✨ Proposed fix
               <input
                 type="number"
                 className="block textbox w-[60%]"
                 placeholder="e.g. 5"
                 autoComplete="off"
                 autoCorrect="off"
                 autoCapitalize="off"
                 spellCheck="false"
                 value={delay}
                 onChange={(e) => setDelay(e.target.value)}
+                data-testid="runner-delay-input"
               />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RunnerResults/index.jsx` around lines 284 -
294, In the RunnerResults component's delay input (the <input> that uses
value={delay} and onChange={(e) => setDelay(e.target.value)}), add a data-testid
attribute so Playwright can target it (use the same naming convention as
RunCollectionItem's delay input, e.g. data-testid="run-delay-input"); update the
JSX for that input in RunnerResults to include data-testid="run-delay-input".
packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js (1)

84-94: Add data-testid for Playwright testing.

Per coding guidelines, testable elements should have data-testid attributes.

✨ Proposed fix
             <input
               type="number"
               className="textbox w-1/2"
               placeholder="e.g. 5"
               autoComplete="off"
               autoCorrect="off"
               autoCapitalize="off"
               spellCheck="false"
               value={delay}
               onChange={(e) => setDelay(e.target.value)}
+              data-testid="delay-input"
             />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`
around lines 84 - 94, Add a data-testid attribute to the numeric input used for
delay so Playwright tests can reliably target it: locate the input element in
RunCollectionItem where props/state variables delay and setDelay are used and
add a descriptive data-testid (e.g., data-testid="run-collection-delay-input")
to the input element so tests can select it consistently.
packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx (1)

10-10: Consider memoizing the cloned collection.

cloneDeep(find(...)) runs on every render. For large collections, this could impact performance. Consider using useMemo if profiling shows this as a bottleneck.

✨ Potential optimization
+import { useMemo } from 'react';
...
-  const collection = cloneDeep(find(collections, (c) => c.uid === collectionUid));
+  const collection = useMemo(
+    () => cloneDeep(find(collections, (c) => c.uid === collectionUid)),
+    [collections, collectionUid]
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx` at line
10, The code repeatedly calls cloneDeep(find(collections, c => c.uid ===
collectionUid)) on every render; wrap this in useMemo to avoid unnecessary deep
clones by computing it only when collections or collectionUid change (e.g.,
const collection = useMemo(() => cloneDeep(find(collections, c => c.uid ===
collectionUid)), [collections, collectionUid])); also add an import for useMemo
from React if it's not already imported.
packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js (1)

11-77: Consider extracting shared input styles to reduce duplication.

These styles (.textbox, input[type='radio'], .single-line-editor, :has() container) are duplicated in RunnerResults/StyledWrapper.js. Consider extracting to a shared styled component or CSS mixin for maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js`
around lines 11 - 77, The styles for .textbox, div:has(> .single-line-editor),
.single-line-editor and input[type='radio'] are duplicated in this StyledWrapper
and RunnerResults/StyledWrapper.js; extract these shared rules into a reusable
styled mixin or shared styled-component (e.g., SharedInputWrapper or
inputStyles) and replace the duplicated blocks in both
RunCollectionItem/StyledWrapper.js and RunnerResults/StyledWrapper.js with that
shared import, keeping the exact selector names (.textbox, .single-line-editor,
div:has(> .single-line-editor), input[type='radio']) so behavior and theming
props remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/bruno-app/src/components/RunnerResults/index.jsx`:
- Around line 284-294: In the RunnerResults component's delay input (the <input>
that uses value={delay} and onChange={(e) => setDelay(e.target.value)}), add a
data-testid attribute so Playwright can target it (use the same naming
convention as RunCollectionItem's delay input, e.g.
data-testid="run-delay-input"); update the JSX for that input in RunnerResults
to include data-testid="run-delay-input".

In `@packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx`:
- Line 10: The code repeatedly calls cloneDeep(find(collections, c => c.uid ===
collectionUid)) on every render; wrap this in useMemo to avoid unnecessary deep
clones by computing it only when collections or collectionUid change (e.g.,
const collection = useMemo(() => cloneDeep(find(collections, c => c.uid ===
collectionUid)), [collections, collectionUid])); also add an import for useMemo
from React if it's not already imported.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`:
- Around line 84-94: Add a data-testid attribute to the numeric input used for
delay so Playwright tests can reliably target it: locate the input element in
RunCollectionItem where props/state variables delay and setDelay are used and
add a descriptive data-testid (e.g., data-testid="run-collection-delay-input")
to the input element so tests can select it consistently.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js`:
- Around line 11-77: The styles for .textbox, div:has(> .single-line-editor),
.single-line-editor and input[type='radio'] are duplicated in this StyledWrapper
and RunnerResults/StyledWrapper.js; extract these shared rules into a reusable
styled mixin or shared styled-component (e.g., SharedInputWrapper or
inputStyles) and replace the duplicated blocks in both
RunCollectionItem/StyledWrapper.js and RunnerResults/StyledWrapper.js with that
shared import, keeping the exact selector names (.textbox, .single-line-editor,
div:has(> .single-line-editor), input[type='radio']) so behavior and theming
props remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 25f015b3-928b-4deb-89fd-ffaaebf32bbe

📥 Commits

Reviewing files that changed from the base of the PR and between 68d80b8 and 120e470.

📒 Files selected for processing (6)
  • packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
  • packages/bruno-app/src/components/RunnerResults/index.jsx
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js
  • packages/bruno-app/src/components/TagList/index.js

Copy link
Copy Markdown
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 (3)
packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx (2)

22-24: Minor: useEffect dependency uses derived collection.uid instead of prop.

The collectionUid prop is already available and equivalent. Using the prop directly is cleaner and avoids potential issues if collection is undefined.

♻️ Suggested fix
   useEffect(() => {
     dispatch(updateCollectionTagsList({ collectionUid }));
-  }, [collection.uid, dispatch]);
+  }, [collectionUid, dispatch]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx` around
lines 22 - 24, The useEffect currently depends on derived collection.uid which
can be undefined; change its dependency to the prop collectionUid instead:
update the effect that calls dispatch(updateCollectionTagsList({ collectionUid
})) so the dependency array uses [collectionUid, dispatch] (remove
collection.uid) to ensure the effect tracks the prop directly; keep the same
dispatch call and function name (updateCollectionTagsList) unchanged.

10-10: Unnecessary cloneDeep on every render.

The collection is only used for reading runnerTags, runnerTagsEnabled, and allTags. There's no mutation, so deep cloning on every render is wasteful.

♻️ Suggested fix
-  const collection = cloneDeep(find(collections, (c) => c.uid === collectionUid));
+  const collection = find(collections, (c) => c.uid === collectionUid);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx` at line
10, Remove the unnecessary deep clone by replacing the cloneDeep(find(...))
usage: directly assign the result of find(collections, c => c.uid ===
collectionUid) to the collection variable (used only for reading runnerTags,
runnerTagsEnabled, and allTags) and ensure no subsequent code mutates
collection; keep cloneDeep only if later mutation is introduced. Update the
declaration that currently uses cloneDeep to simply use find(collections, (c) =>
c.uid === collectionUid) and verify all usages of collection (runnerTags,
runnerTagsEnabled, allTags) are read-only.
packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js (1)

81-95: Consider adding min="0" to prevent negative delay values.

Users could enter negative numbers, which would be passed as-is to the runner. Adding a minimum constraint improves UX.

♻️ Suggested enhancement
           <input
              type="number"
              className="textbox w-1/2"
              placeholder="e.g. 5"
              autoComplete="off"
              autoCorrect="off"
              autoCapitalize="off"
              spellCheck="false"
+             min="0"
              value={delay}
              onChange={(e) => setDelay(e.target.value)}
            />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`
around lines 81 - 95, The numeric input for request delay (value bound to delay
and updated via setDelay in the RunCollectionItem component) allows negative
values; add an HTML min constraint to the input (min="0") and ensure onChange
still parses/validates the value before calling setDelay so negative inputs
cannot be set programmatically or via the UI.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-app/src/components/RunnerResults/StyledWrapper.js`:
- Around line 30-43: The custom radio style removes native focus hints via
appearance: none, so add an explicit keyboard focus indicator by implementing a
:focus-visible rule on the same selector (input[type='radio']) to show a clear
outline or box-shadow using the theme (e.g., theme.primary.solid or a
semi-transparent variant) and ensure it does not affect mouse focus; update the
StyledWrapper input[type='radio'] block to include &:focus-visible { ... } with
a visible outline/box-shadow and appropriate border color so keyboard users can
perceive focus.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`:
- Line 17: The useState for delay in RunCollectionItem currently initializes
delay to null which causes React controlled/uncontrolled warnings when used as
value={delay}; change the initialization in the RunCollectionItem component's
const [delay, setDelay] = useState(null) to use an empty string (useState(''))
so the input is controlled from mount, and ensure any places that parse or
setDelay convert to/from string/number as needed (e.g., when submitting or
validating).

---

Nitpick comments:
In `@packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx`:
- Around line 22-24: The useEffect currently depends on derived collection.uid
which can be undefined; change its dependency to the prop collectionUid instead:
update the effect that calls dispatch(updateCollectionTagsList({ collectionUid
})) so the dependency array uses [collectionUid, dispatch] (remove
collection.uid) to ensure the effect tracks the prop directly; keep the same
dispatch call and function name (updateCollectionTagsList) unchanged.
- Line 10: Remove the unnecessary deep clone by replacing the
cloneDeep(find(...)) usage: directly assign the result of find(collections, c =>
c.uid === collectionUid) to the collection variable (used only for reading
runnerTags, runnerTagsEnabled, and allTags) and ensure no subsequent code
mutates collection; keep cloneDeep only if later mutation is introduced. Update
the declaration that currently uses cloneDeep to simply use find(collections,
(c) => c.uid === collectionUid) and verify all usages of collection (runnerTags,
runnerTagsEnabled, allTags) are read-only.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`:
- Around line 81-95: The numeric input for request delay (value bound to delay
and updated via setDelay in the RunCollectionItem component) allows negative
values; add an HTML min constraint to the input (min="0") and ensure onChange
still parses/validates the value before calling setDelay so negative inputs
cannot be set programmatically or via the UI.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f00f5f5d-3f1f-4e40-8bad-6cdb43d1f01f

📥 Commits

Reviewing files that changed from the base of the PR and between 120e470 and 2ecb41d.

📒 Files selected for processing (4)
  • packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
  • packages/bruno-app/src/components/RunnerResults/index.jsx
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/RunnerResults/index.jsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`:
- Line 37: The dispatch call is passing Number(delay) unvalidated; normalize and
clamp delay before dispatching runCollectionFolder by parsing delay to an
integer (e.g., parseInt or Math.floor on Number(delay)), defaulting to 0 for
NaN, and ensuring it is non-negative (clamp negative values to 0) so invalid or
negative delays are never sent to IPC; apply the same normalization wherever
runCollectionFolder is dispatched in this file (including the other handler
around lines 87-96) and use the normalizedDelay variable in the dispatch call
instead of Number(delay).
- Around line 85-96: The label and input for the delay field in
RunCollectionItem are not associated for accessibility; add a unique id to the
input (e.g., "delay-input" or generated from collection data) and set the
label's htmlFor to that id so clicks focus the input and screen readers announce
correctly; update the input element that uses value={delay} and onChange={(e) =>
setDelay(e.target.value)} to include id="your-chosen-id" and update the label to
htmlFor="your-chosen-id".

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js`:
- Around line 74-88: The custom radio reset in StyledWrapper.js removes the
native focus indicator for the selector input[type='radio']; add a visible
keyboard focus state by targeting input[type='radio']:focus-visible (and/or
:focus) and applying a high-contrast outline or focus ring (e.g., outline or
box-shadow using props.theme.primary.solid or a contrast color) that is distinct
from the checked state; ensure the focus style works with the existing &:checked
rules and respects theme colors so keyboard users can see focus without changing
the visual design.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 792d2661-1be6-4712-bf41-31fd9c98befe

📥 Commits

Reviewing files that changed from the base of the PR and between 2ecb41d and ee4e13a.

📒 Files selected for processing (2)
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js

);
if (!isCollectionRunInProgress) {
dispatch(runCollectionFolder(collection.uid, item ? item.uid : null, recursive, 0, tagsEnabled && tags));
dispatch(runCollectionFolder(collection.uid, item ? item.uid : null, recursive, Number(delay), tagsEnabled && tags));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Normalize delay before dispatching the run action.

Line 37 sends Number(delay) directly; invalid or negative values can be forwarded to IPC unguarded.

🛠️ Suggested fix
   const onSubmit = (recursive) => {
+    const parsedDelay = Number.parseInt(delay, 10);
+    const delayMs = Number.isFinite(parsedDelay) && parsedDelay >= 0 ? parsedDelay : 0;
+
     dispatch(
       addTab({
         uid: uuid(),
         collectionUid: collection.uid,
         type: 'collection-runner'
       })
     );
     if (!isCollectionRunInProgress) {
-      dispatch(runCollectionFolder(collection.uid, item ? item.uid : null, recursive, Number(delay), tagsEnabled && tags));
+      dispatch(runCollectionFolder(collection.uid, item ? item.uid : null, recursive, delayMs, tagsEnabled && tags));
     }
     onClose();
   };
@@
             <input
               type="number"
               className="textbox w-1/2"
               placeholder="e.g. 5"
+              min="0"
+              step="1"
               autoComplete="off"
               autoCorrect="off"
               autoCapitalize="off"
               spellCheck="false"
               value={delay}
               onChange={(e) => setDelay(e.target.value)}
             />

Also applies to: 87-96

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`
at line 37, The dispatch call is passing Number(delay) unvalidated; normalize
and clamp delay before dispatching runCollectionFolder by parsing delay to an
integer (e.g., parseInt or Math.floor on Number(delay)), defaulting to 0 for
NaN, and ensuring it is non-negative (clamp negative values to 0) so invalid or
negative delays are never sent to IPC; apply the same normalization wherever
runCollectionFolder is dispatched in this file (including the other handler
around lines 87-96) and use the normalizedDelay variable in the dispatch call
instead of Number(delay).

Comment on lines +74 to +88
input[type='radio'] {
cursor: pointer;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.bg};
flex-shrink: 0;

&:checked {
border: 1px solid ${(props) => props.theme.primary.solid};
background-image: radial-gradient(circle, ${(props) => props.theme.primary.solid} 40%, ${(props) => props.theme.bg} 42%);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add visible keyboard focus state for custom radios.

Line 74-88 removes native radio appearance, but no focus indicator is added back. Keyboard users may lose track of focus.

♿ Suggested fix
   input[type='radio'] {
     cursor: pointer;
     appearance: none;
     width: 16px;
     height: 16px;
     border-radius: 50%;
     border: 1px solid ${(props) => props.theme.input.border};
     background-color: ${(props) => props.theme.bg};
     flex-shrink: 0;
 
+    &:focus-visible {
+      outline: 2px solid ${(props) => props.theme.primary.solid};
+      outline-offset: 2px;
+    }
+
     &:checked {
       border: 1px solid ${(props) => props.theme.primary.solid};
       background-image: radial-gradient(circle, ${(props) => props.theme.primary.solid} 40%, ${(props) => props.theme.bg} 42%);
     }
   }
📝 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.

Suggested change
input[type='radio'] {
cursor: pointer;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.bg};
flex-shrink: 0;
&:checked {
border: 1px solid ${(props) => props.theme.primary.solid};
background-image: radial-gradient(circle, ${(props) => props.theme.primary.solid} 40%, ${(props) => props.theme.bg} 42%);
}
}
input[type='radio'] {
cursor: pointer;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.bg};
flex-shrink: 0;
&:focus-visible {
outline: 2px solid ${(props) => props.theme.primary.solid};
outline-offset: 2px;
}
&:checked {
border: 1px solid ${(props) => props.theme.primary.solid};
background-image: radial-gradient(circle, ${(props) => props.theme.primary.solid} 40%, ${(props) => props.theme.bg} 42%);
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js`
around lines 74 - 88, The custom radio reset in StyledWrapper.js removes the
native focus indicator for the selector input[type='radio']; add a visible
keyboard focus state by targeting input[type='radio']:focus-visible (and/or
:focus) and applying a high-contrast outline or focus ring (e.g., outline or
box-shadow using props.theme.primary.solid or a contrast color) that is distinct
from the checked state; ensure the focus style works with the existing &:checked
rules and respects theme colors so keyboard users can see focus without changing
the visual design.

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js (1)

81-98: Add min attribute to prevent negative delay input.

The number input accepts negative values, which aren't meaningful for a delay. Adding min="0" provides clear UI feedback and aligns with the backend expectation.

♻️ Proposed fix
           <input
             id="runner-delay"
             type="number"
             className="textbox w-1/2"
             placeholder="e.g. 5"
+            min="0"
             autoComplete="off"
             autoCorrect="off"
             autoCapitalize="off"
             spellCheck="false"
             value={delay}
             onChange={(e) => setDelay(e.target.value)}
           />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`
around lines 81 - 98, The number input in the RunCollectionItem component allows
negative values for the delay (value and setter: delay, setDelay on the input
with id "runner-delay"); update the input element to include a min="0" attribute
to prevent negative numbers at the UI level and ensure user input aligns with
backend expectations, keeping the existing value and onChange handler intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js`:
- Around line 81-98: The number input in the RunCollectionItem component allows
negative values for the delay (value and setter: delay, setDelay on the input
with id "runner-delay"); update the input element to include a min="0" attribute
to prevent negative numbers at the UI level and ensure user input aligns with
backend expectations, keeping the existing value and onChange handler intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2de25581-ec5c-4d64-b2df-23cc3fad73dc

📥 Commits

Reviewing files that changed from the base of the PR and between ee4e13a and f68752f.

📒 Files selected for processing (3)
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/StyledWrapper.js

@gopu-bruno gopu-bruno force-pushed the feat/runner-ui-revamp branch from a732b17 to 58761bd Compare March 25, 2026 13:27
Copy link
Copy Markdown
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: 4

🧹 Nitpick comments (1)
tests/runner/runner-configuration.spec.ts (1)

22-156: Add one modal-path test for RunCollectionItem.

These cases only exercise the main Runner tab. The collection/folder modal got its own timings and filters UI in this PR, so regressions there would still slip through.

As per coding guidelines, "Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/runner/runner-configuration.spec.ts` around lines 22 - 156, The test
suite is missing a modal-path test for RunCollectionItem that exercises the
collection/folder modal UI (timings and filters); add a new test in this file
that uses buildRunnerLocators, opens the Runner with
openRunnerTab(COLLECTION_NAME), then triggers the RunCollectionItem modal (e.g.,
locate and click the collection/folder run button or RunCollectionItem trigger),
wait for the modal to appear, and assert the modal’s timing inputs and filter
controls are visible and interactive (use waitForRequestsInitialized where
appropriate and locators similar to locators.configPanel(),
locators.configCounter(), and locators.configResetButton() to verify state and
behavior). Ensure the test toggles a timing/filter value and confirms the change
persists or affects the counter/state, and include appropriate timeouts like the
other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-app/src/components/RunnerResults/index.jsx`:
- Line 180: The call to updateRunnerConfiguration is overwriting the stored
requestItemsOrder by passing selectedRequestItems as both the selected list and
the order; change the third argument so you pass the full preserved order (e.g.,
requestItemsOrder or runnerConfig.requestItemsOrder) instead of
selectedRequestItems so disabled/unselected items keep their original positions
when you dispatch(updateRunnerConfiguration(collection.uid,
selectedRequestItems, /* use existing requestItemsOrder here */, delay)); ensure
you reference the component or store variable that holds the current full order
(requestItemsOrder) rather than the selected subset.
- Around line 269-275: Add a stable Playwright locator by adding a data-testid
attribute (e.g. data-testid="runner-run-button") to the Button in the
RunnerResults component (the Button rendering "Run {selectedRequestItems.length}
Request(s)"), and update the test helper runCollectionButton in
tests/utils/page/runner.ts to use page.getByTestId('runner-run-button') instead
of looking up the text "Run Collection" so e2e flows use the stable locator.
- Line 181: Sanitize the delay before dispatching runCollectionFolder by parsing
delay/savedDelay once into a non-negative integer and reusing that value for
persistence and execution: compute sanitizedDelay = Math.max(0,
parseInt(delayOrSavedDelay, 10)) (or 0 if NaN) and pass sanitizedDelay to
dispatch(runCollectionFolder(collection.uid, null, true, sanitizedDelay, tags,
selectedRequestItems)) and to any save/persist calls; update references around
the dispatch and the similar block at lines 189-197 to use the same sanitized
variable instead of Number(delay)/Number(savedDelay).

In
`@packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx`:
- Around line 283-289: The dispatch calls that invoke updateRunnerConfiguration
(seen near setSelectedItems and where selection/order changes) replace the
entire runnerConfiguration and thus drop any saved delay; fix by threading the
current delay through these calls or updating the reducer to merge fields
instead of replacing: when calling updateRunnerConfiguration(collection.uid,
ordered, allRequestUidsOrder) include the current delay value (e.g., pull
runnerConfiguration.delay from props/state) so the action carries delay along
with selection/order, or change the reducer handling updateRunnerConfiguration
to merge incoming selection/order into existing runnerConfiguration (preserving
delay) rather than overwriting the whole object.

---

Nitpick comments:
In `@tests/runner/runner-configuration.spec.ts`:
- Around line 22-156: The test suite is missing a modal-path test for
RunCollectionItem that exercises the collection/folder modal UI (timings and
filters); add a new test in this file that uses buildRunnerLocators, opens the
Runner with openRunnerTab(COLLECTION_NAME), then triggers the RunCollectionItem
modal (e.g., locate and click the collection/folder run button or
RunCollectionItem trigger), wait for the modal to appear, and assert the modal’s
timing inputs and filter controls are visible and interactive (use
waitForRequestsInitialized where appropriate and locators similar to
locators.configPanel(), locators.configCounter(), and
locators.configResetButton() to verify state and behavior). Ensure the test
toggles a timing/filter value and confirms the change persists or affects the
counter/state, and include appropriate timeouts like the other tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f59e5614-4509-4f37-bf9e-5f1f2fc4cf0e

📥 Commits

Reviewing files that changed from the base of the PR and between f68752f and 58761bd.

📒 Files selected for processing (9)
  • packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/StyledWrapper.jsx
  • packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx
  • packages/bruno-app/src/components/RunnerResults/RunnerTags/index.jsx
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js
  • packages/bruno-app/src/components/RunnerResults/index.jsx
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RunCollectionItem/index.js
  • packages/bruno-app/src/components/TagList/StyledWrapper.js
  • tests/runner/runner-configuration.spec.ts
  • tests/utils/page/runner.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/RunnerResults/StyledWrapper.js

dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags));
}
dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems, delay));
dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tags, selectedRequestItems));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sanitize delay before dispatching the run.

Number(delay) / Number(savedDelay) can forward NaN, negatives, or fractional values straight to IPC. Parse once to a non-negative integer and reuse that value for both persistence and execution.

🛠️ Suggested fix
+const normalizeDelay = (value) => {
+  const parsed = Number(value);
+  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;
+};
+
 const runCollection = () => {
-    dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems, delay));
-    dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tags, selectedRequestItems));
+    const delayMs = normalizeDelay(delay);
+    dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems, delayMs));
+    dispatch(runCollectionFolder(collection.uid, null, true, delayMs, tags, selectedRequestItems));
   };
 
   const runAgain = () => {
     ensureCollectionIsMounted();
     // Get the saved configuration to determine what to run
     const savedConfiguration = get(collection, 'runnerConfiguration', null);
     const savedSelectedItems = savedConfiguration?.selectedRequestItems || [];
     const savedDelay = savedConfiguration?.delay !== undefined ? savedConfiguration.delay : delay;
+    const delayMs = normalizeDelay(savedDelay);
     dispatch(
       runCollectionFolder(
         collection.uid,
         runnerInfo.folderUid,
         true,
-        Number(savedDelay),
+        delayMs,
         tags,
         savedSelectedItems
       )
     );
   };

Also applies to: 189-197

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-app/src/components/RunnerResults/index.jsx` at line 181,
Sanitize the delay before dispatching runCollectionFolder by parsing
delay/savedDelay once into a non-negative integer and reusing that value for
persistence and execution: compute sanitizedDelay = Math.max(0,
parseInt(delayOrSavedDelay, 10)) (or 0 if NaN) and pass sanitizedDelay to
dispatch(runCollectionFolder(collection.uid, null, true, sanitizedDelay, tags,
selectedRequestItems)) and to any save/persist calls; update references around
the dispatch and the similar block at lines 189-197 to use the same sanitized
variable instead of Number(delay)/Number(savedDelay).

Comment on lines +283 to +289
if (changed) {
const ordered = flattenedRequests
.filter((r) => newSelected.includes(r.uid))
.map((r) => r.uid);
setSelectedItems(ordered);
const allRequestUidsOrder = flattenedRequests.map((item) => item.uid);
dispatch(updateRunnerConfiguration(collection.uid, ordered, allRequestUidsOrder));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

These config updates drop the saved delay.

updateRunnerConfiguration() replaces the whole runnerConfiguration object. The new tag-sync/select-all/reset dispatches only send selection and order, so any previously saved delay is cleared as soon as one of these handlers runs. Please thread the live delay through here, or merge config fields in the reducer instead of replacing them.

Also applies to: 359-367, 376-384

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/bruno-app/src/components/RunnerResults/RunConfigurationPanel/index.jsx`
around lines 283 - 289, The dispatch calls that invoke updateRunnerConfiguration
(seen near setSelectedItems and where selection/order changes) replace the
entire runnerConfiguration and thus drop any saved delay; fix by threading the
current delay through these calls or updating the reducer to merge fields
instead of replacing: when calling updateRunnerConfiguration(collection.uid,
ordered, allRequestUidsOrder) include the current delay value (e.g., pull
runnerConfiguration.delay from props/state) so the action carries delay along
with selection/order, or change the reducer handling updateRunnerConfiguration
to merge incoming selection/order into existing runnerConfiguration (preserving
delay) rather than overwriting the whole object.

@bijin-bruno bijin-bruno merged commit c2de480 into usebruno:main Mar 27, 2026
13 checks passed
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.

3 participants