Files
bruno/tests/transient-requests/transient-requests.spec.ts
Chirag Chandrashekhar ca4d0dd40b Feature/transient request (#6878)
* feat: add functionality to retrieve collection path from transient file requests in IPC module

* feat: implement transient directory handling in collection mounting process

* add action to store transient directory paths in Redux state
* update IPC handler to create and return a temporary directory for collections
* modify collection mount action to dispatch transient directory addition

* feat: add CreateTransientRequest component for managing transient requests

* implement CreateTransientRequest component to facilitate the creation of HTTP, GraphQL, gRPC, and WebSocket transient requests
* integrate the component into RequestTabs for user interaction
* update collection and request handling to differentiate between transient and non-transient requests
* enhance Redux actions to support transient request creation and management

* feat: enhance transient request handling and add temp directory watcher

* refactor Redux actions for HTTP, gRPC, and WebSocket requests to utilize a unified task queue for transient requests
* implement a new helper function to retrieve collection paths from temporary directory metadata
* add functionality to watch transient directories for file changes, excluding metadata.json
* integrate transient directory watcher into the collection mounting process

* feat: enhance transient request management with temp directory integration

* update generateTransientRequestName function to accept tempDirectory parameter for improved request naming
* modify CreateTransientRequest component to utilize tempDirectory for transient request creation
* adjust middleware to check for transient status based on tempDirectory
* implement transient file and directory identification in collections slice for better state management

* feat: add SaveTransientRequest component for managing transient requests

* implement SaveTransientRequest component to facilitate saving transient requests to selected folders
* create StyledWrapper for component styling
* introduce useCollectionFolderTree hook for managing folder navigation and state
* update Redux actions to handle saving requests from transient state

* feat: implement SaveTransientRequestContainer and enhance modal management

* add SaveTransientRequestContainer to manage multiple transient request modals
* refactor SaveTransientRequest component to utilize Redux for modal state management
* implement open and close actions for transient request modals in Redux slice
* update Bruno page to include SaveTransientRequestContainer for improved UI integration

* feat: enhance SaveTransientRequest component with new folder creation functionality

* add input for creating new folders within the SaveTransientRequest component
* implement validation for new folder names and filesystem names
* integrate folder creation logic with Redux actions for better state management
* update styling for new folder input elements in StyledWrapper
* improve modal behavior to reset state when opened

* feat: update CreateTransientRequest to utilize collection presets for request URLs

* Refactored CreateTransientRequest component to retrieve request URLs from collection presets.
* Enhanced request handling by dynamically setting request URLs based on the selected collection's configuration.

* refactor: clean up unused imports and adjust request handling in collections actions

* Removed unused imports from actions.js to streamline the code.
* Updated the saveRequest function to reject the modal instead of resolving it when handling transient requests.
* Cleaned up comments in index.js for better clarity.

* refactor: streamline transient request handling and improve save functionality

* Removed success toast notifications from CreateTransientRequest component to simplify user feedback.
* Enhanced SaveTransientRequest component to handle transient requests more effectively, including improved filename resolution and validation.
* Added IPC handler for saving transient requests, ensuring proper file management and error handling.
* Updated Redux actions to check for duplicate transient request names within the temporary directory.

* feat: enhance request handling in ConfirmCollectionCloseDrafts component

* Added logic to differentiate between transient and non-transient drafts, ensuring transient requests are saved individually before closing the collection.
* Improved user feedback by displaying unsaved changes for both regular and transient requests.
* Updated save and discard functionality to handle all drafts appropriately, enhancing overall user experience.

* fix:fixed useCallback dependency array

* fix:added request name checks before save

* fix: added isTransient to files

* fix: added watcher cleanup for temp directory

* refactor: enhance transient request handling and optimize component logic

* Updated CreateTransientRequest to utilize useMemo for improved performance and prevent unnecessary re-renders.
* Refactored generateTransientRequestName to focus solely on transient requests, removing tempDirectory dependency.
* Streamlined SaveTransientRequest by consolidating form reset logic and removing unused state variables.
* Improved ConfirmCollectionCloseDrafts to differentiate between transient and non-transient drafts more effectively.
* Cleaned up imports and optimized Redux actions for better maintainability.

* feat: implement transient request file deletion on tab close

* Added middleware to handle the deletion of transient request files when tabs are closed.
* Enhanced collection-watcher to unlink temporary files, ensuring metadata.json is skipped and only request files are processed.
* Improved error handling for file deletion operations.

* feat: enhance autosave middleware to skip transient requests

* Updated autosave middleware to check for transient requests and skip auto-save operations accordingly.

* fix: update ConfirmCollectionCloseDrafts to display all transient drafts

* Modified the ConfirmCollectionCloseDrafts component to show all transient drafts without limiting the display to a maximum number.
* Removed the conditional message for additional drafts not shown, enhancing the user experience by providing complete visibility of transient requests.

* feat: enhance SaveTransientRequest component for better modal management

* Refactored SaveTransientRequest and its container to improve modal handling for unsaved transient requests.
* Introduced state management for opening specific modals and added functionality to discard all unsaved requests.
* Updated Redux actions to manage transient request modals more effectively, ensuring no duplicates are added.
* Enhanced user interface to display a list of unsaved requests with options to save or discard them.

* feat: improve modal management in SaveTransientRequestContainer

* Added useEffect to reset openItemUid when the corresponding modal is no longer present.
* Implemented functionality to close all tabs associated with transient requests and show a success message upon discarding them.
* Removed unnecessary modal close handler and streamlined modal opening logic for better clarity and performance.

* refactor: streamline code formatting and improve readability in collection actions

* Consolidated multiple lines of code into single lines for better readability in ConfirmCollectionCloseDrafts and actions.js.
* Enhanced consistency in the formatting of function parameters and return statements across the collections slice.
* Removed unnecessary line breaks and improved the structure of the code for easier maintenance.

* refactor: improve code readability and structure in middleware and actions

* Consolidated multiple lines of code into single lines for better readability in middleware.js and actions.js.
* Enhanced consistency in formatting function parameters and return statements across the collections slice.
* Removed unnecessary line breaks and improved the structure of the code for easier maintenance.
* Streamlined dispatch calls for better clarity and performance.

* refactor: enhance code readability and consistency in middleware and actions

* Improved formatting and structure in middleware.js for dispatch calls.
* Streamlined comments and indentation in actions.js for better clarity.
* Consolidated multiple lines into single lines where appropriate to enhance readability.

* refactor: enhance transient request handling and modal interactions

* Improved the modal handling logic for removing collections to differentiate between regular and drafts confirmation modals.
* Added new tests for creating and saving transient requests (HTTP, GraphQL, gRPC, WebSocket) ensuring they do not appear in the sidebar until saved.
* Introduced utility functions for creating transient requests and filling request URLs, improving code reusability and clarity.

* refactor: simplify transient request modal rendering and improve collection watcher logic

* Introduced a new TransientRequestModalsRenderer component to streamline modal rendering based on the number of transient requests.
* Refactored the collection watcher logic to enhance readability by removing unnecessary setTimeout and consolidating file handling functions.
* Improved error handling and logging for the temp directory watcher.

* fix: correct spelling of 'WebSocket' in transient request components and tests

* Updated the spelling of 'Websocket' to 'WebSocket' in CreateTransientRequest component, transient requests test, and action type definitions for consistency and accuracy.
2026-01-29 18:38:42 +05:30

226 lines
8.9 KiB
TypeScript

import { test, expect } from '../../playwright';
import { createTransientRequest, fillRequestUrl, closeAllCollections, createCollection, sendRequest, clickResponseAction, selectRequestPaneTab } from '../utils/page';
import { buildCommonLocators, buildWebsocketCommonLocators } from '../utils/page/locators';
test.describe.serial('Transient Requests', () => {
let locators: ReturnType<typeof buildCommonLocators>;
test.beforeAll(async ({ page, createTmpDir }) => {
locators = buildCommonLocators(page);
// Create a temporary collection
const collectionPath = await createTmpDir('transient-collection');
await createCollection(page, 'transient-requests-test', collectionPath);
// Verify the collection is loaded
await test.step('Verify test collection is loaded', async () => {
await expect(locators.sidebar.collection('transient-requests-test')).toBeVisible();
await locators.sidebar.collection('transient-requests-test').click();
});
});
test.afterAll(async ({ page }) => {
// Clean up all collections
await closeAllCollections(page);
});
test('Create transient HTTP request - should not appear in sidebar', async ({ page }) => {
await test.step('Create transient HTTP request', async () => {
await createTransientRequest(page, {
requestType: 'HTTP'
});
await fillRequestUrl(page, 'http://localhost:8081/ping');
});
await test.step('Verify HTTP request tab is open', async () => {
const activeTab = page.locator('.request-tab.active');
await expect(activeTab).toBeVisible();
await expect(activeTab).toContainText('Untitled');
});
await test.step('Verify request is NOT in sidebar', async () => {
// Click on the collection to ensure it's expanded
await locators.sidebar.collection('transient-requests-test').click();
await page.waitForTimeout(300);
// Check that there are no requests in the collection
// Transient requests should not appear in the sidebar
const collectionItems = page.locator('.collection-item-name');
await expect(collectionItems).toHaveCount(0);
});
});
test('Create transient GraphQL request - should not appear in sidebar', async ({ page }) => {
await test.step('Create transient GraphQL request', async () => {
await createTransientRequest(page, {
requestType: 'GraphQL'
});
await fillRequestUrl(page, 'https://api.example.com/graphql');
});
await test.step('Verify GraphQL request tab is open', async () => {
const activeTab = page.locator('.request-tab.active');
await expect(activeTab).toBeVisible();
await expect(activeTab).toContainText('Untitled');
});
await test.step('Verify request is NOT in sidebar', async () => {
// Check that there are still no requests in the collection
const collectionItems = page.locator('.collection-item-name');
await expect(collectionItems).toHaveCount(0);
});
});
test('Create transient gRPC request - should not appear in sidebar', async ({ page }) => {
await test.step('Create transient gRPC request', async () => {
await createTransientRequest(page, {
requestType: 'gRPC'
});
await fillRequestUrl(page, 'grpc://localhost:50051');
});
await test.step('Verify gRPC request tab is open', async () => {
const activeTab = page.locator('.request-tab.active');
await expect(activeTab).toBeVisible();
await expect(activeTab).toContainText('Untitled');
});
await test.step('Verify request is NOT in sidebar', async () => {
// Check that there are still no requests in the collection
const collectionItems = page.locator('.collection-item-name');
await expect(collectionItems).toHaveCount(0);
});
});
test('Create transient WebSocket request - should not appear in sidebar', async ({ page }) => {
await test.step('Create transient WebSocket request', async () => {
await createTransientRequest(page, {
requestType: 'WebSocket'
});
await fillRequestUrl(page, 'ws://localhost:8082');
});
await test.step('Verify WebSocket request tab is open', async () => {
const activeTab = page.locator('.request-tab.active');
await expect(activeTab).toBeVisible();
await expect(activeTab).toContainText('Untitled');
});
await test.step('Verify request is NOT in sidebar', async () => {
// Check that there are still no requests in the collection
const collectionItems = page.locator('.collection-item-name');
await expect(collectionItems).toHaveCount(0);
});
});
test('Save transient HTTP request - should appear in sidebar after save', async ({ page }) => {
await test.step('Create transient HTTP request', async () => {
await createTransientRequest(page, {
requestType: 'HTTP'
});
await fillRequestUrl(page, 'http://localhost:8081/echo');
});
await test.step('Trigger save action using keyboard shortcut', async () => {
// Try to save using Cmd+S (Mac) or Ctrl+S (other platforms)
await page.keyboard.press('Meta+s');
await page.waitForTimeout(500);
});
await test.step('Fill in save dialog', async () => {
// Wait for save modal to appear
const saveModal = page.locator('.bruno-modal-card').filter({ hasText: 'Save Request' });
await expect(saveModal).toBeVisible({ timeout: 5000 });
// Fill in request name
const requestNameInput = saveModal.locator('#request-name');
await requestNameInput.clear();
await requestNameInput.fill('Saved HTTP Request');
// Click Save button
await saveModal.getByRole('button', { name: 'Save' }).click();
// Wait for success toast
await page.waitForTimeout(1000);
});
await test.step('Verify saved request appears in sidebar', async () => {
// Check collection is expanded
await locators.sidebar.collection('transient-requests-test').click();
// Look for the saved request in sidebar
const savedRequest = locators.sidebar.request('Saved HTTP Request');
await expect(savedRequest).toBeVisible();
});
await test.step('Cleanup: Delete the saved request', async () => {
await locators.sidebar.request('Saved HTTP Request').hover();
await locators.actions.collectionItemActions('Saved HTTP Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
await expect(locators.sidebar.request('Saved HTTP Request')).not.toBeVisible();
});
});
test('Save transient GraphQL request - should appear in sidebar after save', async ({ page }) => {
await test.step('Create transient GraphQL request', async () => {
await createTransientRequest(page, {
requestType: 'GraphQL'
});
await fillRequestUrl(page, 'https://api.example.com/graphql');
});
await test.step('Trigger save action using keyboard shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.waitForTimeout(500);
});
await test.step('Fill in save dialog', async () => {
const saveModal = page.locator('.bruno-modal-card').filter({ hasText: 'Save Request' });
await expect(saveModal).toBeVisible({ timeout: 5000 });
const requestNameInput = saveModal.locator('#request-name');
await requestNameInput.clear();
await requestNameInput.fill('Saved GraphQL Request');
await saveModal.getByRole('button', { name: 'Save' }).click();
await page.waitForTimeout(1000);
});
await test.step('Verify saved request appears in sidebar', async () => {
await locators.sidebar.collection('transient-requests-test').click();
const savedRequest = locators.sidebar.request('Saved GraphQL Request');
await expect(savedRequest).toBeVisible();
});
await test.step('Cleanup: Delete the saved request', async () => {
await locators.sidebar.request('Saved GraphQL Request').hover();
await locators.actions.collectionItemActions('Saved GraphQL Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
await expect(locators.sidebar.request('Saved GraphQL Request')).not.toBeVisible();
});
});
test('Send transient HTTP request - verify response', async ({ page }) => {
await test.step('Create transient HTTP request', async () => {
await createTransientRequest(page, {
requestType: 'HTTP'
});
await fillRequestUrl(page, 'http://localhost:8081/ping');
});
await test.step('Send request and verify response', async () => {
// Send request using the helper function
await sendRequest(page, 200);
// Copy response to clipboard and verify
await clickResponseAction(page, 'response-copy-btn');
await expect(page.getByText('Response copied to clipboard')).toBeVisible();
const clipboardText = await page.evaluate(() => navigator.clipboard.readText());
expect(clipboardText).toBe('pong');
});
});
});