fix(import): Import fails for gRPC request without URL in exported collection (#8367)

* fix(import): Import fails for gRPC request without URL in exported collection

* changed the approach for import request with missing url

* addressed review comment

* added a fixture for http example request

* added an assertion to check whether the request is being imported or not

* added an assertion to check whether the request is being imported or not

* refactored test scripts
This commit is contained in:
sharan-bruno
2026-07-01 14:37:23 +05:30
committed by GitHub
parent ed468dba88
commit 4f31e9b073
7 changed files with 235 additions and 0 deletions

View File

@@ -185,6 +185,7 @@ const ExampleItem = ({ example, item, collection }) => {
return (
<StyledWrapper
ref={exampleRef}
data-testid="sidebar-response-example-item"
className={itemRowClassName}
onClick={handleExampleClick}
onDoubleClick={handleDoubleClick}

View File

@@ -11,6 +11,7 @@ import { BrunoError } from 'utils/common/error';
import { isOpenApiSpec } from './openapi-collection';
import { isPostmanCollection } from './postman-collection';
import { isInsomniaCollection } from './insomnia-collection';
import { valueToString } from '@usebruno/common/utils';
export const validateSchema = async (collections = []) => {
collections = Array.isArray(collections) ? collections : [collections];
@@ -98,6 +99,7 @@ export const transformItemsInCollection = (collection) => {
item.type = `${item.type}-request`;
const isGrpcRequest = item.type === 'grpc-request';
const isWSRequest = item.type === 'ws-request';
item.request.url = valueToString(item.request.url);
if (item.request.query) {
item.request.params = item.request.query.map((queryItem) => ({
@@ -137,6 +139,10 @@ export const transformItemsInCollection = (collection) => {
const isGrpcExample = example.type === 'grpc-request';
const isWSExample = example.type === 'ws-request';
if (example.request) {
example.request.url = valueToString(example.request.url);
}
if (example.request && example.request.query) {
example.request.params = example.request.query.map((queryItem) => ({
...queryItem,

View File

@@ -0,0 +1,51 @@
{
"name": "GRPC Collection",
"version": "1",
"items": [
{
"type": "grpc",
"name": "grpc request without url",
"filename": "request-without-url.bru",
"seq": 2,
"settings": {},
"tags": [],
"examples": [],
"request": {
"method": "",
"headers": [],
"body": {
"mode": "grpc",
"formUrlEncoded": [],
"multipartForm": [],
"file": [],
"grpc": [
{
"name": "message 1",
"content": "{}"
}
]
},
"script": {},
"vars": {},
"assertions": [],
"tests": "",
"docs": "",
"auth": {
"mode": "none"
}
}
}
],
"environments": [],
"brunoConfig": {
"version": "1",
"name": "GRPC Collection",
"type": "collection",
"ignore": [
"node_modules",
".git"
],
"size": 0,
"filesCount": 0
}
}

View File

@@ -0,0 +1,83 @@
{
"name": "HTTP Example Collection",
"version": "1",
"items": [
{
"type": "http",
"name": "http example request without url",
"filename": "request-with-example.bru",
"seq": 1,
"settings": {},
"tags": [],
"examples": [
{
"uid": "ex0011223344556677889",
"itemUid": "it0011223344556677889",
"name": "Http request example response",
"description": "Example whose request has no url",
"type": "http",
"request": {
"method": "GET",
"headers": [],
"params": [],
"body": {
"mode": "none"
}
},
"response": {
"status": 200,
"statusText": "OK",
"headers": [
{
"uid": "hd0011223344556677889",
"name": "content-type",
"value": "application/json",
"enabled": true
}
],
"body": {
"type": "json",
"content": "{\n \"message\": \"hello\"\n}"
}
}
}
],
"request": {
"method": "GET",
"headers": [],
"url": "",
"params": [],
"body": {
"mode": "none",
"json": null,
"text": null,
"xml": null,
"sparql": null,
"formUrlEncoded": [],
"multipartForm": [],
"file": []
},
"script": {},
"vars": {},
"assertions": [],
"tests": "",
"docs": "",
"auth": {
"mode": "none"
}
}
}
],
"environments": [],
"brunoConfig": {
"version": "1",
"name": "HTTP Example Collection",
"type": "collection",
"ignore": [
"node_modules",
".git"
],
"size": 0,
"filesCount": 0
}
}

View File

@@ -0,0 +1,50 @@
{
"name": "HTTP Collection",
"version": "1",
"items": [
{
"type": "http",
"name": "http request without url",
"filename": "request-without-url.bru",
"seq": 2,
"settings": {},
"tags": [],
"examples": [],
"request": {
"method": "GET",
"headers": [],
"params": [],
"body": {
"mode": "none",
"json": null,
"text": null,
"xml": null,
"sparql": null,
"formUrlEncoded": [],
"multipartForm": [],
"file": []
},
"script": {},
"vars": {},
"assertions": [],
"tests": "",
"docs": "",
"auth": {
"mode": "none"
}
}
}
],
"environments": [],
"brunoConfig": {
"version": "1",
"name": "HTTP Collection",
"type": "collection",
"ignore": [
"node_modules",
".git"
],
"size": 0,
"filesCount": 0
}
}

View File

@@ -0,0 +1,40 @@
import { test, expect } from '../../../playwright';
import * as path from 'path';
import { importCollection, closeAllCollections, buildCommonLocators } from '../../utils/page';
test.describe('Import Bruno Collection - request with missing URL', () => {
test.afterAll(async ({ page }) => {
await closeAllCollections(page);
});
const cases = [
{ fixture: 'bruno-http-request-missing-url.json', collectionName: 'HTTP Collection', type: 'HTTP', requestName: 'http request without url' },
{ fixture: 'bruno-grpc-request-missing-url.json', collectionName: 'GRPC Collection', type: 'gRPC', requestName: 'grpc request without url' },
{ fixture: 'bruno-http-example-request-missing-url.json', collectionName: 'HTTP Example Collection', type: 'HTTP example', requestName: 'http example request without url', exampleName: 'Http request example response' }
];
for (const { fixture, collectionName, type, requestName, exampleName } of cases) {
test(`imports a ${type} request without a URL`, async ({ page, createTmpDir }) => {
const brunoFile = path.resolve(__dirname, 'fixtures', fixture);
const location = await createTmpDir(collectionName);
await test.step('Import the collection', async () => {
await importCollection(page, brunoFile, location, {
expectedCollectionName: collectionName
});
});
await test.step(`Verify the collection, request${exampleName ? ' and example' : ''} appear in the sidebar`, async () => {
const locators = buildCommonLocators(page);
await expect(locators.sidebar.collection(collectionName)).toBeVisible();
await expect(locators.sidebar.itemRow(requestName)).toBeVisible();
if (exampleName) {
await locators.sidebar.requestExamplesToggle(requestName).click();
await expect(locators.sidebar.example(exampleName)).toBeVisible();
}
});
});
}
});

View File

@@ -20,6 +20,10 @@ export const buildCommonLocators = (page: Page) => ({
},
closeAllCollectionsButton: () => page.getByTestId('collections-header-actions-menu-close-all'),
collectionRow: (name: string) => page.getByTestId('sidebar-collection-row').filter({ hasText: name }),
itemRow: (name: string) => page.getByTestId('sidebar-collection-item-row').filter({ hasText: name }),
requestExamplesToggle: (requestName: string) =>
page.getByTestId('sidebar-collection-item-row').filter({ hasText: requestName }).getByTestId('request-item-chevron'),
example: (name: string) => page.getByTestId('sidebar-response-example-item').filter({ hasText: name }),
// The sidebar tree wraps each collection in `#collection-<slug>`; scope queries
// to it to disambiguate items that share names across collections.
collectionScope: (name: string) => page.locator(`#collection-${name.replace(/\s+/g, '-').toLowerCase()}`)