feat: Add tabs component for pre-request and post-response scripts (#5926)

This commit is contained in:
Pooja
2025-11-12 12:53:32 +05:30
committed by GitHub
parent 9e19244665
commit 14bece8696
8 changed files with 324 additions and 161 deletions

View File

@@ -1,20 +1,37 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import get from 'lodash/get';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import { updateCollectionRequestScript, updateCollectionResponseScript } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'components/Tabs';
import StyledWrapper from './StyledWrapper';
const Script = ({ collection }) => {
const dispatch = useDispatch();
const [activeTab, setActiveTab] = useState('pre-request');
const preRequestEditorRef = useRef(null);
const postResponseEditorRef = useRef(null);
const requestScript = collection.draft?.root ? get(collection, 'draft.root.request.script.req', '') : get(collection, 'root.request.script.req', '');
const responseScript = collection.draft?.root ? get(collection, 'draft.root.request.script.res', '') : get(collection, 'root.request.script.res', '');
const { displayedTheme } = useTheme();
const preferences = useSelector((state) => state.app.preferences);
// Refresh CodeMirror when tab becomes visible
useEffect(() => {
const timer = setTimeout(() => {
if (activeTab === 'pre-request' && preRequestEditorRef.current?.editor) {
preRequestEditorRef.current.editor.refresh();
} else if (activeTab === 'post-response' && postResponseEditorRef.current?.editor) {
postResponseEditorRef.current.editor.refresh();
}
}, 0);
return () => clearTimeout(timer);
}, [activeTab]);
const onRequestScriptEdit = (value) => {
dispatch(
updateCollectionRequestScript({
@@ -38,38 +55,47 @@ const Script = ({ collection }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full">
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<div className="text-xs mb-4 text-muted">
Write pre and post-request scripts that will run before and after any request in this collection is sent.
</div>
<div className="flex-1 mt-2">
<div className="mb-1 title text-xs">Pre Request</div>
<CodeEditor
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
onEdit={onRequestScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'bru']}
/>
</div>
<div className="flex-1 mt-6">
<div className="mt-1 mb-1 title text-xs">Post Response</div>
<CodeEditor
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
onEdit={onResponseScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'res', 'bru']}
/>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="pre-request">Pre Request</TabsTrigger>
<TabsTrigger value="post-response">Post Response</TabsTrigger>
</TabsList>
<TabsContent value="pre-request" className="mt-2">
<CodeEditor
ref={preRequestEditorRef}
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
onEdit={onRequestScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'bru']}
/>
</TabsContent>
<TabsContent value="post-response" className="mt-2">
<CodeEditor
ref={postResponseEditorRef}
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
onEdit={onResponseScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'res', 'bru']}
/>
</TabsContent>
</Tabs>
<div className="mt-12">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>

View File

@@ -1,20 +1,37 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import get from 'lodash/get';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import { updateFolderRequestScript, updateFolderResponseScript } from 'providers/ReduxStore/slices/collections';
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'components/Tabs';
import StyledWrapper from './StyledWrapper';
const Script = ({ collection, folder }) => {
const dispatch = useDispatch();
const [activeTab, setActiveTab] = useState('pre-request');
const preRequestEditorRef = useRef(null);
const postResponseEditorRef = useRef(null);
const requestScript = folder.draft ? get(folder, 'draft.request.script.req', '') : get(folder, 'root.request.script.req', '');
const responseScript = folder.draft ? get(folder, 'draft.request.script.res', '') : get(folder, 'root.request.script.res', '');
const { displayedTheme } = useTheme();
const preferences = useSelector((state) => state.app.preferences);
// Refresh CodeMirror when tab becomes visible
useEffect(() => {
const timer = setTimeout(() => {
if (activeTab === 'pre-request' && preRequestEditorRef.current?.editor) {
preRequestEditorRef.current.editor.refresh();
} else if (activeTab === 'post-response' && postResponseEditorRef.current?.editor) {
postResponseEditorRef.current.editor.refresh();
}
}, 0);
return () => clearTimeout(timer);
}, [activeTab]);
const onRequestScriptEdit = (value) => {
dispatch(
updateFolderRequestScript({
@@ -40,38 +57,47 @@ const Script = ({ collection, folder }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full">
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<div className="text-xs mb-4 text-muted">
Pre and post-request scripts that will run before and after any request inside this folder is sent.
</div>
<div className="flex flex-col flex-1 mt-2 gap-y-2">
<div className="title text-xs">Pre Request</div>
<CodeEditor
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
onEdit={onRequestScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'bru']}
/>
</div>
<div className="flex flex-col flex-1 mt-2 gap-y-2">
<div className="title text-xs">Post Response</div>
<CodeEditor
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
onEdit={onResponseScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'res', 'bru']}
/>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="pre-request">Pre Request</TabsTrigger>
<TabsTrigger value="post-response">Post Response</TabsTrigger>
</TabsList>
<TabsContent value="pre-request" className="mt-2">
<CodeEditor
ref={preRequestEditorRef}
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
onEdit={onRequestScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'bru']}
/>
</TabsContent>
<TabsContent value="post-response" className="mt-2">
<CodeEditor
ref={postResponseEditorRef}
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
onEdit={onResponseScriptEdit}
mode="javascript"
onSave={handleSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
showHintsFor={['req', 'res', 'bru']}
/>
</TabsContent>
</Tabs>
<div className="mt-12">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>

View File

@@ -1,20 +1,37 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import get from 'lodash/get';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from 'components/CodeEditor';
import { updateRequestScript, updateResponseScript } from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme';
import StyledWrapper from './StyledWrapper';
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'components/Tabs';
const Script = ({ item, collection }) => {
const dispatch = useDispatch();
const [activeTab, setActiveTab] = useState('pre-request');
const preRequestEditorRef = useRef(null);
const postResponseEditorRef = useRef(null);
const requestScript = item.draft ? get(item, 'draft.request.script.req') : get(item, 'request.script.req');
const responseScript = item.draft ? get(item, 'draft.request.script.res') : get(item, 'request.script.res');
const { displayedTheme } = useTheme();
const preferences = useSelector((state) => state.app.preferences);
// Refresh CodeMirror when tab becomes visible
useEffect(() => {
// Small delay to ensure DOM is updated
const timer = setTimeout(() => {
if (activeTab === 'pre-request' && preRequestEditorRef.current?.editor) {
preRequestEditorRef.current.editor.refresh();
} else if (activeTab === 'post-response' && postResponseEditorRef.current?.editor) {
postResponseEditorRef.current.editor.refresh();
}
}, 0);
return () => clearTimeout(timer);
}, [activeTab]);
const onRequestScriptEdit = (value) => {
dispatch(
updateRequestScript({
@@ -39,38 +56,46 @@ const Script = ({ item, collection }) => {
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
return (
<StyledWrapper className="w-full flex flex-col">
<div className="flex flex-col flex-1 mt-2 gap-y-2">
<div className="title text-xs">Pre Request</div>
<CodeEditor
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
onEdit={onRequestScriptEdit}
mode="javascript"
onRun={onRun}
onSave={onSave}
showHintsFor={['req', 'bru']}
/>
</div>
<div className="flex flex-col flex-1 mt-2 gap-y-2">
<div className="title text-xs">Post Response</div>
<CodeEditor
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
onEdit={onResponseScriptEdit}
mode="javascript"
onRun={onRun}
onSave={onSave}
showHintsFor={['req', 'res', 'bru']}
/>
</div>
</StyledWrapper>
<div className="w-full h-full flex flex-col pt-4">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="pre-request">Pre Request</TabsTrigger>
<TabsTrigger value="post-response">Post Response</TabsTrigger>
</TabsList>
<TabsContent value="pre-request" className="mt-2" dataTestId="pre-request-script-editor">
<CodeEditor
ref={preRequestEditorRef}
collection={collection}
value={requestScript || ''}
theme={displayedTheme}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
onEdit={onRequestScriptEdit}
mode="javascript"
onRun={onRun}
onSave={onSave}
showHintsFor={['req', 'bru']}
/>
</TabsContent>
<TabsContent value="post-response" className="mt-2" dataTestId="post-response-script-editor">
<CodeEditor
ref={postResponseEditorRef}
collection={collection}
value={responseScript || ''}
theme={displayedTheme}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
onEdit={onResponseScriptEdit}
mode="javascript"
onRun={onRun}
onSave={onSave}
showHintsFor={['req', 'res', 'bru']}
/>
</TabsContent>
</Tabs>
</div>
);
};

View File

@@ -0,0 +1,59 @@
import React, { createContext, useContext } from 'react';
import { useTheme } from 'providers/Theme';
const TabsContext = createContext();
export const Tabs = ({ value, onValueChange, children, className = '' }) => {
return (
<TabsContext.Provider value={{ value, onValueChange }}>
<div className={`flex flex-col h-full flex-1 ${className}`}>{children}</div>
</TabsContext.Provider>
);
};
export const TabsList = ({ children, className = '' }) => {
const { theme } = useTheme();
return (
<div
className={`inline-flex h-8 w-fit justify-center rounded-md p-0.5 gap-[2px] ${className}`}
style={{ background: theme.tabs.secondary.inactive.bg }}
>
{children}
</div>
);
};
export const TabsTrigger = ({ value: triggerValue, children, className = '' }) => {
const { value, onValueChange } = useContext(TabsContext);
const { theme } = useTheme();
const isActive = value === triggerValue;
return (
<button
onClick={() => onValueChange(triggerValue)}
className={`inline-flex items-center justify-center rounded-[4px] p-[8px] text-sm whitespace-nowrap transition-all cursor-pointer border border-transparent hover:opacity-90 ${className}`}
style={{
background: isActive ? theme.tabs.secondary.active.bg : 'transparent',
color: isActive ? theme.tabs.secondary.active.color : theme.tabs.secondary.inactive.color
}}
>
{children}
</button>
);
};
export const TabsContent = ({ value: contentValue, children, className = '', dataTestId = '' }) => {
const { value } = useContext(TabsContext);
const isActive = value === contentValue;
return (
<div
className={`outline-none flex flex-col h-full flex-1 ${className}`}
data-testid={dataTestId}
style={{ display: isActive ? 'flex' : 'none' }}
>
{children}
</div>
);
};

View File

@@ -241,6 +241,16 @@ const darkTheme = {
active: {
color: '#CCCCCC',
border: '#F59E0B'
},
secondary: {
active: {
bg: '#2D2D2D',
color: '#CCCCCC'
},
inactive: {
bg: '#3F3F3F',
color: '#CCCCCC'
}
}
},

View File

@@ -242,6 +242,16 @@ const lightTheme = {
active: {
color: '#343434',
border: '#D97706'
},
secondary: {
active: {
bg: '#FFFFFF',
color: '#343434'
},
inactive: {
bg: '#ECECEE',
color: '#989898'
}
}
},

View File

@@ -60,20 +60,20 @@ test.describe('Import OpenAPI Collection with Examples', () => {
}
});
await test.step('Verify OpenAPI import settings modal appears', async () => {
const settingsModal = page.getByRole('dialog');
await expect(settingsModal.locator('.bruno-modal-header-title')).toContainText('OpenAPI Import Settings');
await settingsModal.getByRole('button', { name: 'Import' }).click();
await test.step('Verify Import Collection location modal appears', async () => {
const locationModal = page.getByRole('dialog');
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
await expect(locationModal.getByText('API with Examples')).toBeVisible();
});
await test.step('Click Browse link to select collection folder', async () => {
const settingsModal = page.getByRole('dialog');
await settingsModal.getByText('Browse').click();
const locationModal = page.getByRole('dialog');
await locationModal.getByText('Browse').click();
});
await test.step('Complete import by clicking import button', async () => {
const settingsModal = page.getByRole('dialog');
await settingsModal.getByRole('button', { name: 'Import' }).click();
const locationModal = page.getByRole('dialog');
await locationModal.getByRole('button', { name: 'Import' }).click();
});
await test.step('Handle sandbox modal', async () => {
@@ -175,34 +175,32 @@ test.describe('Import OpenAPI Collection with Examples', () => {
}
});
await test.step('Verify OpenAPI import settings modal appears', async () => {
const settingsModal = page.getByRole('dialog');
await expect(settingsModal.locator('.bruno-modal-header-title')).toContainText('OpenAPI Import Settings');
await test.step('Verify Import Collection location modal appears', async () => {
const locationModal = page.getByRole('dialog');
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
await expect(locationModal.getByText('API with Examples')).toBeVisible();
});
await test.step('Select path-based grouping option from dropdown', async () => {
const settingsModal = page.getByRole('dialog');
const locationModal = page.getByRole('dialog');
// Click on the grouping dropdown to open it
const groupingDropdown = settingsModal.getByTestId('grouping-dropdown');
const groupingDropdown = locationModal.getByTestId('grouping-dropdown');
await groupingDropdown.click();
// Wait for dropdown to open and select "Paths" option (note: it's "Paths" not "Path")
const pathOption = page.getByTestId('grouping-option-path');
await pathOption.click();
// click on import button
const importButton = settingsModal.getByRole('button', { name: 'Import' });
await importButton.click();
});
await test.step('Click Browse link to select collection folder', async () => {
const settingsModal = page.getByRole('dialog');
await settingsModal.getByText('Browse').click();
const locationModal = page.getByRole('dialog');
await locationModal.getByText('Browse').click();
});
await test.step('Complete import by clicking import button', async () => {
const settingsModal = page.getByRole('dialog');
await settingsModal.getByRole('button', { name: 'Import' }).click();
const locationModal = page.getByRole('dialog');
await locationModal.getByRole('button', { name: 'Import' }).click();
});
await test.step('Handle sandbox modal', async () => {

View File

@@ -8,60 +8,57 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
await page.getByRole('tab', { name: 'Script' }).click();
await expect(page.getByText('Pre Request')).toBeVisible();
await expect(page.locator('.title.text-xs').filter({ hasText: 'Post Response' })).toBeVisible();
// Pre Request tab should be active by default
await expect(page.getByRole('button', { name: 'Pre Request' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Post Response' })).toBeVisible();
const preRequestEditor = page.locator('text=Pre Request').locator('..').locator('.CodeMirror').first();
// Click on Pre Request tab to ensure it's active
await page.getByRole('button', { name: 'Pre Request' }).click();
const preRequestEditor = page.getByTestId('pre-request-script-editor').locator('.CodeMirror').first();
const preTextarea = preRequestEditor.locator('textarea[tabindex="0"]');
await preTextarea.focus();
const preContent = await preRequestEditor.textContent();
console.log('Pre Request content loaded:', preContent?.substring(0, 100));
const postResponseEditor = page.locator('text=Post Response').locator('..').locator('.CodeMirror').first();
const postTextarea = postResponseEditor.locator('textarea[tabindex="0"]');
await postTextarea.focus();
const postContent = await postResponseEditor.textContent();
console.log('Post Response content loaded:', postContent?.substring(0, 100));
await preTextarea.focus();
await page.keyboard.press('Meta+f');
// Verify search box appears
await expect(page.locator('.bruno-search-bar input[placeholder="Search..."]')).toBeVisible();
const preEditorSearchBar = page.getByTestId('pre-request-script-editor');
await expect(preEditorSearchBar.locator('.bruno-search-bar input[placeholder="Search..."]')).toBeVisible();
// Test search functionality
const searchInput = page.locator('.bruno-search-bar input[placeholder="Search..."]');
const searchInput = preEditorSearchBar.locator('.bruno-search-bar input[placeholder="Search..."]');
await searchInput.fill('searchableText');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 4');
await expect(preEditorSearchBar.locator('.searchbar-result-count')).toContainText('1 / 4');
// Test search options
const regexButton = page.locator('.searchbar-icon-btn').filter({ hasText: '' }).first();
const caseSensitiveButton = page.locator('.searchbar-icon-btn').filter({ hasText: '' }).nth(1);
const wholeWordButton = page.locator('.searchbar-icon-btn').filter({ hasText: '' }).nth(2);
const regexButton = preEditorSearchBar.locator('.searchbar-icon-btn').filter({ hasText: '' }).first();
const caseSensitiveButton = preEditorSearchBar.locator('.searchbar-icon-btn').filter({ hasText: '' }).nth(1);
const wholeWordButton = preEditorSearchBar.locator('.searchbar-icon-btn').filter({ hasText: '' }).nth(2);
// Test regex search
await regexButton.click();
await searchInput.fill('test\\w+');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 1');
await expect(preEditorSearchBar.locator('.searchbar-result-count')).toContainText('1 / 1');
// Test case sensitive search
await regexButton.click();
await caseSensitiveButton.click();
await searchInput.fill('Test');
await expect(page.locator('.searchbar-result-count')).toContainText('0 results');
await expect(preEditorSearchBar.locator('.searchbar-result-count')).toContainText('0 results');
// Test whole word search
await caseSensitiveButton.click();
await wholeWordButton.click();
await searchInput.fill('hello');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 1');
await expect(preEditorSearchBar.locator('.searchbar-result-count')).toContainText('1 / 1');
// Test close search
const closeButton = page.locator('.searchbar-icon-btn').last();
const closeButton = page.getByTestId('pre-request-script-editor').locator('.searchbar-icon-btn').last();
await closeButton.click();
await expect(page.locator('.bruno-search-bar')).not.toBeVisible();
await expect(page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar')).not.toBeVisible();
});
test('should handle search in different script editors independently', async ({ pageWithUserData: page }) => {
@@ -71,63 +68,75 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
await page.getByRole('tab', { name: 'Script' }).click();
await expect(page.getByText('Pre Request')).toBeVisible();
await expect(page.locator('.title.text-xs').filter({ hasText: 'Post Response' })).toBeVisible();
const preRequestEditor = page.locator('text=Pre Request').locator('..').locator('.CodeMirror').first();
const postResponseEditor = page.locator('text=Post Response').locator('..').locator('.CodeMirror').first();
// Test Pre Request tab
await page.getByRole('button', { name: 'Pre Request' }).click();
const preRequestEditor = page.getByTestId('pre-request-script-editor').locator('.CodeMirror').first();
const preTextarea = preRequestEditor.locator('textarea[tabindex="0"]');
await preTextarea.focus();
await page.keyboard.press('Meta+f');
const preSearchInput = page.locator('.bruno-search-bar input[placeholder="Search..."]');
const preSearchInput = page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await preSearchInput.fill('uniquePreVar');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 1');
await expect(page.getByTestId('pre-request-script-editor').locator('.searchbar-result-count')).toContainText('1 / 1');
await page.keyboard.press('Escape');
// Switch to Post Response tab
await page.getByRole('button', { name: 'Post Response' }).click();
const postResponseEditor = page.getByTestId('post-response-script-editor').locator('.CodeMirror').first();
const postTextarea = postResponseEditor.locator('textarea[tabindex="0"]');
await postTextarea.focus();
await page.keyboard.press('Meta+f');
const postSearchInput = page.locator('.bruno-search-bar input[placeholder="Search..."]');
const postSearchInput = page.getByTestId('post-response-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await postSearchInput.fill('uniquePostVar');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 1');
await expect(page.getByTestId('post-response-script-editor').locator('.searchbar-result-count')).toContainText('1 / 1');
await page.keyboard.press('Escape');
});
test('should maintain search state when switching between editors', async ({ pageWithUserData: page }) => {
test('should maintain search state when switching between tabs', async ({ pageWithUserData: page }) => {
await page.getByTitle('custom-search').click();
await page.getByText('search-test-request').click();
await page.getByRole('tab', { name: 'Script' }).click();
await expect(page.getByText('Pre Request')).toBeVisible();
await expect(page.locator('.title.text-xs').filter({ hasText: 'Post Response' })).toBeVisible();
const preRequestEditor = page.locator('text=Pre Request').locator('..').locator('.CodeMirror').first();
const postResponseEditor = page.locator('text=Post Response').locator('..').locator('.CodeMirror').first();
// Open search in Pre Request editor
await page.getByRole('button', { name: 'Pre Request' }).click();
const preRequestEditor = page.getByTestId('pre-request-script-editor').locator('.CodeMirror').first();
const preTextarea = preRequestEditor.locator('textarea[tabindex="0"]');
await preTextarea.focus();
await page.keyboard.press('Meta+f');
const searchInput = page.locator('.bruno-search-bar input[placeholder="Search..."]');
await searchInput.fill('commonVar');
await expect(page.locator('.searchbar-result-count')).toContainText('1 / 1');
const preSearchInput = page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await preSearchInput.fill('commonVar');
await expect(page.getByTestId('pre-request-script-editor').locator('.searchbar-result-count')).toContainText('1 / 1');
// Switch to Post Response editor while search is open
// Switch to Post Response tab while search is open
await page.getByRole('button', { name: 'Post Response' }).click();
// Open search in Post Response editor
const postResponseEditor = page.getByTestId('post-response-script-editor').locator('.CodeMirror').first();
const postTextarea = postResponseEditor.locator('textarea[tabindex="0"]');
await postTextarea.focus();
await page.keyboard.press('Meta+f');
// Search should still be visible and functional
await expect(page.locator('.bruno-search-bar')).toBeVisible();
await expect(searchInput).toHaveValue('commonVar');
const postSearchInput = page.getByTestId('post-response-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await postSearchInput.fill('postVar');
await expect(page.getByTestId('post-response-script-editor').locator('.searchbar-result-count')).toContainText('1 / 1');
const closeButton = page.locator('.searchbar-icon-btn').last();
// Switch back to Pre Request tab
await page.getByRole('button', { name: 'Pre Request' }).click();
// Search state should be maintained in Pre Request editor
await expect(page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar')).toBeVisible();
await expect(preSearchInput).toHaveValue('commonVar');
// Close the search in Pre Request editor
const closeButton = page.getByTestId('pre-request-script-editor').locator('.searchbar-icon-btn').last();
await closeButton.click();
await expect(page.locator('.bruno-search-bar')).not.toBeVisible();
await expect(page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar')).not.toBeVisible();
});
});