Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/lazy-tables-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@usebruno/api-docs": patch
---

Bugfixes:
19. The folder and collection pages now have an Execution Context section, separate from the configuration section. Headers and auth stay under Folder Configuration and Collection Configuration; vars, script and tests move into Execution Context, which collapses and expands like the one on request pages. The environment switcher is hidden from the docs header when the collection has no environments.

25. A long environment name is cut short with an ellipsis in the switcher and in its dropdown, and the full name shows in a tooltip when it is cut.

32. Empty Post-Response variables now read "None." on the folder and collection pages, the same as on request pages, instead of the section disappearing.

33. The playground folder settings Vars tab now has a Post Response table, like the Bruno app. Edits are saved with the folder.

Also, when a collection has no environments, the playground switcher no longer opens a dropdown whose only entry repeats "No environments". It shows the text with no arrow.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BaseComponent } from '../base.component';

export class FolderConfigurationComponent extends BaseComponent {
readonly root = this.page.getByTestId('folder-config');
readonly executionContext = this.page.getByTestId('folder-execution-context');
readonly headers = this.page.getByTestId('folder-config-headers');
readonly auth = this.page.getByTestId('folder-config-auth');
readonly script = this.page.getByTestId('folder-config-script');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export class EnvSwitcherComponent extends BaseComponent {
readonly trigger: Locator;
readonly menu: Locator;
readonly surface: Locator;
readonly emptyOption: Locator;

constructor(
page: Page,
Expand All @@ -25,7 +24,6 @@ export class EnvSwitcherComponent extends BaseComponent {
this.trigger = this.root.getByTestId(base);
this.menu = this.page.getByTestId(`${base}-dropdown`);
this.surface = this.menu.locator('xpath=ancestor::div[@data-tippy-root]');
this.emptyOption = this.option('no-environments');
}

option(name: string): Locator {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import type { Locator } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { BaseComponent } from '../base.component';
import { SecretValueComponent } from '../secret-value.component';

export class ConfigurationSection extends BaseComponent {
readonly root = this.page.getByTestId('collection-config');
export type ConfigurationGroup = 'headers' | 'auth' | 'vars' | 'script' | 'tests';

readonly copyButton = this.root.getByTestId('collection-config-tests-copy');
export class ConfigurationSection extends BaseComponent {
readonly copyButton: Locator;
readonly secret: SecretValueComponent;
readonly disabledRows: Locator;

readonly secret = new SecretValueComponent(
this.page,
'collection-config-auth-token-secret'
);
private readonly testId: string;

readonly disabledRows = this.root
.getByTestId('property-value-line')
.filter({ has: this.page.getByTestId('disabled-badge') });
constructor(page: Page, testId = 'collection-config') {
super(page, page.getByTestId(testId));
this.testId = testId;
this.copyButton = this.root.getByTestId(`${testId}-tests-copy`);
this.secret = new SecretValueComponent(page, `${testId}-auth-token-secret`);
this.disabledRows = this.root
.getByTestId('property-value-line')
.filter({ has: page.getByTestId('disabled-badge') });
}

subHeading(name: string): Locator {
return this.root
.getByTestId('collection-config-subheading')
.filter({ hasText: name });
subHeading(group: ConfigurationGroup): Locator {
return this.root.getByTestId(`${this.testId}-${group}-subheading`);
}

async copyToClipboard(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { DockMode } from '../../src/utils/playgroundDock';
export class PlaygroundComponent extends BaseComponent {
readonly keyValueTable = new KeyValueTableComponent(this.page);
readonly preRequestVars = new KeyValueTableComponent(this.page, 'variables-pre-request');
readonly postResponseVars = new KeyValueTableComponent(this.page, 'variables-post-response');
readonly pathParams = new KeyValueTableComponent(this.page, 'params-path');
// The Auth tab lives inside the playground request pane; open it with selectTab('auth').
readonly auth = new RequestAuthComponent(this.page);
Expand Down
18 changes: 16 additions & 2 deletions packages/bruno-api-docs/e2e/pages/folder.page.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './base.page';
import { SidebarComponent } from '../components/sidebar.component';
import { BreadcrumbComponent } from '../components/breadcrumb.component';
import { FolderConfigurationComponent } from '../components/folder/folder-configuration.component';
import { VariableCardComponent } from '../components/variable-card/variable-card.component';

export type FolderConfigGroup = 'headers' | 'auth' | 'vars' | 'script' | 'tests';

export class FolderPage extends BasePage {
readonly root = this.page.getByTestId('folder-page');
readonly title = this.page.getByTestId('folder-title');
readonly requestCount = this.page.getByTestId('folder-request-count');
readonly folderMarkdownDocs = this.page.getByTestId('folder-docs');
readonly folderMarkdownDocsToggle = this.page.getByTestId('folder-docs-toggle');
readonly emptyState = this.page.getByTestId('folder-config-empty');
readonly configurationSection = this.page.getByTestId('folder-section-configuration');
readonly executionContextSection = this.page.getByTestId('folder-section-execution-context');
readonly executionContextEmptyState = this.page.getByTestId('folder-execution-context-empty');

readonly sidebar = new SidebarComponent(this.page);
readonly breadcrumb = new BreadcrumbComponent(this.page, 'folder-breadcrumb');
readonly configuration = new FolderConfigurationComponent(this.page);
readonly variableCard = new VariableCardComponent(this.page, this.root);

async open(trail: string[]): Promise<void> {
await this.navigate('/');
configurationGroup(group: FolderConfigGroup): Locator {
return this.configurationSection.getByTestId(`folder-config-${group}`);
}

executionContextGroup(group: FolderConfigGroup): Locator {
return this.executionContextSection.getByTestId(`folder-config-${group}`);
}

async open(trail: string[], path = '/'): Promise<void> {
await this.navigate(path);
await this.sidebar.open(trail);
await this.root.waitFor({ state: 'visible' });
}
Expand Down
2 changes: 2 additions & 0 deletions packages/bruno-api-docs/e2e/pages/overview.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class OverviewPage extends BasePage {
readonly stats = new StatsSection(this.page);
readonly environments = new EnvironmentsSection(this.page);
readonly configuration = new ConfigurationSection(this.page);
readonly executionContext = new ConfigurationSection(this.page, 'collection-execution-context');
readonly executionContextEmptyState = this.page.getByTestId('collection-execution-context-empty');
readonly docMarkdown = new MarkdownComponent(this.page, this.page.getByTestId('overview-markdown-documentation'));
readonly variableCard = new VariableCardComponent(this.page, this.root);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { test, expect } from '../../playwright';

const NO_ENVIRONMENTS = '/?fixture=descriptions';

test.describe('Environment switcher with no environments', () => {
test('is hidden in the docs header', async ({ page, envSwitcher }) => {
await page.goto(NO_ENVIRONMENTS);
await expect(envSwitcher.showVarsToggle).toBeVisible();
await expect(envSwitcher.root).toHaveCount(0);
});

test('still shows its empty state in the playground sidebar', async ({ page, playground }) => {
await page.goto(`${NO_ENVIRONMENTS}#/?pg=1&dock=bottom`);
await playground.ensureSidebarOpen();
await expect(playground.envSwitcher.trigger).toBeVisible();
await expect(playground.envSwitcher.trigger).toContainText('No environments');
});

test('shows plain text in the playground, with no caret and nothing to open', async ({ page, playground }) => {
await page.goto(`${NO_ENVIRONMENTS}#/?pg=1&dock=bottom`);
await playground.ensureSidebarOpen();
const { trigger } = playground.envSwitcher;

await expect(trigger).toContainText('No environments');
await expect(playground.envSwitcher.root.getByRole('button')).toHaveCount(0);
await expect(trigger.locator('.env-switcher-chevron')).toHaveCount(0);

await trigger.click();
await expect(playground.envSwitcher.menu).toHaveCount(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { test, expect } from '../../playwright';

const LONG_ENV = 'staging-regression-automation-eu-west-1';
const FIXTURE = '/?fixture=folders';

const isClipped = (el: HTMLElement): boolean => el.scrollWidth > el.clientWidth + 1;

test.describe('Environment switcher name truncation', () => {
test('clips a long name in the trigger and reveals it in a tooltip on hover', async ({
page,
envSwitcher,
tooltip
}) => {
await page.goto(FIXTURE);
await envSwitcher.selectEnvironment(LONG_ENV);

const name = envSwitcher.trigger.getByTestId('truncated-text');
await expect(name).toHaveText(LONG_ENV);
expect(await name.evaluate(isClipped)).toBe(true);

await name.hover();
await expect(tooltip.popup).toBeVisible();
await expect(tooltip.popup).toHaveText(LONG_ENV);
});

test('clips the same long name inside the open dropdown, revealing it via the native title', async ({
page,
envSwitcher
}) => {
await page.goto(FIXTURE);
await envSwitcher.open();

const option = envSwitcher.option(LONG_ENV);
await expect(option).toHaveAttribute('title', LONG_ENV);
const name = option.locator('.environment-label-name--clamped');
await expect(name).toHaveText(LONG_ENV);
expect(await name.evaluate(isClipped)).toBe(true);
});

test('shows no tooltip for a short name, which is not clipped', async ({ page, envSwitcher, tooltip }) => {
await page.goto(FIXTURE);
await envSwitcher.selectEnvironment('Dev');

const name = envSwitcher.trigger.getByTestId('truncated-text');
await expect(name).toHaveText('Dev');
expect(await name.evaluate(isClipped)).toBe(false);

await name.hover();
await page.waitForTimeout(400);
await expect(tooltip.popup).toHaveCount(0);
});
});
65 changes: 65 additions & 0 deletions packages/bruno-api-docs/e2e/tests/folder/folder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,68 @@ test.describe('Folder page', () => {
});
});
});

test.describe('Folder page sections', () => {
test.beforeEach(async ({ folderPage }) => {
await folderPage.open(['billing', 'customers']);
});

test('Folder Configuration holds the headers and auth', async ({ folderPage }) => {
await expect(folderPage.configurationSection).toContainText('Folder Configuration');
await expect(folderPage.configurationGroup('headers')).toBeVisible();
await expect(folderPage.configurationGroup('auth')).toBeVisible();

await test.step('vars, script and tests are not under Folder Configuration', async () => {
await expect(folderPage.configurationGroup('vars')).toHaveCount(0);
await expect(folderPage.configurationGroup('script')).toHaveCount(0);
await expect(folderPage.configurationGroup('tests')).toHaveCount(0);
});
});

test('Execution Context holds the variables, script and tests', async ({ folderPage }) => {
await expect(folderPage.executionContextSection).toContainText('Execution Context');
await expect(folderPage.executionContextGroup('vars')).toBeVisible();
await expect(folderPage.executionContextGroup('script')).toBeVisible();
await expect(folderPage.executionContextGroup('tests')).toBeVisible();

await test.step('headers and auth are not under Execution Context', async () => {
await expect(folderPage.executionContextGroup('headers')).toHaveCount(0);
await expect(folderPage.executionContextGroup('auth')).toHaveCount(0);
});
Comment thread
sundram-bruno marked this conversation as resolved.
});
});

test.describe('Folder page empty sections', () => {
test('shows an empty state in both sections when the folder has nothing configured', async ({ folderPage }) => {
await folderPage.open(['Authentication'], '/?fixture=folders');
await expect(folderPage.emptyState).toBeVisible();
await expect(folderPage.executionContextEmptyState).toBeVisible();
await expect(folderPage.executionContextEmptyState).toContainText('No execution context');
});
});

test.describe('Folder variable phases', () => {
test('shows both Pre-Request and Post-Response, marking the empty one "None."', async ({ folderPage }) => {
await folderPage.open(['Realtime']);
await expect(folderPage.configuration.vars).toContainText('Pre-Request');
await expect(folderPage.configuration.vars).toContainText('Post-Response');
await expect(folderPage.configuration.vars).toContainText('None.');
});
});

test.describe('Folder execution context accordion', () => {
test('collapses the Execution Context section and remembers it across a reload', async ({ folderPage, page }) => {
await folderPage.open(['Realtime']);
const toggle = folderPage.executionContextSection.getByRole('button', { name: /Execution Context/i });

await expect(toggle).toHaveAttribute('aria-expanded', 'true');
await expect(folderPage.configuration.vars).toBeVisible();

await toggle.click();
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
await expect(folderPage.configuration.vars).toBeHidden();

await page.reload();
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
});
});
Loading
Loading