Files
bruno/packages/bruno-converters/tests/postman/postman-to-bruno/partial-import.spec.js
sanish chirayath 244f528277 feat(import): enhance import functionality with issue tracking and logging (#8098)
* feat: enhance import functionality with issue tracking and logging

- Updated the import process to return both collections and issues for better error handling.
- Introduced a new toast notification for displaying import issues, allowing users to copy or report them.
- Enhanced logging for import issues, capturing errors and warnings during the import process.
- Added new components for actionable toasts and import issues display.
- Updated tests to validate the new import behavior and issue tracking.

* feat: enhance import issues handling with new toast notifications and tests

- Added optional testId prop to ActionableToast for better test targeting.
- Updated ImportIssuesToast to include data-testid attributes for improved e2e testing.
- Introduced a new Postman collection fixture to test partial import scenarios.
- Created new tests to validate the import process, including issue reporting and copying functionality.
- Implemented utility functions to manage import issues toasts during tests.

* fix: improve clipboard copy functionality and handle import issues more robustly

- Updated BulkImportCollectionLocation to always set import issues, ensuring consistent state management.
- Enhanced clipboard copy functionality in ImportIssuesToast and BulkImportCollectionLocation to handle errors gracefully with user feedback.
- Added aria-label for better accessibility in ActionableToast close button.

* refactor: enhance import issue logging and toast notifications

- Improved logging in BulkImportCollectionLocation and ImportCollectionLocation to provide detailed summaries of import issues, including counts of skipped items and warnings.
- Updated ImportIssuesToast to handle long issue descriptions and provide user feedback for copying issue details to the clipboard.
- Removed ActionableToast component and its styles, consolidating toast functionality within ImportIssuesToast for better maintainability.
- Enhanced styling for ImportIssuesToast to improve user experience and accessibility.

* refactor: update logging level for import issues in BulkImportCollectionLocation and ImportCollectionLocation

- Changed log type from 'error' to 'warn' for import issue summaries in both components to better reflect the severity of the messages.
- This adjustment improves clarity in the logging system and aligns with the intended handling of import warnings.

* feat: enhance ImportIssuesToast with URL length warning and styling improvements

- Added an alert icon and improved styling for the URL-too-long warning in ImportIssuesToast to enhance user experience.
- Introduced a new test for verifying the display of the URL length warning when importing collections with many issues.
- Updated locators to include a test ID for the URL-too-long warning, facilitating better end-to-end testing.

* style: update ImportIssuesToast styling for improved user experience

- Changed background and border colors in StyledWrapper for better visual consistency.
- Enhanced box-shadow and close button styles for improved accessibility and interaction.
- Adjusted padding and gap in warning messages for better layout and readability.
2026-05-28 19:41:03 +05:30

200 lines
7.0 KiB
JavaScript

import { describe, it, expect } from '@jest/globals';
import postmanToBruno from '../../../src/postman/postman-to-bruno';
const makeCollection = (items, overrides = {}) => ({
info: {
_postman_id: 'test-id',
name: 'Test Collection',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: items,
...overrides
});
const makeRequest = (name, method = 'GET', url = 'https://example.com') => ({
name,
request: {
method,
header: [],
url: { raw: url, protocol: 'https', host: ['example', 'com'] }
}
});
describe('partial-import', () => {
it('should import valid items and skip items with missing method', async () => {
const items = [
makeRequest('Valid Request 1'),
{ name: 'Bad Request', request: { method: null, header: [], url: { raw: 'https://example.com' } } },
makeRequest('Valid Request 2')
];
const { collection, issues } = await postmanToBruno(makeCollection(items));
expect(collection.items).toHaveLength(2);
expect(collection.items[0].name).toBe('Valid Request 1');
expect(collection.items[1].name).toBe('Valid Request 2');
expect(issues).toHaveLength(1);
expect(issues[0]).toMatchObject({
path: 'Bad Request',
severity: 'error',
message: 'Missing or invalid request method'
});
});
it('should import valid items and record errors for items that throw', async () => {
// Create a request with a value that will cause ensureString to throw (circular ref)
const circular = {};
circular.self = circular;
const items = [
makeRequest('Valid Request'),
{
name: 'Circular Request',
request: {
method: 'POST',
header: [{ key: circular, value: 'test' }],
url: { raw: 'https://example.com' }
}
}
];
const { collection, issues } = await postmanToBruno(makeCollection(items));
expect(collection.items).toHaveLength(1);
expect(collection.items[0].name).toBe('Valid Request');
expect(issues).toHaveLength(1);
expect(issues[0].path).toBe('Circular Request');
expect(issues[0].severity).toBe('error');
});
it('should record issues for nested folder items with full path', async () => {
const items = [
{
name: 'My Folder',
item: [
{
name: 'Subfolder',
item: [
{ name: 'Bad Nested', request: { method: null, header: [], url: { raw: 'https://example.com' } } },
makeRequest('Good Nested')
]
}
]
}
];
const { collection, issues } = await postmanToBruno(makeCollection(items));
expect(collection.items).toHaveLength(1);
expect(collection.items[0].type).toBe('folder');
expect(collection.items[0].items[0].type).toBe('folder');
expect(collection.items[0].items[0].items).toHaveLength(1);
expect(collection.items[0].items[0].items[0].name).toBe('Good Nested');
expect(issues).toHaveLength(1);
expect(issues[0].path).toBe('My Folder / Subfolder / Bad Nested');
expect(issues[0].severity).toBe('error');
});
it('should return empty issues array for valid collections', async () => {
const items = [
makeRequest('Request 1'),
makeRequest('Request 2')
];
const { collection, issues } = await postmanToBruno(makeCollection(items));
expect(collection.items).toHaveLength(2);
expect(issues).toEqual([]);
});
it('should handle empty collections with no issues', async () => {
const { collection, issues } = await postmanToBruno(makeCollection([]));
expect(collection.items).toEqual([]);
expect(issues).toEqual([]);
});
it('should handle all items being malformed without throwing', async () => {
const items = [
{ name: 'Bad 1', request: { method: null, header: [], url: { raw: 'https://example.com' } } },
{ name: 'Bad 2', request: { method: undefined, header: [], url: { raw: 'https://example.com' } } }
];
const { collection, issues } = await postmanToBruno(makeCollection(items));
expect(collection.items).toEqual([]);
expect(issues).toHaveLength(2);
expect(issues.every((i) => i.severity === 'error')).toBe(true);
});
it('should record warning for malformed collection-level variables', async () => {
const collectionData = makeCollection([makeRequest('Valid')], {
variable: 'not-an-array'
});
const { collection, issues } = await postmanToBruno(collectionData);
expect(collection.items).toHaveLength(1);
const warnings = issues.filter((i) => i.severity === 'warning');
expect(warnings).toHaveLength(1);
expect(warnings[0].path).toBe('Collection Variables');
});
it('should handle mixed valid/invalid items with folders and bad variables', async () => {
const items = [
makeRequest('r1', 'POST'),
{ name: 'r2-missing-method', request: { header: [], url: { raw: 'https://example.com' } } },
makeRequest('r3', 'POST'),
{
name: 'API Folder',
item: [
makeRequest('valid-in-folder', 'GET'),
{ name: 'r4-null-method', request: { method: null, header: [], url: { raw: 'https://example.com' } } },
{
name: 'Nested Subfolder',
item: [
{ name: 'r5-empty-method', request: { method: '', header: [], url: { raw: 'https://example.com' } } },
makeRequest('valid-nested', 'DELETE')
]
}
]
}
];
const { collection, issues } = await postmanToBruno(makeCollection(items, { variable: 'not-an-array' }));
// 4 valid items: r1, r3, valid-in-folder, valid-nested
expect(collection.items).toHaveLength(3); // r1, r3, API Folder
expect(collection.items[0].name).toBe('r1');
expect(collection.items[1].name).toBe('r3');
expect(collection.items[2].name).toBe('API Folder');
expect(collection.items[2].items).toHaveLength(2); // valid-in-folder, Nested Subfolder
expect(collection.items[2].items[0].name).toBe('valid-in-folder');
expect(collection.items[2].items[1].name).toBe('Nested Subfolder');
expect(collection.items[2].items[1].items).toHaveLength(1); // valid-nested
expect(collection.items[2].items[1].items[0].name).toBe('valid-nested');
// 4 issues: 1 warning (variables) + 3 errors (missing/null/empty method)
expect(issues).toHaveLength(4);
expect(issues.filter((i) => i.severity === 'warning')).toHaveLength(1);
expect(issues.filter((i) => i.severity === 'error')).toHaveLength(3);
expect(issues.find((i) => i.path === 'r2-missing-method')).toBeTruthy();
expect(issues.find((i) => i.path === 'API Folder / r4-null-method')).toBeTruthy();
expect(issues.find((i) => i.path === 'API Folder / Nested Subfolder / r5-empty-method')).toBeTruthy();
});
it('should use fallback name for items without a name', async () => {
const items = [
{ request: { method: null, header: [], url: { raw: 'https://example.com' } } }
];
const { issues } = await postmanToBruno(makeCollection(items));
expect(issues).toHaveLength(1);
expect(issues[0].path).toBe('Item 1');
});
});