Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/bruno-common/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ export {
BRUNO_VARIABLE_DATATYPES,
isBrunoVariableDataType
} from './datatype';

export const TIMEOUT_INHERIT = 'inherit' as const;
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { fromOpenCollectionVariables, toOpenCollectionVariables } from './variab
export { fromOpenCollectionActions, toOpenCollectionActions } from './actions';
export { fromOpenCollectionScripts, toOpenCollectionScripts } from './scripts';
export { fromOpenCollectionAssertions, toOpenCollectionAssertions } from './assertions';
export { TIMEOUT_INHERIT } from '@usebruno/common/utils';
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
fromOpenCollectionActions,
toOpenCollectionActions,
fromOpenCollectionAssertions,
toOpenCollectionAssertions
toOpenCollectionAssertions,
TIMEOUT_INHERIT
} from '../common';
import type {
GraphQLRequest,
Expand Down Expand Up @@ -179,7 +180,7 @@ export const toOpenCollectionGraphqlItem = (item: BrunoItem): GraphQLRequest =>

const settings: GraphQLRequestSettings = {
encodeUrl: typeof brunoSettings.encodeUrl === 'boolean' ? brunoSettings.encodeUrl : true,
timeout: typeof brunoSettings.timeout === 'number' ? brunoSettings.timeout : 0,
timeout: typeof brunoSettings.timeout === 'number' || brunoSettings.timeout === TIMEOUT_INHERIT ? brunoSettings.timeout : 0,
followRedirects: typeof brunoSettings.followRedirects === 'boolean' ? brunoSettings.followRedirects : true,
maxRedirects: typeof brunoSettings.maxRedirects === 'number' ? brunoSettings.maxRedirects : 5
};
Expand Down
7 changes: 4 additions & 3 deletions packages/bruno-converters/src/opencollection/items/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
fromOpenCollectionActions,
toOpenCollectionActions,
fromOpenCollectionAssertions,
toOpenCollectionAssertions
toOpenCollectionAssertions,
TIMEOUT_INHERIT
} from '../common';
import type {
HttpRequest,
Expand Down Expand Up @@ -105,7 +106,7 @@ export const fromOpenCollectionHttpItem = (ocRequest: HttpRequest): BrunoItem =>
if (ocRequest.settings) {
const settings: BrunoHttpItemSettings = {
encodeUrl: typeof ocRequest.settings.encodeUrl === 'boolean' ? ocRequest.settings.encodeUrl : true,
timeout: typeof ocRequest.settings.timeout === 'number' ? ocRequest.settings.timeout : 0,
timeout: typeof ocRequest.settings.timeout === 'number' || ocRequest.settings.timeout === TIMEOUT_INHERIT ? ocRequest.settings.timeout : 0,
followRedirects: typeof ocRequest.settings.followRedirects === 'boolean' ? ocRequest.settings.followRedirects : true,
maxRedirects: typeof ocRequest.settings.maxRedirects === 'number' ? ocRequest.settings.maxRedirects : 5
};
Expand Down Expand Up @@ -221,7 +222,7 @@ export const toOpenCollectionHttpItem = (item: BrunoItem): HttpRequest => {

const settings: HttpRequestSettings = {
encodeUrl: typeof brunoSettings?.encodeUrl === 'boolean' ? brunoSettings.encodeUrl : true,
timeout: typeof brunoSettings?.timeout === 'number' ? brunoSettings.timeout : 0,
timeout: typeof brunoSettings?.timeout === 'number' || brunoSettings?.timeout === TIMEOUT_INHERIT ? brunoSettings.timeout : 0,
followRedirects: typeof brunoSettings?.followRedirects === 'boolean' ? brunoSettings.followRedirects : true,
maxRedirects: typeof brunoSettings?.maxRedirects === 'number' ? brunoSettings.maxRedirects : 5
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { toOpenCollectionVariables } from '../common/variables';
import { toOpenCollectionActions } from '../common/actions';
import { toOpenCollectionScripts } from '../common/scripts';
import { toOpenCollectionAssertions } from '../common/assertions';
import { TIMEOUT_INHERIT } from '@usebruno/common/utils';

const stringifyGraphQLRequest = (item: BrunoItem): string => {
try {
Expand Down Expand Up @@ -130,7 +131,8 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
}

const timeout = httpSettings?.timeout;
if (isNumber(timeout)) {

if (isNumber(timeout) || timeout === TIMEOUT_INHERIT) {
settings.timeout = timeout;
} else {
settings.timeout = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { toOpenCollectionActions } from '../common/actions';
import { toOpenCollectionScripts } from '../common/scripts';
import { toOpenCollectionAssertions } from '../common/assertions';
import { isNumber, isNonEmptyString } from '../../../utils';
import { TIMEOUT_INHERIT } from '@usebruno/common/utils';

const stringifyHttpRequest = (item: BrunoItem): string => {
try {
Expand Down Expand Up @@ -118,7 +119,7 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
}

const timeout = httpSettings?.timeout;
if (isNumber(timeout)) {
if (isNumber(timeout) || timeout === TIMEOUT_INHERIT) {
settings.timeout = timeout;
} else {
settings.timeout = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
opencollection: "1.0.0"

info:
name: settings-yaml

bundled: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
info:
name: timeout-test-yaml
type: http
seq: 1

http:
method: GET
url: https://testbench-sanity.usebruno.com/redirect-to-ping
auth: inherit

settings:
followRedirects: false
maxRedirects: 0
timeout: 5
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
{
"collections": [
{
"path": "{{projectRoot}}/tests/request/settings/collection",
"path": "{{projectRoot}}/tests/request/settings/collection/requests-settings-bru",
"securityConfig": {
"jsSandboxMode": "safe"
}
},
{
"path": "{{projectRoot}}/tests/request/settings/collection/requests-settings-yml",
"securityConfig": {
"jsSandboxMode": "safe"
}
Expand Down
3 changes: 2 additions & 1 deletion tests/request/settings/init-user-data/preferences.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"maximized": false,
"lastOpenedCollections": [
"{{projectRoot}}/tests/request/settings/collection"
"{{projectRoot}}/tests/request/settings/collection/requests-settings-bru",
"{{projectRoot}}/tests/request/settings/collection/requests-settings-yml"
],
"preferences": {
"onboarding": {
Expand Down
163 changes: 125 additions & 38 deletions tests/request/settings/timeout.spec.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,143 @@
import { test, expect } from '../../../playwright';
import { expect, Page, test } from '../../../playwright';
import { closeAllCollections, selectRequestPaneTab } from '../../utils/page';

const setGlobalRequestTimeout = async (page: Page, value: string) => {
// Open preferences tab

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant comment. please remove this

await page.locator('.status-bar button[data-trigger="preferences"]').click();

// Navigate to General tab (default, but ensure it)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

const generalTab = page.getByRole('tab', { name: 'General' });
await expect(generalTab).toBeVisible({ timeout: 10000 });
await generalTab.click();

// Update the Request Timeout (in ms) preference
const timeoutPreference = page.locator('input[name="timeout"]');
await expect(timeoutPreference).toBeVisible({ timeout: 10000 });
await timeoutPreference.fill(value);
await expect(timeoutPreference).toHaveValue(value, { timeout: 5000 });

// Closing the preferences tab unmounts the form, which flushes the debounced save immediately
const preferencesTab = page.locator('.request-tab').filter({ hasText: 'Preferences' });
await preferencesTab.hover();
await preferencesTab.locator('.close-icon').click({ force: true });
await expect(preferencesTab).not.toBeVisible({ timeout: 10000 });
};

test.describe('Timeout Settings Tests', () => {
test('should configure and test timeout settings', async ({
pageWithUserData: page
}) => {
// Navigate to the test collection and request
await expect(page.locator('#sidebar-collection-name').getByText('settings-test')).toBeVisible();
test.afterEach(async ({ pageWithUserData: page }) => {
await closeAllCollections(page);
});

await page.locator('#sidebar-collection-name').getByText('settings-test').click();
// Navigate to thetimeout request
await page.getByRole('complementary').getByText('timeout-test').click();
test.describe('bru request timeout settings', () => {
test('should configure and test timeout settings', async ({
pageWithUserData: page
}) => {
// Navigate to the test collection and request
await expect(page.locator('#sidebar-collection-name').getByText('settings-test')).toBeVisible();

// Go to Settings tab
await selectRequestPaneTab(page, 'Settings');
await page.locator('#sidebar-collection-name').getByText('settings-test').click();
// Navigate to thetimeout request
await page.getByRole('complementary').getByText('timeout-test').click();

// Test Timeout Settings with custom value
const timeoutInput = page.locator('input[id="timeout"]');
await expect(timeoutInput).toBeVisible();
// Go to Settings tab
await selectRequestPaneTab(page, 'Settings');

// Verify default value from .bru file (5)
await expect(timeoutInput).toHaveValue('5');
// Test Timeout Settings with custom value
const timeoutInput = page.locator('input[id="timeout"]');
await expect(timeoutInput).toBeVisible();

await page.getByTestId('send-arrow-icon').click();
// Verify default value from .bru file (5)
await expect(timeoutInput).toHaveValue('5');

const responsePane = page.locator('.response-pane');
await expect(responsePane).toContainText('timeout of 5ms exceeded');
await page.getByTestId('send-arrow-icon').click();

// Now test inherit functionality
// Click the X button to reset to inherit
const resetButton = page.locator('button[title="Reset to inherit"]');
await expect(resetButton).toBeVisible();
await resetButton.click();
const responsePane = page.locator('.response-pane');
await expect(responsePane).toContainText('timeout of 5ms exceeded');

// After reset, should see "Inherit" button instead of input
const inheritButton = page.locator('button:has-text("Inherit")');
await expect(inheritButton).toBeVisible();
await expect(timeoutInput).not.toBeVisible();
// Change the global request timeout preference that "inherit" should fall back to
await setGlobalRequestTimeout(page, '10');

// Run the request with inherit timeout
await page.getByTestId('send-arrow-icon').click();
// Return to the request and Settings tab
await page.getByRole('complementary').getByText('timeout-test').click();
await selectRequestPaneTab(page, 'Settings');

// Verify the request runs successfully with inherited timeout (should not timeout)
await expect(responsePane).toContainText('302');
// Now test inherit functionality
// Click the X button to reset to inherit
const resetButton = page.locator('button[title="Reset to inherit"]');
await expect(resetButton).toBeVisible();
await resetButton.click();

// Close without saving to avoid modifying the .bru file
await page.locator('.close-icon-container').click({ force: true });
await page.locator('button:has-text("Don\'t Save")').first().click();
// After reset, should see "Inherit" button instead of input
const inheritButton = page.locator('button:has-text("Inherit")');
await expect(inheritButton).toBeVisible();
await expect(timeoutInput).not.toBeVisible();

// Run the request with inherit timeout
await page.getByTestId('send-arrow-icon').click();

// Verify the request runs successfully with inherited timeout (should not timeout)
await expect(responsePane).toContainText('302');

Check failure on line 79 in tests/request/settings/timeout.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings

1) [default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toContainText(expected) Locator: locator('.response-pane') Expected string: "302" Received string: "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" Call log: - expect.toContainText with timeout 5000ms - waiting for locator('.response-pane') 2 × locator resolved to <section data-testid="response-pane" class="response-pane flex-grow overflow-x-auto">…</section> - unexpected value "ResponseHeadersTimelineTestsResponseHeadersTimelineTestsRaw 0Btimeout of 10ms exceeded" 7 × locator resolved to <section data-testid="response-pane" class="response-pane flex-grow overflow-x-auto">…</section> - unexpected value "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" 77 | 78 | // Verify the request runs successfully with inherited timeout (should not timeout) > 79 | await expect(responsePane).toContainText('302'); | ^ 80 | 81 | // Close without saving to avoid modifying the .bru file 82 | await page.locator('.close-icon-container').click({ force: true }); at /Users/admin/actions-runner/_work/bruno/bruno/tests/request/settings/timeout.spec.ts:79:34

Check failure on line 79 in tests/request/settings/timeout.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings

1) [default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toContainText(expected) Locator: locator('.response-pane') Expected string: "302" Received string: "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" Call log: - expect.toContainText with timeout 5000ms - waiting for locator('.response-pane') 9 × locator resolved to <section data-testid="response-pane" class="response-pane flex-grow overflow-x-auto">…</section> - unexpected value "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" 77 | 78 | // Verify the request runs successfully with inherited timeout (should not timeout) > 79 | await expect(responsePane).toContainText('302'); | ^ 80 | 81 | // Close without saving to avoid modifying the .bru file 82 | await page.locator('.close-icon-container').click({ force: true }); at /Users/admin/actions-runner/_work/bruno/bruno/tests/request/settings/timeout.spec.ts:79:34

Check failure on line 79 in tests/request/settings/timeout.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests (macOS)

[default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings

1) [default] › tests/request/settings/timeout.spec.ts:32:9 › Timeout Settings Tests › bru request timeout settings › should configure and test timeout settings Error: Timed out 5000ms waiting for expect(locator).toContainText(expected) Locator: locator('.response-pane') Expected string: "302" Received string: "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" Call log: - expect.toContainText with timeout 5000ms - waiting for locator('.response-pane') 9 × locator resolved to <section data-testid="response-pane" class="response-pane flex-grow overflow-x-auto">…</section> - unexpected value "ResponseHeadersTimelineTestsResponseHeadersRaw 0Btimeout of 10ms exceeded" 77 | 78 | // Verify the request runs successfully with inherited timeout (should not timeout) > 79 | await expect(responsePane).toContainText('302'); | ^ 80 | 81 | // Close without saving to avoid modifying the .bru file 82 | await page.locator('.close-icon-container').click({ force: true }); at /Users/admin/actions-runner/_work/bruno/bruno/tests/request/settings/timeout.spec.ts:79:34

// Close without saving to avoid modifying the .bru file
await page.locator('.close-icon-container').click({ force: true });
await page.locator('button:has-text("Don\'t Save")').first().click();
});
});

test.afterEach(async ({ pageWithUserData: page }) => {
// cleanup: close all collections
await closeAllCollections(page);
test.describe('yaml request timeout settings', () => {
test('should configure and test timeout settings for yaml request', async ({
pageWithUserData: page
}) => {
// Navigate to the yaml test collection and request
await expect(page.locator('#sidebar-collection-name').getByText('settings-yaml')).toBeVisible();

await page.locator('#sidebar-collection-name').getByText('settings-yaml').click();
// Navigate to the timeout request
await page.getByRole('complementary').getByText('timeout-test-yaml').click();

// Go to Settings tab
await selectRequestPaneTab(page, 'Settings');

// Test Timeout Settings with custom value
const timeoutInput = page.locator('input[id="timeout"]');

@sachin-thakur-bruno sachin-thakur-bruno Jun 30, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can use page.locator('#timeout'); directly

await expect(timeoutInput).toBeVisible();

// Verify default value from .yml file (5)
await expect(timeoutInput).toHaveValue('5');

await page.getByTestId('send-arrow-icon').click();

// Verify the custom timeout (5ms) is applied
const responsePane = page.locator('.response-pane');
await expect(responsePane).toContainText('timeout of 5ms exceeded');

// Change the global request timeout preference that "inherit" should fall back to
await setGlobalRequestTimeout(page, '10');

// Return to the request and Settings tab
await page.getByRole('complementary').getByText('timeout-test-yaml').click();
await selectRequestPaneTab(page, 'Settings');

// Now test inherit functionality
// Click the X button to reset to inherit
const resetButton = page.locator('button[title="Reset to inherit"]');
await expect(resetButton).toBeVisible();
await resetButton.click();

// After reset, should see "Inherit" button instead of input
const inheritButton = page.locator('button:has-text("Inherit")');
await expect(inheritButton).toBeVisible();
await expect(timeoutInput).not.toBeVisible();

// Run the request with inherit timeout
await page.getByTestId('send-arrow-icon').click();

// Verify the inherited timeout resolves to the new global preference (10ms), not the file value (5ms)
await expect(responsePane).toContainText('timeout of 10ms exceeded', { timeout: 15000 });

// Close without saving to avoid modifying the .yml file
await page.locator('.close-icon-container').click({ force: true });
await page.locator('button:has-text("Don\'t Save")').first().click();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
});
});
});
Loading