mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
* feat: ws multi message * fix * fix * fix * improve: UX * improve: new message ui * fix * fix * fix * fix * fix * fix: rename message title * chore: cleanup * change: add message color * fix(websocket): correct cursor and truncate long message names --------- Co-authored-by: Sid <siddharth@usebruno.com>
257 lines
10 KiB
TypeScript
257 lines
10 KiB
TypeScript
import { expect, test } from '../../../playwright';
|
|
import { buildWebsocketCommonLocators } from '../../utils/page/locators';
|
|
import { openRequest, saveRequest, closeAllCollections } from '../../utils/page/actions';
|
|
import { readFile, writeFile } from 'fs/promises';
|
|
import { join } from 'path';
|
|
|
|
const COLLECTION_NAME = 'ws-multi-message';
|
|
const MULTI_MSG_REQ = 'ws-multi-msg';
|
|
const SINGLE_MSG_REQ = 'ws-single-msg';
|
|
const MULTI_MSG_BRU_PATH = join(__dirname, 'fixtures/collection/ws-multi-msg.bru');
|
|
const SINGLE_MSG_BRU_PATH = join(__dirname, 'fixtures/collection/ws-single-msg.bru');
|
|
const MAX_CONNECTION_TIME = 3000;
|
|
|
|
test.describe('websocket multi-message (bru format)', () => {
|
|
let originalMultiMsgData = '';
|
|
let originalSingleMsgData = '';
|
|
|
|
test.beforeAll(async () => {
|
|
originalMultiMsgData = await readFile(MULTI_MSG_BRU_PATH, 'utf8');
|
|
originalSingleMsgData = await readFile(SINGLE_MSG_BRU_PATH, 'utf8');
|
|
});
|
|
|
|
test.afterEach(async () => {
|
|
await writeFile(MULTI_MSG_BRU_PATH, originalMultiMsgData, 'utf8');
|
|
await writeFile(SINGLE_MSG_BRU_PATH, originalSingleMsgData, 'utf8');
|
|
});
|
|
|
|
test.afterAll(async ({ pageWithUserData: page }) => {
|
|
await closeAllCollections(page);
|
|
});
|
|
|
|
test('add a new message and save', async ({ pageWithUserData: page }) => {
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
await page.getByTestId('ws-add-message').click();
|
|
|
|
const nameInput = page.getByTestId(/^ws-message-name-input-/);
|
|
await expect(nameInput).toBeVisible();
|
|
|
|
await nameInput.selectText();
|
|
await page.keyboard.type('ping message');
|
|
await nameInput.press('Enter');
|
|
|
|
await expect(page.getByTestId(/^ws-message-label-/).filter({ hasText: 'ping message' })).toBeVisible();
|
|
await expect(page.getByTestId(/^ws-message-header-/)).toHaveCount(3);
|
|
|
|
await saveRequest(page);
|
|
|
|
const bruContent = await readFile(MULTI_MSG_BRU_PATH, 'utf8');
|
|
expect(bruContent).toContain('name: ping message');
|
|
});
|
|
|
|
test('edit message content and verify persistence', async ({ pageWithUserData: page }) => {
|
|
const selectAllShortcut = process.platform === 'darwin' ? 'Meta+a' : 'Control+a';
|
|
|
|
await openRequest(page, COLLECTION_NAME, SINGLE_MSG_REQ);
|
|
|
|
// Expand the first message if not already expanded
|
|
const editorBody = page.getByTestId('ws-message-body-0');
|
|
if (!(await editorBody.isVisible())) {
|
|
await page.getByTestId('ws-message-header-0').click();
|
|
}
|
|
const editor = editorBody.locator('.CodeMirror');
|
|
await editor.click();
|
|
const textarea = editor.locator('textarea');
|
|
await textarea.focus();
|
|
await page.keyboard.press(selectAllShortcut);
|
|
await page.keyboard.insertText('{"updated": "content"}');
|
|
|
|
await saveRequest(page);
|
|
|
|
const bruContent = await readFile(SINGLE_MSG_BRU_PATH, 'utf8');
|
|
expect(bruContent).toContain('{"updated": "content"}');
|
|
});
|
|
|
|
test('messages with different types persist correctly', async ({ pageWithUserData: page }) => {
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
const firstHeader = page.getByTestId('ws-message-header-0');
|
|
await expect(firstHeader.locator('.selected-body-mode')).toContainText('JSON');
|
|
|
|
const secondHeader = page.getByTestId('ws-message-header-1');
|
|
await expect(secondHeader.locator('.selected-body-mode')).toContainText('TEXT');
|
|
|
|
// Change message 1 type from json to xml
|
|
await firstHeader.locator('.body-mode-selector').click();
|
|
await page.locator('.dropdown-item').filter({ hasText: 'XML' }).click();
|
|
|
|
await expect(firstHeader.locator('.selected-body-mode')).toContainText('XML');
|
|
|
|
await saveRequest(page);
|
|
|
|
const bruContent = await readFile(MULTI_MSG_BRU_PATH, 'utf8');
|
|
expect(bruContent).toContain('type: xml');
|
|
expect(bruContent).toContain('type: text');
|
|
|
|
// Re-open to verify persistence
|
|
await openRequest(page, COLLECTION_NAME, SINGLE_MSG_REQ);
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
await expect(page.getByTestId('ws-message-header-0').locator('.selected-body-mode')).toContainText('XML');
|
|
await expect(page.getByTestId('ws-message-header-1').locator('.selected-body-mode')).toContainText('TEXT');
|
|
});
|
|
|
|
test('send selected message to active connection', async ({ pageWithUserData: page }) => {
|
|
const locators = buildWebsocketCommonLocators(page);
|
|
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
await locators.connectionControls.connect().click();
|
|
await expect(locators.connectionControls.disconnect()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
|
|
const messageItems = locators.messages().locator('.text-ellipsis');
|
|
const beforeCount = await messageItems.count();
|
|
|
|
// Click the main send button — sends the currently selected message
|
|
await page.getByTestId('run-button').click();
|
|
|
|
// Expect at least one new message (outgoing + echo response from server)
|
|
await expect.poll(() => messageItems.count(), { timeout: MAX_CONNECTION_TIME }).toBeGreaterThan(beforeCount);
|
|
|
|
await locators.connectionControls.disconnect().click();
|
|
await expect(locators.connectionControls.connect()).toBeVisible();
|
|
});
|
|
|
|
test('first message is implicitly selected when no message is marked selected', async ({ pageWithUserData: page }) => {
|
|
const locators = buildWebsocketCommonLocators(page);
|
|
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
// ws-multi-msg.bru has two messages with no `selected: true` flag. The
|
|
// main send button should therefore dispatch the first message.
|
|
await locators.connectionControls.connect().click();
|
|
await expect(locators.connectionControls.disconnect()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
|
|
await page.getByTestId('run-button').click();
|
|
|
|
// the first message's content ("subscribe"), and none should carry the
|
|
// second message's content ("hello world").
|
|
await expect(locators.messages().filter({ hasText: 'subscribe' }).first()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
await expect(locators.messages().filter({ hasText: 'hello world' })).toHaveCount(0);
|
|
|
|
await locators.connectionControls.disconnect().click();
|
|
});
|
|
|
|
test('selecting a different message routes run-button to that message', async ({ pageWithUserData: page }) => {
|
|
const locators = buildWebsocketCommonLocators(page);
|
|
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
// Select the second message by clicking its header
|
|
await page.getByTestId('ws-message-header-1').click();
|
|
|
|
await locators.connectionControls.connect().click();
|
|
await expect(locators.connectionControls.disconnect()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
|
|
await page.getByTestId('run-button').click();
|
|
|
|
await expect(locators.messages().filter({ hasText: 'hello world' }).first()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
await expect(locators.messages().filter({ hasText: 'subscribe' })).toHaveCount(0);
|
|
|
|
await locators.connectionControls.disconnect().click();
|
|
});
|
|
|
|
test('per-message send button sends that specific message', async ({ pageWithUserData: page }) => {
|
|
const locators = buildWebsocketCommonLocators(page);
|
|
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
// Hover the header to reveal hover-actions, then click the second
|
|
await page.getByTestId('ws-message-header-1').hover();
|
|
await page.getByTestId('ws-send-msg-1').click();
|
|
|
|
await expect(locators.connectionControls.disconnect()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
await expect(locators.messages().filter({ hasText: 'hello world' }).first()).toBeAttached({
|
|
timeout: MAX_CONNECTION_TIME
|
|
});
|
|
|
|
await locators.connectionControls.disconnect().click();
|
|
});
|
|
|
|
test('prettify json message content', async ({ pageWithUserData: page }) => {
|
|
const selectAllShortcut = process.platform === 'darwin' ? 'Meta+a' : 'Control+a';
|
|
|
|
await openRequest(page, COLLECTION_NAME, SINGLE_MSG_REQ);
|
|
|
|
// Expand the first message if not already expanded
|
|
const editorBody = page.getByTestId('ws-message-body-0');
|
|
if (!(await editorBody.isVisible())) {
|
|
await page.getByTestId('ws-message-header-0').click();
|
|
}
|
|
const editor = editorBody.locator('.CodeMirror');
|
|
await editor.click();
|
|
const textarea = editor.locator('textarea');
|
|
await textarea.focus();
|
|
await page.keyboard.press(selectAllShortcut);
|
|
await page.keyboard.insertText('{"name":"bruno","version":"1.0"}');
|
|
|
|
await page.getByTestId('ws-prettify-all').click();
|
|
|
|
// Verify prettification split single line into multiple lines
|
|
const lineNumbers = await editor.locator('.CodeMirror-linenumber').count();
|
|
expect(lineNumbers).toBeGreaterThan(1);
|
|
});
|
|
|
|
test('delete a message', async ({ pageWithUserData: page }) => {
|
|
await openRequest(page, COLLECTION_NAME, MULTI_MSG_REQ);
|
|
|
|
await expect(page.getByTestId(/^ws-message-header-/)).toHaveCount(2);
|
|
|
|
// Hover over the message header to reveal the delete button
|
|
await page.getByTestId('ws-message-header-1').hover();
|
|
await page.getByTestId('ws-delete-msg-1').click();
|
|
|
|
await expect(page.getByTestId(/^ws-message-header-/)).toHaveCount(1);
|
|
|
|
await saveRequest(page);
|
|
|
|
const bruContent = await readFile(MULTI_MSG_BRU_PATH, 'utf8');
|
|
const bodyWsCount = (bruContent.match(/body:ws/g) || []).length;
|
|
expect(bodyWsCount).toBe(1);
|
|
});
|
|
|
|
test('rename a message via double-click', async ({ pageWithUserData: page }) => {
|
|
await openRequest(page, COLLECTION_NAME, SINGLE_MSG_REQ);
|
|
|
|
const messageLabel = page.getByTestId('ws-message-label-0');
|
|
await messageLabel.dblclick();
|
|
|
|
const nameInput = page.getByTestId('ws-message-name-input-0');
|
|
await expect(nameInput).toBeVisible();
|
|
|
|
await nameInput.selectText();
|
|
await page.keyboard.type('subscribe request');
|
|
await nameInput.press('Enter');
|
|
|
|
await expect(page.getByTestId('ws-message-label-0').filter({ hasText: 'subscribe request' })).toBeVisible();
|
|
|
|
await saveRequest(page);
|
|
|
|
const bruContent = await readFile(SINGLE_MSG_BRU_PATH, 'utf8');
|
|
expect(bruContent).toContain('name: subscribe request');
|
|
});
|
|
});
|