Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/feat-wspage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@usebruno/api-docs": minor
---

WebSocket requests now render as a read-only docs page instead of "Preview not available". The page shows the connection URL, the request docs, every message with its name and format (JSON and XML are pretty-printed), headers including inherited ones, and auth including inherited auth.
21 changes: 0 additions & 21 deletions packages/bruno-api-docs/e2e/pages/unsupported-request.page.ts

This file was deleted.

28 changes: 28 additions & 0 deletions packages/bruno-api-docs/e2e/pages/websocket-request.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './base.page';
import { SidebarComponent } from '../components/sidebar.component';
import { BreadcrumbComponent } from '../components/breadcrumb.component';
import { RequestUrlBarComponent } from '../components/request/url-bar.component';
import { GrpcMessagesComponent } from '../components/request/grpc-messages.component';

export class WebsocketRequestPage extends BasePage {
readonly sidebar = new SidebarComponent(this.page);
readonly breadcrumb = new BreadcrumbComponent(this.page, 'request-breadcrumb');
readonly urlBar = new RequestUrlBarComponent(this.page);
readonly messages = new GrpcMessagesComponent(this.page, 'websocket-request-messages');

readonly root: Locator = this.page.getByTestId('websocket-request-page');
readonly title: Locator = this.page.getByTestId('request-title');
readonly description: Locator = this.page.getByTestId('request-description');

readonly messagesSection: Locator = this.page.getByTestId('request-section-messages');
readonly headersSection: Locator = this.page.getByTestId('request-section-headers');
readonly authSection: Locator = this.page.getByTestId('request-section-auth');
readonly authInheritedBadge: Locator = this.page.getByTestId('request-auth-inherited');

async open(path: string[]): Promise<void> {
await this.navigate('/');
await this.sidebar.open(path);
await this.root.waitFor({ state: 'visible' });
}
}
10 changes: 5 additions & 5 deletions packages/bruno-api-docs/e2e/playwright/pages.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { RequestPage } from '../pages/request.page';
import { GraphqlRequestPage } from '../pages/graphql-request.page';
import { ScriptPage } from '../pages/script.page';
import { FolderPage } from '../pages/folder.page';
import { UnsupportedRequestPage } from '../pages/unsupported-request.page';
import { GrpcRequestPage } from '../pages/grpc-request.page';
import { WebsocketRequestPage } from '../pages/websocket-request.page';
import { SidebarComponent } from '../components/sidebar.component';
import { TooltipComponent } from '../components/tooltip.component';
import { PlaygroundComponent } from '../components/playground.component';
Expand All @@ -27,8 +27,8 @@ type Fixtures = {
graphqlRequestPage: GraphqlRequestPage;
scriptPage: ScriptPage;
folderPage: FolderPage;
unsupportedRequestPage: UnsupportedRequestPage;
grpcRequestPage: GrpcRequestPage;
websocketRequestPage: WebsocketRequestPage;
sidebar: SidebarComponent;
tooltip: TooltipComponent;
playground: PlaygroundComponent;
Expand Down Expand Up @@ -62,12 +62,12 @@ export const test = base.extend<Fixtures>({
folderPage: async ({ page }, use) => {
await use(new FolderPage(page));
},
unsupportedRequestPage: async ({ page }, use) => {
await use(new UnsupportedRequestPage(page));
},
grpcRequestPage: async ({ page }, use) => {
await use(new GrpcRequestPage(page));
},
websocketRequestPage: async ({ page }, use) => {
await use(new WebsocketRequestPage(page));
},
sidebar: async ({ page }, use) => {
await use(new SidebarComponent(page));
},
Expand Down

This file was deleted.

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

const LIVE_UPDATES = ['Realtime', 'Live Updates'];

test.describe('Request page — WebSocket requests', () => {
test('renders the request identity without offering to run it', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);

await expect(websocketRequestPage.title).toHaveText('Live Updates');
await expect(websocketRequestPage.breadcrumb.segment('Realtime')).toBeVisible();
await expect(websocketRequestPage.breadcrumb.current).toHaveText('Live Updates');
await expect(websocketRequestPage.urlBar.method).toHaveText('WS');
await expect(websocketRequestPage.urlBar.url).toContainText('/ws/updates');
await expect(websocketRequestPage.urlBar.tryButton).toHaveCount(0);
});

test('renders the request docs', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);

await expect(websocketRequestPage.description).toContainText('full-duplex communication channel');
});

test('lists every message with its name and format, the first one open', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);
const { messages } = websocketRequestPage;

await expect(websocketRequestPage.messagesSection).toContainText('5 messages');
await expect(messages.card(0)).toContainText('Subscribe');
await expect(messages.card(0)).toContainText('JSON');
await expect(messages.card(1)).toContainText('Ping');
await expect(messages.card(1)).toContainText('Text');

await expect(messages.toggle(0)).toHaveAttribute('aria-expanded', 'true');
await expect(messages.toggle(1)).toHaveAttribute('aria-expanded', 'false');
await expect(messages.code(1)).toHaveCount(0);
});

test('pretty-prints a JSON message stored on one line', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);

// The sample stores Subscribe as a single line; each key lands on its own line once formatted.
await expect(websocketRequestPage.messages.code(0).locator('.code-line-numbers span')).toHaveCount(4);
});

test('opens a collapsed message on click', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);
const { messages } = websocketRequestPage;

await messages.expand(1);

await expect(messages.toggle(1)).toHaveAttribute('aria-expanded', 'true');
await expect(messages.code(1)).toContainText('ping');
});

test('shows the headers and the auth it inherits', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);

await expect(websocketRequestPage.headersSection).toContainText('Accept');
await expect(websocketRequestPage.headersSection).toContainText('X-Trace-Id');
await expect(websocketRequestPage.authSection).toBeVisible();
await expect(websocketRequestPage.authInheritedBadge).toBeVisible();
});

test('shows only the first three messages until show more is used', async ({ websocketRequestPage }) => {
await websocketRequestPage.open(LIVE_UPDATES);
const { messages } = websocketRequestPage;

await expect(messages.card(2)).toBeVisible();
await expect(messages.card(3)).toHaveCount(0);
await expect(messages.showToggle).toHaveText('Show more');

await messages.showToggle.click();

await expect(messages.card(4)).toBeVisible();
await expect(messages.showToggle).toHaveText('Show less');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,11 @@ test.describe('Section navigator (the "on this page" outline)', () => {
await expect(page.getByTestId('section-nav-rail')).toBeHidden();
});

test('appears on an unsupported-request page that has documentation headings', async ({
unsupportedRequestPage,
test('appears on a websocket request page that has documentation headings', async ({
websocketRequestPage,
page
}) => {
await unsupportedRequestPage.open(['Realtime', 'Live Updates']);
await websocketRequestPage.open(['Realtime', 'Live Updates']);
await expect(page.getByTestId('section-nav-rail')).toBeVisible();
});

Expand Down
Loading
Loading