diff --git a/packages/bruno-app/src/components/RequestPane/WsQueryUrl/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WsQueryUrl/StyledWrapper.js index f8f88a764..ecada4e18 100644 --- a/packages/bruno-app/src/components/RequestPane/WsQueryUrl/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/WsQueryUrl/StyledWrapper.js @@ -2,6 +2,7 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` height: 2.3rem; + position: relative; .input-container { background-color: ${(props) => props.theme.requestTabPanel.url.bg}; diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 6355fb9a3..7652f003d 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -425,18 +425,24 @@ const sem = grammar.createSemantics().addAttribute('ast', { let settings = mapPairListToKeyValPair(dictionary.ast); const getNumFromRecord = createGetNumFromRecord(settings); - const keepAliveInterval = getNumFromRecord('keepAliveInterval', { - fallback: 0 - }); + const keepAliveInterval = getNumFromRecord('keepAliveInterval'); const timeout = getNumFromRecord('timeout'); + const _settings = { + encodeUrl: typeof settings.encodeUrl === 'boolean' ? settings.encodeUrl : settings.encodeUrl === 'true' + }; + + if (keepAliveInterval) { + _settings.keepAliveInterval = keepAliveInterval; + } + + if (timeout) { + _settings.timeout = timeout; + } + return { - settings: { - encodeUrl: typeof settings.encodeUrl === 'boolean' ? settings.encodeUrl : settings.encodeUrl === 'true', - keepAliveInterval, - timeout - } + settings: _settings }; }, grpc(_1, dictionary) { diff --git a/packages/bruno-lang/v2/tests/bruToJson.spec.js b/packages/bruno-lang/v2/tests/bruToJson.spec.js index a6199abb6..aa94ccef1 100644 --- a/packages/bruno-lang/v2/tests/bruToJson.spec.js +++ b/packages/bruno-lang/v2/tests/bruToJson.spec.js @@ -30,18 +30,11 @@ settings { }, settings: { encodeUrl: false, - keepAliveInterval: 0, timeout: 30 } }; const output = parser(input); - - // Stub value if it doesn't exist in the input - expect(output).toHaveProperty('settings.keepAliveInterval'); - - // value needs to be a number - expect(output.settings.keepAliveInterval).toBe(0); expect(output).toEqual(expected); }); }); diff --git a/tests/request/save/save.spec.ts b/tests/request/save/save.spec.ts new file mode 100644 index 000000000..fbd74ea0c --- /dev/null +++ b/tests/request/save/save.spec.ts @@ -0,0 +1,72 @@ +import { test, expect, Locator, Page } from '../../../playwright'; +import { closeAllCollections } from '../../utils/page'; +import { buildCommonLocators } from '../../utils/page/locators'; +import { waitForPredicate } from '../../utils/wait'; + +const isRequestSaved = async (saveButton: Locator) => { + const savedColor = '#9f9f9f'; + return (await saveButton.evaluate((d) => d.querySelector('svg')?.getAttribute('stroke') ?? '#invalid')) === savedColor; +}; + +const setup = async (page: Page, createTmpDir: (tag?: string | undefined) => Promise) => { + await page.locator('.dropdown-icon').click(); + await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click(); + await page.getByLabel('Name').fill('source-collection'); + await page.getByLabel('Location').fill(await createTmpDir('source-collection')); + await page.getByRole('button', { name: 'Create', exact: true }).click(); + await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' })).toBeVisible(); + await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' }).click(); + await page.getByLabel('Safe Mode').check(); + await page.getByRole('button', { name: 'Save' }).click(); + const sourceCollection = page.locator('.collection-name').filter({ hasText: 'source-collection' }); + await sourceCollection.locator('.collection-actions').hover(); + await sourceCollection.locator('.collection-actions .icon').click(); + await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click(); + await page.getByPlaceholder('Request Name').fill('test-request'); + await page.locator('#new-request-url .CodeMirror').click(); + await page.locator('textarea').fill('https://httpbin.org/get'); + await page.getByRole('button', { name: 'Create' }).click(); + await expect(page.locator('.collection-item-name').filter({ hasText: 'test-request' })).toBeVisible(); +}; + +test.describe.serial('save requests', () => { + test.beforeAll(async ({ page }) => { + await closeAllCollections(page); + }); + + test('saves new http request', async ({ page, createTmpDir }) => { + // prep the collection by creating a new collection and a new http request + await setup(page, createTmpDir); + + const locators = buildCommonLocators(page); + const originalUrl = 'https://httpbin.org/get'; + const replacementUrl = 'ws://localhost:8082'; + + const clearText = async (text: string) => { + for (let i = text.length; i > 0; i--) { + await page.keyboard.press('Backspace'); + } + }; + + // Open the request tab + await page.locator('.collection-item-name').filter({ hasText: 'test-request' }).dblclick(); + await expect(page.locator('.request-tab .tab-label').filter({ hasText: 'test-request' })).toBeVisible(); + + // remove the original url from the request + await page.locator('.input-container').filter({ hasText: originalUrl }).first().click(); + await clearText(originalUrl); + + // replace it with an arbitrary url + await page.keyboard.insertText(replacementUrl); + + // check if the request is now unsaved + expect(await isRequestSaved(locators.saveButton())).toBe(false); + + // trigger a save + locators.saveButton().click(); + + // Wait for it to be saved + const result = await waitForPredicate(() => isRequestSaved(locators.saveButton())); + expect(result).toBe(true); + }); +}); diff --git a/tests/utils/page/locators.ts b/tests/utils/page/locators.ts index 47d674d4b..bd969eaeb 100644 --- a/tests/utils/page/locators.ts +++ b/tests/utils/page/locators.ts @@ -1,10 +1,14 @@ import { Page } from '../../../playwright'; -export const buildWebsocketCommonLocators = (page: Page) => ({ +export const buildCommonLocators = (page: Page) => ({ runner: () => page.getByTestId('run-button'), saveButton: () => page .locator('.infotip') - .filter({ hasText: /^Save/ }), + .filter({ hasText: /^Save/ }) +}); + +export const buildWebsocketCommonLocators = (page: Page) => ({ + ...buildCommonLocators(page), connectionControls: { connect: () => page