mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 22:45:25 +00:00
feat(environments): split variables and secrets into separate tabs (#8191)
This commit is contained in:
@@ -86,6 +86,8 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true }) — typed va
|
||||
newPage.locator('[data-testid="env-var-row-typed_str"] .type-label').first()
|
||||
).toHaveText('string');
|
||||
|
||||
await newPage.getByTestId('responsive-tab-secrets').click();
|
||||
|
||||
await expect(
|
||||
newPage.locator('[data-testid="env-var-row-existing_secret"]')
|
||||
).toBeVisible();
|
||||
|
||||
@@ -7,13 +7,19 @@ import {
|
||||
saveEnvironment,
|
||||
sendRequest,
|
||||
expectResponseContains,
|
||||
closeAllCollections
|
||||
closeAllCollections,
|
||||
deleteAllGlobalEnvironments
|
||||
} from '../../utils/page';
|
||||
import { buildCommonLocators } from '../../utils/page/locators';
|
||||
|
||||
test.describe('Global Environment Create Tests', () => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await deleteAllGlobalEnvironments(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('should import collection and create global environment for request usage', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
@@ -56,9 +62,5 @@ test.describe('Global Environment Create Tests', () => {
|
||||
'"apiToken": "global-secret-token-12345"'
|
||||
]);
|
||||
});
|
||||
|
||||
await test.step('Cleanup', async () => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -207,10 +207,15 @@ for (const { format, collectionName } of FORMATS) {
|
||||
await test.step('Verify the imported env editor shows datatypes correctly', async () => {
|
||||
await expect(locators.tabs.activeRequestTab()).toContainText('Environments');
|
||||
|
||||
for (const [name, label] of [...TYPED_LABEL_ROWS, ...MISMATCHED_LABEL_ROWS]) {
|
||||
// Secrets live on their own tab, so assert per-tab. The fixture names every
|
||||
// secret var `env_secret_*`, so its name is a reliable tab discriminator.
|
||||
const isSecret = (name: string) => name.includes('secret');
|
||||
|
||||
// Variables tab (default): the non-secret typed, mismatched, and string rows.
|
||||
for (const [name, label] of [...TYPED_LABEL_ROWS, ...MISMATCHED_LABEL_ROWS].filter(([n]) => !isSecret(n))) {
|
||||
await expectTypeLabel(page, name, label);
|
||||
}
|
||||
for (const name of STRING_LABEL_ROWS) {
|
||||
for (const name of STRING_LABEL_ROWS.filter((n) => !isSecret(n))) {
|
||||
await expectTypeLabel(page, name, 'string');
|
||||
}
|
||||
|
||||
@@ -221,6 +226,16 @@ for (const { format, collectionName } of FORMATS) {
|
||||
await expectNoMismatch(page, 'env_num');
|
||||
await expectNoMismatch(page, 'env_untyped_obj');
|
||||
|
||||
// Secrets tab: the secret typed rows keep their dataType label; the bare-string
|
||||
// secrets fall back to 'string'.
|
||||
await locators.environment.secretsTab().click();
|
||||
for (const [name, label] of TYPED_LABEL_ROWS.filter(([n]) => isSecret(n))) {
|
||||
await expectTypeLabel(page, name, label);
|
||||
}
|
||||
for (const name of STRING_LABEL_ROWS.filter((n) => isSecret(n))) {
|
||||
await expectTypeLabel(page, name, 'string');
|
||||
}
|
||||
|
||||
await closeEnvEditor(page);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -44,6 +44,7 @@ test.describe.serial('Collection Environment Import Tests', () => {
|
||||
await expect(envTab).toBeVisible();
|
||||
|
||||
await expect(page.getByRole('row', { name: 'host' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
@@ -125,6 +126,9 @@ test.describe.serial('Collection Environment Import Tests', () => {
|
||||
|
||||
// Verify prod environment variables
|
||||
await expect(page.getByRole('row', { name: 'host' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
// secretToken was imported as a secret, so it lives on the Secrets tab, not Variables.
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
|
||||
@@ -59,6 +59,7 @@ test.describe.serial('Global Environment Import Tests', () => {
|
||||
|
||||
// Verify imported variables
|
||||
await expect(page.getByRole('row', { name: 'host' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
@@ -142,6 +143,9 @@ test.describe.serial('Global Environment Import Tests', () => {
|
||||
|
||||
// Verify imported variables
|
||||
await expect(page.getByRole('row', { name: 'host' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
// secretToken was imported as a secret, so it lives on the Secrets tab, not Variables.
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
|
||||
@@ -68,8 +68,10 @@ test.describe('Collection Environment Import Tests', () => {
|
||||
|
||||
await expect(page.locator('input[name$=".name"][value="postTitle"]')).toBeVisible();
|
||||
await expect(page.locator('input[name$=".name"][value="postBody"]')).toBeVisible();
|
||||
await expect(page.locator('input[name$=".name"][value="secretApiToken"]')).toBeVisible();
|
||||
await expect(page.locator('input[name="5.secret"]')).toBeChecked();
|
||||
await expect(page.locator('input[name$=".name"][value="secretApiToken"]')).toHaveCount(0);
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByTestId('env-var-row-secretApiToken')).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
|
||||
@@ -63,8 +63,10 @@ test.describe('Global Environment Import Tests', () => {
|
||||
|
||||
await expect(variablesTable.locator('input[name$=".name"][value="postTitle"]')).toBeVisible();
|
||||
await expect(variablesTable.locator('input[name$=".name"][value="postBody"]')).toBeVisible();
|
||||
await expect(variablesTable.locator('input[name$=".name"][value="secretApiToken"]')).toBeVisible();
|
||||
await expect(variablesTable.locator('input[name="5.secret"]')).toBeChecked();
|
||||
await expect(variablesTable.locator('input[name$=".name"][value="secretApiToken"]')).toHaveCount(0);
|
||||
await page.getByTestId('responsive-tab-secrets').click();
|
||||
await expect(page.getByTestId('env-var-row-secretApiToken')).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
|
||||
76
tests/environments/save-shortcut/save-shortcut.spec.ts
Normal file
76
tests/environments/save-shortcut/save-shortcut.spec.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
import { Page } from '@playwright/test';
|
||||
import {
|
||||
importCollection,
|
||||
createEnvironment,
|
||||
addRowToActiveTab,
|
||||
closeAllCollections,
|
||||
deleteAllGlobalEnvironments
|
||||
} from '../../utils/page';
|
||||
|
||||
const collectionFile = path.join(__dirname, '..', 'create-environment', 'fixtures', 'bruno-collection.json');
|
||||
|
||||
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
|
||||
const tabDraftIcon = (page: Page) => page.locator('.request-tab.active').getByTestId('tab-draft-icon');
|
||||
|
||||
const saveWithShortcut = async (page: Page) => {
|
||||
await page.keyboard.press(`${modifier}+KeyS`);
|
||||
};
|
||||
|
||||
test.describe('Environment save shortcut (Cmd/Ctrl+S)', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('saves a collection environment and shows the success toast', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('env-save-shortcut'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Shortcut Save Env', 'collection');
|
||||
|
||||
await test.step('Add a variable to create an unsaved draft', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await expect(tabDraftIcon(page)).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Pressing the save shortcut shows the success toast', async () => {
|
||||
await saveWithShortcut(page);
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('The unsaved-changes dot is gone once saved', async () => {
|
||||
await expect(tabDraftIcon(page)).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Global environment save shortcut (Cmd/Ctrl+S)', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await deleteAllGlobalEnvironments(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('saves a global environment and shows the success toast', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-env-save-shortcut'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Shortcut Save Env', 'global');
|
||||
|
||||
await test.step('Add a variable to create an unsaved draft', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await expect(tabDraftIcon(page)).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Pressing the save shortcut shows the success toast', async () => {
|
||||
await saveWithShortcut(page);
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('The unsaved-changes dot is gone once saved', async () => {
|
||||
await expect(tabDraftIcon(page)).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,397 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
import { Page } from '@playwright/test';
|
||||
import { importCollection, createEnvironment, closeAllCollections, addRowToActiveTab, saveEnvironment, deleteAllGlobalEnvironments } from '../../utils/page';
|
||||
import { buildCommonLocators } from '../../utils/page/locators';
|
||||
|
||||
const variablesTab = (page: Page) => buildCommonLocators(page).environment.variablesTab();
|
||||
const secretsTab = (page: Page) => buildCommonLocators(page).environment.secretsTab();
|
||||
const varRow = (page: Page, name: string) => buildCommonLocators(page).environment.varRow(name);
|
||||
const saveTab = (page: Page) => buildCommonLocators(page).environment.saveTab();
|
||||
const tabDraftIcon = (page: Page) => page.locator('.request-tab.active').getByTestId('tab-draft-icon');
|
||||
|
||||
const searchEnv = async (page: Page, query: string) => {
|
||||
const input = page.locator('.search-input');
|
||||
if ((await input.count()) === 0) {
|
||||
await page.locator('.env-search-container button[title="Search"]').click();
|
||||
await input.waitFor({ state: 'visible' });
|
||||
}
|
||||
await input.fill(query);
|
||||
};
|
||||
|
||||
const deleteRow = async (page: Page, name: string) => {
|
||||
await varRow(page, name).locator('button:has(.icon-tabler-trash)').click();
|
||||
};
|
||||
|
||||
const collectionFile = path.join(__dirname, '..', 'create-environment', 'fixtures', 'bruno-collection.json');
|
||||
|
||||
test.describe('Environment Variables / Secrets tab separation', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('keeps variables and secrets on their own tabs', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('var-secret-tabs'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Tab Separation Env', 'collection');
|
||||
|
||||
await test.step('Add a variable on the Variables tab', async () => {
|
||||
await expect(variablesTab(page)).toHaveClass(/active/);
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Add a secret on the Secrets tab', async () => {
|
||||
await secretsTab(page).click();
|
||||
await expect(secretsTab(page)).toHaveClass(/active/);
|
||||
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Variables tab shows only the variable, not the secret', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(variablesTab(page)).toHaveClass(/active/);
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('saves variables and secrets independently and persists both', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('var-secret-save'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Independent Save Env', 'collection');
|
||||
|
||||
await test.step('Add and save a variable on the Variables tab', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
// newest toast: the two back-to-back saves can briefly show two identical toasts
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Add and save a secret on the Secrets tab', async () => {
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Saving the Secrets tab did not wipe the saved variable', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
|
||||
await secretsTab(page).click();
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('common save icon persists both variables and secrets at once', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('var-secret-save-all'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Save All Env', 'collection');
|
||||
|
||||
await test.step('Add a variable on the Variables tab without saving', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
});
|
||||
|
||||
await test.step('Add a secret on the Secrets tab without saving', async () => {
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
});
|
||||
|
||||
await test.step('The environment tab shows the unsaved-changes dot while drafts exist', async () => {
|
||||
await expect(tabDraftIcon(page)).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('The common save icon saves both tabs in a single click', async () => {
|
||||
await saveEnvironment(page);
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Both the variable and the secret are persisted on their tabs', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
|
||||
await secretsTab(page).click();
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
});
|
||||
|
||||
await test.step('The unsaved-changes dot is gone once both tabs are saved', async () => {
|
||||
await expect(tabDraftIcon(page)).not.toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('A second save reports nothing to save, proving both were committed', async () => {
|
||||
await saveEnvironment(page);
|
||||
await expect(page.getByText('No changes to save')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('search is scoped to the active tab', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('var-secret-search'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Search Scope Env', 'collection');
|
||||
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await test.step('Searching the Variables tab never surfaces the secret', async () => {
|
||||
await variablesTab(page).click();
|
||||
await searchEnv(page, 'host');
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
|
||||
await searchEnv(page, 'apiToken');
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Searching the Secrets tab never surfaces the variable', async () => {
|
||||
await secretsTab(page).click();
|
||||
await searchEnv(page, 'apiToken');
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
|
||||
await searchEnv(page, 'host');
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
});
|
||||
|
||||
// The search query is stored in Redux and persists across environments, so clear
|
||||
// it before the test ends — otherwise it filters the next test's table to "No
|
||||
// results" and the empty "Name" row never renders.
|
||||
await searchEnv(page, '');
|
||||
});
|
||||
|
||||
test('deleting on one tab leaves the other tab untouched', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('var-secret-delete'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Delete Scope Env', 'collection');
|
||||
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await test.step('Delete the secret on the Secrets tab', async () => {
|
||||
await deleteRow(page, 'apiToken');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
});
|
||||
|
||||
await test.step('The variable on the Variables tab is untouched', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Global Environment Variables / Secrets tab separation', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await deleteAllGlobalEnvironments(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('keeps variables and secrets on their own tabs', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-var-secret-tabs'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Tab Separation Env', 'global');
|
||||
|
||||
await test.step('Add a variable on the Variables tab', async () => {
|
||||
await expect(variablesTab(page)).toHaveClass(/active/);
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Add a secret on the Secrets tab', async () => {
|
||||
await secretsTab(page).click();
|
||||
await expect(secretsTab(page)).toHaveClass(/active/);
|
||||
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Variables tab shows only the variable, not the secret', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(variablesTab(page)).toHaveClass(/active/);
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('saves variables and secrets independently and persists both', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-var-secret-save'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Independent Save Env', 'global');
|
||||
|
||||
await test.step('Add and save a variable on the Variables tab', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
// newest toast: the two back-to-back saves can briefly show two identical toasts
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Add and save a secret on the Secrets tab', async () => {
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Saving the Secrets tab did not wipe the saved variable', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
|
||||
await secretsTab(page).click();
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('common save icon persists both variables and secrets at once', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-var-secret-save-all'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Save All Env', 'global');
|
||||
|
||||
await test.step('Add a variable on the Variables tab without saving', async () => {
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
});
|
||||
|
||||
await test.step('Add a secret on the Secrets tab without saving', async () => {
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
});
|
||||
|
||||
await test.step('The environment tab shows the unsaved-changes dot while drafts exist', async () => {
|
||||
await expect(tabDraftIcon(page)).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('The common save icon saves both tabs in a single click', async () => {
|
||||
await saveEnvironment(page);
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Both the variable and the secret are persisted on their tabs', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
|
||||
await secretsTab(page).click();
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
});
|
||||
|
||||
await test.step('The unsaved-changes dot is gone once both tabs are saved', async () => {
|
||||
await expect(tabDraftIcon(page)).not.toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('A second save reports nothing to save, proving both were committed', async () => {
|
||||
await saveEnvironment(page);
|
||||
await expect(page.getByText('No changes to save')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('search is scoped to the active tab', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-var-secret-search'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Search Scope Env', 'global');
|
||||
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await test.step('Searching the Variables tab never surfaces the secret', async () => {
|
||||
await variablesTab(page).click();
|
||||
await searchEnv(page, 'host');
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
|
||||
await searchEnv(page, 'apiToken');
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Searching the Secrets tab never surfaces the variable', async () => {
|
||||
await secretsTab(page).click();
|
||||
await searchEnv(page, 'apiToken');
|
||||
await expect(varRow(page, 'apiToken')).toBeVisible();
|
||||
|
||||
await searchEnv(page, 'host');
|
||||
await expect(varRow(page, 'host')).toHaveCount(0);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
});
|
||||
|
||||
// The search query is stored in Redux and persists across environments, so clear
|
||||
// it before the test ends — otherwise it filters the next test's table to "No
|
||||
// results" and the empty "Name" row never renders.
|
||||
await searchEnv(page, '');
|
||||
});
|
||||
|
||||
test('deleting on one tab leaves the other tab untouched', async ({ page, createTmpDir }) => {
|
||||
await importCollection(page, collectionFile, await createTmpDir('global-var-secret-delete'), {
|
||||
expectedCollectionName: 'test_collection'
|
||||
});
|
||||
|
||||
await createEnvironment(page, 'Global Delete Scope Env', 'global');
|
||||
|
||||
await addRowToActiveTab(page, 'host', 'https://echo.usebruno.com');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await secretsTab(page).click();
|
||||
await addRowToActiveTab(page, 'apiToken', 'super-secret-token-12345');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
|
||||
await test.step('Delete the secret on the Secrets tab', async () => {
|
||||
await deleteRow(page, 'apiToken');
|
||||
await saveTab(page).click();
|
||||
await expect(page.getByText('Changes saved successfully').last()).toBeVisible();
|
||||
await expect(varRow(page, 'apiToken')).toHaveCount(0);
|
||||
});
|
||||
|
||||
await test.step('The variable on the Variables tab is untouched', async () => {
|
||||
await variablesTab(page).click();
|
||||
await expect(varRow(page, 'host')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user