mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
252 lines
14 KiB
TypeScript
252 lines
14 KiB
TypeScript
import { test, expect } from '../../../playwright';
|
|
import * as path from 'path';
|
|
import { openCollection, closeAllCollections } from '../../utils/page/actions';
|
|
|
|
test.describe('Import Insomnia v5 Collection - Environment Import', () => {
|
|
test.afterEach(async ({ page }) => {
|
|
await closeAllCollections(page);
|
|
});
|
|
/**
|
|
* Tests Insomnia v5 environment import with nested data flattening and environment merging.
|
|
* Verifies that base and sub-environments are imported correctly with JavaScript-style keys
|
|
* (e.g., user.name, user.roles[0]) and proper value inheritance/overrides.
|
|
*
|
|
* Test Structure:
|
|
* - Base Environment: Contains nested objects, arrays, and primitive values
|
|
* - Staging Environment: Overrides some base values, inherits others
|
|
* - Development Environment: Adds new variables while inheriting base values
|
|
*/
|
|
test('Import Insomnia v5 collection with nested environments and verify flattening', async ({
|
|
page,
|
|
createTmpDir
|
|
}) => {
|
|
const insomniaFile = path.resolve(__dirname, 'fixtures', 'insomnia-v5-with-envs.yaml');
|
|
|
|
await test.step('Import Insomnia v5 collection with environments', async () => {
|
|
await page.getByTestId('collections-header-add-menu').click();
|
|
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();
|
|
|
|
const importModal = page.getByTestId('import-collection-modal');
|
|
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 page.locator('#collection-location').fill(await createTmpDir('insomnia-v5-env-test'));
|
|
await locationModal.getByRole('button', { name: 'Import' }).click();
|
|
await locationModal.waitFor({ state: 'hidden' });
|
|
|
|
await openCollection(page, 'Test API Collection v5 with Environments');
|
|
});
|
|
|
|
await test.step('Open collection environments panel', async () => {
|
|
await page.getByTestId('environment-selector-trigger').click();
|
|
await page.getByTestId('env-tab-collection').click();
|
|
await page.getByRole('button', { name: 'Configure' }).click();
|
|
});
|
|
|
|
await test.step('Verify all environments are present', async () => {
|
|
await expect(page
|
|
.locator('div')
|
|
.filter({ hasText: /^Base Environment$/ })
|
|
.first()).toBeVisible();
|
|
await expect(page
|
|
.locator('div')
|
|
.filter({ hasText: /^Staging$/ })
|
|
.first()).toBeVisible();
|
|
await expect(page
|
|
.locator('div')
|
|
.filter({ hasText: /^Development$/ })
|
|
.first()).toBeVisible();
|
|
});
|
|
|
|
await test.step('Test Base Environment - verify flattened keys', async () => {
|
|
await page
|
|
.locator('div')
|
|
.filter({ hasText: /^Base Environment$/ })
|
|
.first()
|
|
.click();
|
|
|
|
// Gate on the env-switch flatten pass having fully landed before
|
|
// per-row asserts. The flatten renders top-level keys first and the
|
|
// deepest nested keys (`config.*`) last; on slow runners the trailing
|
|
// batch can take longer than the 5s default. Waiting on the deepest
|
|
// key here guarantees every shallower input is also in DOM by the
|
|
// time the per-input asserts below run.
|
|
await page.locator('input[value="config.debug"]').waitFor({ state: 'visible', timeout: 15000 });
|
|
|
|
// **Assertion 1: Basic Variables (Top-level keys)**
|
|
// Verifies that simple key-value pairs from the base environment are imported correctly
|
|
const baseUrlInput = page.locator('input[value="base_url"]');
|
|
const authTokenInput = page.locator('input[value="auth_token"]');
|
|
await expect(baseUrlInput).toBeVisible();
|
|
await expect(authTokenInput).toBeVisible();
|
|
|
|
// Assert: Top-level string values are preserved exactly as in the source
|
|
const baseUrlRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"base_url\"]') });
|
|
await expect(baseUrlRow.locator('.CodeMirror-line').first()).toHaveText('https://api.example.com');
|
|
const authTokenRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"auth_token\"]') });
|
|
await expect(authTokenRow.locator('.CodeMirror-line').first()).toHaveText('your_auth_token_here');
|
|
|
|
// **Assertion 2: Nested Object Flattening**
|
|
// Verifies that nested objects are flattened to dot-notation keys (e.g., user.name, user.id)
|
|
const userNameInput = page.locator('input[value="user.name"]');
|
|
const userIdInput = page.locator('input[value="user.id"]');
|
|
await expect(userNameInput).toBeVisible();
|
|
await expect(userIdInput).toBeVisible();
|
|
|
|
// Assert: Nested object properties are accessible via dot notation
|
|
const userNameRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.name\"]') });
|
|
await expect(userNameRow.locator('.CodeMirror-line').first()).toHaveText('admin');
|
|
// Assert: Numeric values are converted to strings and preserved
|
|
const userIdRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.id\"]') });
|
|
await expect(userIdRow.locator('.CodeMirror-line').first()).toHaveText('123');
|
|
|
|
// **Assertion 3: Array Flattening**
|
|
// Verifies that arrays are flattened using JavaScript-style square bracket notation (e.g., user.roles[0], user.roles[1])
|
|
const userRoles0Input = page.locator('input[value="user.roles[0]"]');
|
|
const userRoles1Input = page.locator('input[value="user.roles[1]"]');
|
|
await expect(userRoles0Input).toBeVisible();
|
|
await expect(userRoles1Input).toBeVisible();
|
|
|
|
// Assert: Array elements are accessible via JavaScript-style square bracket notation
|
|
const userRoles0Row = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.roles[0]\"]') });
|
|
await expect(userRoles0Row.locator('.CodeMirror-line').first()).toHaveText('admin');
|
|
const userRoles1Row = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.roles[1]\"]') });
|
|
await expect(userRoles1Row.locator('.CodeMirror-line').first()).toHaveText('user');
|
|
|
|
// **Assertion 4: Deeply Nested Config Objects**
|
|
// Verifies that deeply nested objects are properly flattened (e.g., config.timeout, config.debug)
|
|
const configTimeoutInput = page.locator('input[value="config.timeout"]');
|
|
const configDebugInput = page.locator('input[value="config.debug"]');
|
|
await expect(configTimeoutInput).toBeVisible();
|
|
await expect(configDebugInput).toBeVisible();
|
|
|
|
// Assert: Numeric values in nested objects are converted to strings
|
|
const configTimeoutRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"config.timeout\"]') });
|
|
await expect(configTimeoutRow.locator('.CodeMirror-line').first()).toHaveText('30000');
|
|
// Assert: Boolean values in nested objects are converted to strings
|
|
const configDebugRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"config.debug\"]') });
|
|
await expect(configDebugRow.locator('.CodeMirror-line').first()).toHaveText('true');
|
|
});
|
|
|
|
await test.step('Test Staging Environment - verify merging and overrides', async () => {
|
|
await page
|
|
.locator('div')
|
|
.filter({ hasText: /^Staging$/ })
|
|
.first()
|
|
.click();
|
|
|
|
// Gate on the env-switch flatten pass having fully landed before
|
|
// per-row asserts. The deepest overridden key (`config.debug`) lands
|
|
// last in this env; waiting on it here guarantees every shallower
|
|
// input is also in DOM by the time the per-input asserts run.
|
|
await page.locator('input[value="config.debug"]').waitFor({ state: 'visible', timeout: 15000 });
|
|
|
|
// **Assertion 1: Top-level Variable Override**
|
|
// Verifies that staging environment overrides base environment values
|
|
const stagingBaseUrlInput = page.locator('input[value="base_url"]');
|
|
await expect(stagingBaseUrlInput).toBeVisible();
|
|
// Assert: Staging overrides base_url with its own value
|
|
const baseUrlRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"base_url\"]') });
|
|
await expect(baseUrlRow.locator('.CodeMirror-line').first()).toHaveText('https://staging-api.example.com');
|
|
|
|
// **Assertion 2: Top-level Variable Inheritance**
|
|
// Verifies that staging environment inherits base environment values when not overridden
|
|
const stagingAuthTokenInput = page.locator('input[value="auth_token"]');
|
|
await expect(stagingAuthTokenInput).toBeVisible();
|
|
// Assert: Staging inherits auth_token from base (not overridden in staging)
|
|
const authTokenRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"auth_token\"]') });
|
|
await expect(authTokenRow.locator('.CodeMirror-line').first()).toHaveText('your_auth_token_here');
|
|
|
|
// **Assertion 3: Nested Object Variable Override and Inheritance**
|
|
// Verifies that nested object properties can be selectively overridden while inheriting others
|
|
const stagingUserNameInput = page.locator('input[value="user.name"]');
|
|
const stagingUserIdInput = page.locator('input[value="user.id"]');
|
|
await expect(stagingUserNameInput).toBeVisible();
|
|
await expect(stagingUserIdInput).toBeVisible();
|
|
|
|
// Assert: Staging overrides user.name with its own value
|
|
const userNameRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.name\"]') });
|
|
await expect(userNameRow.locator('.CodeMirror-line').first()).toHaveText('staging_admin');
|
|
// Assert: Staging inherits user.id from base (not overridden in staging)
|
|
const userIdRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.id\"]') });
|
|
await expect(userIdRow.locator('.CodeMirror-line').first()).toHaveText('123');
|
|
|
|
// **Assertion 4: Deeply Nested Config Override**
|
|
// Verifies that deeply nested object properties can be overridden
|
|
const stagingConfigTimeoutInput = page.locator('input[value="config.timeout"]');
|
|
const stagingConfigDebugInput = page.locator('input[value="config.debug"]');
|
|
await expect(stagingConfigTimeoutInput).toBeVisible();
|
|
await expect(stagingConfigDebugInput).toBeVisible();
|
|
|
|
// Assert: Staging overrides config.timeout with its own value
|
|
const configTimeoutRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"config.timeout\"]') });
|
|
await expect(configTimeoutRow.locator('.CodeMirror-line').first()).toHaveText('60000');
|
|
// Assert: Staging overrides config.debug with its own value
|
|
const configDebugRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"config.debug\"]') });
|
|
await expect(configDebugRow.locator('.CodeMirror-line').first()).toHaveText('false');
|
|
});
|
|
|
|
await test.step('Test Development Environment - verify new variables', async () => {
|
|
await page
|
|
.locator('div')
|
|
.filter({ hasText: /^Development$/ })
|
|
.first()
|
|
.click();
|
|
|
|
// Gate on the env-switch flatten pass having fully landed before
|
|
// per-row asserts. Inherited base keys (like `user.roles[0]`) are the
|
|
// last to merge in for a sub-env; waiting on it here guarantees every
|
|
// other input is also in DOM by the time the per-input asserts run.
|
|
await page.locator('input[value="user.roles[0]"]').waitFor({ state: 'visible', timeout: 15000 });
|
|
|
|
// **Assertion 1: Multiple Top-level Variable Overrides**
|
|
// Verifies that development environment can override multiple base environment values
|
|
const devBaseUrlInput = page.locator('input[value="base_url"]');
|
|
const devAuthTokenInput = page.locator('input[value="auth_token"]');
|
|
await expect(devBaseUrlInput).toBeVisible();
|
|
await expect(devAuthTokenInput).toBeVisible();
|
|
|
|
// Assert: Development overrides base_url with its own value
|
|
const baseUrlRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"base_url\"]') });
|
|
await expect(baseUrlRow.locator('.CodeMirror-line').first()).toHaveText('https://dev-api.example.com');
|
|
// Assert: Development overrides auth_token with its own value
|
|
const authTokenRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"auth_token\"]') });
|
|
await expect(authTokenRow.locator('.CodeMirror-line').first()).toHaveText('dev_token_123');
|
|
|
|
// **Assertion 2: New Nested Variables Addition**
|
|
// Verifies that development environment can add completely new nested variables not present in base
|
|
const newFeatureEnabledInput = page.locator('input[value="new_feature.enabled"]');
|
|
const newFeatureVersionInput = page.locator('input[value="new_feature.version"]');
|
|
await expect(newFeatureEnabledInput).toBeVisible();
|
|
await expect(newFeatureVersionInput).toBeVisible();
|
|
|
|
// Assert: New boolean variable is added and converted to string
|
|
const newFeatureEnabledRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"new_feature.enabled\"]') });
|
|
await expect(newFeatureEnabledRow.locator('.CodeMirror-line').first()).toHaveText('true');
|
|
// Assert: New numeric variable is added and converted to string with full precision
|
|
const newFeatureVersionRow = page.locator('tbody tr').filter({ has: page.locator('input[value=\"new_feature.version\"]') });
|
|
await expect(newFeatureVersionRow.locator('.CodeMirror-line').first()).toHaveText('2.099123123');
|
|
|
|
// **Assertion 3: Base Variable Inheritance**
|
|
// Verifies that development environment still inherits base variables that are not overridden
|
|
const devUserRoles0Input = page.locator('input[value="user.roles[0]"]');
|
|
await expect(devUserRoles0Input).toBeVisible();
|
|
// Assert: Development inherits user.roles[0] from base (not overridden in development)
|
|
const userRoles0Row = page.locator('tbody tr').filter({ has: page.locator('input[value=\"user.roles[0]\"]') });
|
|
await expect(userRoles0Row.locator('.CodeMirror-line').first()).toHaveText('admin');
|
|
});
|
|
|
|
await test.step('Close environment tab', async () => {
|
|
const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' });
|
|
await envTab.hover();
|
|
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
|
});
|
|
});
|
|
});
|