Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const BulkImportCollectionLocation = ({
const [applyToGlobal, setApplyToGlobal] = useState(true);
const [applyToCollection, setApplyToCollection] = useState(false);
const [groupingType, setGroupingType] = useState('tags');
const [collectionFormat, setCollectionFormat] = useState('bru');
const [collectionFormat, setCollectionFormat] = useState('yml');

@vijayh-bruno vijayh-bruno Jun 26, 2026

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.

we have 'yml' set as a constant for default format- https://github.com/usebruno/bruno/blob/main/packages/bruno-filestore/src/constants.ts

use that instead of hardcoding. Do this everywhere

const [renamedCollectionNames, setRenamedCollectionNames] = useState({});
const [renamedEnvironmentNames, setRenamedEnvironmentNames] = useState({});
const [importIssues, setImportIssues] = useState({});
Expand Down Expand Up @@ -585,6 +585,7 @@ export const BulkImportCollectionLocation = ({
<Modal
size="md"
title="Bulk Import"
dataTestId="bulk-import-collection-location-modal"
confirmText={importStarted ? 'Close' : 'Import'}
confirmDisabled={Boolean(!selectedCollections?.length)}
handleConfirm={onSubmit}
Expand Down Expand Up @@ -836,6 +837,7 @@ export const BulkImportCollectionLocation = ({
<div className="font-semibold mb-2">Location</div>
<input
id="collection-location"
data-testid="bulk-import-collection-location-input"
type="text"
placeholder="Select a location to save the collection"
name="collectionLocation"
Expand Down Expand Up @@ -878,6 +880,7 @@ export const BulkImportCollectionLocation = ({
<select
id="format"
name="format"
data-testid="bulk-import-collection-format-selector"
className="block textbox mt-2 w-full"
value={collectionFormat}
onChange={(e) => setCollectionFormat(e.target.value)}
Expand Down
4 changes: 2 additions & 2 deletions packages/bruno-cli/src/utils/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ const safeWriteFileSync = (filePath, content) => {
* @param {string} dirPath - The output directory path
*/
const createCollectionFromBrunoObject = async (collection, dirPath, options = {}) => {
const { format = 'bru' } = options;
const { format = 'yml' } = options;
// Create brunoConfig for yml format
const brunoConfig = {
version: '1',
Expand Down Expand Up @@ -595,7 +595,7 @@ const createCollectionFromBrunoObject = async (collection, dirPath, options = {}
* @param {"bru"|"yml"} options.format - Current directory path
*/
const processCollectionItems = async (items = [], currentPath, options = {}) => {
const { format = 'bru' } = options;
const { format = 'yml' } = options;
for (const item of items) {
if (item.type === 'folder') {
// Create folder
Expand Down
57 changes: 57 additions & 0 deletions tests/import/bulk-import/yml-as-default-file-format.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { test, expect } from '../../../playwright';
import * as path from 'path';
import * as fs from 'fs';
import { closeAllCollections, openBulkImportModal } from '../../utils/page';
import { buildCommonLocators } from '../../utils/page/locators';

test.describe('Bulk Import default file format', () => {
const testDataDir = path.join(__dirname, '../test-data');
const filesToImport = [
path.join(testDataDir, 'sample-postman.json'),
path.join(testDataDir, 'sample-insomnia.json')
];

test.afterEach(async ({ page }) => {
await closeAllCollections(page);
});

test('Bulk import defaults to OpenCollection (YAML) and writes opencollection.yml collections', async ({ page, createTmpDir }) => {
const locators = buildCommonLocators(page);
const importDir = await createTmpDir('bulk-import-default-format');

await test.step('Open the bulk import modal and verify the File Format defaults to OpenCollection (YAML)', async () => {
await openBulkImportModal(page, filesToImport);

const formatSelect = locators.import.bulkFormatSelect();
await expect(formatSelect).toBeVisible();
await expect(formatSelect).toHaveValue('yml');
await expect(formatSelect.locator('option')).toHaveText(['OpenCollection (YAML)', 'BRU Format (.bru)']);
});

await test.step('Set the location, Click Import, then close the modal', async () => {
await locators.import.bulkLocationInput().fill(importDir);

await expect(locators.import.bulkSubmitButton()).toHaveText('Import');
await locators.import.bulkSubmitButton().click();

await expect(locators.import.bulkSubmitButton()).toHaveText('Close');
await locators.import.bulkSubmitButton().click();
await expect(locators.import.bulkModal()).toBeHidden();
});

await test.step('Verify each collection is written as opencollection.yml, not bruno.json', async () => {
const collectionDirs = fs
.readdirSync(importDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => path.join(importDir, entry.name));

expect(collectionDirs.length).toBeGreaterThan(0);

for (const dir of collectionDirs) {
const files = fs.readdirSync(dir);
expect(files).toContain('opencollection.yml');
expect(files).not.toContain('bruno.json');
}
});
});
});
21 changes: 21 additions & 0 deletions tests/utils/page/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@
// causing Playwright's "element is stable" actionability check to fail
// intermittently on slower machines. Use force to skip the stability check;
// visibility is already verified above via waitFor.
await page.getByRole('button', { name: 'Discard All and Remove' }).click({ force: true });

Check failure on line 73 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Detect Flaky Tests

[default] › tests/response/response-clearing.spec.ts:51:9 › Response Clearing › should clear response and test results (mixed)

3) [default] › tests/response/response-clearing.spec.ts:51:9 › Response Clearing › should clear response and test results (mixed) Error: locator.click: Target page, context or browser has been closed Call log: - waiting for getByRole('button', { name: 'Discard All and Remove' }) - locator resolved to <button type="button">…</button> - attempting click action - scrolling into view if needed - element was detached from the DOM, retrying at tests/utils/page/actions.ts:73 71 | // intermittently on slower machines. Use force to skip the stability check; 72 | // visibility is already verified above via waitFor. > 73 | await page.getByRole('button', { name: 'Discard All and Remove' }).click({ force: true }); | ^ 74 | } else { 75 | // Regular modal - click the submit button 76 | await page.locator('.bruno-modal-footer .submit').click(); at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:73:76 at closeAllCollections (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:46:3) at /home/runner/work/bruno/bruno/tests/response/response-clearing.spec.ts:19:5
} else {
// Regular modal - click the submit button
await page.locator('.bruno-modal-footer .submit').click();

Check failure on line 76 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/codeeditor-state/fold-persistence.spec.ts:111:7 › CodeEditor — fold state persists across tab switches › Body editor: folded {} block survives Body → Headers → Body

1) [default] › tests/codeeditor-state/fold-persistence.spec.ts:111:7 › CodeEditor — fold state persists across tab switches › Body editor: folded {} block survives Body → Headers → Body Error: locator.click: Target page, context or browser has been closed Call log: - waiting for locator('.bruno-modal-footer .submit') - locator resolved to <div class="StyledWrapper-fWWGoA Fecll submit">…</div> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 100ms - waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 500ms - waiting for element to be visible, enabled and stable at tests/utils/page/actions.ts:76 74 | } else { 75 | // Regular modal - click the submit button > 76 | await page.locator('.bruno-modal-footer .submit').click(); | ^ 77 | } 78 | 79 | // Wait for modal to close at /Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:76:59 at closeAllCollections (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:46:3) at /Users/runner/work/bruno/bruno/tests/codeeditor-state/fold-persistence.spec.ts:108:5
}

// Wait for modal to close
Expand Down Expand Up @@ -481,7 +481,7 @@
await test.step(`Import collection from "${filePath}"`, async () => {
const locators = buildCommonLocators(page);

await page.getByTestId('collections-header-add-menu').click();

Check failure on line 484 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (Linux)

[default] › tests/import/postman/import-many-issues-collection.spec.ts:12:7 › Import Postman Collection with many issues (URL too long warning) › should show URL-too-long warning when include failed request data is checked

2) [default] › tests/import/postman/import-many-issues-collection.spec.ts:12:7 › Import Postman Collection with many issues (URL too long warning) › should show URL-too-long warning when include failed request data is checked › Import collection from "/home/runner/work/bruno/bruno/tests/import/postman/fixtures/postman-with-many-import-issues.json" Error: locator.click: Target page, context or browser has been closed Call log: - waiting for getByTestId('collections-header-add-menu') - locator resolved to <button tabindex="-1" aria-expanded="false" data-tabindex="inline" title="Add new collection" data-testid="collections-header-add-menu" class="StyledWrapper-iYvzMB btWNSr action-icon">…</button> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 100ms 58 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 500ms at tests/utils/page/actions.ts:484 482 | const locators = buildCommonLocators(page); 483 | > 484 | await page.getByTestId('collections-header-add-menu').click(); | ^ 485 | await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click(); 486 | 487 | // Wait for import modal at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:484:59 at importCollection (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:481:14) at /home/runner/work/bruno/bruno/tests/import/postman/import-many-issues-collection.spec.ts:17:27

Check failure on line 484 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/import/postman/import-many-issues-collection.spec.ts:12:7 › Import Postman Collection with many issues (URL too long warning) › should show URL-too-long warning when include failed request data is checked

5) [default] › tests/import/postman/import-many-issues-collection.spec.ts:12:7 › Import Postman Collection with many issues (URL too long warning) › should show URL-too-long warning when include failed request data is checked › Import collection from "/Users/runner/work/bruno/bruno/tests/import/postman/fixtures/postman-with-many-import-issues.json" TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByTestId('collections-header-add-menu') - locator resolved to <button tabindex="-1" aria-expanded="false" data-tabindex="inline" title="Add new collection" data-testid="collections-header-add-menu" class="StyledWrapper-iYvzMB btWNSr action-icon">…</button> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 100ms 47 × waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper__Wrapper-jjqtyM DwvBf">…</div> subtree intercepts pointer events - retrying click action - waiting 500ms - waiting for element to be visible, enabled and stable - element is visible, enabled and stable - scrolling into view if needed at tests/utils/page/actions.ts:484 482 | const locators = buildCommonLocators(page); 483 | > 484 | await page.getByTestId('collections-header-add-menu').click(); | ^ 485 | await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click(); 486 | 487 | // Wait for import modal at /Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:484:59 at importCollection (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:481:14) at /Users/runner/work/bruno/bruno/tests/import/postman/import-many-issues-collection.spec.ts:17:27
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();

// Wait for import modal
Expand Down Expand Up @@ -524,6 +524,26 @@
});
};

/**
* Open the Bulk Import modal by importing multiple files at once.
* Selecting more than one file routes the import flow to the Bulk Import modal
* (instead of the single-collection location modal).
* @param page - The page object
* @param filePaths - Absolute paths of the files to import (must be 2 or more)
*/
const openBulkImportModal = async (page: Page, filePaths: string[]) => {
await test.step('Open the Bulk Import modal', async () => {
const locators = buildCommonLocators(page);

await locators.plusMenu.button().click();
await locators.plusMenu.importCollection().click();
await expect(locators.import.modal()).toBeVisible();

await locators.import.fileInput().setInputFiles(filePaths);
await expect(locators.import.bulkModal()).toBeVisible();
});
};

/**
* Remove a specific collection from the sidebar
* @param page - The page object
Expand Down Expand Up @@ -693,7 +713,7 @@
) => {
await test.step(`Add environment variable "${variable.name}"`, async () => {
const nameInput = page.locator(`input[name="${index}.name"]`);
await nameInput.waitFor({ state: 'visible' });

Check failure on line 716 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/environments/create-environment/collection-env-create.spec.ts:15:7 › Collection Environment Create Tests › should import collection and create environment for request usage

2) [default] › tests/environments/create-environment/collection-env-create.spec.ts:15:7 › Collection Environment Create Tests › should import collection and create environment for request usage › Create environment with variables › Add 5 environment variables › Add environment variable "postBody" Error: locator.waitFor: Target page, context or browser has been closed Call log: - waiting for locator('input[name="3.name"]') to be visible at tests/utils/page/actions.ts:716 714 | await test.step(`Add environment variable "${variable.name}"`, async () => { 715 | const nameInput = page.locator(`input[name="${index}.name"]`); > 716 | await nameInput.waitFor({ state: 'visible' }); | ^ 717 | await nameInput.fill(variable.name); 718 | 719 | // Wait for the CodeMirror editor in the row to be ready at /Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:716:21 at addEnvironmentVariable (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:714:14) at /Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:743:13 at addEnvironmentVariables (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:741:3) at /Users/runner/work/bruno/bruno/tests/environments/create-environment/collection-env-create.spec.ts:31:7 at /Users/runner/work/bruno/bruno/tests/environments/create-environment/collection-env-create.spec.ts:28:5
await nameInput.fill(variable.name);

// Wait for the CodeMirror editor in the row to be ready
Expand Down Expand Up @@ -1191,7 +1211,7 @@
}

throw new Error(`Tab "${tabName}" not found in visible tabs or overflow dropdown`);
}).toPass({ timeout: 15000 });

Check failure on line 1214 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (Linux)

[default] › tests/request/timeline/timeline-nested-runrequest.spec.ts:86:7 › Timeline — nested bru.runRequest bubbles inner scripted entries to outer Timeline › inner request's post-response sendRequest also bubbles to the outer Timeline

4) [default] › tests/request/timeline/timeline-nested-runrequest.spec.ts:86:7 › Timeline — nested bru.runRequest bubbles inner scripted entries to outer Timeline › inner request's post-response sendRequest also bubbles to the outer Timeline › Outer Timeline shows the bubbled post-response sendRequest row › Select tab "Timeline" in [data-testid="response-pane"] Error: Timed out 2000ms waiting for expect(locator).toContainClass(expected) Locator: locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Expected string: "active" Received: <element(s) not found> Call log: - expect.toContainClass with timeout 2000ms - waiting for locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Call Log: - Timeout 15000ms exceeded while waiting on the predicate at tests/utils/page/actions.ts:1214 1212 | 1213 | throw new Error(`Tab "${tabName}" not found in visible tabs or overflow dropdown`); > 1214 | }).toPass({ timeout: 15000 }); | ^ 1215 | }); 1216 | }; 1217 | at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1214:8 at selectPaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1174:3) at selectResponsePaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1219:3) at /home/runner/work/bruno/bruno/tests/request/timeline/timeline-nested-runrequest.spec.ts:119:7 at /home/runner/work/bruno/bruno/tests/request/timeline/timeline-nested-runrequest.spec.ts:118:5

Check failure on line 1214 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (Linux)

[default] › tests/request/timeline/timeline-nested-runrequest.spec.ts:20:7 › Timeline — nested bru.runRequest bubbles inner scripted entries to outer Timeline › inner request's sendRequest call shows up on the outer request's Timeline

3) [default] › tests/request/timeline/timeline-nested-runrequest.spec.ts:20:7 › Timeline — nested bru.runRequest bubbles inner scripted entries to outer Timeline › inner request's sendRequest call shows up on the outer request's Timeline › Outer Timeline shows three rows: main + runRequest + bubbled inner sendRequest › Select tab "Timeline" in [data-testid="response-pane"] Error: Timed out 2000ms waiting for expect(locator).toContainClass(expected) Locator: locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Expected string: "active" Received: <element(s) not found> Call log: - expect.toContainClass with timeout 2000ms - waiting for locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Call Log: - Timeout 15000ms exceeded while waiting on the predicate at tests/utils/page/actions.ts:1214 1212 | 1213 | throw new Error(`Tab "${tabName}" not found in visible tabs or overflow dropdown`); > 1214 | }).toPass({ timeout: 15000 }); | ^ 1215 | }); 1216 | }; 1217 | at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1214:8 at selectPaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1174:3) at selectResponsePaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:1219:3) at /home/runner/work/bruno/bruno/tests/request/timeline/timeline-nested-runrequest.spec.ts:56:7 at /home/runner/work/bruno/bruno/tests/request/timeline/timeline-nested-runrequest.spec.ts:55:5

Check failure on line 1214 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/request/timeline/timeline-runrequest-skip.spec.ts:18:7 › Timeline — bru.runRequest skips unsupported item types › shows Skipped rows for WS and gRPC targets

6) [default] › tests/request/timeline/timeline-runrequest-skip.spec.ts:18:7 › Timeline — bru.runRequest skips unsupported item types › shows Skipped rows for WS and gRPC targets › Timeline has main + two Skipped runRequest rows › Select tab "Timeline" in [data-testid="response-pane"] Error: Timed out 2000ms waiting for expect(locator).toContainClass(expected) Locator: locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Expected string: "active" Received: <element(s) not found> Call log: - expect.toContainClass with timeout 2000ms - waiting for locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) Call Log: - Timeout 15000ms exceeded while waiting on the predicate at tests/utils/page/actions.ts:1214 1212 | 1213 | throw new Error(`Tab "${tabName}" not found in visible tabs or overflow dropdown`); > 1214 | }).toPass({ timeout: 15000 }); | ^ 1215 | }); 1216 | }; 1217 | at /Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:1214:8 at selectPaneTab (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:1174:3) at selectResponsePaneTab (/Users/runner/work/bruno/bruno/tests/utils/page/actions.ts:1219:3) at /Users/runner/work/bruno/bruno/tests/request/timeline/timeline-runrequest-skip.spec.ts:43:7 at /Users/runner/work/bruno/bruno/tests/request/timeline/timeline-runrequest-skip.spec.ts:42:5
});
};

Expand Down Expand Up @@ -2099,6 +2119,7 @@
deleteRequest,
deleteCollectionFromOverview,
importCollection,
openBulkImportModal,
removeCollection,
createFolder,
openEnvironmentSelector,
Expand Down
4 changes: 4 additions & 0 deletions tests/utils/page/locators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ export const buildCommonLocators = (page: Page) => ({
locationModal: () => page.locator('[data-testid="import-collection-location-modal"]'),
locationInput: () => page.locator('#collection-location'),
fileInput: () => page.locator('input[type="file"]'),
bulkModal: () => page.getByTestId('bulk-import-collection-location-modal'),
bulkFormatSelect: () => page.getByTestId('bulk-import-collection-location-modal').getByTestId('bulk-import-collection-format-selector'),
bulkLocationInput: () => page.getByTestId('bulk-import-collection-location-modal').getByTestId('bulk-import-collection-location-input'),
bulkSubmitButton: () => page.getByTestId('bulk-import-collection-location-modal-submit-btn'),
envOption: (name: string) => page.locator('.dropdown-item').getByText(name, { exact: true }),
parsingError: () => page.getByTestId('import-error-message'),
browseLink: (root?: Locator) => (root ?? page).getByTestId('import-collection-browse-link'),
Expand Down
Loading