refactor: update UI interactions and improve test stability (#6042)

This commit is contained in:
Abhishek S Lal
2025-11-08 01:54:50 +05:30
committed by GitHub
parent 3fe5299d8e
commit a186df3ac4
21 changed files with 171 additions and 211 deletions

View File

@@ -205,7 +205,11 @@ class SingleLineEditor extends Component {
render() {
return (
<div className={`flex flex-row justify-between w-full overflow-x-auto ${this.props.className}`}>
<StyledWrapper ref={this.editorRef} className={`single-line-editor grow ${this.props.readOnly ? 'read-only' : ''}`} />
<StyledWrapper
ref={this.editorRef}
className={`single-line-editor grow ${this.props.readOnly ? 'read-only' : ''}`}
{...(this.props['data-testid'] ? { 'data-testid': this.props['data-testid'] } : {})}
/>
{this.secretEye(this.props.isSecret)}
</div>
);

View File

@@ -23,13 +23,18 @@ const StyledWrapper = styled.div`
max-width: 200px;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
cursor: default;
&:hover {
&:has(.tag-remove:hover) {
background-color: ${(props) => props.theme.requestTabs.active.bg};
border-color: ${(props) => props.theme.requestTabs.active.border || props.theme.requestTabs.bottomBorder};
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
.tag-remove {
cursor: pointer;
}
}
.tag-icon {

View File

@@ -49,6 +49,7 @@ const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSav
onChange={handleInputChange}
onRun={handleKeyDown}
onSave={onSave}
data-testid="tag-input"
/>
{error && <span className='text-xs text-red-500'>{error}</span>}
<ul className="flex flex-wrap gap-1">
@@ -57,14 +58,15 @@ const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSav
<li key={_tag}>
<button
className="tag-item"
onClick={() => handleRemoveTag(_tag)}
type="button"
>
<IconTag size={12} className="tag-icon" aria-hidden="true" />
<span className="tag-text" title={_tag}>
{_tag}
</span>
<IconX size={12} strokeWidth={2} aria-hidden="true" />
<span className="tag-remove" title="Remove tag" onClick={() => handleRemoveTag(_tag)}>
<IconX size={12} strokeWidth={2} aria-hidden="true" />
</span>
</button>
</li>
))

View File

@@ -11,12 +11,14 @@ test.describe('Create GraphQL Requests', () => {
test.afterAll(async ({ pageWithUserData: page }) => {
// Clean up Root GraphQL Request
await locators.sidebar.request('Root GraphQL Request').click({ button: 'right' });
await locators.sidebar.request('Root GraphQL Request').hover();
await locators.actions.collectionItemActions('Root GraphQL Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
// Clean up Folder GraphQL Request
await locators.sidebar.request('Folder GraphQL Request').click({ button: 'right' });
await locators.sidebar.request('Folder GraphQL Request').hover();
await locators.actions.collectionItemActions('Folder GraphQL Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
@@ -58,8 +60,8 @@ test.describe('Create GraphQL Requests', () => {
});
await test.step('Create GraphQL request via folder1 three dots menu', async () => {
const folderItem = locators.sidebar.folder('folder1');
await folderItem.click({ button: 'right' });
await locators.sidebar.folder('folder1').hover();
await locators.actions.collectionItemActions('folder1').click();
await locators.dropdown.item('New Request').click();
await page.getByTestId('graphql-request').click();

View File

@@ -13,12 +13,14 @@ test.describe('Create gRPC Requests', () => {
const locators = buildCommonLocators(page);
// Clean up Root gRPC Request
await locators.sidebar.request('Root gRPC Request').click({ button: 'right' });
await locators.sidebar.request('Root gRPC Request').hover();
await locators.actions.collectionItemActions('Root gRPC Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
// Clean up Folder gRPC Request
await locators.sidebar.request('Folder gRPC Request').click({ button: 'right' });
await locators.sidebar.request('Folder gRPC Request').hover();
await locators.actions.collectionItemActions('Folder gRPC Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
@@ -60,8 +62,8 @@ test.describe('Create gRPC Requests', () => {
});
await test.step('Create gRPC request via folder1 three dots menu', async () => {
const folderItem = locators.sidebar.folder('folder1');
await folderItem.click({ button: 'right' });
await locators.sidebar.folder('folder1').hover();
await locators.actions.collectionItemActions('folder1').click();
await locators.dropdown.item('New Request').click();
await page.getByTestId('grpc-request').click();

View File

@@ -13,12 +13,14 @@ test.describe('Create HTTP Requests', () => {
const locators = buildCommonLocators(page);
// Clean up Root HTTP Request
await locators.sidebar.request('Root HTTP Request').click({ button: 'right' });
await locators.sidebar.request('Root HTTP Request').hover();
await locators.actions.collectionItemActions('Root HTTP Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
// Clean up Folder HTTP Request
await locators.sidebar.request('Folder HTTP Request').click({ button: 'right' });
await locators.sidebar.request('Folder HTTP Request').hover();
await locators.actions.collectionItemActions('Folder HTTP Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
@@ -59,8 +61,8 @@ test.describe('Create HTTP Requests', () => {
});
await test.step('Create HTTP request via folder1 three dots menu', async () => {
const folderItem = locators.sidebar.folder('folder1');
await folderItem.click({ button: 'right' });
await locators.sidebar.folder('folder1').hover();
await locators.actions.collectionItemActions('folder1').click();
await locators.dropdown.item('New Request').click();
await page.getByTestId('request-name').fill('Folder HTTP Request');

View File

@@ -13,12 +13,14 @@ test.describe('Create WebSocket Requests', () => {
const locators = buildCommonLocators(page);
// Clean up Folder WebSocket Request
await locators.sidebar.request('Folder WebSocket Request').click({ button: 'right' });
await locators.sidebar.request('Folder WebSocket Request').hover();
await locators.actions.collectionItemActions('Folder WebSocket Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
// Clean up Root WebSocket Request
await locators.sidebar.request('Root WebSocket Request').click({ button: 'right' });
await locators.sidebar.request('Root WebSocket Request').hover();
await locators.actions.collectionItemActions('Root WebSocket Request').click();
await locators.dropdown.item('Delete').click();
await locators.modal.button('Delete').click();
@@ -60,8 +62,8 @@ test.describe('Create WebSocket Requests', () => {
});
await test.step('Create WebSocket request via folder1 three dots menu', async () => {
const folderItem = locators.sidebar.folder('folder1');
await folderItem.click({ button: 'right' });
await locators.sidebar.folder('folder1').hover();
await locators.actions.collectionItemActions('folder1').click();
await locators.dropdown.item('New Request').click();
await page.getByTestId('ws-request').click();

View File

@@ -1,5 +1,5 @@
import { test, expect } from '../../../playwright';
import { closeAllCollections } from '../../utils/page';
import { closeAllCollections, createCollection } from '../../utils/page';
test.describe('Cross-Collection Drag and Drop for folder', () => {
test.afterEach(async ({ page }) => {
@@ -8,18 +8,8 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
});
test('Verify cross-collection folder drag and drop', async ({ page, createTmpDir }) => {
// Create first collection - click dropdown menu first
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();
// Wait for collection to appear and click on it
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();
// Create first collection - open with sandbox mode
await createCollection(page, 'source-collection', await createTmpDir('source-collection'), { openWithSandboxMode: 'safe' });
// Create a folder in the first collection
// Look for the collection menu button for the source collection specifically
@@ -34,11 +24,12 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
await page.getByRole('button', { name: 'Create' }).click();
// Wait for the folder to be created and appear in the sidebar
await page.waitForTimeout(2000);
await page.waitForTimeout(200);
await expect(page.locator('.collection-item-name').filter({ hasText: 'test-folder' })).toBeVisible();
// Add a request to the folder to make it more realistic
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).click({ button: 'right' });
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).hover();
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).locator('.menu-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
await page.getByPlaceholder('Request Name').fill('test-request-in-folder');
await page.locator('#new-request-url .CodeMirror').click();
@@ -46,25 +37,15 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
await page.getByRole('button', { name: 'Create' }).click();
// Wait for the request to be created
await page.waitForTimeout(1000);
await page.waitForTimeout(200);
// Expand the folder to see the request inside
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(page.locator('.collection-item-name').filter({ hasText: 'test-request-in-folder' })).toBeVisible();
// Create second collection - click dropdown menu first
await page.locator('.dropdown-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
await page.getByLabel('Name').fill('target-collection');
await page.getByLabel('Location').fill(await createTmpDir('target-collection'));
await page.getByRole('button', { name: 'Create', exact: true }).click();
// Wait for second collection to appear and click on it
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible();
await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click();
await page.getByLabel('Safe Mode').check();
await page.getByRole('button', { name: 'Save' }).click();
// Create second collection - open with sandbox mode
await createCollection(page, 'target-collection', await createTmpDir('target-collection'), { openWithSandboxMode: 'safe' });
// Wait for both collections to be visible in sidebar
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' })).toBeVisible();
@@ -82,12 +63,12 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
await sourceFolder.dragTo(targetCollection);
// Wait for the operation to complete
await page.waitForTimeout(3000);
await page.waitForTimeout(200);
// Verify the folder has been moved to the target collection
// Click on target collection to expand it if needed
await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click();
await page.waitForTimeout(1000);
await page.waitForTimeout(200);
// Check that the folder now appears under target collection
const targetCollectionContainer = page
@@ -100,7 +81,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
// Expand the moved folder to verify the request inside is also moved
await targetCollectionContainer.locator('.collection-item-name').filter({ hasText: 'test-folder' }).click();
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await expect(
targetCollectionContainer.locator('.collection-item-name').filter({ hasText: 'test-request-in-folder' })
).toBeVisible();
@@ -125,17 +106,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
createTmpDir
}) => {
// Create first collection (source) - use unique names for this test
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();
// Wait for collection to appear and click on it
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();
await createCollection(page, 'source-collection', await createTmpDir('source-collection'), { openWithSandboxMode: 'safe' });
// Create a folder in the first collection
await page
@@ -158,7 +129,8 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
await expect(page.locator('.collection-item-name').filter({ hasText: 'folder-1' })).toBeVisible();
// Add a request to the folder to make it more realistic
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).click({ button: 'right' });
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).hover();
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).locator('.menu-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
await page.getByPlaceholder('Request Name').fill('http-request');
await page.locator('#new-request-url .CodeMirror').click();
@@ -169,17 +141,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
await expect(page.locator('.collection-item-name').filter({ hasText: 'http-request' })).toBeVisible();
// Create second collection (target)
await page.locator('.dropdown-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
await page.getByLabel('Name').fill('target-collection');
await page.getByLabel('Location').fill(await createTmpDir('target-collection'));
await page.getByRole('button', { name: 'Create', exact: true }).click();
// Wait for second collection to appear and click on it
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible();
await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click();
await page.getByLabel('Safe Mode').check();
await page.getByRole('button', { name: 'Save' }).click();
await createCollection(page, 'target-collection', await createTmpDir('target-collection'), { openWithSandboxMode: 'safe' });
// Create a folder with the same name in the target collection
await page

View File

@@ -1,5 +1,5 @@
import { test, expect } from '../../../playwright';
import { closeAllCollections } from '../../utils/page';
import { closeAllCollections, createCollection } from '../../utils/page';
test.describe('Cross-Collection Drag and Drop', () => {
test.afterEach(async ({ page }) => {
@@ -8,17 +8,8 @@ test.describe('Cross-Collection Drag and Drop', () => {
});
test('Verify request drag and drop', async ({ page, createTmpDir }) => {
// Create first collection - click dropdown menu first
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();
// Create first collection - open with sandbox mode
await createCollection(page, 'source-collection', await createTmpDir('source-collection'), { openWithSandboxMode: 'safe' });
// Create a request in the first collection
await page.locator('#create-new-tab').getByRole('img').click();
@@ -29,17 +20,8 @@ test.describe('Cross-Collection Drag and Drop', () => {
await expect(page.locator('.collection-item-name').filter({ hasText: 'test-request' })).toBeVisible();
// Create second collection - click dropdown menu first
await page.locator('.dropdown-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
await page.getByLabel('Name').fill('target-collection');
await page.getByLabel('Location').fill(await createTmpDir('target-collection'));
await page.getByRole('button', { name: 'Create', exact: true }).click();
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible();
await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click();
await page.getByLabel('Safe Mode').check();
await page.getByRole('button', { name: 'Save' }).click();
// Create second collection - open with sandbox mode
await createCollection(page, 'target-collection', await createTmpDir('target-collection'), { openWithSandboxMode: 'safe' });
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' })).toBeVisible();
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible();
@@ -83,17 +65,7 @@ test.describe('Cross-Collection Drag and Drop', () => {
createTmpDir
}) => {
// Create first collection (source-collection)
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();
// Open collection
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();
await createCollection(page, 'source-collection', await createTmpDir('source-collection'), { openWithSandboxMode: 'safe' });
// Create a request in the first collection (request-1)
await page.locator('#create-new-tab').getByRole('img').click();
@@ -106,17 +78,7 @@ test.describe('Cross-Collection Drag and Drop', () => {
await expect(page.locator('.collection-item-name').filter({ hasText: 'request-1' })).toBeVisible();
// Create second collection (target-collection)
await page.locator('.dropdown-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
await page.getByLabel('Name').fill('target-collection');
await page.getByLabel('Location').fill(await createTmpDir('target-collection'));
await page.getByRole('button', { name: 'Create', exact: true }).click();
// Open collection
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' })).toBeVisible();
await page.locator('#sidebar-collection-name').filter({ hasText: 'target-collection' }).click();
await page.getByLabel('Safe Mode').check();
await page.getByRole('button', { name: 'Save' }).click();
await createCollection(page, 'target-collection', await createTmpDir('target-collection'), { openWithSandboxMode: 'safe' });
// Create a request in the target collection with the same name (request-1)
await page.locator('#create-new-tab').getByRole('img').click();

View File

@@ -19,19 +19,19 @@ test.describe('Tag persistence', () => {
// Create a new request
await page.locator('#create-new-tab').getByRole('img').click();
await page.getByRole('textbox', { name: 'Request Name' }).fill('r1');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-1');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
// create another request
await page.locator('#create-new-tab').getByRole('img').click();
await page.getByRole('textbox', { name: 'Request Name' }).fill('r2');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-2');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
// create another request
await page.locator('#create-new-tab').getByRole('img').click();
await page.getByRole('textbox', { name: 'Request Name' }).fill('r3');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-3');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
@@ -39,37 +39,38 @@ test.describe('Tag persistence', () => {
// Add a tag to the request
await page.getByRole('tab', { name: 'Settings' }).click();
await page.getByText('Tagse.g., smoke, regression').click();
await page.getByRole('textbox').nth(2).fill('smoke');
await page.getByRole('textbox').nth(2).press('Enter');
await page.waitForTimeout(200);
const tagInput = await page.getByTestId('tag-input').getByRole('textbox');
await tagInput.fill('smoke');
await tagInput.press('Enter');
await page.waitForTimeout(200);
// Verify the tag was added
await expect(page.getByRole('button', { name: 'smoke' })).toBeVisible();
await expect(page.locator('.tag-item', { hasText: 'smoke' })).toBeVisible();
await page.keyboard.press('Meta+s');
// Move the r2 request to just above r1 within the same collection
const r3Request = page.locator('.collection-item-name').filter({ hasText: 'r3' });
const r1Request = page.locator('.collection-item-name').filter({ hasText: 'r1' });
// Move the request-3 request to just above request-1 within the same collection
const r3Request = page.locator('.collection-item-name').filter({ hasText: 'request-3' });
const r1Request = page.locator('.collection-item-name').filter({ hasText: 'request-1' });
await expect(r3Request).toBeVisible();
await expect(r1Request).toBeVisible();
// Perform drag and drop operation to move r3 below r1 using source position
// Perform drag and drop operation to move request-3 below request-1 using source position
await r3Request.dragTo(r1Request, {
targetPosition: { x: 0, y: 1 }
});
// Verify the requests are still in the collection and r3 is now above r1
await expect(page.locator('.collection-item-name').filter({ hasText: 'r3' })).toBeVisible();
await expect(page.locator('.collection-item-name').filter({ hasText: 'r1' })).toBeVisible();
// Verify the requests are still in the collection and request-3 is now above request-1
await expect(page.locator('.collection-item-name').filter({ hasText: 'request-3' })).toBeVisible();
await expect(page.locator('.collection-item-name').filter({ hasText: 'request-1' })).toBeVisible();
// Click on r3 to verify the tag persisted after the move
await page.locator('.collection-item-name').filter({ hasText: 'r3' }).click();
await page.locator('.request-tab.active', { hasText: 'r3' }).waitFor({ state: 'visible' });
// Click on request-3 to verify the tag persisted after the move
await page.locator('.collection-item-name').filter({ hasText: 'request-3' }).click();
await page.locator('.request-tab.active').filter({ hasText: 'request-3' }).waitFor({ state: 'visible' });
await page.getByRole('tab', { name: 'Settings' }).click();
await page.waitForTimeout(200);
// Verify the tag is still present after the move
await expect(page.getByRole('button', { name: 'smoke' })).toBeVisible();
await expect(page.locator('.tag-item', { hasText: 'smoke' })).toBeVisible();
});
test('verify tag persistence while moving requests between folders', async ({ page, createTmpDir }) => {
@@ -83,75 +84,82 @@ test.describe('Tag persistence', () => {
await page.getByRole('button', { name: 'Save' }).click();
// Create a new folder
await page.getByTitle('test-collection').click({
button: 'right'
});
await page.waitForTimeout(200);
await page.locator('.collection-name')
.filter({ hasText: 'test-collection' }).hover();
await page.locator('.collection-name')
.filter({ hasText: 'test-collection' }).locator('.collection-actions .icon').click();
await page.waitForTimeout(1);
await page.getByText('New Folder').click();
await page.locator('#folder-name').fill('f1');
await page.locator('#folder-name').fill('folder-1');
await page.getByRole('button', { name: 'Create' }).click();
await page.waitForTimeout(100);
// Create a new request within folder-1 folder
await page.getByText('folder-1').click();
await page.waitForTimeout(200);
// Create a new request within f1 folder
await page.getByText('f1').click();
await page.waitForTimeout(200);
await page.getByTitle('f1', { exact: true }).click({
button: 'right'
});
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).hover();
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).locator('.menu-icon').click();
await page.locator('.dropdown-item').getByText('New Request').click()
await page.getByRole('textbox', { name: 'Request Name' }).fill('r1');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-1');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
// create another request within f1 folder
await page.getByTitle('f1', { exact: true }).click({
button: 'right'
});
// create another request within folder-1 folder
await page.locator('.collection-item-name')
.filter({ hasText: 'folder-1' }).hover();
await page.locator('.collection-item-name')
.filter({ hasText: 'folder-1' }).locator('.menu-icon').click();
await page.locator('.dropdown-item').getByText('New Request').click()
await page.getByRole('textbox', { name: 'Request Name' }).fill('r2');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-2');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
await page.waitForTimeout(200);
// Add a tag to the request
await page.getByRole('tab', { name: 'Settings' }).click();
await page.getByText('Tagse.g., smoke, regression').click();
await page.getByRole('textbox').nth(2).fill('smoke');
await page.getByRole('textbox').nth(2).press('Enter');
await expect(page.getByRole('button', { name: 'smoke' })).toBeVisible();
await page.waitForTimeout(200);
const tagInput = await page.getByTestId('tag-input').getByRole('textbox');
await tagInput.fill('smoke');
await tagInput.press('Enter');
await page.waitForTimeout(200);
await expect(page.locator('.tag-item', { hasText: 'smoke' })).toBeVisible();
await page.keyboard.press('Meta+s');
// Create another folder
await page.getByTitle('test-collection').click({
button: 'right'
});
await page.locator('.collection-name')
.filter({ hasText: 'test-collection' }).hover();
await page.locator('.collection-name')
.filter({ hasText: 'test-collection' }).locator('.collection-actions .icon').click();
await page.locator('.dropdown-item').getByText('New Folder').click();
await page.locator('#folder-name').fill('f2');
await page.locator('#folder-name').fill('folder-2');
await page.getByRole('button', { name: 'Create' }).click();
// open f2 folder
await page.getByText('f2').click();
await page.getByTitle('f2', { exact: true }).click({
button: 'right'
});
// open folder-2 folder
await page.getByText('folder-2').click();
await page.locator('.collection-item-name')
.filter({ hasText: 'folder-2' }).hover();
await page.locator('.collection-item-name')
.filter({ hasText: 'folder-2' }).locator('.menu-icon').click();
await page.locator('.dropdown-item').getByText('New Request').click();
await page.getByRole('textbox', { name: 'Request Name' }).fill('r3');
await page.getByRole('textbox', { name: 'Request Name' }).fill('request-3');
await page.locator('#new-request-url textarea').fill('https://httpfaker.org/api/echo');
await page.getByRole('button', { name: 'Create' }).click();
// Drag and drop r2 request to f2 folder
const r2Request = page.locator('.collection-item-name').filter({ hasText: 'r2' });
const f2Folder = page.locator('.collection-item-name').filter({ hasText: 'f2' });
// Drag and drop request-2 request to folder-2 folder
const r2Request = page.locator('.collection-item-name').filter({ hasText: 'request-2' });
const f2Folder = page.locator('.collection-item-name').filter({ hasText: 'folder-2' });
await r2Request.dragTo(f2Folder);
// Verify the requests are still in the collection and r2 is now in f2 folder
await expect(page.locator('.collection-item-name').filter({ hasText: 'r2' })).toBeVisible();
await expect(page.locator('.collection-item-name').filter({ hasText: 'f2' })).toBeVisible();
// Verify the requests are still in the collection and request-2 is now in folder-2 folder
await expect(page.locator('.collection-item-name').filter({ hasText: 'request-2' })).toBeVisible();
await expect(page.locator('.collection-item-name').filter({ hasText: 'folder-2' })).toBeVisible();
// Click on r2 to verify the tag persisted after the move
await page.locator('.collection-item-name').filter({ hasText: 'r2' }).click();
await page.locator('.request-tab.active', { hasText: 'r2' }).waitFor({ state: 'visible' });
// Click on request-2 to verify the tag persisted after the move
await page.locator('.collection-item-name').filter({ hasText: 'request-2' }).click();
await page.locator('.request-tab.active').filter({ hasText: 'request-2' }).waitFor({ state: 'visible' });
await page.getByRole('tab', { name: 'Settings' }).click();
await expect(page.getByRole('button', { name: 'smoke' })).toBeVisible();
await page.waitForTimeout(200);
await expect(page.locator('.tag-item', { hasText: 'smoke' })).toBeVisible();
});
});

View File

@@ -10,7 +10,9 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
if (page && !page.isClosed()) {
await page.locator('#sidebar-collection-name').click();
await page.getByTestId('environment-selector-trigger').click();
await page.waitForTimeout(200);
await page.locator('#configure-env').click();
await page.waitForTimeout(200);
// Remove the test environment variables
const key1Row = page.getByRole('row', { name: 'multiple-persist-vars-key1' });
@@ -40,6 +42,7 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
await test.step('Select stage environment', async () => {
await page.getByTestId('environment-selector-trigger').click();
await page.waitForTimeout(200);
await expect(page.locator('.environment-list .dropdown-item', { hasText: 'Stage' })).toBeVisible();
await page.locator('.environment-list .dropdown-item', { hasText: 'Stage' }).click();
await expect(page.locator('.current-environment', { hasText: 'Stage' })).toBeVisible();
@@ -49,8 +52,9 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
// Ensure we're in the correct collection context before selecting the folder
await expect(page.locator('#sidebar-collection-name', { hasText: 'collection' })).toBeVisible();
// Right-click on the folder to open context menu
await page.getByText('multiple-persist-vars-folder', { exact: true }).click({ button: 'right' });
// Hover on the folder and open context menu
await page.getByText('multiple-persist-vars-folder', { exact: true }).hover();
await page.locator('.collection-item-name').filter({ hasText: 'multiple-persist-vars-folder' }).locator('.menu-icon').click();
// Click on Run option
await page.getByText('Run', { exact: true }).click();
@@ -67,7 +71,9 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
await expect(page.locator('#sidebar-collection-name', { hasText: 'collection' })).toBeVisible();
await page.getByTestId('environment-selector-trigger').click();
await page.waitForTimeout(200);
await page.locator('#configure-env').click();
await page.waitForTimeout(200);
await expect(page.getByRole('row', { name: 'multiple-persist-vars-key1' }).getByRole('cell').nth(1)).toBeVisible();
await expect(page.getByRole('row', { name: 'value1' }).getByRole('cell').nth(2)).toBeVisible();
await expect(page.getByRole('row', { name: 'multiple-persist-vars-key2' }).getByRole('cell').nth(1)).toBeVisible();

View File

@@ -249,7 +249,7 @@ test.describe.serial('Global Environment Export Tests', () => {
await page.getByRole('button', { name: 'Export 1 Environment' }).click();
// Verify success message
await expect(page.getByText('Environment(s) exported successfully', { exact: false })).toBeVisible();
await expect(page.getByText('Environment(s) exported successfully', { exact: false }).first()).toBeVisible();
await page.getByTestId('modal-close-button').click();
});
@@ -296,9 +296,9 @@ test.describe.serial('Global Environment Export Tests', () => {
await test.step('Execute export and verify success', async () => {
// Export the environments
await page.getByRole('button', { name: 'Export 2 Environments' }).click();
await page.waitForTimeout(200);
// Verify success message
await expect(page.getByText('Environment(s) exported successfully', { exact: false })).toBeVisible();
await expect(page.getByText('Environment(s) exported successfully', { exact: false }).first()).toBeVisible();
await page.getByTestId('modal-close-button').click();
});

View File

@@ -83,17 +83,5 @@ test.describe('Collection Environment Import Tests', () => {
await page.locator('[data-testid="send-arrow-icon"]').click();
await page.locator('[data-testid="response-status-code"]').waitFor({ state: 'visible' });
await expect(page.locator('[data-testid="response-status-code"]')).toContainText('201');
// Cleanup
await page.locator('#sidebar-collection-name').filter({ hasText: 'Environment Test Collection' }).click();
await page
.locator('.collection-name')
.filter({ has: page.locator('#sidebar-collection-name:has-text("Environment Test Collection")') })
.locator('.collection-actions')
.click();
// Wait for the close collection modal to be hidden
await page.locator('.dropdown-item').filter({ hasText: 'Close' }).click();
await page.locator('.dropdown-item').filter({ hasText: 'Close' }).waitFor({ state: 'detached' });
await page.getByRole('button', { name: 'Close' }).click();
});
});

View File

@@ -87,11 +87,13 @@ test.describe('Onboarding', () => {
const page = await app.firstWindow();
// First launch - sample collection should be created
const sampleCollection = page.locator('#sidebar-collection-name').getByText('Sample API Collection');
const sampleCollection = page.locator('.collection-name').filter({ hasText: 'Sample API Collection' });
await expect(sampleCollection).toBeVisible();
// User closes the sample collection (right-click to open context menu)
await sampleCollection.click({ button: 'right' });
// User closes the sample collection (hover on the collection and open context menu)
await sampleCollection.hover();
await sampleCollection.locator('.collection-actions .icon').click();
// Close the sample collection
const closeOption = page.locator('.dropdown-item').getByText('Close');

View File

@@ -28,7 +28,7 @@ test.describe('Default Collection Location Feature', () => {
await page.getByRole('button', { name: 'Save' }).click();
// verify success message
await expect(page.locator('text=Preferences saved successfully')).toBeVisible();
await expect(page.locator('text=Preferences saved successfully').first()).toBeVisible();
// wait for 2 seconds
await page.waitForTimeout(2000);
@@ -48,7 +48,7 @@ test.describe('Default Collection Location Feature', () => {
await page.getByRole('button', { name: 'Save' }).click();
// verify success message
await expect(page.locator('text=Preferences saved successfully')).toBeVisible();
await expect(page.locator('text=Preferences saved successfully').first()).toBeVisible();
// wait for 2 seconds
await page.waitForTimeout(2000);

View File

@@ -1,9 +1,13 @@
import { execSync } from 'child_process';
import { test, expect } from '../../playwright';
import { closeAllCollections } from '../utils/page';
import path from 'path';
test.describe('manage protofile', () => {
test.afterAll(async ({ pageWithUserData: page }) => {
await closeAllCollections(page);
// Reset the collection request file to the original state
execSync(`git checkout -- ${path.join(__dirname, 'collection', 'bruno.json')}`);
});
test('protofiles, import paths from bruno.json are visible in the protobuf settings', async ({ pageWithUserData: page }) => {

View File

@@ -27,7 +27,8 @@ test.describe('Copy and Paste Requests', () => {
await page.locator('.dropdown-item').filter({ hasText: 'Copy' }).click();
// Paste into the collection root
await collection.click({ button: 'right' });
await collection.hover();
await collection.locator('.collection-actions .icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Paste' }).click();
// Verify the pasted request appears with the same name
@@ -45,7 +46,8 @@ test.describe('Copy and Paste Requests', () => {
// Paste into the folder
const folder = page.locator('.collection-item-name').filter({ hasText: 'test-folder' });
await folder.click();
await folder.click({ button: 'right' });
await folder.hover();
await folder.locator('.menu-icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Paste' }).click();
await page.waitForTimeout(2000);
@@ -57,7 +59,8 @@ test.describe('Copy and Paste Requests', () => {
const collection = page.locator('.collection-name').filter({ hasText: 'test-collection-2' });
// Paste into the collection root
await collection.click({ button: 'right' });
await collection.hover();
await collection.locator('.collection-actions .icon').click();
await page.locator('.dropdown-item').filter({ hasText: 'Paste' }).click();
// Verify the pasted request appears with the same name

View File

@@ -10,8 +10,8 @@ test.describe.serial('Create and Delete Response Examples', () => {
test('should create a response example from response bookmark', async ({ pageWithUserData: page }) => {
await test.step('Open collection and request', async () => {
await page.locator('#sidebar-collection-name').getByText('collection').click();
await page.locator('.collection-item-name').getByText('create-example').click();
await page.locator('#sidebar-collection-name').filter({ hasText: 'collection' }).click();
await page.locator('.collection-item-name').filter({ hasText: 'create-example' }).click();
});
await test.step('Send request and validate example creation', async () => {

View File

@@ -27,7 +27,7 @@ test.describe.serial('Edit Response Examples', () => {
await test.step('Open existing example', async () => {
await page.locator('.collection-item-name', { hasText: 'edit-example' }).getByTestId('request-item-chevron').click();
const exampleItem = page.locator('.collection-item-name').getByText('Test Example', { exact: true });
const exampleItem = page.locator('.collection-item-name').filter({ hasText: 'Test Example' });
await expect(exampleItem).toBeVisible();
await exampleItem.click();
});

View File

@@ -3,6 +3,7 @@ import { execSync } from 'child_process';
import path from 'path';
test.describe.serial('Response Example Menu Operations', () => {
test.setTimeout(1 * 60 * 1000); // 1 minute for all tests in this describe block, default is 30 seconds.
test.afterAll(async () => {
// Reset the collection request file to the original state
execSync(`git checkout -- ${path.join(__dirname, 'fixtures', 'collection', 'menu-operations.bru')}`);
@@ -15,7 +16,7 @@ test.describe.serial('Response Example Menu Operations', () => {
});
await test.step('Create example', async () => {
await page.locator('#send-request').getByRole('img').nth(2).click();
await page.getByTestId('send-arrow-icon').click();
await page.getByTestId('response-bookmark-btn').click();
await page.getByTestId('create-example-name-input').clear();
await page.getByTestId('create-example-name-input').fill('Example to Clone');

View File

@@ -21,7 +21,12 @@ export const buildCommonLocators = (page: Page) => ({
collectionActions: (collectionName: string) =>
page.locator('.collection-name')
.filter({ hasText: collectionName })
.locator('.collection-actions .icon')
.locator('.collection-actions .icon'),
collectionItemActions: (itemName: string) =>
page.locator('.collection-item-name')
.filter({ hasText: itemName })
.locator('.menu-icon')
},
dropdown: {
item: (text: string) => page.locator('.dropdown-item').filter({ hasText: text })