Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ const MessageToolbar = ({
</ToolHint>

<ToolHint text="Generate sample" toolhintId={`regenerate-msg-${index}`}>
<button onClick={onRegenerateMessage} className="toolbar-btn">
<button
onClick={onRegenerateMessage}
className="toolbar-btn"
data-testid={`grpc-regenerate-message-${index}`}
>
<IconRefresh size={16} strokeWidth={1.5} />
</button>
</ToolHint>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation, initi
<div
className="dropdown-item"
key="show-file-format"
data-testid="show-file-format-toggle"
onClick={(e) => {
dropdownTippyRef.current.hide();
setShowFileFormat(!showFileFormat);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
import type { GrpcRequest, GrpcMetadata } from '@opencollection/types/requests/grpc';
import type { GrpcRequest, GrpcMetadata, GrpcMessageVariant } from '@opencollection/types/requests/grpc';
import type { KeyValue as BrunoKeyValue } from '@usebruno/schema-types/common/key-value';
import { toBrunoAuth } from '../common/auth';
import { toBrunoVariables } from '../common/variables';
import { toBrunoScripts } from '../common/scripts';
import { toBrunoAssertions } from '../common/assertions';
import { isNonEmptyString, uuid, ensureString } from '../../../utils';
import { uuid, ensureString, isNonEmptyString } from '../../../utils';

const toBrunoGrpcMetadata = (metadata: GrpcMetadata[] | null | undefined): BrunoKeyValue[] | undefined => {
if (!metadata?.length) {
Expand Down Expand Up @@ -57,10 +57,16 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
};

// message
if (isNonEmptyString(grpc?.message)) {
const rawMessage = grpc?.message;
if (Array.isArray(rawMessage)) {
brunoRequest.body.grpc = (rawMessage as GrpcMessageVariant[]).map(({ title, message }, index) => ({
name: title || `message ${index + 1}`,
content: ensureString(message)
}));
} else if (isNonEmptyString(rawMessage)) {
brunoRequest.body.grpc = [{
name: '',
content: grpc?.message as string
content: rawMessage as string
}];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Item as BrunoItem } from '@usebruno/schema-types/collection/item';
import type { KeyValue as BrunoKeyValue } from '@usebruno/schema-types/common/key-value';
import type { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
import type { GrpcRequest, GrpcMetadata, GrpcMessage, GrpcRequestInfo, GrpcRequestDetails, GrpcRequestRuntime } from '@opencollection/types/requests/grpc';
import type { GrpcRequest, GrpcMetadata, GrpcMessageVariant, GrpcRequestInfo, GrpcRequestDetails, GrpcRequestRuntime } from '@opencollection/types/requests/grpc';
import type { Auth } from '@opencollection/types/common/auth';
import type { Scripts } from '@opencollection/types/common/scripts';
import type { Variable } from '@opencollection/types/common/variables';
Expand Down Expand Up @@ -73,16 +73,11 @@ const stringifyGrpcRequest = (item: BrunoItem): string => {

// message
if (brunoRequest.body?.mode === 'grpc' && brunoRequest.body.grpc?.length) {
const messages = brunoRequest.body.grpc;

// todo: bruno app supports only one message for now
// update this when bruno app supports multiple messages
if (messages.length) {
const message: GrpcMessage = messages[0].content || '';
if (message.trim().length) {
grpc.message = message;
}
}
const messages: GrpcMessageVariant[] = brunoRequest.body.grpc.map(({ name, content }, index) => ({
title: name || `message ${index + 1}`,
message: content || ''
}));
grpc.message = messages;
}

// auth
Expand Down
70 changes: 70 additions & 0 deletions tests/grpc/multi-message-yml/multi-message.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import fs from 'node:fs';
import path from 'node:path';
import yaml from 'js-yaml';
import { bruToJsonV2 } from '@usebruno/lang';
import { expect, test } from '../../../playwright';
import {
addGrpcMessage,
createCollection,
createRequest,
generateGrpcSampleMessage,
REQUEST_TYPE,
saveRequest,
selectGrpcMethod
} from '../../utils/page/actions';

const REQUEST_NAME = 'grpc-multi-msg';
const GRPC_URL = 'grpcb.in:9000';
const GRPC_METHOD = 'BidiHello';

type GrpcRequestYml = {
grpc?: {
message?: { title: string; message: string }[];
};
};

const FORMATS = [
{ format: 'yml', collectionName: 'grpc-yml-multi-msg', tmpDirPrefix: 'grpc-yml-collection' },
{ format: 'bru', collectionName: 'grpc-bru-multi-msg', tmpDirPrefix: 'grpc-bru-collection' }
] as const;

for (const { format, collectionName, tmpDirPrefix } of FORMATS) {
test.describe.serial(`grpc multi-message (${format} format)`, () => {
let collectionPath: string;

test('creates a gRPC request with multiple messages and saves it', async ({ page, createTmpDir }) => {
collectionPath = await createTmpDir(tmpDirPrefix);

await createCollection(page, collectionName, collectionPath, { format });
await createRequest(page, REQUEST_NAME, collectionName, { url: GRPC_URL, requestType: REQUEST_TYPE.GRPC });

await selectGrpcMethod(page, GRPC_METHOD);

await addGrpcMessage(page);
await addGrpcMessage(page);

await generateGrpcSampleMessage(page, 0);
await generateGrpcSampleMessage(page, 1);
await generateGrpcSampleMessage(page, 2);

await saveRequest(page);
});

test(`verifies all messages are saved in the request .${format} file`, async () => {
const requestFilePath = path.join(collectionPath, collectionName, `${REQUEST_NAME}.${format}`);
expect(fs.existsSync(requestFilePath)).toBe(true);

Check failure on line 55 in tests/grpc/multi-message-yml/multi-message.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests

[default] › tests/grpc/multi-message-yml/multi-message.spec.ts:53:9 › grpc multi-message (yml format) › verifies all messages are saved in the request .yml file

1) [default] › tests/grpc/multi-message-yml/multi-message.spec.ts:53:9 › grpc multi-message (yml format) › verifies all messages are saved in the request .yml file Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false 53 | test(`verifies all messages are saved in the request .${format} file`, async () => { 54 | const requestFilePath = path.join(collectionPath, collectionName, `${REQUEST_NAME}.${format}`); > 55 | expect(fs.existsSync(requestFilePath)).toBe(true); | ^ 56 | 57 | const fileContent = fs.readFileSync(requestFilePath, 'utf8'); 58 | at /home/runner/work/bruno/bruno/tests/grpc/multi-message-yml/multi-message.spec.ts:55:46

const fileContent = fs.readFileSync(requestFilePath, 'utf8');

if (format === 'yml') {
const parsed = yaml.load(fileContent) as GrpcRequestYml;
const messages = parsed.grpc?.message ?? [];
expect(messages.length).toBe(3);
} else if (format === 'bru') {
const parsed = bruToJsonV2(fileContent) as { body?: { grpc?: { name: string; content: string }[] } };
const messages = parsed.body?.grpc ?? [];
expect(messages.length).toBe(3);
}
});
});
}
102 changes: 93 additions & 9 deletions tests/utils/page/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

for (let i = 0; i < numberOfCollections; i++) {
const firstCollection = page.locator('[data-testid="collections"] .collection-name').first();
await firstCollection.hover();

Check failure on line 18 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests

[default] › tests/import/bulk-import/003-selection-list-viewport.spec.ts:32:7 › Bulk Import Selection List › shows the configured number of visible rows and reveals later rows when scrolled

2) [default] › tests/import/bulk-import/003-selection-list-viewport.spec.ts:32:7 › Bulk Import Selection List › shows the configured number of visible rows and reveals later rows when scrolled TimeoutError: locator.hover: Timeout 30000ms exceeded. Call log: - waiting for locator('[data-testid="collections"] .collection-name').first() - locator resolved to <div tabindex="-1" draggable="true" data-tabindex="0" data-testid="sidebar-collection-row" class="flex py-1 collection-name items-center">…</div> - attempting hover action 2 × waiting for element to be visible and stable - element is visible and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper-djDxET blDHtP">…</div> subtree intercepts pointer events - retrying hover action - waiting 20ms 2 × waiting for element to be visible and stable - element is visible and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper-djDxET blDHtP">…</div> subtree intercepts pointer events - retrying hover action - waiting 100ms 58 × waiting for element to be visible and stable - element is visible and stable - scrolling into view if needed - done scrolling - <div class="bruno-modal-backdrop"></div> from <div class="StyledWrapper-djDxET blDHtP">…</div> subtree intercepts pointer events - retrying hover action - waiting 500ms at tests/utils/page/actions.ts:18 16 | for (let i = 0; i < numberOfCollections; i++) { 17 | const firstCollection = page.locator('[data-testid="collections"] .collection-name').first(); > 18 | await firstCollection.hover(); | ^ 19 | await firstCollection.locator('.collection-actions .icon').click(); 20 | await page.locator('.dropdown-item').getByText('Remove').click(); 21 | at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:18:29 at closeAllCollections (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:13:3) at /home/runner/work/bruno/bruno/tests/import/bulk-import/003-selection-list-viewport.spec.ts:29:5
await firstCollection.locator('.collection-actions .icon').click();

Check failure on line 19 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Detect Flaky Tests

[default] › tests/graphql/query-builder/query-builder.spec.ts:235:7 › GraphQL Query Builder › Changing variable value in variables editor updates argument in query builder

1) [default] › tests/graphql/query-builder/query-builder.spec.ts:235:7 › GraphQL Query Builder › Changing variable value in variables editor updates argument in query builder TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for locator('[data-testid="collections"] .collection-name').first().locator('.collection-actions .icon') - locator resolved to <svg width="18" height="18" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-dots">…</svg> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is not visible - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is not visible - retrying click action - waiting 100ms 58 × waiting for element to be visible, enabled and stable - element is not visible - retrying click action - waiting 500ms at tests/utils/page/actions.ts:19 17 | const firstCollection = page.locator('[data-testid="collections"] .collection-name').first(); 18 | await firstCollection.hover(); > 19 | await firstCollection.locator('.collection-actions .icon').click(); | ^ 20 | await page.locator('.dropdown-item').getByText('Remove').click(); 21 | 22 | // Wait for modal to appear - could be either regular remove or drafts confirmation at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:19:66 at closeAllCollections (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:13:3) at /home/runner/work/bruno/bruno/tests/graphql/query-builder/query-builder.spec.ts:28:5
await page.locator('.dropdown-item').getByText('Remove').click();

// Wait for modal to appear - could be either regular remove or drafts confirmation
Expand All @@ -28,7 +28,7 @@

if (hasDiscardButton) {
// Drafts modal - click "Discard All and Remove"
await page.getByRole('button', { name: 'Discard All and Remove' }).click();

Check failure on line 31 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests

[default] › tests/request/response-pane-update-when-focused.spec.ts:30:7 › Response pane updates when focused and request is re-sent › Response pane shows new response after re-send with Cmd+Enter while focused in response

6) [default] › tests/request/response-pane-update-when-focused.spec.ts:30:7 › Response pane updates when focused and request is re-sent › Response pane shows new response after re-send with Cmd+Enter while focused in response TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Discard All and Remove' }) - locator resolved to <button type="button">…</button> - attempting click action 2 × waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 20ms 2 × waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 100ms - waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting 500ms - waiting for element to be visible, enabled and stable - element was detached from the DOM, retrying at tests/utils/page/actions.ts:31 29 | if (hasDiscardButton) { 30 | // Drafts modal - click "Discard All and Remove" > 31 | await page.getByRole('button', { name: 'Discard All and Remove' }).click(); | ^ 32 | } else { 33 | // Regular modal - click the submit button 34 | await page.locator('.bruno-modal-footer .submit').click(); at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:31:76 at closeAllCollections (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:13:3) at /home/runner/work/bruno/bruno/tests/request/response-pane-update-when-focused.spec.ts:27:5
} else {
// Regular modal - click the submit button
await page.locator('.bruno-modal-footer .submit').click();
Expand All @@ -55,17 +55,30 @@
});
};

type CollectionFormat = 'yml' | 'bru';

type CreateCollectionOptions = {
format?: CollectionFormat;
};

/**
* Create a collection
* @param page - The page object
* @param collectionName - The name of the collection to create
* @param collectionLocation - The location of the collection to create (eg)
* @param options - The options for creating the collection
* @param options - Optional settings (format: 'yml' (default) or 'bru')
*
* @returns void
*/
const createCollection = async (page, collectionName: string, collectionLocation: string) => {
await test.step(`Create collection "${collectionName}"`, async () => {
const createCollection = async (
page,
collectionName: string,
collectionLocation: string,
options: CreateCollectionOptions = {}
) => {
const { format = 'yml' } = options;

await test.step(`Create ${format} collection "${collectionName}"`, async () => {
await page.getByTestId('collections-header-add-menu').click();
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click();

Expand Down Expand Up @@ -93,9 +106,18 @@
await nameInput.fill(collectionName);
// Verify the name is correct before creating
await expect(nameInput).toHaveValue(collectionName, { timeout: 2000 });

// The File Format dropdown is hidden by default. Open the Advanced Options menu
// and toggle "Show File Format" before selecting a non-default format.
if (format === 'bru') {
await createCollectionModal.locator('.advanced-options .btn-advanced').click();
await page.getByTestId('show-file-format-toggle').click();
await createCollectionModal.locator('select#format').selectOption(format);
}

Comment thread
sharan-bruno marked this conversation as resolved.
await createCollectionModal.getByRole('button', { name: 'Create', exact: true }).click();

await createCollectionModal.waitFor({ state: 'detached', timeout: 15000 });

Check failure on line 120 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Playwright E2E Tests

[default] › tests/shortcuts/bound-actions.spec.ts:1692:9 › Shortcut Keys - BOUND_ACTIONS › SHORTCUT: Edit Environment › open environment tab of collection Cmd/Ctrl+E

7) [default] › tests/shortcuts/bound-actions.spec.ts:1692:9 › Shortcut Keys - BOUND_ACTIONS › SHORTCUT: Edit Environment › open environment tab of collection Cmd/Ctrl+E TimeoutError: locator.waitFor: Timeout 15000ms exceeded. Call log: - waiting for locator('.bruno-modal-card').filter({ hasText: 'Create Collection' }) to be detached 35 × locator resolved to visible <div role="dialog" aria-labelledby="modal-title" class="bruno-modal-card modal-md" aria-describedby="modal-description">…</div> at tests/utils/page/actions.ts:120 118 | await createCollectionModal.getByRole('button', { name: 'Create', exact: true }).click(); 119 | > 120 | await createCollectionModal.waitFor({ state: 'detached', timeout: 15000 }); | ^ 121 | // Wait for the collection name to appear in the sidebar before proceeding 122 | await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).waitFor({ state: 'visible', timeout: 5000 }); 123 | await openCollection(page, collectionName); at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:120:33 at createCollection (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:81:3) at setupBoundActionsData (/home/runner/work/bruno/bruno/tests/shortcuts/bound-actions.spec.ts:19:3) at /home/runner/work/bruno/bruno/tests/shortcuts/bound-actions.spec.ts:142:5

Check failure on line 120 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / Detect Flaky Tests

[default] › tests/workspace/close-tab-stays-in-workspace.spec.ts:23:7 › Close tab stays in workspace › after closing last request tab in WorkspaceB

6) [default] › tests/workspace/close-tab-stays-in-workspace.spec.ts:23:7 › Close tab stays in workspace › after closing last request tab in WorkspaceB, active tab is not from WorkspaceA and workspace stays WorkspaceB › Create ColB/ReqB in WorkspaceB and open ReqB › Create yml collection "ColB" TimeoutError: locator.waitFor: Timeout 15000ms exceeded. Call log: - waiting for locator('.bruno-modal-card').filter({ hasText: 'Create Collection' }) to be detached 35 × locator resolved to visible <div role="dialog" aria-labelledby="modal-title" class="bruno-modal-card modal-md" aria-describedby="modal-description">…</div> at tests/utils/page/actions.ts:120 118 | await createCollectionModal.getByRole('button', { name: 'Create', exact: true }).click(); 119 | > 120 | await createCollectionModal.waitFor({ state: 'detached', timeout: 15000 }); | ^ 121 | // Wait for the collection name to appear in the sidebar before proceeding 122 | await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).waitFor({ state: 'visible', timeout: 5000 }); 123 | await openCollection(page, collectionName); at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:120:33 at createCollection (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:81:3) at /home/runner/work/bruno/bruno/tests/workspace/close-tab-stays-in-workspace.spec.ts:63:9 at /home/runner/work/bruno/bruno/tests/workspace/close-tab-stays-in-workspace.spec.ts:62:7
// Wait for the collection name to appear in the sidebar before proceeding
await page.locator('#sidebar-collection-name').filter({ hasText: collectionName }).waitFor({ state: 'visible', timeout: 5000 });
await openCollection(page, collectionName);
Expand All @@ -104,14 +126,31 @@

const STANDARD_HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'TRACE', 'CONNECT'];

export const REQUEST_TYPE = Object.freeze({
HTTP: 'HTTP',
GRAPHQL: 'GraphQL',
GRPC: 'gRPC',
WEBSOCKET: 'WebSocket'
} as const);

export type RequestType = typeof REQUEST_TYPE[keyof typeof REQUEST_TYPE];

type CreateRequestOptions = {
url?: string;
method?: string;
inFolder?: boolean;
requestType?: RequestType;
};

const REQUEST_TYPE_TESTID = Object.freeze({
[REQUEST_TYPE.HTTP]: 'http-request',
[REQUEST_TYPE.GRAPHQL]: 'graphql-request',
[REQUEST_TYPE.GRPC]: 'grpc-request',
[REQUEST_TYPE.WEBSOCKET]: 'ws-request'
} as const);

type CreateUntitledRequestOptions = {
requestType?: 'HTTP' | 'GraphQL' | 'WebSocket' | 'gRPC';
requestType?: RequestType;
requestName?: string;
url?: string;
tag?: string;
Expand Down Expand Up @@ -174,7 +213,7 @@
};

type CreateTransientRequestOptions = {
requestType?: 'HTTP' | 'GraphQL' | 'gRPC' | 'WebSocket';
requestType?: RequestType;
};

/**
Expand Down Expand Up @@ -258,10 +297,10 @@
parentName: string,
options: CreateRequestOptions = {}
) => {
const { url, method, inFolder = false } = options;
const { url, method, inFolder = false, requestType = 'HTTP' } = options;
const parentType = inFolder ? 'folder' : 'collection';

await test.step(`Create request "${requestName}" in ${parentType} "${parentName}"`, async () => {
await test.step(`Create ${requestType} request "${requestName}" in ${parentType} "${parentName}"`, async () => {
const locators = buildCommonLocators(page);

if (inFolder) {
Expand All @@ -275,9 +314,14 @@
}

await locators.dropdown.item('New Request').click();

if (requestType !== REQUEST_TYPE.HTTP) {
await page.getByTestId(REQUEST_TYPE_TESTID[requestType]).click();
}

await page.getByPlaceholder('Request Name').fill(requestName);

if (method) {
if (method && requestType === 'HTTP') {
await page.locator('.bruno-modal .method-selector').click();
const isStandardMethod = STANDARD_HTTP_METHODS.includes(method.toUpperCase());
if (isStandardMethod) {
Expand Down Expand Up @@ -822,7 +866,7 @@

// Check if tab is directly visible
if (await visibleTab.isVisible()) {
await visibleTab.click();

Check failure on line 869 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / OAuth 1.0 Auth Tests - Linux

[auth] › tests/auth/oauth1/oauth1-runner.spec.ts:179:9 › OAuth 1.0 Runner › [yml] › Verify Add Params To placement via timeline

2) [auth] › tests/auth/oauth1/oauth1-runner.spec.ts:179:9 › OAuth 1.0 Runner › [yml] › Verify Add Params To placement via timeline › Body: HMAC-SHA1 JSON (non-form body) › Select tab "Timeline" in [data-testid="response-pane"] TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) - locator resolved to <div role="tab" aria-selected="false" class="tab select-none timeline" data-testid="responsive-tab-timeline">Timeline</div> - attempting click action - waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting for element to be visible, enabled and stable - element was detached from the DOM, retrying at tests/utils/page/actions.ts:869 867 | // Check if tab is directly visible 868 | if (await visibleTab.isVisible()) { > 869 | await visibleTab.click(); | ^ 870 | await expect(visibleTab).toContainClass('active'); 871 | return; 872 | } at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:869:24 at selectPaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:860:3) at selectResponsePaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:894:3) at openTimelineRequest (/home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:78:3) at verifyPlacement (/home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:91:24) at /home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:217:9 at /home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:216:7

Check failure on line 869 in tests/utils/page/actions.ts

View workflow job for this annotation

GitHub Actions / OAuth 1.0 Auth Tests - Linux

[auth] › tests/auth/oauth1/oauth1-runner.spec.ts:125:9 › OAuth 1.0 Runner › [bru] › Verify Add Params To placement via timeline

1) [auth] › tests/auth/oauth1/oauth1-runner.spec.ts:125:9 › OAuth 1.0 Runner › [bru] › Verify Add Params To placement via timeline › Body: HMAC-SHA1 › Select tab "Timeline" in [data-testid="response-pane"] TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for locator('[data-testid="response-pane"]').locator('.tabs').getByRole('tab', { name: 'Timeline' }) - locator resolved to <div role="tab" aria-selected="false" class="tab select-none timeline" data-testid="responsive-tab-timeline">Timeline</div> - attempting click action - waiting for element to be visible, enabled and stable - element is not stable - retrying click action - waiting for element to be visible, enabled and stable - element was detached from the DOM, retrying at tests/utils/page/actions.ts:869 867 | // Check if tab is directly visible 868 | if (await visibleTab.isVisible()) { > 869 | await visibleTab.click(); | ^ 870 | await expect(visibleTab).toContainClass('active'); 871 | return; 872 | } at /home/runner/work/bruno/bruno/tests/utils/page/actions.ts:869:24 at selectPaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:860:3) at selectResponsePaneTab (/home/runner/work/bruno/bruno/tests/utils/page/actions.ts:894:3) at openTimelineRequest (/home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:78:3) at verifyPlacement (/home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:91:24) at /home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:147:9 at /home/runner/work/bruno/bruno/tests/auth/oauth1/oauth1-runner.spec.ts:146:7
await expect(visibleTab).toContainClass('active');
return;
}
Expand Down Expand Up @@ -1029,6 +1073,7 @@
*/
const saveRequest = async (page: Page) => {
await test.step('Save request', async () => {
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharan-bruno do we really need this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we need this line, without that the playwrite test cases are failing

const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
await expect(page.getByText('Request saved successfully').last()).toBeVisible({ timeout: 3000 });
Expand Down Expand Up @@ -1198,6 +1243,42 @@
});
};

/**
* Click the gRPC "Add Message" button to append a new message to the request
* @param page - The page object
*/
const addGrpcMessage = async (page: Page) => {
await test.step('Add gRPC message', async () => {
await page.getByTestId('grpc-add-message-button').click();
});
};

/**
* Click the "Generate sample" button on a gRPC message to populate it with a sample payload
* @param page - The page object
* @param index - The 0-based index of the message (default: 0)
*/
const generateGrpcSampleMessage = async (page: Page, index: number = 0) => {
await test.step(`Generate sample for gRPC message #${index}`, async () => {
await page.getByTestId(`grpc-regenerate-message-${index}`).click();
});
};
Comment thread
sharan-bruno marked this conversation as resolved.

/**
* Open the gRPC method dropdown and select a method by name
* @param page - The page object
* @param methodName - The name of the gRPC method to select (e.g. "BidiHello")
*/
const selectGrpcMethod = async (page: Page, methodName: string) => {
await test.step(`Select gRPC method "${methodName}"`, async () => {
await page.getByTestId('grpc-method-dropdown-trigger').click();
const dropdown = page.getByTestId('grpc-methods-dropdown');
await dropdown.waitFor({ state: 'visible', timeout: 5000 });
await dropdown.getByTestId('grpc-method-item').filter({ hasText: methodName }).first().click();
await expect(page.getByTestId('selected-grpc-method-name')).toContainText(methodName);
});
};

export {
closeAllCollections,
openCollection,
Expand Down Expand Up @@ -1243,7 +1324,10 @@
addPostResponseScript,
addTestScript,
sendAndWaitForErrorCard,
sendAndWaitForResponse
sendAndWaitForResponse,
selectGrpcMethod,
addGrpcMessage,
generateGrpcSampleMessage
};

export type { SandboxMode, EnvironmentType, EnvironmentVariable, ImportCollectionOptions, CreateRequestOptions, CreateUntitledRequestOptions, CreateTransientRequestOptions, AssertionInput };
Loading