diff --git a/packages/bruno-app/src/components/RequestTabs/index.js b/packages/bruno-app/src/components/RequestTabs/index.js index 647d0cb69..34c8f479b 100644 --- a/packages/bruno-app/src/components/RequestTabs/index.js +++ b/packages/bruno-app/src/components/RequestTabs/index.js @@ -11,6 +11,7 @@ import RequestTab from './RequestTab'; import StyledWrapper from './StyledWrapper'; import DraggableTab from './DraggableTab'; import CreateUntitledRequest from 'components/CreateUntitledRequest'; +import { IconPlus } from '@tabler/icons'; const RequestTabs = () => { const dispatch = useDispatch(); @@ -174,14 +175,15 @@ const RequestTabs = () => { ) : null}
- - {activeCollection && ( - - )} + { + activeCollection && ( + setNewRequestModalOpen(true)} + /> + ) + }
{/* Moved to post mvp */} {/*
  • diff --git a/tests/collection/create/create-collection.spec.ts b/tests/collection/create/create-collection.spec.ts index 687aa58b8..ecd395d95 100644 --- a/tests/collection/create/create-collection.spec.ts +++ b/tests/collection/create/create-collection.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../../playwright'; -import { closeAllCollections, createUntitledRequest } from '../../utils/page'; +import { closeAllCollections, createRequest } from '../../utils/page'; test.describe('Create collection', () => { test.afterEach(async ({ page }) => { @@ -8,20 +8,23 @@ test.describe('Create collection', () => { }); test('Create collection and add a simple HTTP request', async ({ page, createTmpDir }) => { + const collectionName = 'test-collection'; + const requestName = 'ping'; + await page.getByTestId('collections-header-add-menu').click(); await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click(); await page.getByLabel('Name').click(); - await page.getByLabel('Name').fill('test-collection'); + await page.getByLabel('Name').fill(collectionName); await page.getByLabel('Name').press('Tab'); const locationInput = page.locator('.bruno-modal').getByLabel('Location'); if (await locationInput.isVisible()) { - await locationInput.fill(await createTmpDir('test-collection')); + await locationInput.fill(await createTmpDir(collectionName)); } await page.locator('.bruno-modal').getByRole('button', { name: 'Create', exact: true }).click(); - await page.locator('#sidebar-collection-name').filter({ hasText: 'test-collection' }).click(); + await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).click(); - // Create a new request using the new dropdown flow - await createUntitledRequest(page, { requestType: 'HTTP' }); + // Create a new request using the dialog/modal flow + await createRequest(page, requestName, collectionName); // Set the URL await page.locator('#request-url .CodeMirror').click(); diff --git a/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts b/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts index 12572cc30..a0636c214 100644 --- a/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts +++ b/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../../playwright'; -import { closeAllCollections, createCollection, createUntitledRequest } from '../../utils/page'; +import { closeAllCollections, createCollection, createRequest } from '../../utils/page'; test.describe('Cross-Collection Drag and Drop', () => { test.afterEach(async ({ page }) => { @@ -8,18 +8,13 @@ test.describe('Cross-Collection Drag and Drop', () => { }); test('Verify request drag and drop', async ({ page, createTmpDir }) => { + const requestName = 'drag-drop-request'; + // Create first collection - open with sandbox mode await createCollection(page, 'source-collection', await createTmpDir('source-collection')); - // Create a request in the first collection using the new dropdown flow - await createUntitledRequest(page, { requestType: 'HTTP' }); - - // Set the URL - await page.locator('#request-url .CodeMirror').click(); - await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com'); - await page.locator('#send-request').getByTitle('Save Request').click(); - - await expect(page.locator('.item-name').filter({ hasText: /^Untitled/ })).toBeVisible(); + // Create a request in the first collection using the dialog/modal flow + await createRequest(page, requestName, 'source-collection', { url: 'https://echo.usebruno.com' }); // Create second collection - open with sandbox mode await createCollection(page, 'target-collection', await createTmpDir('target-collection')); @@ -28,7 +23,11 @@ test.describe('Cross-Collection Drag and Drop', () => { await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible(); // Locate the request in source collection - const sourceRequest = page.locator('.item-name').filter({ hasText: /^Untitled/ }).first(); + const sourceCollectionContainer = page + .locator('.collection-name') + .filter({ hasText: 'source-collection' }) + .locator('..'); + const sourceRequest = sourceCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName }).first(); await expect(sourceRequest).toBeVisible(); // Locate the target collection area (the collection name element) @@ -44,51 +43,44 @@ test.describe('Cross-Collection Drag and Drop', () => { .locator('.collection-name') .filter({ hasText: 'target-collection' }) .locator('..'); - await expect( - targetCollectionContainer.locator('.item-name').filter({ hasText: /^Untitled/ }) - ).toBeVisible(); + await expect(targetCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName })).toBeVisible(); // Verify the request is no longer in the source collection - const sourceCollectionContainer = page - .locator('.collection-name') - .filter({ hasText: 'source-collection' }) - .locator('..'); - await expect( - sourceCollectionContainer.locator('.item-name').filter({ hasText: /^Untitled/ }) - ).not.toBeVisible(); + await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' }).click(); + await expect(sourceCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName })).toHaveCount(0); }); test('Expected to show error toast message, when duplicate request found in drop location', async ({ page, createTmpDir }) => { + const requestName = 'duplicate-request'; + // Create first collection (source-collection) await createCollection(page, 'source-collection', await createTmpDir('source-collection')); - // Create a request in the first collection using the new dropdown flow - await createUntitledRequest(page, { requestType: 'HTTP' }); - - // Set the URL - await page.locator('#request-url .CodeMirror').click(); - await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com'); - await page.locator('#send-request').getByTitle('Save Request').click(); - - // check if untitled request is created and visible in sidebar - await expect(page.locator('.item-name').filter({ hasText: /^Untitled/ })).toBeVisible(); + // Create a request in the first collection using the dialog/modal flow + await createRequest(page, requestName, 'source-collection', { url: 'https://echo.usebruno.com' }); // Create second collection (target-collection) await createCollection(page, 'target-collection', await createTmpDir('target-collection')); - // Create a request in the target collection using the new dropdown flow - await createUntitledRequest(page, { requestType: 'HTTP' }); - - // Set the URL - await page.locator('#request-url .CodeMirror').click(); - await page.locator('#request-url').locator('textarea').fill('https://echo.usebruno.com'); - await page.locator('#send-request').getByTitle('Save Request').click(); + // Create a request with the same name in the target collection using the dialog/modal flow + await createRequest(page, requestName, 'target-collection', { url: 'https://echo.usebruno.com' }); // Go back to source collection to drag the request - const sourceRequest = page.locator('.item-name').filter({ hasText: /^Untitled/ }).first(); + await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' }).click(); + + const sourceCollectionContainer = page + .locator('.collection-name') + .filter({ hasText: 'source-collection' }) + .locator('..'); + const targetCollectionContainer = page + .locator('.collection-name') + .filter({ hasText: 'target-collection' }) + .locator('..'); + + const sourceRequest = sourceCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName }).first(); await expect(sourceRequest).toBeVisible(); // Locate the target collection area @@ -102,20 +94,8 @@ test.describe('Cross-Collection Drag and Drop', () => { await expect(page.getByText(/Error: Cannot copy.*already exists/i)).toBeVisible(); // source and target collection request should remain unchanged - const targetCollectionContainer = page - .locator('.collection-name') - .filter({ hasText: 'target-collection' }) - .locator('..'); - await expect( - targetCollectionContainer.locator('.item-name').filter({ hasText: /^Untitled/ }) - ).toBeVisible(); - - const sourceCollectionContainer = page - .locator('.collection-name') - .filter({ hasText: 'source-collection' }) - .locator('..'); - await expect( - sourceCollectionContainer.locator('.item-name').filter({ hasText: /^Untitled/ }) - ).toBeVisible(); + await expect(sourceCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName })).toBeVisible(); + await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click(); + await expect(targetCollectionContainer.locator('.collection-item-name').filter({ hasText: requestName })).toBeVisible(); }); }); diff --git a/tests/collection/moving-requests/tag-persistence.spec.ts b/tests/collection/moving-requests/tag-persistence.spec.ts index 6f06eeb80..61c17f43c 100644 --- a/tests/collection/moving-requests/tag-persistence.spec.ts +++ b/tests/collection/moving-requests/tag-persistence.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../../playwright'; -import { closeAllCollections, createUntitledRequest, selectRequestPaneTab } from '../../utils/page'; +import { closeAllCollections, createRequest, selectRequestPaneTab } from '../../utils/page'; import { buildCommonLocators } from '../../utils/page/locators'; test.describe('Tag persistence', () => { @@ -10,43 +10,40 @@ test.describe('Tag persistence', () => { test('Verify tag persistence while moving requests within a collection', async ({ page, createTmpDir }) => { const locators = buildCommonLocators(page); + const collectionName = 'test-collection'; + const requestUrl = 'https://httpfaker.org/api/echo'; + const tagName = 'smoke'; + // Create first collection - click plus icon button to open dropdown await locators.plusMenu.button().click(); await locators.plusMenu.createCollection().click(); - await page.getByLabel('Name').fill('test-collection'); + await page.getByLabel('Name').fill(collectionName); const locationInput = locators.modal.byTitle('Create Collection').getByLabel('Location'); if (await locationInput.isVisible()) { - await locationInput.fill(await createTmpDir('test-collection')); + await locationInput.fill(await createTmpDir(collectionName)); } await locators.modal.button('Create').click(); - await locators.sidebar.collection('test-collection').click(); + await locators.sidebar.collection(collectionName).click(); await page.waitForTimeout(1000); - // Create three requests, each with URL and tag (auto-saved after each is completely created) - // The createUntitledRequest function now waits for each request to be fully created - // before returning, ensuring unique names are generated - await createUntitledRequest(page, { - requestType: 'HTTP', - url: 'https://httpfaker.org/api/echo', - tag: 'smoke' - }); - await createUntitledRequest(page, { - requestType: 'HTTP', - url: 'https://httpfaker.org/api/echo', - tag: 'smoke' - }); - await createUntitledRequest(page, { - requestType: 'HTTP', - url: 'https://httpfaker.org/api/echo', - tag: 'smoke' - }); + // Create three requests via the dialog/modal flow, then add a tag to each + const requestNames = ['request-1', 'request-2', 'request-3']; + for (const requestName of requestNames) { + await createRequest(page, requestName, collectionName, { url: requestUrl }); + await locators.sidebar.request(requestName).click(); + await locators.tabs.requestTab(requestName).waitFor({ state: 'visible' }); + await selectRequestPaneTab(page, 'Settings'); + await page.waitForTimeout(200); + await locators.tags.input().fill(tagName); + await locators.tags.input().press('Enter'); + await page.waitForTimeout(200); + await expect(locators.tags.item(tagName)).toBeVisible(); + await page.keyboard.press('Meta+s'); + await page.waitForTimeout(200); + } - // Wait for all 3 requests to be visible in the sidebar - const untitledRequests = page.locator('.item-name').filter({ hasText: /^Untitled/ }); - await expect(untitledRequests).toHaveCount(3); - - // Move the last untitled request to just above the first untitled request within the same collection - const r3Request = untitledRequests.nth(2); // Third request (0-indexed) - const r1Request = untitledRequests.first(); // First request + // Move the last request to just above the first request within the same collection + const r3Request = locators.sidebar.request('request-3'); + const r1Request = locators.sidebar.request('request-1'); await expect(r3Request).toBeVisible(); await expect(r1Request).toBeVisible(); @@ -57,15 +54,17 @@ test.describe('Tag persistence', () => { }); // Verify the requests are still in the collection - await expect(untitledRequests).toHaveCount(3); + for (const requestName of requestNames) { + await expect(locators.sidebar.request(requestName)).toBeVisible(); + } - // Click on the moved request (now first) to verify the tag persisted after the move - await untitledRequests.first().click(); - await locators.tabs.activeRequestTab().waitFor({ state: 'visible' }); + // Click on the moved request to verify the tag persisted after the move + await r3Request.click(); + await locators.tabs.requestTab('request-3').waitFor({ state: 'visible' }); await selectRequestPaneTab(page, 'Settings'); await page.waitForTimeout(200); // Verify the tag is still present after the move - await expect(locators.tags.item('smoke')).toBeVisible(); + await expect(locators.tags.item(tagName)).toBeVisible(); }); test('verify tag persistence while moving requests between folders', async ({ page, createTmpDir }) => { diff --git a/tests/request/encoding/curl-encoding.spec.ts b/tests/request/encoding/curl-encoding.spec.ts index aebd2246c..a817081ae 100644 --- a/tests/request/encoding/curl-encoding.spec.ts +++ b/tests/request/encoding/curl-encoding.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../../playwright'; -import { closeAllCollections, createUntitledRequest } from '../../utils/page'; +import { closeAllCollections, createRequest } from '../../utils/page'; test.describe('Code Generation URL Encoding', () => { test.afterEach(async ({ page }) => { @@ -18,27 +18,27 @@ test.describe('Code Generation URL Encoding', () => { page, createTmpDir }) => { + const collectionName = 'unencoded-test-collection'; + const requestName = 'curl-encoding-unencoded'; + // Use plus icon button in new workspace UI await page.getByTestId('collections-header-add-menu').click(); await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click(); - await page.getByLabel('Name').fill('unencoded-test-collection'); + await page.getByLabel('Name').fill(collectionName); const locationInput = page.getByLabel('Location'); if (await locationInput.isVisible()) { - await locationInput.fill(await createTmpDir('unencoded-test-collection')); + await locationInput.fill(await createTmpDir(collectionName)); } await page.locator('.bruno-modal').getByRole('button', { name: 'Create', exact: true }).click(); - await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'unencoded-test-collection' })).toBeVisible(); - await page.locator('#sidebar-collection-name').filter({ hasText: 'unencoded-test-collection' }).click(); + await expect(page.locator('#sidebar-collection-name').filter({ hasText: collectionName })).toBeVisible(); + await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).click(); - // Create a new request using the new dropdown flow - await createUntitledRequest(page, { - requestType: 'HTTP', - url: 'http://base.source?name=John Doe' - }); + // Create a new request using the dialog/modal flow + await createRequest(page, requestName, collectionName, { url: 'http://base.source?name=John Doe' }); - // Find the untitled request and click on it - await page.locator('.item-name').filter({ hasText: /^Untitled/ }).first().click(); + // Click the request in the sidebar + await page.locator('.collection-item-name').filter({ hasText: requestName }).first().click(); await page.locator('#send-request .infotip').first().click(); @@ -61,27 +61,27 @@ test.describe('Code Generation URL Encoding', () => { page, createTmpDir }) => { + const collectionName = 'encoded-test-collection'; + const requestName = 'curl-encoding-encoded'; + // Use plus icon button in new workspace UI await page.getByTestId('collections-header-add-menu').click(); await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click(); - await page.getByLabel('Name').fill('encoded-test-collection'); + await page.getByLabel('Name').fill(collectionName); const locationInput = page.getByLabel('Location'); if (await locationInput.isVisible()) { - await locationInput.fill(await createTmpDir('encoded-test-collection')); + await locationInput.fill(await createTmpDir(collectionName)); } await page.locator('.bruno-modal').getByRole('button', { name: 'Create', exact: true }).click(); - await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'encoded-test-collection' })).toBeVisible(); - await page.locator('#sidebar-collection-name').filter({ hasText: 'encoded-test-collection' }).click(); + await expect(page.locator('#sidebar-collection-name').filter({ hasText: collectionName })).toBeVisible(); + await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).click(); - // Create a new request using the new dropdown flow - await createUntitledRequest(page, { - requestType: 'HTTP', - url: 'http://base.source?name=John%20Doe' - }); + // Create a new request using the dialog/modal flow + await createRequest(page, requestName, collectionName, { url: 'http://base.source?name=John%20Doe' }); - // Find the untitled request and click on it - await page.locator('.item-name').filter({ hasText: /^Untitled/ }).first().click(); + // Click the request in the sidebar + await page.locator('.collection-item-name').filter({ hasText: requestName }).first().click(); await page.locator('#send-request .infotip').first().click(); diff --git a/tests/response/large-response-crash-prevention.spec.ts b/tests/response/large-response-crash-prevention.spec.ts index bf1a42946..ccb03e9bb 100644 --- a/tests/response/large-response-crash-prevention.spec.ts +++ b/tests/response/large-response-crash-prevention.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '../../playwright'; -import { closeAllCollections, createCollection, createUntitledRequest } from '../utils/page/actions'; +import { closeAllCollections, createCollection, createRequest } from '../utils/page/actions'; test.describe('Large Response Crash/High Memory Usage Prevention', () => { // Increase timeout to 1 minute for all tests in this describe block, default is 30 seconds. @@ -13,13 +13,13 @@ test.describe('Large Response Crash/High Memory Usage Prevention', () => { test('Show appropriate warning for responses over 10MB', async ({ page, createTmpDir }) => { const collectionName = 'size-warning-test'; + const requestName = 'large-response'; // Create collection (auto-opens the collection) await createCollection(page, collectionName, await createTmpDir(collectionName)); - // Create request using the new dropdown flow - await createUntitledRequest(page, { - requestType: 'HTTP', + // Create request using the dialog/modal flow + await createRequest(page, requestName, collectionName, { url: 'https://samples.json-format.com/employees/json/employees_50MB.json' });