diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js index 3152bef8c..fe180d76d 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -9,6 +9,7 @@ import Dropdown from 'components/Dropdown'; import { toggleCollection } from 'providers/ReduxStore/slices/collections'; import { mountCollection, moveCollectionAndPersist, handleCollectionItemDrop } from 'providers/ReduxStore/slices/collections/actions'; import { useDispatch, useSelector } from 'react-redux'; +import { hideHomePage } from 'providers/ReduxStore/slices/app'; import { addTab, makeTabPermanent } from 'providers/ReduxStore/slices/tabs'; import NewRequest from 'components/Sidebar/NewRequest'; import NewFolder from 'components/Sidebar/NewFolder'; @@ -92,6 +93,7 @@ const Collection = ({ collection, searchText }) => { } if(!isChevronClick) { + dispatch(hideHomePage()); // @TODO Playwright tests are often stuck on home page, rather than collection settings tab. Revisit for a proper fix. dispatch( addTab({ uid: collection.uid, @@ -209,7 +211,7 @@ const Collection = ({ collection, searchText }) => { const folderItems = sortByNameThenSequence(filter(collection.items, (i) => isItemAFolder(i))); return ( - + {showNewRequestModal && setShowNewRequestModal(false)} />} {showNewFolderModal && setShowNewFolderModal(false)} />} {showRenameCollectionModal && ( diff --git a/packages/bruno-converters/src/openapi/openapi-to-bruno.js b/packages/bruno-converters/src/openapi/openapi-to-bruno.js index 79438c231..27811dc6c 100644 --- a/packages/bruno-converters/src/openapi/openapi-to-bruno.js +++ b/packages/bruno-converters/src/openapi/openapi-to-bruno.js @@ -38,7 +38,7 @@ const buildEmptyJsonBody = (bodySchema, visited = new Map()) => { return _jsonBody; }; -const transformOpenapiRequestItem = (request) => { +const transformOpenapiRequestItem = (request, usedNames = new Set()) => { let _operationObject = request.operationObject; let operationName = _operationObject.summary || _operationObject.operationId || _operationObject.description; @@ -46,6 +46,27 @@ const transformOpenapiRequestItem = (request) => { operationName = `${request.method} ${request.path}`; } + // Sanitize operation name to prevent Bruno parsing issues + if (operationName) { + // Replace line breaks and normalize whitespace + operationName = operationName.replace(/[\r\n\s]+/g, ' ').trim(); + } + if (usedNames.has(operationName)) { + // Make name unique to prevent filename collisions + // Try adding method info first + let uniqueName = `${operationName} (${request.method.toUpperCase()})`; + + // If still not unique, add counter + let counter = 1; + while (usedNames.has(uniqueName)) { + uniqueName = `${operationName} (${counter})`; + counter++; + } + + operationName = uniqueName; + } + usedNames.add(operationName); + // replace OpenAPI links in path by Bruno variables let path = request.path.replace(/{([a-zA-Z]+)}/g, `{{${_operationObject.operationId}_$1}}`); @@ -419,6 +440,8 @@ const openAPIRuntimeExpressionToScript = (expression) => { }; export const parseOpenApiCollection = (data) => { + const usedNames = new Set(); + const brunoCollection = { name: '', uid: uuid(), @@ -512,11 +535,11 @@ export const parseOpenApiCollection = (data) => { name: group.name } }, - items: group.requests.map(transformOpenapiRequestItem) + items: group.requests.map(req => transformOpenapiRequestItem(req, usedNames)), }; }); - let ungroupedItems = ungroupedRequests.map(transformOpenapiRequestItem); + let ungroupedItems = ungroupedRequests.map(req => transformOpenapiRequestItem(req, usedNames)); let brunoCollectionItems = brunoFolders.concat(ungroupedItems); brunoCollection.items = brunoCollectionItems; diff --git a/tests/environments/import-environment/global-env-import.spec.ts b/tests/environments/import-environment/global-env-import.spec.ts index 73e5d7af0..5b545c896 100644 --- a/tests/environments/import-environment/global-env-import.spec.ts +++ b/tests/environments/import-environment/global-env-import.spec.ts @@ -63,7 +63,7 @@ test.describe('Global Environment Import Tests', () => { await page.getByText('×').click(); // Test GET request with global environment - await page.locator('.collection-item-name').first().click(); + await page.locator('#collection-environment-test-collection .collection-item-name').first().click(); await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}/posts/{{userId}}'); await page.locator('[data-testid="send-arrow-icon"]').click(); await page.locator('[data-testid="response-status-code"]').waitFor({ state: 'visible' }); @@ -74,7 +74,7 @@ test.describe('Global Environment Import Tests', () => { await expect(responsePane).toContainText('"userId": 1'); // Test POST request - await page.locator('.collection-item-name').nth(1).click(); + await page.locator('#collection-environment-test-collection .collection-item-name').nth(1).click(); await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}/posts'); await page.locator('[data-testid="send-arrow-icon"]').click(); await page.locator('[data-testid="response-status-code"]').waitFor({ state: 'visible' }); @@ -89,7 +89,5 @@ test.describe('Global Environment Import Tests', () => { .click(); await page.locator('.dropdown-item').filter({ hasText: 'Close' }).click(); await page.getByRole('button', { name: 'Close' }).click(); - - await page.locator('.bruno-logo').click(); }); }); diff --git a/tests/environments/multiline-variables/write-multiline-variable.spec.ts b/tests/environments/multiline-variables/write-multiline-variable.spec.ts index cb4cdb7ae..1127ed369 100644 --- a/tests/environments/multiline-variables/write-multiline-variable.spec.ts +++ b/tests/environments/multiline-variables/write-multiline-variable.spec.ts @@ -88,4 +88,8 @@ test.describe('Multiline Variables - Write Test', () => { fs.writeFileSync(testBruPath, content); }); + + test.afterAll(async ({ page }) => { + await page.locator('.bruno-logo').click(); + }); }); diff --git a/tests/import/test-data/bruno-invalid-corrupted.json b/tests/import/bruno/fixtures/bruno-invalid-corrupted.json similarity index 100% rename from tests/import/test-data/bruno-invalid-corrupted.json rename to tests/import/bruno/fixtures/bruno-invalid-corrupted.json diff --git a/tests/import/test-data/bruno-malformed.json b/tests/import/bruno/fixtures/bruno-malformed.json similarity index 100% rename from tests/import/test-data/bruno-malformed.json rename to tests/import/bruno/fixtures/bruno-malformed.json diff --git a/tests/import/test-data/bruno-missing-required-fields.json b/tests/import/bruno/fixtures/bruno-missing-required-fields.json similarity index 100% rename from tests/import/test-data/bruno-missing-required-fields.json rename to tests/import/bruno/fixtures/bruno-missing-required-fields.json diff --git a/tests/import/test-data/bruno-testbench.json b/tests/import/bruno/fixtures/bruno-testbench.json similarity index 100% rename from tests/import/test-data/bruno-testbench.json rename to tests/import/bruno/fixtures/bruno-testbench.json diff --git a/tests/import/bruno/002-import-bruno-corrupted-fails.spec.ts b/tests/import/bruno/import-bruno-corrupted-fails.spec.ts similarity index 89% rename from tests/import/bruno/002-import-bruno-corrupted-fails.spec.ts rename to tests/import/bruno/import-bruno-corrupted-fails.spec.ts index 9d6d10bf4..6fe2a0799 100644 --- a/tests/import/bruno/002-import-bruno-corrupted-fails.spec.ts +++ b/tests/import/bruno/import-bruno-corrupted-fails.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Corrupted Bruno Collection - Should Fail', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Bruno collection with invalid JSON structure should fail', async ({ page }) => { - const brunoFile = path.join(testDataDir, 'bruno-malformed.json'); + const brunoFile = path.resolve(__dirname, 'fixtures', 'bruno-malformed.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/bruno/003-import-bruno-missing-required-schema.spec.ts b/tests/import/bruno/import-bruno-missing-required-schema.spec.ts similarity index 88% rename from tests/import/bruno/003-import-bruno-missing-required-schema.spec.ts rename to tests/import/bruno/import-bruno-missing-required-schema.spec.ts index 0f76559f6..e3c73269b 100644 --- a/tests/import/bruno/003-import-bruno-missing-required-schema.spec.ts +++ b/tests/import/bruno/import-bruno-missing-required-schema.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Bruno Collection - Missing Required Schema Fields', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Bruno collection missing required version field should fail', async ({ page }) => { - const brunoFile = path.join(testDataDir, 'bruno-missing-required-fields.json'); + const brunoFile = path.resolve(__dirname, 'fixtures', 'bruno-missing-required-fields.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/bruno/001-import-bruno-testbench.spec.ts b/tests/import/bruno/import-bruno-testbench.spec.ts similarity index 91% rename from tests/import/bruno/001-import-bruno-testbench.spec.ts rename to tests/import/bruno/import-bruno-testbench.spec.ts index cb436c1dd..848c8f980 100644 --- a/tests/import/bruno/001-import-bruno-testbench.spec.ts +++ b/tests/import/bruno/import-bruno-testbench.spec.ts @@ -1,17 +1,14 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; - test.describe('Import Bruno Testbench Collection', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test.beforeAll(async ({ page }) => { // Navigate back to homescreen after all tests await page.locator('.bruno-logo').click(); }); test('Import Bruno Testbench collection successfully', async ({ page }) => { - const brunoFile = path.join(testDataDir, 'bruno-testbench.json'); + const brunoFile = path.resolve(__dirname, 'fixtures', 'bruno-testbench.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/file-types/001-file-input-acceptance.spec.ts b/tests/import/file-types/file-input-acceptance.spec.ts similarity index 100% rename from tests/import/file-types/001-file-input-acceptance.spec.ts rename to tests/import/file-types/file-input-acceptance.spec.ts diff --git a/tests/import/test-data/invalid.txt b/tests/import/file-types/fixtures/invalid.txt similarity index 100% rename from tests/import/test-data/invalid.txt rename to tests/import/file-types/fixtures/invalid.txt diff --git a/tests/import/file-types/002-invalid-file-handling.spec.ts b/tests/import/file-types/invalid-file-handling.spec.ts similarity index 77% rename from tests/import/file-types/002-invalid-file-handling.spec.ts rename to tests/import/file-types/invalid-file-handling.spec.ts index 4284bf921..e8439fa51 100644 --- a/tests/import/file-types/002-invalid-file-handling.spec.ts +++ b/tests/import/file-types/invalid-file-handling.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid File Handling', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle invalid file without crashing', async ({ page }) => { - const invalidFile = path.join(testDataDir, 'invalid.txt'); + const invalidFile = path.resolve(__dirname, 'fixtures', 'invalid.txt'); await page.getByRole('button', { name: 'Import Collection' }).click(); @@ -19,7 +17,7 @@ test.describe('Invalid File Handling', () => { // Wait for the loader to disappear await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); - const hasError = await page.getByText("Failed to parse the file – ensure it is valid JSON or YAML").first().isVisible(); + const hasError = await page.getByText('Failed to parse the file – ensure it is valid JSON or YAML').first().isVisible(); expect(hasError).toBe(true); // Cleanup: close any open modals diff --git a/tests/import/test-data/insomnia-malformed.json b/tests/import/insomnia/fixtures/insomnia-malformed.json similarity index 100% rename from tests/import/test-data/insomnia-malformed.json rename to tests/import/insomnia/fixtures/insomnia-malformed.json diff --git a/tests/import/test-data/insomnia-v4.json b/tests/import/insomnia/fixtures/insomnia-v4.json similarity index 100% rename from tests/import/test-data/insomnia-v4.json rename to tests/import/insomnia/fixtures/insomnia-v4.json diff --git a/tests/import/test-data/insomnia-v5-invalid-missing-collection.yaml b/tests/import/insomnia/fixtures/insomnia-v5-invalid-missing-collection.yaml similarity index 100% rename from tests/import/test-data/insomnia-v5-invalid-missing-collection.yaml rename to tests/import/insomnia/fixtures/insomnia-v5-invalid-missing-collection.yaml diff --git a/tests/import/test-data/insomnia-v5.yaml b/tests/import/insomnia/fixtures/insomnia-v5.yaml similarity index 100% rename from tests/import/test-data/insomnia-v5.yaml rename to tests/import/insomnia/fixtures/insomnia-v5.yaml diff --git a/tests/import/insomnia/001-import-insomnia-v4.spec.ts b/tests/import/insomnia/import-insomnia-v4.spec.ts similarity index 90% rename from tests/import/insomnia/001-import-insomnia-v4.spec.ts rename to tests/import/insomnia/import-insomnia-v4.spec.ts index 1afeed306..8975fe479 100644 --- a/tests/import/insomnia/001-import-insomnia-v4.spec.ts +++ b/tests/import/insomnia/import-insomnia-v4.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Insomnia Collection v4', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Insomnia Collection v4 successfully', async ({ page }) => { - const insomniaFile = path.join(testDataDir, 'insomnia-v4.json'); + const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v4.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/insomnia/002-import-insomnia-v5.spec.ts b/tests/import/insomnia/import-insomnia-v5.spec.ts similarity index 90% rename from tests/import/insomnia/002-import-insomnia-v5.spec.ts rename to tests/import/insomnia/import-insomnia-v5.spec.ts index b246a2454..e875756a3 100644 --- a/tests/import/insomnia/002-import-insomnia-v5.spec.ts +++ b/tests/import/insomnia/import-insomnia-v5.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Insomnia Collection v5', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Insomnia Collection v5 successfully', async ({ page }) => { - const insomniaFile = path.join(testDataDir, 'insomnia-v5.yaml'); + const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v5.yaml'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/insomnia/003-invalid-missing-collection.spec.ts b/tests/import/insomnia/invalid-missing-collection.spec.ts similarity index 87% rename from tests/import/insomnia/003-invalid-missing-collection.spec.ts rename to tests/import/insomnia/invalid-missing-collection.spec.ts index 0237a8240..f0085f9a8 100644 --- a/tests/import/insomnia/003-invalid-missing-collection.spec.ts +++ b/tests/import/insomnia/invalid-missing-collection.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Insomnia Collection - Missing Collection Array', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle Insomnia v5 collection missing collection array', async ({ page }) => { - const insomniaFile = path.join(testDataDir, 'insomnia-v5-invalid-missing-collection.yaml'); + const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v5-invalid-missing-collection.yaml'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/insomnia/004-malformed-structure.spec.ts b/tests/import/insomnia/malformed-structure.spec.ts similarity index 87% rename from tests/import/insomnia/004-malformed-structure.spec.ts rename to tests/import/insomnia/malformed-structure.spec.ts index 644bd35fe..05e9be062 100644 --- a/tests/import/insomnia/004-malformed-structure.spec.ts +++ b/tests/import/insomnia/malformed-structure.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Insomnia Collection - Malformed Structure', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle malformed Insomnia collection structure', async ({ page }) => { - const insomniaFile = path.join(testDataDir, 'insomnia-malformed.json'); + const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-malformed.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); @@ -20,7 +18,7 @@ test.describe('Invalid Insomnia Collection - Malformed Structure', () => { await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); // Check for error message - this should fail during JSON parsing - const hasError = await page.getByText('Failed to parse the file').isVisible(); + const hasError = await page.getByText('Failed to parse the file').first().isVisible(); expect(hasError).toBe(true); // Cleanup: close any open modals diff --git a/tests/import/openapi/duplicate-operation-names-fix.spec.ts b/tests/import/openapi/duplicate-operation-names-fix.spec.ts new file mode 100644 index 000000000..cc6e38742 --- /dev/null +++ b/tests/import/openapi/duplicate-operation-names-fix.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from '../../../playwright'; +import * as path from 'path'; + +test.describe('OpenAPI Duplicate Names Handling', () => { + test('should handle duplicate operation names', async ({ page, createTmpDir }) => { + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-duplicate-operation-name.yaml'); + + // start the import process + await page.getByRole('button', { name: 'Import Collection' }).click(); + + // wait for the import collection modal to appear + const importModal = page.getByTestId('import-collection-modal'); + await importModal.waitFor({ state: 'visible' }); + + // upload the OpenAPI file with duplicate operation names + await page.setInputFiles('input[type="file"]', openApiFile); + + // wait for the file processing to complete + await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); + + // verify that the collection location modal appears (OpenAPI files go directly to location modal) + const locationModal = page.getByTestId('import-collection-location-modal'); + // verify the collection name is correctly parsed despite duplicate operation names + await expect(locationModal.getByText('Duplicate Test Collection')).toBeVisible(); + + // select a location + await page.locator('#collection-location').fill(await createTmpDir('duplicate-test')); + await page.getByRole('button', { name: 'Import', exact: true }).click(); + + // verify the collection was imported successfully + await expect(page.locator('#sidebar-collection-name').getByText('Duplicate Test Collection')).toBeVisible(); + + // configure the collection settings + await page.locator('#sidebar-collection-name').getByText('Duplicate Test Collection').click(); + await page.getByLabel('Safe Mode').check(); + await page.getByRole('button', { name: 'Save' }).click(); + + // verify that all 3 requests were imported correctly despite duplicate operation names + await expect(page.locator('#collection-duplicate-test-collection .collection-item-name')).toHaveCount(3); + + // cleanup: close the collection + await page + .locator('.collection-name') + .filter({ has: page.locator('#sidebar-collection-name:has-text("Duplicate Test Collection")') }) + .locator('.collection-actions') + .click(); + await page.locator('.dropdown-item').getByText('Close').click(); + await page.getByRole('button', { name: 'Close' }).click(); + }); +}); diff --git a/tests/import/test-data/openapi-comprehensive.yaml b/tests/import/openapi/fixtures/openapi-comprehensive.yaml similarity index 100% rename from tests/import/test-data/openapi-comprehensive.yaml rename to tests/import/openapi/fixtures/openapi-comprehensive.yaml diff --git a/tests/import/openapi/fixtures/openapi-duplicate-operation-name.yaml b/tests/import/openapi/fixtures/openapi-duplicate-operation-name.yaml new file mode 100644 index 000000000..8e95b9ed2 --- /dev/null +++ b/tests/import/openapi/fixtures/openapi-duplicate-operation-name.yaml @@ -0,0 +1,44 @@ +openapi: 3.0.0 +info: + title: Duplicate Test Collection + version: 1.0.0 + description: Test collection for handling duplicate operation names +servers: + - url: https://api.example.com + description: Example server +paths: + /users: + get: + summary: 'Get Users' + description: 'Get all users' + operationId: getUsers + responses: + '200': + description: Success + content: + application/json: + schema: + type: object + post: + summary: 'Get Users' + description: 'Create a new user (same summary as GET)' + operationId: createUser + responses: + '201': + description: Created + content: + application/json: + schema: + type: object + /products: + get: + summary: 'Get Users' + description: 'Get all products (same summary as users GET)' + operationId: getProducts + responses: + '200': + description: Success + content: + application/json: + schema: + type: object diff --git a/tests/import/test-data/openapi-invalid-version.yaml b/tests/import/openapi/fixtures/openapi-invalid-version.yaml similarity index 100% rename from tests/import/test-data/openapi-invalid-version.yaml rename to tests/import/openapi/fixtures/openapi-invalid-version.yaml diff --git a/tests/import/test-data/openapi-malformed.yaml b/tests/import/openapi/fixtures/openapi-malformed.yaml similarity index 100% rename from tests/import/test-data/openapi-malformed.yaml rename to tests/import/openapi/fixtures/openapi-malformed.yaml diff --git a/tests/import/test-data/openapi-missing-info.yaml b/tests/import/openapi/fixtures/openapi-missing-info.yaml similarity index 100% rename from tests/import/test-data/openapi-missing-info.yaml rename to tests/import/openapi/fixtures/openapi-missing-info.yaml diff --git a/tests/import/openapi/fixtures/openapi-newline-in-operation-name.yaml b/tests/import/openapi/fixtures/openapi-newline-in-operation-name.yaml new file mode 100644 index 000000000..213933158 --- /dev/null +++ b/tests/import/openapi/fixtures/openapi-newline-in-operation-name.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + title: Newline Test Collection + version: 1.0.0 + description: Test collection for operation names with newlines +servers: + - url: https://api.example.com + description: Example server +paths: + /users: + get: + summary: "Get users\nwith newline" + description: 'This operation has newlines in the summary' + operationId: getUsersWithNewline + responses: + '200': + description: Success + content: + application/json: + schema: + type: object + post: + summary: "Create user\n\nwith multiple\n\nnewlines" + description: 'This operation has multiple consecutive newlines' + operationId: createUserWithNewlines + responses: + '201': + description: Created + content: + application/json: + schema: + type: object diff --git a/tests/import/test-data/openapi-simple.json b/tests/import/openapi/fixtures/openapi-simple.json similarity index 100% rename from tests/import/test-data/openapi-simple.json rename to tests/import/openapi/fixtures/openapi-simple.json diff --git a/tests/import/openapi/002-import-openapi-json.spec.ts b/tests/import/openapi/import-openapi-json.spec.ts similarity index 90% rename from tests/import/openapi/002-import-openapi-json.spec.ts rename to tests/import/openapi/import-openapi-json.spec.ts index 847d4c49b..750f438bd 100644 --- a/tests/import/openapi/002-import-openapi-json.spec.ts +++ b/tests/import/openapi/import-openapi-json.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import OpenAPI v3 JSON Collection', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import simple OpenAPI v3 JSON successfully', async ({ page }) => { - const openApiFile = path.join(testDataDir, 'openapi-simple.json'); + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-simple.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/openapi/001-import-openapi-yaml.spec.ts b/tests/import/openapi/import-openapi-yaml.spec.ts similarity index 90% rename from tests/import/openapi/001-import-openapi-yaml.spec.ts rename to tests/import/openapi/import-openapi-yaml.spec.ts index 0d705f1b1..8d490876c 100644 --- a/tests/import/openapi/001-import-openapi-yaml.spec.ts +++ b/tests/import/openapi/import-openapi-yaml.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import OpenAPI v3 YAML Collection', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import comprehensive OpenAPI v3 YAML successfully', async ({ page }) => { - const openApiFile = path.join(testDataDir, 'openapi-comprehensive.yaml'); + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-comprehensive.yaml'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/openapi/004-malformed-yaml.spec.ts b/tests/import/openapi/malformed-yaml.spec.ts similarity index 90% rename from tests/import/openapi/004-malformed-yaml.spec.ts rename to tests/import/openapi/malformed-yaml.spec.ts index 2f2479e9a..ec596d18a 100644 --- a/tests/import/openapi/004-malformed-yaml.spec.ts +++ b/tests/import/openapi/malformed-yaml.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid OpenAPI - Malformed YAML', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle malformed OpenAPI YAML structure', async ({ page }) => { - const openApiFile = path.join(testDataDir, 'openapi-malformed.yaml'); + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-malformed.yaml'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/openapi/003-missing-info.spec.ts b/tests/import/openapi/missing-info.spec.ts similarity index 89% rename from tests/import/openapi/003-missing-info.spec.ts rename to tests/import/openapi/missing-info.spec.ts index aa8298dcd..bb8a672c1 100644 --- a/tests/import/openapi/003-missing-info.spec.ts +++ b/tests/import/openapi/missing-info.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid OpenAPI - Missing Info Section', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle OpenAPI specification missing required info section', async ({ page }) => { - const openApiFile = path.join(testDataDir, 'openapi-missing-info.yaml'); + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-missing-info.yaml'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/openapi/operation-name-with-newlines-fix.spec.ts b/tests/import/openapi/operation-name-with-newlines-fix.spec.ts new file mode 100644 index 000000000..2971ee4c9 --- /dev/null +++ b/tests/import/openapi/operation-name-with-newlines-fix.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from '../../../playwright'; +import * as path from 'path'; + +test.describe('OpenAPI Newline Handling', () => { + test('should handle operation names with newlines', async ({ page, createTmpDir }) => { + const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-newline-in-operation-name.yaml'); + + // start the import process + await page.getByRole('button', { name: 'Import Collection' }).click(); + + // wait for the import collection modal to appear + const importModal = page.getByTestId('import-collection-modal'); + await importModal.waitFor({ state: 'visible' }); + + // upload the OpenAPI file with problematic operation names + await page.setInputFiles('input[type="file"]', openApiFile); + + // wait for the file processing to complete + await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); + + // verify that the collection location modal appears (OpenAPI files go directly to location modal) + const locationModal = page.getByTestId('import-collection-location-modal'); + await expect(locationModal.getByText('Newline Test Collection')).toBeVisible(); + + // select a location + await page.locator('#collection-location').fill(await createTmpDir('newline-test')); + await page.getByRole('button', { name: 'Import', exact: true }).click(); + + // verify the collection was imported successfully + await expect(page.locator('#sidebar-collection-name').getByText('Newline Test Collection')).toBeVisible(); + + // configure the collection settings + await page.locator('#sidebar-collection-name').getByText('Newline Test Collection').click(); + await page.getByLabel('Safe Mode').check(); + await page.getByRole('button', { name: 'Save' }).click(); + + // verify that all requests were imported correctly despite newlines in operation names + // the parser should clean up the operation names and create valid request names + await expect(page.locator('#collection-newline-test-collection .collection-item-name')).toHaveCount(2); + + // cleanup: close the collection + await page + .locator('.collection-name') + .filter({ has: page.locator('#sidebar-collection-name:has-text("Newline Test Collection")') }) + .locator('.collection-actions') + .click(); + await page.locator('.dropdown-item').getByText('Close').click(); + await page.getByRole('button', { name: 'Close' }).click(); + }); +}); diff --git a/tests/import/test-data/postman-invalid-missing-info.json b/tests/import/postman/fixtures/postman-invalid-missing-info.json similarity index 100% rename from tests/import/test-data/postman-invalid-missing-info.json rename to tests/import/postman/fixtures/postman-invalid-missing-info.json diff --git a/tests/import/test-data/postman-invalid-schema.json b/tests/import/postman/fixtures/postman-invalid-schema.json similarity index 100% rename from tests/import/test-data/postman-invalid-schema.json rename to tests/import/postman/fixtures/postman-invalid-schema.json diff --git a/tests/import/test-data/postman-malformed.json b/tests/import/postman/fixtures/postman-malformed.json similarity index 100% rename from tests/import/test-data/postman-malformed.json rename to tests/import/postman/fixtures/postman-malformed.json diff --git a/tests/import/test-data/postman-v20.json b/tests/import/postman/fixtures/postman-v20.json similarity index 100% rename from tests/import/test-data/postman-v20.json rename to tests/import/postman/fixtures/postman-v20.json diff --git a/tests/import/test-data/postman-v21.json b/tests/import/postman/fixtures/postman-v21.json similarity index 100% rename from tests/import/test-data/postman-v21.json rename to tests/import/postman/fixtures/postman-v21.json diff --git a/tests/import/postman/002-import-postman-v20.spec.ts b/tests/import/postman/import-postman-v20.spec.ts similarity index 89% rename from tests/import/postman/002-import-postman-v20.spec.ts rename to tests/import/postman/import-postman-v20.spec.ts index b6866fa3e..919da393f 100644 --- a/tests/import/postman/002-import-postman-v20.spec.ts +++ b/tests/import/postman/import-postman-v20.spec.ts @@ -2,18 +2,16 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Postman Collection v2.0', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Postman Collection v2.0 successfully', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-v20.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-v20.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); - + // Wait for import collection modal to be ready const importModal = page.getByRole('dialog'); await importModal.waitFor({ state: 'visible' }); await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + await page.setInputFiles('input[type="file"]', postmanFile); // Wait for the loader to disappear @@ -22,10 +20,10 @@ test.describe('Import Postman Collection v2.0', () => { // Verify that the Import Collection modal is displayed (for location selection) const locationModal = page.getByRole('dialog'); await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + // Wait for collection to appear in the location modal await expect(locationModal.getByText('Postman v2.0 Collection')).toBeVisible(); - + // Cleanup: close any open modals await page.locator('[data-test-id="modal-close-button"]').click(); }); diff --git a/tests/import/postman/001-import-postman-v21.spec.ts b/tests/import/postman/import-postman-v21.spec.ts similarity index 89% rename from tests/import/postman/001-import-postman-v21.spec.ts rename to tests/import/postman/import-postman-v21.spec.ts index 08a7029df..ad7ccaca5 100644 --- a/tests/import/postman/001-import-postman-v21.spec.ts +++ b/tests/import/postman/import-postman-v21.spec.ts @@ -2,30 +2,28 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Import Postman Collection v2.1', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Import Postman Collection v2.1 successfully', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-v21.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-v21.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); - + // Wait for import collection modal to be ready const importModal = page.getByRole('dialog'); await importModal.waitFor({ state: 'visible' }); await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + await page.setInputFiles('input[type="file"]', postmanFile); // Wait for the loader to disappear await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); - + // Verify that the Import Collection modal is displayed (for location selection) const locationModal = page.getByRole('dialog'); await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + // Wait for collection to appear in the location modal await expect(locationModal.getByText('Postman v2.1 Collection')).toBeVisible(); - + // Cleanup: close any open modals await page.locator('[data-test-id="modal-close-button"]').click(); }); diff --git a/tests/import/postman/006-invalid-json.spec.ts b/tests/import/postman/invalid-json.spec.ts similarity index 86% rename from tests/import/postman/006-invalid-json.spec.ts rename to tests/import/postman/invalid-json.spec.ts index ef1e881ca..fd214ced5 100644 --- a/tests/import/postman/006-invalid-json.spec.ts +++ b/tests/import/postman/invalid-json.spec.ts @@ -2,27 +2,25 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Postman Collection - Invalid JSON', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle invalid JSON syntax', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-invalid-schema.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-invalid-schema.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); - + // Wait for import collection modal to be ready const importModal = page.getByRole('dialog'); await importModal.waitFor({ state: 'visible' }); await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + await page.setInputFiles('input[type="file"]', postmanFile); - + // Wait for the loader to disappear await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); // Check for error message const hasError = await page.getByText('Conversion failed').first().isVisible(); expect(hasError).toBe(true); - + // Cleanup: close any open modals await page.locator('[data-test-id="modal-close-button"]').click(); }); diff --git a/tests/import/postman/003-invalid-missing-info.spec.ts b/tests/import/postman/invalid-missing-info.spec.ts similarity index 87% rename from tests/import/postman/003-invalid-missing-info.spec.ts rename to tests/import/postman/invalid-missing-info.spec.ts index a68518388..90252f28a 100644 --- a/tests/import/postman/003-invalid-missing-info.spec.ts +++ b/tests/import/postman/invalid-missing-info.spec.ts @@ -2,10 +2,8 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Postman Collection - Missing Info', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle Postman collection missing required info field', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-invalid-missing-info.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-invalid-missing-info.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); diff --git a/tests/import/postman/004-invalid-schema.spec.ts b/tests/import/postman/invalid-schema.spec.ts similarity index 79% rename from tests/import/postman/004-invalid-schema.spec.ts rename to tests/import/postman/invalid-schema.spec.ts index 34ddfa07b..aa9474d4b 100644 --- a/tests/import/postman/004-invalid-schema.spec.ts +++ b/tests/import/postman/invalid-schema.spec.ts @@ -2,27 +2,25 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Postman Collection - Invalid Schema', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle Postman collection with invalid schema version', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-invalid-schema.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-invalid-schema.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); - + // Wait for import collection modal to be ready const importModal = page.getByRole('dialog'); await importModal.waitFor({ state: 'visible' }); await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + await page.setInputFiles('input[type="file"]', postmanFile); - + // Wait for the loader to disappear await page.locator('#import-collection-loader').waitFor({ state: 'hidden' }); - + // Check for error message - const hasError = await page.getByText('Conversion failed').isVisible(); + const hasError = await page.getByText('Conversion failed').first().isVisible(); expect(hasError).toBe(true); - + // Cleanup: close any open modals await page.locator('[data-test-id="modal-close-button"]').click(); }); diff --git a/tests/import/postman/005-malformed-structure.spec.ts b/tests/import/postman/malformed-structure.spec.ts similarity index 87% rename from tests/import/postman/005-malformed-structure.spec.ts rename to tests/import/postman/malformed-structure.spec.ts index 2c39faf0d..e65109dd1 100644 --- a/tests/import/postman/005-malformed-structure.spec.ts +++ b/tests/import/postman/malformed-structure.spec.ts @@ -2,18 +2,16 @@ import { test, expect } from '../../../playwright'; import * as path from 'path'; test.describe('Invalid Postman Collection - Malformed Structure', () => { - const testDataDir = path.join(__dirname, '../test-data'); - test('Handle malformed Postman collection structure', async ({ page }) => { - const postmanFile = path.join(testDataDir, 'postman-malformed.json'); + const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-malformed.json'); await page.getByRole('button', { name: 'Import Collection' }).click(); - + // Wait for import collection modal to be ready const importModal = page.getByRole('dialog'); await importModal.waitFor({ state: 'visible' }); await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection'); - + await page.setInputFiles('input[type="file"]', postmanFile); // Wait for the loader to disappear @@ -22,7 +20,7 @@ test.describe('Invalid Postman Collection - Malformed Structure', () => { // Check for error message const hasError = await page.getByText('Import collection failed').first().isVisible(); expect(hasError).toBe(true); - + // Cleanup: close any open modals await page.locator('[data-test-id="modal-close-button"]').click(); }); diff --git a/tests/import/test-data/invalid-json.json b/tests/import/test-data/invalid-json.json deleted file mode 100644 index f3ca33078..000000000 --- a/tests/import/test-data/invalid-json.json +++ /dev/null @@ -1 +0,0 @@ -{ "invalid": json syntax } diff --git a/tests/import/test-data/empty-file.json b/tests/utils/pageUtils/actions.js similarity index 100% rename from tests/import/test-data/empty-file.json rename to tests/utils/pageUtils/actions.js diff --git a/tests/utils/pageUtils/index.js b/tests/utils/pageUtils/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utils/pageUtils/navigation.js b/tests/utils/pageUtils/navigation.js new file mode 100644 index 000000000..e69de29bb