Skip to content

feat: add preserve scripts option for Postman import/export#8726

Open
gopu-bruno wants to merge 7 commits into
usebruno:mainfrom
gopu-bruno:feat/preserve-scripts-on-postman-conversion
Open

feat: add preserve scripts option for Postman import/export#8726
gopu-bruno wants to merge 7 commits into
usebruno:mainfrom
gopu-bruno:feat/preserve-scripts-on-postman-conversion

Conversation

@gopu-bruno

@gopu-bruno gopu-bruno commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds support for preserving Postman scripts during import and export instead of translating them into Bruno scripts.
Introduce an optional preserve scripts mode that skips script translation while continuing to convert the rest of the collection collection structure, requests, variables, and settings.

Ref: BRU-2573

When enabled:

  • Pre-request and test scripts are preserved exactly as they exist in the source collection.
  • Collection structure, requests, variables, folders, and settings continue to be converted normally.
  • Users can toggle between translated scripts (existing behavior - default) and preserved scripts depending on their use case.

Screen Recording

  • Import flow
Screen.Recording.2026-07-17.at.1.10.38.PM.mov
  • Export flow
Screen.Recording.2026-07-17.at.1.11.45.PM.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

    • Added a dedicated Postman export modal with filename, location, overwrite, and advanced options.
    • Added an option to preserve scripts when importing from or exporting to Postman.
    • Added improved validation and feedback for export conflicts and invalid filenames.
    • Renamed file format controls to clearer “Advanced Options” controls.
  • Bug Fixes

    • Improved handling of existing export files, including replace and rename flows.
    • Added safeguards to prevent exporting files outside the selected destination.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Postman import and export now support optional script preservation. Exporting uses a dedicated modal and Electron file-writing IPC, while importing propagates the option through UI, IPC, and conversion layers. Tests cover translation, round trips, validation, overwrite, and rename flows.

Changes

Postman preservation and export

