mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
@@ -1,6 +1,7 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { sendRequest } from '../../utils/page';
|
||||
|
||||
test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => {
|
||||
test('set env var with persist using script', async ({ pageWithUserData: page, restartApp }) => {
|
||||
@@ -21,9 +22,7 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => {
|
||||
await expect(page.locator('.current-environment', { hasText: 'Stage' })).toBeVisible();
|
||||
|
||||
// Send the request
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await page.getByTestId('response-status-code').getByText(/200/).waitFor({ state: 'visible' });
|
||||
await page.waitForTimeout(100);
|
||||
await sendRequest(page, 200);
|
||||
|
||||
// confirm that the environment variable is set
|
||||
await page.getByTestId('environment-selector-trigger').click();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { sendRequest } from '../../utils/page';
|
||||
|
||||
test.describe.serial('bru.setEnvVar(name, value)', () => {
|
||||
test('set env var using script', async ({ pageWithUserData: page, restartApp }) => {
|
||||
@@ -15,9 +16,7 @@ test.describe.serial('bru.setEnvVar(name, value)', () => {
|
||||
await expect(page.locator('.current-environment', { hasText: 'Stage' })).toBeVisible();
|
||||
|
||||
// Send the request
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await page.getByTestId('response-status-code').getByText(/200/).waitFor({ state: 'visible' });
|
||||
await page.waitForTimeout(100);
|
||||
await sendRequest(page, 200);
|
||||
|
||||
// confirm that the environment variable is set
|
||||
await page.getByTestId('environment-selector-trigger').click();
|
||||
|
||||
@@ -1,126 +1,65 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
import {
|
||||
importCollection,
|
||||
createEnvironment,
|
||||
addEnvironmentVariables,
|
||||
saveEnvironment,
|
||||
closeEnvironmentPanel,
|
||||
sendRequest,
|
||||
expectResponseContains,
|
||||
removeCollection
|
||||
} from '../../utils/page';
|
||||
import { buildCommonLocators } from '../../utils/page/locators';
|
||||
|
||||
test.describe('Collection Environment Create Tests', () => {
|
||||
test('should import collection and create environment for request usage', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
const collectionFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
// Import test collection
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
await test.step('Import collection', async () => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('env-test'), {
|
||||
expectedCollectionName: 'test_collection',
|
||||
openWithSandboxMode: 'safe'
|
||||
});
|
||||
});
|
||||
|
||||
const importModal = page.locator('[data-testid="import-collection-modal"]');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await test.step('Create environment with variables', async () => {
|
||||
await createEnvironment(page, 'Test Environment', 'collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
await addEnvironmentVariables(page, [
|
||||
{ name: 'host', value: 'https://echo.usebruno.com' },
|
||||
{ name: 'userId', value: '1' },
|
||||
{ name: 'postTitle', value: 'Test Post from Environment' },
|
||||
{ name: 'postBody', value: 'This is a test post body with environment variables' },
|
||||
{ name: 'secretApiToken', value: 'super-secret-token-12345', isSecret: true }
|
||||
]);
|
||||
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
await expect(locationModal.getByText('test_collection')).toBeVisible();
|
||||
await saveEnvironment(page);
|
||||
await closeEnvironmentPanel(page);
|
||||
await expect(locators.environment.currentEnvironment()).toContainText('Test Environment');
|
||||
});
|
||||
|
||||
await page.locator('#collection-location').fill(await createTmpDir('env-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
await test.step('Test GET request with environment variables', async () => {
|
||||
await page.locator('.collection-item-name').first().click();
|
||||
await expect(locators.request.urlLine()).toContainText('{{host}}');
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'test_collection' })).toBeVisible();
|
||||
await test.step('Verify response contains environment variables', async () => {
|
||||
await expectResponseContains(page, [
|
||||
'"userId": 1',
|
||||
'"title": "Test Post from Environment"',
|
||||
'"body": "This is a test post body with environment variables"',
|
||||
'"apiToken": "super-secret-token-12345"'
|
||||
]);
|
||||
});
|
||||
|
||||
// Configure collection
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'test_collection' }).click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// Create environment
|
||||
await page.locator('[data-testid="environment-selector-trigger"]').click();
|
||||
await expect(page.locator('[data-testid="env-tab-collection"]')).toHaveClass(/active/);
|
||||
|
||||
// Create new environment
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
|
||||
// Fill environment name
|
||||
const environmentNameInput = page.locator('input[name="name"]');
|
||||
await expect(environmentNameInput).toBeVisible();
|
||||
await environmentNameInput.fill('Test Environment');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Add environment variables
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="0.name"]').fill('host');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="0.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
|
||||
// Add userId
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="1.name"]').fill('userId');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="1.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('1');
|
||||
|
||||
// Add postTitle
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="2.name"]').fill('postTitle');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="2.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('Test Post from Environment');
|
||||
|
||||
// Add postBody
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="3.name"]').fill('postBody');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="3.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('This is a test post body with environment variables');
|
||||
|
||||
// Add secret token
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="4.name"]').fill('secretApiToken');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="4.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('super-secret-token-12345');
|
||||
await page.locator('input[name="4.secret"]').check();
|
||||
|
||||
// Save environment
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByText('×').click();
|
||||
await expect(page.locator('.current-environment')).toContainText('Test Environment');
|
||||
|
||||
// Test GET request with environment variables
|
||||
await page.locator('.collection-item-name').first().click();
|
||||
await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}');
|
||||
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('200');
|
||||
|
||||
// Verify the JSON response contains the environment variables
|
||||
const responsePane = page.locator('.response-pane');
|
||||
await expect(responsePane).toContainText('"userId": 1');
|
||||
await expect(responsePane).toContainText('"title": "Test Post from Environment"');
|
||||
await expect(responsePane).toContainText('"body": "This is a test post body with environment variables"');
|
||||
await expect(responsePane).toContainText('"apiToken": "super-secret-token-12345"');
|
||||
|
||||
// Cleanup - use new "Remove" action in workspace UI
|
||||
const collectionRow = page.locator('.collection-name').filter({ has: page.locator('#sidebar-collection-name:has-text("test_collection")') });
|
||||
await collectionRow.hover();
|
||||
await collectionRow.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Remove' }).click();
|
||||
|
||||
const closeModal = page.getByRole('dialog').filter({ has: page.getByText('Remove Collection') });
|
||||
await closeModal.getByRole('button', { name: 'Remove' }).click();
|
||||
await test.step('Cleanup', async () => {
|
||||
await removeCollection(page, 'test_collection');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,125 +1,67 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import {
|
||||
importCollection,
|
||||
createEnvironment,
|
||||
addEnvironmentVariables,
|
||||
saveEnvironment,
|
||||
closeEnvironmentPanel,
|
||||
sendRequest,
|
||||
expectResponseContains,
|
||||
closeAllCollections
|
||||
} from '../../utils/page';
|
||||
import { buildCommonLocators } from '../../utils/page/locators';
|
||||
|
||||
test.describe('Global Environment Create Tests', () => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
test('should import collection and create global environment for request usage', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
const collectionFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
// Import test collection
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
await test.step('Import collection', async () => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-env-test'), {
|
||||
expectedCollectionName: 'test_collection',
|
||||
openWithSandboxMode: 'safe'
|
||||
});
|
||||
});
|
||||
|
||||
const importModal = page.locator('[data-testid="import-collection-modal"]');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await test.step('Create global environment with variables', async () => {
|
||||
await createEnvironment(page, 'Test Global Environment', 'global');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
await addEnvironmentVariables(page, [
|
||||
{ name: 'host', value: 'https://echo.usebruno.com' },
|
||||
{ name: 'userId', value: '1' },
|
||||
{ name: 'postTitle', value: 'Global Test Post from Environment' },
|
||||
{ name: 'postBody', value: 'This is a global test post body with environment variables' },
|
||||
{ name: 'secretApiToken', value: 'global-secret-token-12345', isSecret: true }
|
||||
]);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
await expect(locationModal.getByText('test_collection')).toBeVisible();
|
||||
await saveEnvironment(page);
|
||||
await closeEnvironmentPanel(page);
|
||||
await expect(locators.environment.currentEnvironment()).toContainText('Test Global Environment');
|
||||
});
|
||||
|
||||
await page.locator('#collection-location').fill(await createTmpDir('global-env-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
await test.step('Test GET request with environment variables', async () => {
|
||||
await page.locator('.collection-item-name').first().click();
|
||||
await expect(locators.request.urlLine()).toContainText('{{host}}');
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'test_collection' })).toBeVisible();
|
||||
await test.step('Verify response contains environment variables', async () => {
|
||||
await expectResponseContains(page, [
|
||||
'"userId": 1',
|
||||
'"title": "Global Test Post from Environment"',
|
||||
'"body": "This is a global test post body with environment variables"',
|
||||
'"apiToken": "global-secret-token-12345"'
|
||||
]);
|
||||
});
|
||||
|
||||
// Configure collection
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'test_collection' }).click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// Create global environment
|
||||
await page.locator('[data-testid="environment-selector-trigger"]').click();
|
||||
await page.locator('[data-testid="env-tab-global"]').click();
|
||||
await expect(page.locator('[data-testid="env-tab-global"]')).toHaveClass(/active/);
|
||||
|
||||
// Create new global environment
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
|
||||
// Fill environment name
|
||||
const environmentNameInput = page.locator('input[name="name"]');
|
||||
await expect(environmentNameInput).toBeVisible();
|
||||
await environmentNameInput.fill('Test Global Environment');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
// Add environment variables
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="0.name"]').fill('host');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="0.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
|
||||
// Add userId
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="1.name"]').fill('userId');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="1.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('1');
|
||||
|
||||
// Add postTitle
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="2.name"]').fill('postTitle');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="2.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('Global Test Post from Environment');
|
||||
|
||||
// Add postBody
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="3.name"]').fill('postBody');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="3.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('This is a global test post body with environment variables');
|
||||
|
||||
// Add secret token
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="4.name"]').fill('secretApiToken');
|
||||
await page
|
||||
.locator('tr')
|
||||
.filter({ has: page.locator('input[name="4.name"]') })
|
||||
.locator('.CodeMirror')
|
||||
.click();
|
||||
await page.keyboard.type('global-secret-token-12345');
|
||||
await page.locator('input[name="4.secret"]').check();
|
||||
await expect(page.locator('input[name="4.secret"]')).toBeChecked();
|
||||
|
||||
// Save environment
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByText('×').click();
|
||||
await expect(page.locator('.current-environment')).toContainText('Test Global Environment');
|
||||
|
||||
// Test GET request with environment variables
|
||||
await page.locator('.collection-item-name').first().click();
|
||||
await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}');
|
||||
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('200');
|
||||
|
||||
// Verify the JSON response contains the environment variables
|
||||
const responsePane = page.locator('.response-pane');
|
||||
await expect(responsePane).toContainText('"userId": 1');
|
||||
await expect(responsePane).toContainText('"title": "Global Test Post from Environment"');
|
||||
await expect(responsePane).toContainText('"body": "This is a global test post body with environment variables"');
|
||||
await expect(responsePane).toContainText('"apiToken": "global-secret-token-12345"');
|
||||
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
await test.step('Cleanup', async () => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,11 +101,13 @@ test.describe.serial('Collection Environment Import Tests', () => {
|
||||
await test.step('Verify both environments are available in selector', async () => {
|
||||
// Check that both environments are available in the selector
|
||||
await page.getByText('×').click(); // Close environment settings modal
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
await page.getByTestId('environment-selector-trigger').click();
|
||||
|
||||
// Verify both environments are in the dropdown
|
||||
await expect(page.locator('.dropdown-item').filter({ hasText: /^local$/ })).toBeVisible();
|
||||
await expect(page.locator('.dropdown-item').filter({ hasText: /^prod$/ })).toBeVisible();
|
||||
await page.waitForTimeout(300);
|
||||
await expect(page.locator('.dropdown-item').filter({ hasText: /^local$/ })).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.locator('.dropdown-item').filter({ hasText: /^prod$/ })).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
await test.step('Test switching to prod environment and verify variables', async () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { openCollectionAndAcceptSandbox, closeAllCollections } from '../utils/page';
|
||||
import { openCollectionAndAcceptSandbox, closeAllCollections, sendRequest } from '../utils/page';
|
||||
import { buildCommonLocators } from '../utils/page/locators';
|
||||
|
||||
test.describe('Global Environment Variables - Non-string Values', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
@@ -40,12 +41,9 @@ test.describe('Global Environment Variables - Non-string Values', () => {
|
||||
|
||||
// Request contains a script that sets the non-string global variables.
|
||||
await test.step('Run the request to seed non-string global variables via post-script', async () => {
|
||||
await page.getByText('set-global-nonstring').click();
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
|
||||
// wait for the response to arrive
|
||||
await page.getByTestId('response-status-code').waitFor({ state: 'visible' });
|
||||
await expect(page.getByTestId('response-status-code')).toHaveText(/200/);
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.request('set-global-nonstring').click();
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
await test.step('Re-open Global Environments to see the seeded variables', async () => {
|
||||
|
||||
@@ -15,11 +15,8 @@ test.describe('Import Corrupted Bruno Collection - Should Fail', () => {
|
||||
|
||||
await page.setInputFiles('input[type="file"]', brunoFile);
|
||||
|
||||
// Check for JSON parsing error
|
||||
const hasImportError = await page.getByText('Failed to parse the file – ensure it is valid JSON or YAML').first().isVisible({ timeout: 5000 });
|
||||
|
||||
// Either parsing error or import error should be shown
|
||||
expect(hasImportError).toBe(true);
|
||||
const errorLocator = page.getByText(/Failed to parse the file|Unsupported collection format|Invalid|Error/).first();
|
||||
await expect(errorLocator).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
@@ -15,10 +15,8 @@ test.describe('Import Bruno Collection - Missing Required Schema Fields', () =>
|
||||
|
||||
await page.setInputFiles('input[type="file"]', brunoFile);
|
||||
|
||||
// Check for schema validation error messages
|
||||
const hasImportError = await page.getByText('Unsupported collection format').first().isVisible({ timeout: 5000 });
|
||||
|
||||
expect(hasImportError).toBe(true);
|
||||
const errorMessage = page.getByText('Unsupported collection format').first();
|
||||
await expect(errorMessage).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import Bruno Testbench Collection', () => {
|
||||
test.afterAll(async ({ page }) => {
|
||||
@@ -10,27 +10,8 @@ test.describe('Import Bruno Testbench Collection', () => {
|
||||
test('Import Bruno Testbench collection successfully', async ({ page, createTmpDir }) => {
|
||||
const brunoFile = path.resolve(__dirname, 'fixtures', 'bruno-testbench.json');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', brunoFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Wait for collection to appear in the location modal
|
||||
await expect(locationModal.getByText('bruno-testbench')).toBeVisible();
|
||||
|
||||
await page.locator('#collection-location').fill(await createTmpDir('bruno-testbench-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('bruno-testbench')).toBeVisible();
|
||||
await importCollection(page, brunoFile, await createTmpDir('bruno-testbench-test'), {
|
||||
expectedCollectionName: 'bruno-testbench'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,19 +21,24 @@ test.describe('Import Bruno Collection with Examples', () => {
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
});
|
||||
|
||||
await test.step('Upload collection file', async () => {
|
||||
await test.step('Upload collection file and verify location modal appears', async () => {
|
||||
await page.setInputFiles('input[type="file"]', brunoFile);
|
||||
});
|
||||
|
||||
await test.step('Verify no parsing errors occurred', async () => {
|
||||
const hasError = await page.getByText('Failed to parse the file').isVisible().catch(() => false);
|
||||
if (hasError) {
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
const errorMessage = page.getByText('Failed to parse the file');
|
||||
|
||||
const result = await Promise.race([
|
||||
locationModal.waitFor({ state: 'visible', timeout: 15000 }).then(() => 'success'),
|
||||
errorMessage.waitFor({ state: 'visible', timeout: 15000 }).then(() => 'error')
|
||||
]).catch(() => 'timeout');
|
||||
|
||||
if (result === 'error') {
|
||||
throw new Error('Collection import failed with parsing error');
|
||||
}
|
||||
});
|
||||
if (result === 'timeout') {
|
||||
throw new Error('Import timed out - neither success nor error state was reached');
|
||||
}
|
||||
|
||||
await test.step('Verify location selection modal appears', async () => {
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
});
|
||||
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import Insomnia Collection v4', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import Insomnia Collection v4 successfully', async ({ page, createTmpDir }) => {
|
||||
const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v4.json');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', insomniaFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.locator('#collection-location').fill(await createTmpDir('insomnia-v4-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Test API Collection v4')).toBeVisible();
|
||||
await importCollection(page, insomniaFile, await createTmpDir('insomnia-v4-test'), {
|
||||
expectedCollectionName: 'Test API Collection v4'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import Insomnia Collection v5', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import Insomnia Collection v5 successfully', async ({ page, createTmpDir }) => {
|
||||
const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v5.yaml');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', insomniaFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.locator('#collection-location').fill(await createTmpDir('insomnia-v5-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Test API Collection v5')).toBeVisible();
|
||||
await importCollection(page, insomniaFile, await createTmpDir('insomnia-v5-test'), {
|
||||
expectedCollectionName: 'Test API Collection v5'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,11 +15,10 @@ test.describe('Invalid Insomnia Collection - Missing Collection Array', () => {
|
||||
|
||||
await page.setInputFiles('input[type="file"]', insomniaFile);
|
||||
|
||||
// Check for error message
|
||||
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
|
||||
expect(hasError).toBe(true);
|
||||
const errorLocator = page.getByText('Unsupported collection format').first();
|
||||
await expect(errorLocator).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,39 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import OpenAPI v3 JSON Collection', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import simple OpenAPI v3 JSON successfully', async ({ page, createTmpDir }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-simple.json');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Wait for collection to appear in the location modal
|
||||
await expect(locationModal.getByText('Simple Test API')).toBeVisible();
|
||||
|
||||
// Select a location and import
|
||||
await page.locator('#collection-location').fill(await createTmpDir('simple-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
// Verify the collection was imported successfully
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Simple Test API')).toBeVisible();
|
||||
await importCollection(page, openApiFile, await createTmpDir('simple-test'), {
|
||||
expectedCollectionName: 'Simple Test API'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,39 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import OpenAPI v3 YAML Collection', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import comprehensive OpenAPI v3 YAML successfully', async ({ page, createTmpDir }) => {
|
||||
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-comprehensive.yaml');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Wait for collection to appear in the location modal
|
||||
await expect(locationModal.getByText('Comprehensive API Test Collection')).toBeVisible();
|
||||
|
||||
// Select a location and import
|
||||
await page.locator('#collection-location').fill(await createTmpDir('comprehensive-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
// Verify the collection was imported successfully
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Comprehensive API Test Collection')).toBeVisible();
|
||||
await importCollection(page, openApiFile, await createTmpDir('comprehensive-test'), {
|
||||
expectedCollectionName: 'Comprehensive API Test Collection'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,12 +15,11 @@ test.describe('Invalid OpenAPI - Malformed YAML', () => {
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
|
||||
// Check for error message - this should fail during YAML parsing
|
||||
const hasParseError = await page.getByText('Failed to parse the file').isVisible();
|
||||
const hasImportError = await page.getByText('Import collection failed').isVisible();
|
||||
const parseError = page.getByText('Failed to parse the file');
|
||||
const importError = page.getByText('Import collection failed');
|
||||
|
||||
// Either parsing error or import error should be shown
|
||||
expect(hasParseError || hasImportError).toBe(true);
|
||||
// Wait for at least one error message to be visible
|
||||
await expect(parseError.or(importError)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
|
||||
@@ -15,11 +15,8 @@ test.describe('Invalid OpenAPI - Missing Info Section', () => {
|
||||
|
||||
await page.setInputFiles('input[type="file"]', openApiFile);
|
||||
|
||||
// The OpenAPI parser might handle missing info gracefully with defaults
|
||||
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
|
||||
|
||||
// Either should show an error or create an "Untitled Collection"
|
||||
expect(hasError).toBe(true);
|
||||
const errorMessage = page.getByText('Unsupported collection format').first();
|
||||
await expect(errorMessage).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
|
||||
@@ -1,39 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import Postman Collection v2.0', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import Postman Collection v2.0 successfully', async ({ page, createTmpDir }) => {
|
||||
const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-v20.json');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', postmanFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Wait for collection to appear in the location modal
|
||||
await expect(locationModal.getByText('Postman v2.0 Collection')).toBeVisible();
|
||||
|
||||
// Select a location and import
|
||||
await page.locator('#collection-location').fill(await createTmpDir('postman-v20-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
// Verify the collection was imported successfully
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Postman v2.0 Collection')).toBeVisible();
|
||||
await importCollection(page, postmanFile, await createTmpDir('postman-v20-test'), {
|
||||
expectedCollectionName: 'Postman v2.0 Collection'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,39 +1,17 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test } from '../../../playwright';
|
||||
import * as path from 'path';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
import { closeAllCollections, importCollection } from '../../utils/page';
|
||||
|
||||
test.describe('Import Postman Collection v2.1', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Import Postman Collection v2.1 successfully', async ({ page, createTmpDir }) => {
|
||||
const postmanFile = path.resolve(__dirname, 'fixtures', 'postman-v21.json');
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import collection modal to be ready
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
await page.setInputFiles('input[type="file"]', postmanFile);
|
||||
|
||||
// Wait for location modal to appear after file processing
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Wait for collection to appear in the location modal
|
||||
await expect(locationModal.getByText('Postman v2.1 Collection')).toBeVisible();
|
||||
|
||||
// Select a location and import
|
||||
await page.locator('#collection-location').fill(await createTmpDir('postman-v21-test'));
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
// Verify the collection was imported successfully
|
||||
await expect(page.locator('#sidebar-collection-name').getByText('Postman v2.1 Collection')).toBeVisible();
|
||||
await importCollection(page, postmanFile, await createTmpDir('postman-v21-test'), {
|
||||
expectedCollectionName: 'Postman v2.1 Collection'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,11 +15,10 @@ test.describe('Invalid Postman Collection - Malformed Structure', () => {
|
||||
|
||||
await page.setInputFiles('input[type="file"]', postmanFile);
|
||||
|
||||
// Check for error message
|
||||
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
|
||||
expect(hasError).toBe(true);
|
||||
const errorLocator = page.getByText(/Unsupported collection format|Failed to parse|Invalid|Error/).first();
|
||||
await expect(errorLocator).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections, openCollectionAndAcceptSandbox } from '../../utils/page';
|
||||
import { closeAllCollections, openCollectionAndAcceptSandbox, sendRequest } from '../../utils/page';
|
||||
import { buildCommonLocators } from '../../utils/page/locators';
|
||||
|
||||
test.describe.serial('Dynamic Variable Interpolation', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Verifying if the bru.setVar method interpolates random generator functions properly', async ({ pageWithUserData: page }) => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
// Open collection and accept sandbox mode
|
||||
await openCollectionAndAcceptSandbox(page, 'dynamic-variable-interpolation', 'safe');
|
||||
|
||||
// Navigate to the request
|
||||
await page.getByRole('complementary').getByText('set-var-dynamic-variable').click();
|
||||
await locators.sidebar.request('set-var-dynamic-variable').click();
|
||||
|
||||
// Send the request
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
|
||||
// Wait for the response and verify status code
|
||||
await expect(page.getByTestId('response-status-code')).toHaveText(/200/);
|
||||
await sendRequest(page, 200);
|
||||
|
||||
// Verify response contains the title field and that it's not the literal interpolation string
|
||||
const responsePane = page.locator('.response-pane');
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import { closeAllCollections, sendRequest } from '../utils/page';
|
||||
import { buildCommonLocators } from '../utils/page/locators';
|
||||
|
||||
test.describe.serial('URL Interpolation', () => {
|
||||
test.afterAll(async ({ pageWithUserData: page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Interpolate basic path params', async ({ pageWithUserData: page }) => {
|
||||
await page.locator('#sidebar-collection-name').click();
|
||||
await page.getByRole('complementary').getByText('echo-request-url').click();
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
|
||||
await expect(page.getByTestId('response-status-code')).toHaveText(/200/);
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.collection('interpolation').click();
|
||||
await locators.sidebar.request('echo-request-url').click();
|
||||
await sendRequest(page, 200);
|
||||
|
||||
const texts = await page.locator('div:nth-child(2) > .CodeMirror-scroll').allInnerTexts();
|
||||
await expect(texts.some((d) => d.includes(`"url": "/path/some-data"`))).toBe(true);
|
||||
});
|
||||
|
||||
test('Interpolate oData path params', async ({ pageWithUserData: page }) => {
|
||||
await page.getByRole('complementary').getByText('echo-request-odata').click();
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
|
||||
await expect(page.getByTestId('response-status-code')).toHaveText(/200/);
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.request('echo-request-odata').click();
|
||||
await sendRequest(page, 200);
|
||||
|
||||
const texts = await page.locator('div:nth-child(2) > .CodeMirror-scroll').allInnerTexts();
|
||||
await expect(texts.some((d) => d.includes(`"url": "/path/Category('category123')/Item(item456)/foobar/Tags(%22tag%20test%22)"`))).toBe(true);
|
||||
|
||||
@@ -2,8 +2,13 @@ import { test, expect } from '../../../playwright';
|
||||
import { createCollection, closeAllCollections, createRequest } from '../../utils/page';
|
||||
|
||||
test.describe('Autosave', () => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
// Only try to cleanup if page is still open
|
||||
if (!page.isClosed()) {
|
||||
await closeAllCollections(page);
|
||||
}
|
||||
});
|
||||
|
||||
test('should automatically save request changes when autosave is enabled', async ({ page, createTmpDir }) => {
|
||||
@@ -58,11 +63,8 @@ test.describe('Autosave', () => {
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Test Request' }) });
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
// Wait for autosave to trigger (interval + some buffer)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify draft indicator disappears after autosave
|
||||
await expect(requestTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
await expect(requestTab.locator('.has-changes-icon')).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
await test.step('Verify changes persisted', async () => {
|
||||
@@ -109,11 +111,7 @@ test.describe('Autosave', () => {
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Test Request' }) });
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
// Wait a bit (longer than autosave interval would be)
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Draft indicator should still be visible (autosave is disabled)
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Save the request
|
||||
await page.keyboard.press('Control+s');
|
||||
@@ -173,12 +171,10 @@ test.describe('Autosave', () => {
|
||||
// Wait for preferences to close
|
||||
await expect(preferencesModal).not.toBeVisible();
|
||||
|
||||
// Wait for autosave to trigger for existing draft
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify draft indicator disappears (existing draft was auto-saved)
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Draft Request' }) });
|
||||
await expect(requestTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
await expect(requestTab.locator('.has-changes-icon')).not.toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
await test.step('Verify changes persisted', async () => {
|
||||
|
||||
@@ -1,50 +1,33 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { closeAllCollections, createCollection } from '../utils/page/actions';
|
||||
import {
|
||||
closeAllCollections,
|
||||
createCollection,
|
||||
createRequest,
|
||||
sendRequest
|
||||
} from '../utils/page/actions';
|
||||
import { buildCommonLocators } from '../utils/page/locators';
|
||||
|
||||
test.describe('Response Pane Actions', () => {
|
||||
test.afterAll(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('should copy response to clipboard', async ({ page, createTmpDir }) => {
|
||||
const collectionName = 'response-copy-test';
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await test.step('Create collection and request', async () => {
|
||||
// Create collection
|
||||
await createCollection(page, collectionName, await createTmpDir(collectionName), { openWithSandboxMode: 'safe' });
|
||||
|
||||
// Create request
|
||||
const collection = page.locator('.collection-name').filter({ hasText: collectionName });
|
||||
await collection.locator('.collection-actions').hover();
|
||||
await collection.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
|
||||
|
||||
await page.getByPlaceholder('Request Name').fill('copy-test');
|
||||
await page.locator('#new-request-url .CodeMirror').click();
|
||||
// Using httpbin.org for a simple JSON response
|
||||
await page.locator('textarea').fill('https://www.httpfaker.org/api/random/json?size=1kb');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await createRequest(page, 'copy-test', collectionName, { url: 'https://httpbin.org/json' });
|
||||
});
|
||||
|
||||
await test.step('Send request and wait for response', async () => {
|
||||
// Send request
|
||||
const sendButton = page.getByTestId('send-arrow-icon');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for response
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 30000 });
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
await test.step('should copy response to clipboard', async () => {
|
||||
// Find the copy button
|
||||
const copyButton = page.locator('button[title="Copy response to clipboard"]');
|
||||
await expect(copyButton).toBeVisible();
|
||||
|
||||
// Click the copy button
|
||||
await copyButton.click();
|
||||
|
||||
// Verify toast notification appears
|
||||
await test.step('Copy response to clipboard', async () => {
|
||||
await expect(locators.response.copyButton()).toBeVisible();
|
||||
await locators.response.copyButton().click();
|
||||
await expect(page.getByText('Response copied to clipboard')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test, expect, Page } from '../../../playwright';
|
||||
import { buildCommonLocators } from './locators';
|
||||
|
||||
type SandboxMode = 'safe' | 'developer';
|
||||
|
||||
/**
|
||||
* Close all collections
|
||||
* @param page - The page object
|
||||
@@ -82,24 +84,54 @@ const createCollection = async (page, collectionName: string, collectionLocation
|
||||
});
|
||||
};
|
||||
|
||||
type CreateRequestOptions = {
|
||||
url?: string;
|
||||
inFolder?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a request in a collection
|
||||
* Create a request in a collection or folder
|
||||
* @param page - The page object
|
||||
* @param requestName - The name of the request to create
|
||||
* @param collectionName - The name of the collection
|
||||
* @param parentName - The name of the collection or folder
|
||||
* @param options - Optional settings (url, inFolder)
|
||||
* @returns void
|
||||
*/
|
||||
const createRequest = async (page, requestName: string, collectionName: string) => {
|
||||
await test.step(`Create request "${requestName}" in collection "${collectionName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const collection = locators.sidebar.collection(collectionName);
|
||||
const createRequest = async (
|
||||
page: Page,
|
||||
requestName: string,
|
||||
parentName: string,
|
||||
options: CreateRequestOptions = {}
|
||||
) => {
|
||||
const { url, inFolder = false } = options;
|
||||
const parentType = inFolder ? 'folder' : 'collection';
|
||||
|
||||
await test.step(`Create request "${requestName}" in ${parentType} "${parentName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
if (inFolder) {
|
||||
await locators.sidebar.folder(parentName).hover();
|
||||
await locators.actions.collectionItemActions(parentName).click();
|
||||
} else {
|
||||
await locators.sidebar.collection(parentName).hover();
|
||||
await locators.actions.collectionActions(parentName).click();
|
||||
}
|
||||
|
||||
await collection.hover();
|
||||
await locators.actions.collectionActions(collectionName).click();
|
||||
await locators.dropdown.item('New Request').click();
|
||||
await page.getByPlaceholder('Request Name').fill(requestName);
|
||||
|
||||
if (url) {
|
||||
await page.locator('#new-request-url .CodeMirror').click();
|
||||
await page.keyboard.type(url);
|
||||
}
|
||||
|
||||
await locators.modal.button('Create').click();
|
||||
await expect(locators.sidebar.request(requestName)).toBeVisible();
|
||||
|
||||
if (inFolder) {
|
||||
await expect(locators.sidebar.folderRequest(parentName, requestName)).toBeVisible();
|
||||
} else {
|
||||
await expect(locators.sidebar.request(requestName)).toBeVisible();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -130,4 +162,381 @@ const deleteRequest = async (page, requestName: string, collectionName: string)
|
||||
});
|
||||
};
|
||||
|
||||
export { closeAllCollections, openCollectionAndAcceptSandbox, createCollection, createRequest, deleteRequest };
|
||||
/**
|
||||
* Import a collection from a file
|
||||
* @param page - The page object
|
||||
* @param filePath - The path to the collection file to import
|
||||
* @param collectionLocation - The directory where the collection will be saved
|
||||
* @param options - Optional settings for import
|
||||
* @returns void
|
||||
*/
|
||||
type ImportCollectionOptions = {
|
||||
expectedCollectionName?: string;
|
||||
openWithSandboxMode?: SandboxMode;
|
||||
};
|
||||
|
||||
const importCollection = async (
|
||||
page: Page,
|
||||
filePath: string,
|
||||
collectionLocation: string,
|
||||
options: ImportCollectionOptions = {}
|
||||
) => {
|
||||
await test.step(`Import collection from "${filePath}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await page.locator('.plus-icon-button').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
||||
|
||||
// Wait for import modal
|
||||
const importModal = page.getByRole('dialog');
|
||||
await importModal.waitFor({ state: 'visible' });
|
||||
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Set the file
|
||||
await page.setInputFiles('input[type="file"]', filePath);
|
||||
|
||||
// Wait for location modal to appear
|
||||
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
|
||||
await locationModal.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await expect(locationModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');
|
||||
|
||||
// Verify expected collection name if provided
|
||||
if (options.expectedCollectionName) {
|
||||
await expect(locationModal.getByText(options.expectedCollectionName)).toBeVisible();
|
||||
}
|
||||
|
||||
// Set location and import
|
||||
await page.locator('#collection-location').fill(collectionLocation);
|
||||
await locationModal.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
// Wait for collection to appear in sidebar
|
||||
if (options.expectedCollectionName) {
|
||||
await expect(
|
||||
page.locator('#sidebar-collection-name').filter({ hasText: options.expectedCollectionName })
|
||||
).toBeVisible();
|
||||
|
||||
// Configure sandbox mode if requested
|
||||
if (options.openWithSandboxMode) {
|
||||
await openCollectionAndAcceptSandbox(page, options.expectedCollectionName, options.openWithSandboxMode);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a specific collection from the sidebar
|
||||
* @param page - The page object
|
||||
* @param collectionName - The name of the collection to remove
|
||||
* @returns void
|
||||
*/
|
||||
const removeCollection = async (page: Page, collectionName: string) => {
|
||||
await test.step(`Remove collection "${collectionName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
const collectionRow = page.locator('.collection-name').filter({
|
||||
has: page.locator('#sidebar-collection-name', { hasText: collectionName })
|
||||
});
|
||||
|
||||
await collectionRow.hover();
|
||||
await collectionRow.locator('.collection-actions .icon').click();
|
||||
await locators.dropdown.item('Remove').click();
|
||||
|
||||
// Wait for and confirm modal
|
||||
await locators.modal.title('Remove Collection').waitFor({ state: 'visible' });
|
||||
await locators.modal.button('Remove').click();
|
||||
await locators.modal.title('Remove Collection').waitFor({ state: 'hidden' });
|
||||
|
||||
// Verify collection is removed
|
||||
await expect(
|
||||
page.locator('#sidebar-collection-name').filter({ hasText: collectionName })
|
||||
).not.toBeVisible();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a folder inside a collection or another folder
|
||||
* @param page - The page object
|
||||
* @param folderName - The name of the folder to create
|
||||
* @param parentName - The name of the parent collection or folder
|
||||
* @param isCollection - Whether the parent is a collection (true) or folder (false)
|
||||
* @returns void
|
||||
*/
|
||||
const createFolder = async (
|
||||
page: Page,
|
||||
folderName: string,
|
||||
parentName: string,
|
||||
isCollection: boolean = true
|
||||
) => {
|
||||
await test.step(`Create folder "${folderName}" in "${parentName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
if (isCollection) {
|
||||
await locators.sidebar.collection(parentName).hover();
|
||||
await locators.actions.collectionActions(parentName).click();
|
||||
} else {
|
||||
await locators.sidebar.folder(parentName).hover();
|
||||
await locators.actions.collectionItemActions(parentName).click();
|
||||
}
|
||||
|
||||
await locators.dropdown.item('New Folder').click();
|
||||
await page.getByPlaceholder('Folder Name').fill(folderName);
|
||||
await locators.modal.button('Create').click();
|
||||
await expect(locators.sidebar.folder(folderName)).toBeVisible();
|
||||
});
|
||||
};
|
||||
|
||||
type EnvironmentType = 'collection' | 'global';
|
||||
|
||||
/**
|
||||
* Open the environment selector panel
|
||||
* @param page - The page object
|
||||
* @param type - The type of environment tab to select
|
||||
* @returns void
|
||||
*/
|
||||
const openEnvironmentSelector = async (page: Page, type: EnvironmentType = 'collection') => {
|
||||
await test.step(`Open ${type} environment selector`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await locators.environment.selector().click();
|
||||
|
||||
if (type === 'global') {
|
||||
await locators.environment.globalTab().click();
|
||||
await expect(locators.environment.globalTab()).toHaveClass(/active/);
|
||||
} else {
|
||||
await expect(locators.environment.collectionTab()).toHaveClass(/active/);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new environment
|
||||
* @param page - The page object
|
||||
* @param environmentName - The name of the environment
|
||||
* @param type - The type of environment (collection or global)
|
||||
* @returns void
|
||||
*/
|
||||
const createEnvironment = async (
|
||||
page: Page,
|
||||
environmentName: string,
|
||||
type: EnvironmentType = 'collection'
|
||||
) => {
|
||||
await test.step(`Create ${type} environment "${environmentName}"`, async () => {
|
||||
await openEnvironmentSelector(page, type);
|
||||
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
|
||||
const nameInput = page.locator('input[name="name"]');
|
||||
await expect(nameInput).toBeVisible();
|
||||
await nameInput.fill(environmentName);
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
});
|
||||
};
|
||||
|
||||
type EnvironmentVariable = {
|
||||
name: string;
|
||||
value: string;
|
||||
isSecret?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an environment variable to the currently open environment
|
||||
* @param page - The page object
|
||||
* @param variable - The variable to add (name, value, and optional secret flag)
|
||||
* @param index - The index of the variable (0-based)
|
||||
* @returns void
|
||||
*/
|
||||
const addEnvironmentVariable = async (
|
||||
page: Page,
|
||||
variable: EnvironmentVariable,
|
||||
index: number
|
||||
) => {
|
||||
await test.step(`Add environment variable "${variable.name}"`, async () => {
|
||||
const addButton = page.locator('button[data-testid="add-variable"]');
|
||||
await addButton.waitFor({ state: 'visible' });
|
||||
await addButton.click();
|
||||
|
||||
// Wait for the new row to be added and the name input to be visible
|
||||
const nameInput = page.locator(`input[name="${index}.name"]`);
|
||||
await nameInput.waitFor({ state: 'visible' });
|
||||
await nameInput.fill(variable.name);
|
||||
|
||||
// Wait for the CodeMirror editor in the row to be ready
|
||||
const variableRow = page.locator('tr').filter({ has: page.locator(`input[name="${index}.name"]`) });
|
||||
const codeMirror = variableRow.locator('.CodeMirror');
|
||||
await codeMirror.waitFor({ state: 'visible' });
|
||||
await codeMirror.click();
|
||||
await page.keyboard.type(variable.value);
|
||||
|
||||
if (variable.isSecret) {
|
||||
const secretCheckbox = page.locator(`input[name="${index}.secret"]`);
|
||||
await secretCheckbox.waitFor({ state: 'visible' });
|
||||
await secretCheckbox.check();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add multiple environment variables to the currently open environment
|
||||
* @param page - The page object
|
||||
* @param variables - Array of variables to add
|
||||
* @returns void
|
||||
*/
|
||||
const addEnvironmentVariables = async (page: Page, variables: EnvironmentVariable[]) => {
|
||||
await test.step(`Add ${variables.length} environment variables`, async () => {
|
||||
for (let i = 0; i < variables.length; i++) {
|
||||
await addEnvironmentVariable(page, variables[i], i);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the current environment settings
|
||||
* @param page - The page object
|
||||
* @returns void
|
||||
*/
|
||||
const saveEnvironment = async (page: Page) => {
|
||||
await test.step('Save environment', async () => {
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the environment modal/panel
|
||||
* @param page - The page object
|
||||
* @returns void
|
||||
*/
|
||||
const closeEnvironmentPanel = async (page: Page) => {
|
||||
await test.step('Close environment panel', async () => {
|
||||
await page.getByText('×').click();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Select an environment from the dropdown
|
||||
* @param page - The page object
|
||||
* @param environmentName - The name of the environment to select
|
||||
* @param type - The type of environment (collection or global)
|
||||
* @returns void
|
||||
*/
|
||||
const selectEnvironment = async (
|
||||
page: Page,
|
||||
environmentName: string,
|
||||
type: EnvironmentType = 'collection'
|
||||
) => {
|
||||
await test.step(`Select ${type} environment "${environmentName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
|
||||
await locators.environment.selector().click();
|
||||
|
||||
if (type === 'global') {
|
||||
await locators.environment.globalTab().click();
|
||||
}
|
||||
|
||||
await locators.environment.envOption(environmentName).click();
|
||||
|
||||
// Verify selection
|
||||
await expect(page.locator('.current-environment')).toContainText(environmentName);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send the current request and wait for response
|
||||
* @param page - The page object
|
||||
* @param expectedStatusCode - Optional expected status code to wait for
|
||||
* @param timeout - Timeout in milliseconds (default: 30000)
|
||||
* @returns void
|
||||
*/
|
||||
const sendRequest = async (
|
||||
page: Page,
|
||||
expectedStatusCode?: number | string,
|
||||
timeout: number = 30000
|
||||
) => {
|
||||
await test.step('Send request', async () => {
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await page.getByTestId('response-status-code').waitFor({ state: 'visible', timeout });
|
||||
|
||||
if (expectedStatusCode !== undefined) {
|
||||
await expect(page.getByTestId('response-status-code')).toContainText(
|
||||
String(expectedStatusCode),
|
||||
{ timeout }
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a request by clicking on it in the sidebar
|
||||
* @param page - The page object
|
||||
* @param requestName - The name of the request to open
|
||||
* @returns void
|
||||
*/
|
||||
const openRequest = async (page: Page, requestName: string) => {
|
||||
await test.step(`Open request "${requestName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.request(requestName).click();
|
||||
await expect(locators.tabs.activeRequestTab()).toContainText(requestName);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a request within a folder
|
||||
* @param page - The page object
|
||||
* @param folderName - The name of the folder
|
||||
* @param requestName - The name of the request
|
||||
* @returns void
|
||||
*/
|
||||
const openFolderRequest = async (page: Page, folderName: string, requestName: string) => {
|
||||
await test.step(`Open request "${requestName}" in folder "${folderName}"`, async () => {
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.folderRequest(folderName, requestName).click();
|
||||
await expect(locators.tabs.activeRequestTab()).toContainText(requestName);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the response body text
|
||||
* @param page - The page object
|
||||
* @returns The response body text
|
||||
*/
|
||||
const getResponseBody = async (page: Page): Promise<string> => {
|
||||
return await page.locator('.response-pane').innerText();
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify response contains specific text
|
||||
* @param page - The page object
|
||||
* @param texts - Array of texts to verify in the response
|
||||
* @returns void
|
||||
*/
|
||||
const expectResponseContains = async (page: Page, texts: string[]) => {
|
||||
await test.step('Verify response content', async () => {
|
||||
const responsePane = page.locator('.response-pane');
|
||||
for (const text of texts) {
|
||||
await expect(responsePane).toContainText(text);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
closeAllCollections,
|
||||
openCollectionAndAcceptSandbox,
|
||||
createCollection,
|
||||
createRequest,
|
||||
deleteRequest,
|
||||
importCollection,
|
||||
removeCollection,
|
||||
createFolder,
|
||||
openEnvironmentSelector,
|
||||
createEnvironment,
|
||||
addEnvironmentVariable,
|
||||
addEnvironmentVariables,
|
||||
saveEnvironment,
|
||||
closeEnvironmentPanel,
|
||||
selectEnvironment,
|
||||
sendRequest,
|
||||
openRequest,
|
||||
openFolderRequest,
|
||||
getResponseBody,
|
||||
expectResponseContains
|
||||
};
|
||||
|
||||
export type { SandboxMode, EnvironmentType, EnvironmentVariable, CreateCollectionOptions, ImportCollectionOptions, CreateRequestOptions };
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './actions';
|
||||
export * from './runner';
|
||||
export * from './locators';
|
||||
|
||||
@@ -17,7 +17,10 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
const folderWrapper = page.locator('.collection-item-name').filter({ hasText: folderName }).locator('..');
|
||||
return folderWrapper.locator('.collection-item-name').filter({ hasText: requestName });
|
||||
},
|
||||
closeAllCollectionsButton: () => page.getByTestId('close-all-collections-button')
|
||||
closeAllCollectionsButton: () => page.getByTestId('close-all-collections-button'),
|
||||
collectionRow: (name: string) => page.locator('.collection-name').filter({
|
||||
has: page.locator('#sidebar-collection-name', { hasText: name })
|
||||
})
|
||||
},
|
||||
actions: {
|
||||
collectionActions: (collectionName: string) =>
|
||||
@@ -28,14 +31,15 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
page.locator('.collection-item-name')
|
||||
.filter({ hasText: itemName })
|
||||
.locator('.menu-icon')
|
||||
|
||||
},
|
||||
dropdown: {
|
||||
item: (text: string) => page.locator('.dropdown-item').filter({ hasText: text })
|
||||
item: (text: string) => page.locator('.dropdown-item').filter({ hasText: text }),
|
||||
tippyItem: (text: string) => page.locator('.tippy-box .dropdown-item').filter({ hasText: text })
|
||||
},
|
||||
tabs: {
|
||||
requestTab: (requestName: string) => page.locator('.request-tab .tab-label').filter({ hasText: requestName }),
|
||||
activeRequestTab: () => page.locator('.request-tab.active')
|
||||
activeRequestTab: () => page.locator('.request-tab.active'),
|
||||
closeTab: (requestName: string) => page.locator('.request-tab').filter({ hasText: requestName }).locator('.close-icon')
|
||||
},
|
||||
folder: {
|
||||
chevron: (folderName: string) => page.locator('.collection-item-name').filter({ hasText: folderName }).getByTestId('folder-chevron')
|
||||
@@ -44,13 +48,48 @@ export const buildCommonLocators = (page: Page) => ({
|
||||
title: (title: string) => page.locator('.bruno-modal-header-title').filter({ hasText: title }),
|
||||
byTitle: (title: string) => page.locator('.bruno-modal').filter({ has: page.locator('.bruno-modal-header-title').filter({ hasText: title }) }),
|
||||
button: (name: string) => page.locator('.bruno-modal').getByRole('button', { name: name, exact: true }),
|
||||
closeButton: () => page.locator('.bruno-modal').getByTestId('modal-close-button')
|
||||
closeButton: () => page.locator('.bruno-modal').getByTestId('modal-close-button'),
|
||||
card: () => page.locator('.bruno-modal-card'),
|
||||
footer: () => page.locator('.bruno-modal-footer'),
|
||||
submitButton: () => page.locator('.bruno-modal-footer .submit')
|
||||
},
|
||||
environment: {
|
||||
selector: () => page.getByTestId('environment-selector-trigger'),
|
||||
collectionTab: () => page.getByTestId('env-tab-collection'),
|
||||
globalTab: () => page.getByTestId('env-tab-global'),
|
||||
envOption: (name: string) => page.locator('.dropdown-item').getByText(name, { exact: true })
|
||||
envOption: (name: string) => page.locator('.dropdown-item').getByText(name, { exact: true }),
|
||||
currentEnvironment: () => page.locator('.current-environment'),
|
||||
addVariableButton: () => page.locator('button[data-testid="add-variable"]'),
|
||||
variableNameInput: (index: number) => page.locator(`input[name="${index}.name"]`),
|
||||
variableSecretCheckbox: (index: number) => page.locator(`input[name="${index}.secret"]`),
|
||||
variableRow: (index: number) => page.locator('tr').filter({ has: page.locator(`input[name="${index}.name"]`) }),
|
||||
createEnvButton: () => page.locator('button[id="create-env"]'),
|
||||
envNameInput: () => page.locator('input[name="name"]')
|
||||
},
|
||||
request: {
|
||||
urlInput: () => page.locator('#request-url .CodeMirror'),
|
||||
urlLine: () => page.locator('#request-url .CodeMirror-line'),
|
||||
sendButton: () => page.getByTestId('send-arrow-icon'),
|
||||
methodDropdown: () => page.getByTestId('request-method-selector'),
|
||||
newRequestUrl: () => page.locator('#new-request-url .CodeMirror'),
|
||||
requestNameInput: () => page.getByPlaceholder('Request Name'),
|
||||
requestTestId: () => page.getByTestId('request-name')
|
||||
},
|
||||
response: {
|
||||
statusCode: () => page.getByTestId('response-status-code'),
|
||||
pane: () => page.locator('.response-pane'),
|
||||
copyButton: () => page.locator('button[title="Copy response to clipboard"]')
|
||||
},
|
||||
plusMenu: {
|
||||
button: () => page.locator('.plus-icon-button'),
|
||||
createCollection: () => page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }),
|
||||
importCollection: () => page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' })
|
||||
},
|
||||
import: {
|
||||
modal: () => page.locator('[data-testid="import-collection-modal"]'),
|
||||
locationModal: () => page.locator('[data-testid="import-collection-location-modal"]'),
|
||||
locationInput: () => page.locator('#collection-location'),
|
||||
fileInput: () => page.locator('input[type="file"]')
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
import { Page, expect } from '../../../playwright';
|
||||
import { Page, expect, test } from '../../../playwright';
|
||||
|
||||
/**
|
||||
* Builds locators for the runner results view
|
||||
* @param page - The Playwright page object
|
||||
* @returns Object with locators for runner elements
|
||||
*/
|
||||
export const buildRunnerLocators = (page: Page) => ({
|
||||
allButton: () => page.locator('button').filter({ hasText: /^All/ }),
|
||||
passedButton: () => page.locator('button').filter({ hasText: /^Passed/ }),
|
||||
failedButton: () => page.locator('button').filter({ hasText: /^Failed/ }),
|
||||
skippedButton: () => page.locator('button').filter({ hasText: /^Skipped/ }),
|
||||
resetButton: () => page.getByRole('button', { name: 'Reset' }),
|
||||
runCollectionButton: () => page.getByRole('button', { name: 'Run Collection' }),
|
||||
runAgainButton: () => page.getByRole('button', { name: 'Run Again' })
|
||||
});
|
||||
|
||||
/**
|
||||
* Reads test result counts from the filter buttons in the runner results view
|
||||
@@ -6,15 +21,12 @@ import { Page, expect } from '../../../playwright';
|
||||
* @returns An object with totalRequests, passed, failed, and skipped counts
|
||||
*/
|
||||
export const getRunnerResultCounts = async (page: Page) => {
|
||||
const allButton = page.locator('button').filter({ hasText: /^All/ });
|
||||
const passedButton = page.locator('button').filter({ hasText: /^Passed/ });
|
||||
const failedButton = page.locator('button').filter({ hasText: /^Failed/ });
|
||||
const skippedButton = page.locator('button').filter({ hasText: /^Skipped/ });
|
||||
const locators = buildRunnerLocators(page);
|
||||
|
||||
const totalRequests = parseInt(await allButton.locator('span').innerText());
|
||||
const passed = parseInt(await passedButton.locator('span').innerText());
|
||||
const failed = parseInt(await failedButton.locator('span').innerText());
|
||||
const skipped = parseInt(await skippedButton.locator('span').innerText());
|
||||
const totalRequests = parseInt(await locators.allButton().locator('span').innerText());
|
||||
const passed = parseInt(await locators.passedButton().locator('span').innerText());
|
||||
const failed = parseInt(await locators.failedButton().locator('span').innerText());
|
||||
const skipped = parseInt(await locators.skippedButton().locator('span').innerText());
|
||||
|
||||
return { totalRequests, passed, failed, skipped };
|
||||
};
|
||||
@@ -27,42 +39,59 @@ export const getRunnerResultCounts = async (page: Page) => {
|
||||
* @returns void
|
||||
*/
|
||||
export const runCollection = async (page: Page, collectionName: string) => {
|
||||
// Ensure collection is visible and loaded (scope to sidebar)
|
||||
const collectionContainer = page.getByTestId('collections').locator('.collection-name').filter({ hasText: collectionName });
|
||||
await collectionContainer.waitFor({ state: 'visible' });
|
||||
// Wait a bit for the UI to stabilize
|
||||
await page.waitForTimeout(300);
|
||||
await test.step(`Run collection "${collectionName}"`, async () => {
|
||||
// Ensure collection is visible and loaded (scope to sidebar)
|
||||
const collectionContainer = page.getByTestId('collections').locator('.collection-name').filter({ hasText: collectionName });
|
||||
await collectionContainer.waitFor({ state: 'visible' });
|
||||
|
||||
// Open collection actions menu
|
||||
await collectionContainer.locator('.collection-actions').hover();
|
||||
const icon = collectionContainer.locator('.collection-actions .icon');
|
||||
await icon.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await page.waitForTimeout(200); // Small delay to ensure hover state is stable
|
||||
await icon.click();
|
||||
// Open collection actions menu - wait for the actions container to be actionable
|
||||
const actionsContainer = collectionContainer.locator('.collection-actions');
|
||||
await actionsContainer.waitFor({ state: 'visible' });
|
||||
await actionsContainer.hover();
|
||||
|
||||
// Click Run menu item
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
const icon = actionsContainer.locator('.icon');
|
||||
await icon.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await icon.click();
|
||||
|
||||
// Handle runner tab - reset if needed, then run
|
||||
const resetButton = page.getByRole('button', { name: 'Reset' });
|
||||
const runCollectionButton = page.getByRole('button', { name: 'Run Collection' });
|
||||
// Click Run menu item
|
||||
const runMenuItem = page.getByText('Run', { exact: true });
|
||||
await runMenuItem.waitFor({ state: 'visible' });
|
||||
await runMenuItem.click();
|
||||
|
||||
// Check if Reset button is visible (means there are existing results)
|
||||
const resetVisible = await resetButton.isVisible().catch(() => false);
|
||||
if (resetVisible) {
|
||||
await resetButton.click();
|
||||
// Wait a bit for the reset to complete
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
// Handle runner tab - reset if needed, then run
|
||||
const locators = buildRunnerLocators(page);
|
||||
|
||||
// Now wait for and click Run Collection button
|
||||
await runCollectionButton.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await runCollectionButton.click();
|
||||
// Check if Reset button is visible (means there are existing results)
|
||||
const resetVisible = await locators.resetButton().isVisible({ timeout: 1000 }).catch(() => false);
|
||||
if (resetVisible) {
|
||||
await locators.resetButton().click();
|
||||
// Wait for the Run Collection button to become visible after reset
|
||||
await locators.runCollectionButton().waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
// Wait for the run to complete
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
// Now wait for and click Run Collection button
|
||||
await locators.runCollectionButton().waitFor({ state: 'visible', timeout: 10000 });
|
||||
await locators.runCollectionButton().click();
|
||||
|
||||
// Wait for the run to complete
|
||||
await locators.runAgainButton().waitFor({ timeout: 2 * 60 * 1000 });
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds locators for sandbox mode settings
|
||||
* @param page - The Playwright page object
|
||||
* @returns Object with locators for sandbox elements
|
||||
*/
|
||||
export const buildSandboxLocators = (page: Page) => ({
|
||||
developerModeBadge: () => page.locator('.developer-mode').filter({ hasText: 'Developer Mode' }),
|
||||
safeModeBadge: () => page.locator('.safe-mode').filter({ hasText: 'Safe Mode' }),
|
||||
safeModeRadio: () => page.getByLabel('Safe Mode'),
|
||||
developerModeRadio: () => page.getByLabel('Developer Mode(use only if'),
|
||||
jsSandboxHeading: () => page.getByText('JavaScript Sandbox'),
|
||||
saveButton: () => page.getByRole('button', { name: 'Save' })
|
||||
});
|
||||
|
||||
/**
|
||||
* Sets up the JavaScript sandbox mode for a collection
|
||||
* @param page - The Playwright page object
|
||||
@@ -71,81 +100,59 @@ export const runCollection = async (page: Page, collectionName: string) => {
|
||||
* @returns void
|
||||
*/
|
||||
export const setSandboxMode = async (page: Page, collectionName: string, mode: 'developer' | 'safe') => {
|
||||
// Click on the collection name in the sidebar
|
||||
// Use the collections testid to scope to the sidebar, then find the specific collection
|
||||
const sidebarCollection = page.getByTestId('collections').locator('#sidebar-collection-name').filter({ hasText: collectionName }).first();
|
||||
await test.step(`Set sandbox mode to "${mode}" for "${collectionName}"`, async () => {
|
||||
const sandboxLocators = buildSandboxLocators(page);
|
||||
|
||||
// Wait for the sidebar to be loaded
|
||||
await page.waitForTimeout(500);
|
||||
// Click on the collection name in the sidebar
|
||||
const sidebarCollection = page.getByTestId('collections').locator('#sidebar-collection-name').filter({ hasText: collectionName }).first();
|
||||
await sidebarCollection.waitFor({ state: 'visible' });
|
||||
await sidebarCollection.click();
|
||||
|
||||
await sidebarCollection.click();
|
||||
// Check if there's already a mode selected - if so, we need to click the badge to open settings tab
|
||||
const developerBadgeVisible = await sandboxLocators.developerModeBadge().isVisible().catch(() => false);
|
||||
const safeBadgeVisible = await sandboxLocators.safeModeBadge().isVisible().catch(() => false);
|
||||
|
||||
// Wait a moment for the UI to load
|
||||
await page.waitForTimeout(300);
|
||||
// If a badge exists, click it to open the security settings tab
|
||||
if (developerBadgeVisible || safeBadgeVisible) {
|
||||
if (developerBadgeVisible) {
|
||||
await sandboxLocators.developerModeBadge().click();
|
||||
} else {
|
||||
await sandboxLocators.safeModeBadge().click();
|
||||
}
|
||||
|
||||
// Check if there's already a mode selected - if so, we need to click the badge to open settings tab
|
||||
// Look for the Developer Mode or Safe Mode badge/button
|
||||
const developerModeBadge = page.locator('.developer-mode').filter({ hasText: 'Developer Mode' });
|
||||
const safeModeBadge = page.locator('.safe-mode').filter({ hasText: 'Safe Mode' });
|
||||
// Wait for the security settings tab to be active
|
||||
await sandboxLocators.jsSandboxHeading().waitFor({ state: 'visible', timeout: 10000 });
|
||||
}
|
||||
// If no badge exists, the modal should have appeared automatically (first time selection)
|
||||
|
||||
const developerBadgeExists = await developerModeBadge.count().then((count) => count > 0).catch(() => false);
|
||||
const safeBadgeExists = await safeModeBadge.count().then((count) => count > 0).catch(() => false);
|
||||
// Wait for security settings form to be visible - wait for either radio button
|
||||
await Promise.race([
|
||||
sandboxLocators.safeModeRadio().waitFor({ state: 'visible', timeout: 10000 }).catch(() => {}),
|
||||
sandboxLocators.developerModeRadio().waitFor({ state: 'visible', timeout: 10000 }).catch(() => {})
|
||||
]);
|
||||
|
||||
// If a badge exists, click it to open the security settings tab
|
||||
if (developerBadgeExists || safeBadgeExists) {
|
||||
// Click the appropriate badge to open the security settings tab
|
||||
if (developerBadgeExists) {
|
||||
await developerModeBadge.click();
|
||||
if (mode === 'developer') {
|
||||
await sandboxLocators.developerModeRadio().waitFor({ state: 'visible', timeout: 5000 });
|
||||
await sandboxLocators.developerModeRadio().check();
|
||||
} else {
|
||||
await safeModeBadge.click();
|
||||
// For safe mode, check if developer mode is currently selected
|
||||
const developerModeChecked = await sandboxLocators.developerModeRadio().isChecked().catch(() => false);
|
||||
|
||||
if (developerModeChecked) {
|
||||
// Click the Developer Mode label text inside the security settings form
|
||||
const securityForm = page.locator('div').filter({ hasText: 'JavaScript Sandbox' }).locator('..').first();
|
||||
const developerLabel = securityForm.locator('label').filter({ hasText: /^Developer Mode/ }).first();
|
||||
await developerLabel.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await developerLabel.click();
|
||||
}
|
||||
|
||||
// Ensure Safe Mode radio is visible and check it
|
||||
await sandboxLocators.safeModeRadio().waitFor({ state: 'visible', timeout: 5000 });
|
||||
await sandboxLocators.safeModeRadio().check();
|
||||
}
|
||||
|
||||
// Wait for the security settings tab to be active and form to be visible
|
||||
// Look for the security settings content - it should have "JavaScript Sandbox" heading
|
||||
await page.getByText('JavaScript Sandbox').waitFor({ state: 'visible', timeout: 10000 });
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
// If no badge exists, the modal should have appeared automatically (first time selection)
|
||||
|
||||
// Wait for security settings form to be visible - wait for either radio button
|
||||
// These should be in the active tab (either modal or tab)
|
||||
const safeModeRadio = page.getByLabel('Safe Mode');
|
||||
const developerRadio = page.getByLabel('Developer Mode(use only if');
|
||||
|
||||
// Wait for at least one of them to be visible
|
||||
await Promise.race([
|
||||
safeModeRadio.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {}),
|
||||
developerRadio.waitFor({ state: 'visible', timeout: 10000 }).catch(() => {})
|
||||
]);
|
||||
|
||||
// Additional wait to ensure UI is stable
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
if (mode === 'developer') {
|
||||
await developerRadio.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await developerRadio.check();
|
||||
} else {
|
||||
// For safe mode, check if developer mode is currently selected
|
||||
const developerModeChecked = await developerRadio.isChecked().catch(() => false);
|
||||
|
||||
if (developerModeChecked) {
|
||||
// Click the Developer Mode label text inside the security settings form
|
||||
// Scope to the form container to avoid clicking the badge
|
||||
// The form should have the "JavaScript Sandbox" heading, so scope to that container
|
||||
const securityForm = page.locator('div').filter({ hasText: 'JavaScript Sandbox' }).locator('..').first();
|
||||
const developerLabel = securityForm.locator('label').filter({ hasText: /^Developer Mode/ }).first();
|
||||
await developerLabel.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await developerLabel.click();
|
||||
// Wait for UI to update
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
|
||||
// Ensure Safe Mode radio is visible and check it
|
||||
await safeModeRadio.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await safeModeRadio.check();
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await sandboxLocators.saveButton().click();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { createCollection, closeAllCollections, createRequest } from '../utils/page';
|
||||
import {
|
||||
createCollection,
|
||||
closeAllCollections,
|
||||
createRequest,
|
||||
createEnvironment,
|
||||
addEnvironmentVariables,
|
||||
saveEnvironment,
|
||||
closeEnvironmentPanel
|
||||
} from '../utils/page';
|
||||
import { buildCommonLocators } from '../utils/page/locators';
|
||||
|
||||
test.describe('Variable Tooltip', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
if (!page.isClosed()) {
|
||||
await closeAllCollections(page);
|
||||
}
|
||||
});
|
||||
|
||||
test('should test tooltip functionality with environment variables', async ({ page, createTmpDir }) => {
|
||||
@@ -13,33 +24,16 @@ test.describe('Variable Tooltip', () => {
|
||||
await createCollection(page, collectionName, await createTmpDir('tooltip-collection'), {
|
||||
openWithSandboxMode: 'safe'
|
||||
});
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: collectionName })).toBeVisible();
|
||||
|
||||
// Open environment settings
|
||||
await page.locator('[data-testid="environment-selector-trigger"]').click();
|
||||
await expect(page.locator('[data-testid="env-tab-collection"]')).toHaveClass(/active/);
|
||||
await createEnvironment(page, 'Test Env', 'collection');
|
||||
|
||||
// Create environment
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
await page.locator('input[name="name"]').fill('Test Env');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await addEnvironmentVariables(page, [
|
||||
{ name: 'apiKey', value: 'test-key-123' },
|
||||
{ name: 'secretToken', value: 'secret-xyz', isSecret: true }
|
||||
]);
|
||||
|
||||
// Add apiKey variable
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="0.name"]').fill('apiKey');
|
||||
await page.locator('tr').filter({ has: page.locator('input[name="0.name"]') }).locator('.CodeMirror').click();
|
||||
await page.keyboard.type('test-key-123');
|
||||
|
||||
// Add secretToken variable
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="1.name"]').fill('secretToken');
|
||||
await page.locator('tr').filter({ has: page.locator('input[name="1.name"]') }).locator('.CodeMirror').click();
|
||||
await page.keyboard.type('secret-xyz');
|
||||
await page.locator('input[name="1.secret"]').check();
|
||||
|
||||
// Save and close
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByText('×').click();
|
||||
await saveEnvironment(page);
|
||||
await closeEnvironmentPanel(page);
|
||||
});
|
||||
|
||||
await test.step('Create request and test tooltip', async () => {
|
||||
@@ -120,32 +114,16 @@ test.describe('Variable Tooltip', () => {
|
||||
await createCollection(page, collectionName, await createTmpDir('tooltip-ref-collection'), {
|
||||
openWithSandboxMode: 'safe'
|
||||
});
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: collectionName })).toBeVisible();
|
||||
|
||||
// Open environment settings
|
||||
await page.locator('[data-testid="environment-selector-trigger"]').click();
|
||||
await expect(page.locator('[data-testid="env-tab-collection"]')).toHaveClass(/active/);
|
||||
await createEnvironment(page, 'Ref Test Env', 'collection');
|
||||
|
||||
// Create environment
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
await page.locator('input[name="name"]').fill('Ref Test Env');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await addEnvironmentVariables(page, [
|
||||
{ name: 'host', value: 'api.example.com' },
|
||||
{ name: 'endpoint', value: 'https://{{host}}/users' }
|
||||
]);
|
||||
|
||||
// Add host variable
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="0.name"]').fill('host');
|
||||
await page.locator('tr').filter({ has: page.locator('input[name="0.name"]') }).locator('.CodeMirror').click();
|
||||
await page.keyboard.type('api.example.com');
|
||||
|
||||
// Add endpoint that references host
|
||||
await page.locator('button[data-testid="add-variable"]').click();
|
||||
await page.locator('input[name="1.name"]').fill('endpoint');
|
||||
await page.locator('tr').filter({ has: page.locator('input[name="1.name"]') }).locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://{{host}}/users');
|
||||
|
||||
// Save and close
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByText('×').click();
|
||||
await saveEnvironment(page);
|
||||
await closeEnvironmentPanel(page);
|
||||
});
|
||||
|
||||
await test.step('Create request with variable references', async () => {
|
||||
@@ -256,22 +234,18 @@ test.describe('Variable Tooltip', () => {
|
||||
await createCollection(page, collectionName, await createTmpDir('tooltip-readonly-collection'), {
|
||||
openWithSandboxMode: 'safe'
|
||||
});
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: collectionName })).toBeVisible();
|
||||
|
||||
// Create environment
|
||||
await page.locator('[data-testid="environment-selector-trigger"]').click();
|
||||
await page.locator('button[id="create-env"]').click();
|
||||
await page.locator('input[name="name"]').fill('Readonly Env');
|
||||
await page.getByRole('button', { name: 'Create' }).click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await page.getByText('×').click();
|
||||
await createEnvironment(page, 'Readonly Env', 'collection');
|
||||
await saveEnvironment(page);
|
||||
await closeEnvironmentPanel(page);
|
||||
|
||||
// Create request using utility method
|
||||
await createRequest(page, 'Readonly Test', collectionName);
|
||||
|
||||
// Set the URL
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Readonly Test' }).click();
|
||||
const urlEditor = page.locator('#request-url .CodeMirror');
|
||||
const locators = buildCommonLocators(page);
|
||||
await locators.sidebar.request('Readonly Test').click();
|
||||
const urlEditor = locators.request.urlInput();
|
||||
await urlEditor.click();
|
||||
await page.keyboard.type('https://example.com');
|
||||
await page.keyboard.press('Control+s');
|
||||
|
||||
Reference in New Issue
Block a user