Layer / File(s) Summary
Postman export modal and routing
packages/bruno-app/src/components/ShareCollection/index.js, packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/*
Postman selection opens a dedicated export modal with filename/location validation, browsing, advanced script preservation, and replace handling.
Import advanced options and option wiring
packages/bruno-app/src/components/Sidebar/CreateCollection/index.js, packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/*, packages/bruno-app/src/utils/importers/postman-collection.js
Import controls use an Advanced Options toggle and expose Postman script preservation while passing options into conversion.
Preserve-scripts conversion behavior
packages/bruno-converters/src/postman/*
Import and export converters preserve script contents when enabled and retain existing translation behavior by default.
JSON export and Electron filesystem bridge
packages/bruno-app/src/utils/exporters/postman-collection.js, packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js, packages/bruno-electron/src/ipc/collection.js
Export generation returns JSON, and a dedicated IPC path validates directories, prevents traversal, applies overwrite rules, and writes files.
Conversion and end-to-end validation
packages/bruno-converters/tests/postman/*, tests/export/postman/*, tests/import/postman/*, tests/utils/page/*
Tests cover preserved and translated scripts, round trips, modal behavior, validation, overwrite, renaming, fixtures, locators, and Playwright helpers.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ShareCollection
  participant ExportToPostman
  participant Converter
  participant ElectronIPC
  User->>ShareCollection: select Postman and proceed
  ShareCollection->>ExportToPostman: show export modal
  User->>ExportToPostman: submit filename, location, and options
  ExportToPostman->>Converter: generate Postman JSON
  ExportToPostman->>ElectronIPC: write JSON with overwrite policy
  ElectronIPC-->>ExportToPostman: return success or conflict
  ExportToPostman-->>User: show export or replace result
Loading

Possibly related PRs

  • usebruno/bruno#8654: Implements the matching Postman preserve-scripts import/export flow and ShareCollection routing.

Suggested reviewers: bijin-bruno

Poem

Scripts may wander, scripts may stay,
Through Postman’s gate they find their way.
A modal blooms, files safely land,
IPC lends a careful hand.
Preserve the code, or translate bright—
Round trips return the text just right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a preserve-scripts option for Postman import/export.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (5)
tests/import/postman/import-preserve-scripts.spec.ts (1)

23-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Route these test IDs through the page module. The spec inlines page.getByTestId('preserve-scripts-toggle') and page.getByTestId('show-advanced-options-toggle'), yet locators.import.preserveScriptsToggle() and locators.import.advancedOptionsToggle() already exist (see tests/utils/page/actions.ts L517-519). Reuse them so the spec only consumes locator groups.

As per path instructions: "do not inline page.locator(...) / page.getByTestId(...) in specs; keep locators and interaction helpers in tests/utils/page/*".

♻️ Suggested change
   await test.step('Checkbox is hidden until Advanced Options is shown', async () => {
-    await expect(page.getByTestId('preserve-scripts-toggle')).toHaveCount(0);
+    await expect(locators.import.preserveScriptsToggle()).toHaveCount(0);
   });

   await test.step('Advanced Options toggle reveals the Preserve scripts checkbox', async () => {
     const locationModal = locators.import.locationModal();
     await locationModal.getByRole('button', { name: 'Options' }).click();
-    await page.getByTestId('show-advanced-options-toggle').click();
-    await expect(page.getByTestId('preserve-scripts-toggle')).toBeVisible();
+    await locators.import.advancedOptionsToggle().click();
+    await expect(locators.import.preserveScriptsToggle()).toBeVisible();
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/import/postman/import-preserve-scripts.spec.ts` around lines 23 - 32,
Replace the inline page.getByTestId calls in the “Checkbox is hidden until
Advanced Options is shown” and “Advanced Options toggle reveals the Preserve
scripts checkbox” steps with the existing
locators.import.preserveScriptsToggle() and
locators.import.advancedOptionsToggle() helpers. Keep the test interactions and
assertions unchanged while routing all locator access through the page module.

Source: Path instructions

tests/export/postman/export-preserve-scripts.spec.ts (1)

1-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Terminate new TypeScript statements with semicolons.

  • tests/export/postman/export-preserve-scripts.spec.ts#L1-L145: add semicolons throughout the new spec.
  • tests/import/postman/import-file-format-options.spec.ts#L34-L34: terminate the click statement.
  • tests/import/postman/import-file-format-options.spec.ts#L52-L54: terminate the new declarations and assertions.

As per coding guidelines, TypeScript statements must end with semicolons.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/export/postman/export-preserve-scripts.spec.ts` around lines 1 - 145,
Terminate all new TypeScript statements with semicolons in
tests/export/postman/export-preserve-scripts.spec.ts, including declarations,
calls, assertions, and test blocks. Also add semicolons to the click statement
at tests/import/postman/import-file-format-options.spec.ts:34 and to the new
declarations and assertions at lines 52-54; make no other changes.

Source: Coding guidelines

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

309-317: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Centralize the new export and advanced-option selectors.

  • tests/utils/page/locators.ts#L309-L317: add modal-scoped conflict and validation-message locators to export.
  • tests/export/postman/export-preserve-scripts.spec.ts#L63-L64: use centralized conflict locators.
  • tests/export/postman/export-preserve-scripts.spec.ts#L89-L90: use centralized required-field error locators.
  • tests/export/postman/export-preserve-scripts.spec.ts#L107-L107: use a centralized invalid-name error locator.
  • tests/export/postman/export-preserve-scripts.spec.ts#L127-L134: use centralized conflict and export-button locators.
  • tests/import/postman/import-file-format-options.spec.ts#L34-L34: use locators.import.advancedOptionsToggle().
  • tests/import/postman/import-file-format-options.spec.ts#L52-L54: use locators.import.advancedOptionsToggle().

As per path instructions, specs must not inline raw selectors and new UI controls belong in tests/utils/page/*.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/utils/page/locators.ts` around lines 309 - 317, Centralize the export
modal conflict, validation-message, and export-button selectors in the export
section of tests/utils/page/locators.ts, alongside postmanModal and the existing
advanced-option locators. Update
tests/export/postman/export-preserve-scripts.spec.ts at lines 63-64, 89-90, 107,
and 127-134 to use those centralized locators instead of inline selectors.
Update tests/import/postman/import-file-format-options.spec.ts at lines 34 and
52-54 to use locators.import.advancedOptionsToggle().

Source: Path instructions

packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/index.js (1)

97-106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated Advanced-Options dropdown pattern; consider extracting + dropping forwardRef.

This inline AdvancedOptions forwardRef component mirrors nearly identical implementations in CreateCollection/index.js and ImportCollectionLocation/index.js (state + dropdown button + caret icon). Since the codebase is on React 19, forwardRef is no longer needed — ref can be received as a plain prop — and a shared <OptionsDropdownButton> component would remove the triplicated boilerplate across all three files.

♻️ Example modernization (React 19 ref-as-prop)
-  const AdvancedOptions = forwardRef((props, ref) => {
-    return (
-      <div ref={ref} className="flex items-center text-link cursor-pointer">
-        <button className="btn-advanced" type="button">
-          Options
-        </button>
-        <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
-      </div>
-    );
-  });
+  const AdvancedOptions = ({ ref }) => (
+    <div ref={ref} className="flex items-center text-link cursor-pointer">
+      <button className="btn-advanced" type="button">
+        Options
+      </button>
+      <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
+    </div>
+  );

Based on React 19 release notes, "In React 19, forwardRef is no longer necessary. Pass ref as a prop instead."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/index.js`
around lines 97 - 106, Extract the duplicated AdvancedOptions dropdown markup
from ExportToPostman, CreateCollection, and ImportCollectionLocation into a
shared OptionsDropdownButton component. Replace each forwardRef wrapper with
React 19 ref-as-a-prop handling, preserving the existing button styling, Options
label, caret icon, and dropdown behavior.
packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js (1)

19-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate "checkbox option label/description" CSS added under two different class names. Both new blocks style the identical pattern (font-weight 500 label; 0.75rem subtext-colored description with top margin) but were named independently, so future style tweaks need to be applied twice.

  • packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js#L19-L27: rename .preserve-scripts-label/.preserve-scripts-description (and the matching JSX classNames in this modal's index.js) to the more generic .checkbox-option-label/.checkbox-option-description already used elsewhere, and drop this duplicate block.
  • packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js#L25-L33: keep .checkbox-option-label/.checkbox-option-description as the single shared definition that the Postman export modal now reuses.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js`
around lines 19 - 27, The checkbox label and description styles are duplicated
under modal-specific class names. In
packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js
lines 19-27, remove the duplicate preserve-scripts styles; update the matching
JSX classNames in that modal’s index.js to use checkbox-option-label and
checkbox-option-description. In
packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js
lines 25-33, keep the existing shared definitions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/index.js`:
- Around line 61-68: Update the catch handling in the ExportToPostman export
flow to detect file-exists failures using a structured backend error code or
flag rather than matching error.message text. When overwrite is disabled and
that structured indicator is present, keep calling formik.setFieldError with
FILE_EXISTS_ERROR and return; leave other errors on the existing generic
handling path.

---

Nitpick comments:
In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/index.js`:
- Around line 97-106: Extract the duplicated AdvancedOptions dropdown markup
from ExportToPostman, CreateCollection, and ImportCollectionLocation into a
shared OptionsDropdownButton component. Replace each forwardRef wrapper with
React 19 ref-as-a-prop handling, preserving the existing button styling, Options
label, caret icon, and dropdown behavior.

In
`@packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js`:
- Around line 19-27: The checkbox label and description styles are duplicated
under modal-specific class names. In
packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js
lines 19-27, remove the duplicate preserve-scripts styles; update the matching
JSX classNames in that modal’s index.js to use checkbox-option-label and
checkbox-option-description. In
packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js
lines 25-33, keep the existing shared definitions unchanged.

In `@tests/export/postman/export-preserve-scripts.spec.ts`:
- Around line 1-145: Terminate all new TypeScript statements with semicolons in
tests/export/postman/export-preserve-scripts.spec.ts, including declarations,
calls, assertions, and test blocks. Also add semicolons to the click statement
at tests/import/postman/import-file-format-options.spec.ts:34 and to the new
declarations and assertions at lines 52-54; make no other changes.

In `@tests/import/postman/import-preserve-scripts.spec.ts`:
- Around line 23-32: Replace the inline page.getByTestId calls in the “Checkbox
is hidden until Advanced Options is shown” and “Advanced Options toggle reveals
the Preserve scripts checkbox” steps with the existing
locators.import.preserveScriptsToggle() and
locators.import.advancedOptionsToggle() helpers. Keep the test interactions and
assertions unchanged while routing all locator access through the page module.

In `@tests/utils/page/locators.ts`:
- Around line 309-317: Centralize the export modal conflict, validation-message,
and export-button selectors in the export section of
tests/utils/page/locators.ts, alongside postmanModal and the existing
advanced-option locators. Update
tests/export/postman/export-preserve-scripts.spec.ts at lines 63-64, 89-90, 107,
and 127-134 to use those centralized locators instead of inline selectors.
Update tests/import/postman/import-file-format-options.spec.ts at lines 34 and
52-54 to use locators.import.advancedOptionsToggle().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2561b53-a6c4-4982-83b9-c00e1aa41990

📥 Commits

Reviewing files that changed from the base of the PR and between 948a34a and fb96c13.

📒 Files selected for processing (22)
  • packages/bruno-app/src/components/ShareCollection/index.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/ExportCollection/ExportToPostman/index.js
  • packages/bruno-app/src/components/Sidebar/CreateCollection/index.js
  • packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
  • packages/bruno-app/src/utils/exporters/postman-collection.js
  • packages/bruno-app/src/utils/importers/postman-collection.js
  • packages/bruno-converters/src/postman/bruno-to-postman.js
  • packages/bruno-converters/src/postman/postman-to-bruno.js
  • packages/bruno-converters/tests/postman/preserve-scripts.spec.js
  • packages/bruno-electron/src/ipc/collection.js
  • tests/export/postman/export-preserve-scripts.spec.ts
  • tests/export/postman/fixtures/collection/bruno.json
  • tests/export/postman/fixtures/collection/login.bru
  • tests/export/postman/init-user-data/preferences.json
  • tests/import/postman/fixtures/postman-with-scripts.json
  • tests/import/postman/import-file-format-options.spec.ts
  • tests/import/postman/import-preserve-scripts.spec.ts
  • tests/utils/page/actions.ts
  • tests/utils/page/locators.ts


if (i.event) {
if (useWorkers) {
if (useWorkers && !preserveScripts) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This check seems to be redundant at 2 more places. If these methods are not called seperately and depend on each other, can you see if we can commpute the boolean value once and use that across ?

@utkarsh-bruno utkarsh-bruno left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Check the comment once, the rest of the PR looks fine. If it needs to be computed at multiple places and resolve the comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants