mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-23 04:35:40 +00:00
fix(tests): flaky bound action e2e tests (#8071)
This commit is contained in:
@@ -127,6 +127,12 @@ const CollectionItem = ({ item, collectionUid, collectionPathname, searchText })
|
||||
return false;
|
||||
}, { enabled: isKeyboardFocused, deps: [isKeyboardFocused] });
|
||||
|
||||
useKeybinding('newRequest', () => {
|
||||
if (!isFolder) return false;
|
||||
setNewRequestModalOpen(true);
|
||||
return false;
|
||||
}, { enabled: isKeyboardFocused && isFolder, deps: [isKeyboardFocused, isFolder] });
|
||||
|
||||
const [dropType, setDropType] = useState(null); // 'adjacent' or 'inside'
|
||||
|
||||
const [{ isDragging }, drag, dragPreview] = useDrag({
|
||||
|
||||
@@ -228,6 +228,11 @@ const Collection = ({ collection, searchText }) => {
|
||||
return false;
|
||||
}, { enabled: isKeyboardFocused, deps: [isKeyboardFocused] });
|
||||
|
||||
useKeybinding('newRequest', () => {
|
||||
setShowNewRequestModal(true);
|
||||
return false;
|
||||
}, { enabled: isKeyboardFocused, deps: [isKeyboardFocused] });
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsKeyboardFocused(true);
|
||||
dispatch(setFocusedSidebarPath(collection.pathname));
|
||||
|
||||
@@ -88,26 +88,6 @@ export const HotkeysProvider = (props) => {
|
||||
};
|
||||
}, [activeTabUid, tabs, collections, dispatch, userKeyBindings, keybindingsEnabled]);
|
||||
|
||||
// new request
|
||||
useEffect(() => {
|
||||
bindAction('newRequest', (e) => {
|
||||
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||
if (activeTab) {
|
||||
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
||||
|
||||
if (collection) {
|
||||
setShowNewRequestModal(true);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // this stops the event bubbling
|
||||
});
|
||||
|
||||
return () => {
|
||||
unbindAction('newRequest');
|
||||
};
|
||||
}, [activeTabUid, tabs, collections, setShowNewRequestModal, userKeyBindings, keybindingsEnabled]);
|
||||
|
||||
// global search
|
||||
useEffect(() => {
|
||||
bindAction('globalSearch', (e) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
173
tests/shortcuts/collections-env.spec.ts
Normal file
173
tests/shortcuts/collections-env.spec.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import {
|
||||
closePreferencesTab,
|
||||
collectionName,
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
openRequest,
|
||||
pressShortcut,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('COLLECTIONS & ENVIRONMENTS', () => {
|
||||
test.describe('SHORTCUT: Import Collection', () => {
|
||||
test('default Cmd/Ctrl+O open import collection modal', async ({ pageWithUserData: page }) => {
|
||||
await pressShortcut(page, modifier, 'KeyO');
|
||||
|
||||
await expect(page.getByTestId('import-collection-modal')).toBeVisible();
|
||||
|
||||
// Close the modal to leave a clean state for subsequent tests
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
await expect(page.getByTestId('import-collection-modal')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('customized Alt+O open import collection modal', async ({ pageWithUserData: page }) => {
|
||||
await pressShortcut(page, modifier, 'KeyW');
|
||||
|
||||
// Remap importCollection to Alt+O
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-importCollection');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-importCollection').click();
|
||||
await expect(page.getByTestId('keybinding-input-importCollection')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyO');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
// Trigger the remapped shortcut
|
||||
await pressShortcut(page, 'Alt', 'KeyO');
|
||||
|
||||
await expect(page.getByTestId('import-collection-modal')).toBeVisible();
|
||||
|
||||
// Close the modal to leave a clean state for subsequent tests
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
await expect(page.getByTestId('import-collection-modal')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Edit Environment (Cmd/Ctrl+E)', () => {
|
||||
test('open environment tab of collection Cmd/Ctrl+E', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, 'kb-collection', 'req-7', { persist: true });
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyE');
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Environments', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
|
||||
test('open environment tab of collection customized Alt+E', async ({ pageWithUserData: page }) => {
|
||||
// Remap editEnvironment to Alt+E
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-editEnvironment');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-editEnvironment').click();
|
||||
await expect(page.getByTestId('keybinding-input-editEnvironment')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyE');
|
||||
|
||||
await openRequest(page, 'kb-collection', 'req-7', { persist: true });
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyE');
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Environments', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: New Request', () => {
|
||||
test('default Cmd/Ctrl+N opens new request modal for collection', async ({ pageWithUserData: page }) => {
|
||||
// Focus the collection so the keybinding is active
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText(collectionName, { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-collection-cenv');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-collection-cenv', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
|
||||
test('default Cmd/Ctrl+N opens new request modal for folder', async ({ pageWithUserData: page }) => {
|
||||
// Focus the folder so the keybinding is active
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-folder-cenv');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-folder-cenv', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
|
||||
test('customized Alt+N opens new request modal for collection', async ({ pageWithUserData: page }) => {
|
||||
// Remap newRequest to Alt+N
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-newRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-newRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-newRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
// Focus the collection so the keybinding is active
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText(collectionName, { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-collection-cenv-altn');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-collection-cenv-altn', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
|
||||
test('customized Alt+N opens new request modal for folder', async ({ pageWithUserData: page }) => {
|
||||
// Remap newRequest to Alt+N
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-newRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-newRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-newRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
// Focus the folder so the keybinding is active
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-folder-cenv-altn');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-folder-cenv-altn', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
95
tests/shortcuts/developer-tool.spec.ts
Normal file
95
tests/shortcuts/developer-tool.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import {
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
pressShortcut,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('DEVELOPER TOOLS', () => {
|
||||
test.describe('SHORTCUT: Open Terminal (Cmd/Ctrl+T)', () => {
|
||||
test('default Cmd/Ctrl+T opens terminal', async ({ pageWithUserData: page }) => {
|
||||
// Open Collection-Settings tab (double-click collection name)
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).click();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible();
|
||||
|
||||
// Press Cmd/Ctrl+T to open terminal at workspace level
|
||||
await pressShortcut(page, modifier, 'KeyT');
|
||||
|
||||
// Verify terminal session is visible using data-testid
|
||||
const collectionTerminalSession = page.getByTestId('session-list-0');
|
||||
await expect(collectionTerminalSession).toBeVisible();
|
||||
|
||||
const collectionSession = collectionTerminalSession;
|
||||
await expect(collectionSession).toContainText('kb-collection');
|
||||
await page.getByTitle('Close console').click();
|
||||
|
||||
// Open Folder-Settings tab (create folder + double-click)
|
||||
// Open folder settings
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-terminal-folder', { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('kb-terminal-folder', { exact: true }) })).toBeVisible();
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyT');
|
||||
const folderTerminalSession = page.getByTestId('session-list-1');
|
||||
await expect(folderTerminalSession).toBeVisible();
|
||||
|
||||
// Verify the terminal session name is the workspace name (default_workspace)
|
||||
const folderSessionName = folderTerminalSession;
|
||||
await expect(folderSessionName).toContainText('kb-terminal-folder');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Open Terminal (customized Alt+T)', () => {
|
||||
test('customized Alt+T opens terminal', async ({ pageWithUserData: page }) => {
|
||||
// Remap openTerminal to Alt+T
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-openTerminal');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-openTerminal').click();
|
||||
await expect(page.getByTestId('keybinding-input-openTerminal')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyT');
|
||||
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).click();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible();
|
||||
|
||||
// Press Cmd/Ctrl+T to open terminal at workspace level
|
||||
await pressShortcut(page, 'Alt', 'KeyT');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify terminal session is visible using data-testid
|
||||
const collectionTerminalSession = page.getByTestId('session-list-0');
|
||||
await expect(collectionTerminalSession).toBeVisible();
|
||||
|
||||
const collectionSession = collectionTerminalSession;
|
||||
await expect(collectionSession).toContainText('kb-collection');
|
||||
|
||||
// Open folder settings
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-terminal-folder', { exact: true }) }).dblclick();
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyT');
|
||||
const folderTerminalSession = page.getByTestId('session-list-1');
|
||||
await expect(folderTerminalSession).toBeVisible();
|
||||
|
||||
// Verify the terminal session name is the workspace name (default_workspace)
|
||||
const folderSessionName = folderTerminalSession;
|
||||
await expect(folderSessionName).toContainText('kb-terminal-folder');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
155
tests/shortcuts/helpers.ts
Normal file
155
tests/shortcuts/helpers.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { expect, Page } from '../../playwright';
|
||||
import {
|
||||
closeAllCollections,
|
||||
createCollection,
|
||||
createFolder,
|
||||
createRequest,
|
||||
openCollection,
|
||||
openRequest as openRequestBase
|
||||
} from '../utils/page';
|
||||
|
||||
export const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
|
||||
export const collectionName = 'kb-collection';
|
||||
export const baseRequests = ['req-1', 'req-2', 'req-3', 'req-4', 'req-5', 'req-6', 'req-7', 'req-8', 'req-9'];
|
||||
|
||||
export const pressShortcut = async (page: Page, ...keys: string[]) => {
|
||||
for (const key of keys) {
|
||||
await page.keyboard.down(key);
|
||||
}
|
||||
for (const key of [...keys].reverse()) {
|
||||
await page.keyboard.up(key);
|
||||
}
|
||||
};
|
||||
|
||||
export const setupBoundActionsData = async (page: Page, createTmpDir: (prefix: string) => Promise<string>) => {
|
||||
await closeAllCollections(page);
|
||||
const path = await createTmpDir('kb-collection-path');
|
||||
await createCollection(page, collectionName, path);
|
||||
|
||||
await createFolder(page, 'kb-folder', collectionName, true);
|
||||
await createFolder(page, 'kb-draft-folder', collectionName, true);
|
||||
await createFolder(page, 'kb-terminal-folder', collectionName, true);
|
||||
};
|
||||
|
||||
const checkIfRequestExists = async (page: Page, requestName: string) => {
|
||||
await openCollection(page, collectionName);
|
||||
const request = page.getByTestId('collections').locator('.collection-item-name').filter({ has: page.getByText(requestName, { exact: true }) });
|
||||
return (await request.count()) > 0;
|
||||
};
|
||||
|
||||
export const openRequest = async (...args: Parameters<typeof openRequestBase>) => {
|
||||
const [page, targetCollectionName, requestName] = args;
|
||||
if (
|
||||
targetCollectionName === collectionName
|
||||
&& baseRequests.includes(requestName)
|
||||
&& !(await checkIfRequestExists(page, requestName))
|
||||
) {
|
||||
await createRequest(page, requestName, targetCollectionName);
|
||||
}
|
||||
|
||||
return openRequestBase(...args);
|
||||
};
|
||||
|
||||
export const openKeybindingsTab = async (page: Page) => {
|
||||
await page.getByRole('button', { name: 'Open Preferences' }).click();
|
||||
await page.getByRole('tab', { name: 'Keybindings' }).click();
|
||||
await expect(page.locator('.section-header').filter({ has: page.getByText('Keybindings', { exact: true }) })).toBeVisible();
|
||||
};
|
||||
|
||||
export const closePreferencesTab = async (page: Page) => {
|
||||
const prefTab = page.locator('.request-tab').filter({ has: page.getByText('Preferences', { exact: true }) });
|
||||
// Nothing to do if it's already closed.
|
||||
if (!(await prefTab.isVisible().catch(() => false))) return;
|
||||
|
||||
await expect(async () => {
|
||||
await prefTab.hover();
|
||||
await prefTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await expect(prefTab).not.toBeVisible();
|
||||
}).toPass({ timeout: 10000 });
|
||||
};
|
||||
|
||||
export const resetKeybindings = async (page: Page) => {
|
||||
await openKeybindingsTab(page);
|
||||
const resetBtn = page.getByTestId('reset-all-keybindings-btn');
|
||||
|
||||
if (await resetBtn.isEnabled().catch(() => false)) {
|
||||
await resetBtn.click({ timeout: 2000 });
|
||||
}
|
||||
await closePreferencesTab(page);
|
||||
};
|
||||
|
||||
export const closeTabByName = async (page: any, name: string | RegExp) => {
|
||||
const tab = page.locator('.request-tab').filter({ has: page.getByText(name, { exact: true }) });
|
||||
await tab.dblclick();
|
||||
await tab.hover();
|
||||
await tab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await expect(tab).not.toBeVisible();
|
||||
};
|
||||
|
||||
export const openFolderSettingsTab = async (page: Page, folderName: string) => {
|
||||
await openCollection(page, collectionName);
|
||||
const folderRow = page.locator('.collection-item-name').filter({ has: page.getByText(folderName, { exact: true }) });
|
||||
await expect(folderRow).toBeVisible();
|
||||
await folderRow.dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText(folderName, { exact: true }) })).toBeVisible();
|
||||
};
|
||||
|
||||
export const reopenClosedTab = async (page: Page, shortcut: () => Promise<void>, expectedTabName: string | RegExp) => {
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
await page.locator('.request-tab').first().click();
|
||||
// Wait for a tab to become active (focus settled) before firing the shortcut.
|
||||
await expect(page.locator('.request-tab.active')).toBeVisible();
|
||||
await shortcut();
|
||||
const reopenedTab = page.locator('.request-tab').filter({ has: page.getByText(expectedTabName, { exact: true }) });
|
||||
if ((await reopenedTab.count()) > 0) {
|
||||
await expect(reopenedTab).toBeVisible();
|
||||
return;
|
||||
}
|
||||
// Event-driven backoff: give the reopened tab a chance to appear before retrying.
|
||||
await reopenedTab.waitFor({ state: 'visible', timeout: 1000 }).catch(() => { });
|
||||
}
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText(expectedTabName, { exact: true }) })).toBeVisible();
|
||||
};
|
||||
|
||||
export const remapKeybinding = async (
|
||||
page: Page,
|
||||
action: string,
|
||||
...keys: string[]
|
||||
) => {
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId(`keybinding-row-${action}`);
|
||||
await expect(row).toBeVisible();
|
||||
await row.scrollIntoViewIfNeeded();
|
||||
await row.hover();
|
||||
const editButton = row.getByTestId(`keybinding-edit-${action}`);
|
||||
const keybindingInput = page.getByTestId(`keybinding-input-${action}`);
|
||||
|
||||
if (await editButton.isVisible().catch(() => false)) {
|
||||
await editButton.click({ force: true });
|
||||
} else {
|
||||
await row.click({ force: true });
|
||||
if (await editButton.isVisible().catch(() => false)) {
|
||||
await editButton.click({ force: true });
|
||||
}
|
||||
}
|
||||
|
||||
await expect(keybindingInput).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
await pressShortcut(page, ...keys);
|
||||
await closePreferencesTab(page);
|
||||
};
|
||||
|
||||
export const getTabIndex = async (page: Page, name: string) => {
|
||||
const tabs = page.locator('.request-tab .tab-label');
|
||||
const count = await tabs.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const text = (await tabs.nth(i).innerText()).trim();
|
||||
if (text.includes(name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
8
tests/shortcuts/init-user-data/preferences.json
Normal file
8
tests/shortcuts/init-user-data/preferences.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"preferences": {
|
||||
"onboarding": {
|
||||
"hasLaunchedBefore": true,
|
||||
"hasSeenWelcomeModal": true
|
||||
}
|
||||
}
|
||||
}
|
||||
48
tests/shortcuts/other-actions.spec.ts
Normal file
48
tests/shortcuts/other-actions.spec.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import {
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
pressShortcut,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('OTHERS', () => {
|
||||
test.describe('SHORTCUT: Open Preferences', () => {
|
||||
test('default Cmd/Ctrl+, open preferences', async ({ pageWithUserData: page }) => {
|
||||
await pressShortcut(page, modifier, 'Comma');
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Preferences', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Open Preferences (customized Cmd/Ctrl+P)', () => {
|
||||
test('customized Cmd/Ctrl+P open preferences', async ({ pageWithUserData: page }) => {
|
||||
// Remap openPreferences to Ctrl+P
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-openPreferences');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-openPreferences').click();
|
||||
await expect(page.getByTestId('keybinding-input-openPreferences')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyP');
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Preferences', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
296
tests/shortcuts/request-actions.spec.ts
Normal file
296
tests/shortcuts/request-actions.spec.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections, createRequest, selectRequestPaneTab } from '../utils/page';
|
||||
import {
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
openRequest,
|
||||
pressShortcut,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('REQUESTS', () => {
|
||||
test.describe('SHORTCUT: Send Request (Cmd/Ctrl+Enter)', () => {
|
||||
test('sends request when cursor is in JSON body editor', async ({ pageWithUserData: page }) => {
|
||||
// Create a POST request in the shared collection pointing to the echo server
|
||||
await createRequest(page, 'cmd-enter-req-body', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'cmd-enter-req-body', { persist: true });
|
||||
|
||||
// Open Body tab and select JSON mode
|
||||
await selectRequestPaneTab(page, 'Body');
|
||||
await page.getByTestId('request-body-mode-selector').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: /^JSON$/ }).click();
|
||||
|
||||
// Focus the body code editor and type JSON
|
||||
const bodyEditor = page.getByTestId('request-body-editor').locator('.CodeMirror');
|
||||
await bodyEditor.click();
|
||||
await page.keyboard.type('{"name": "Bruno", "version": 2, "tags": ["api", "client", "http"], "active": true, "meta": {"author": "user", "created": "2025-01-01", "updated": "2025-06-01"}, "counts": {"requests": 42, "collections": 7}}');
|
||||
await expect(page.getByTestId('request-body-editor')).toContainText('"name": "Bruno"');
|
||||
|
||||
// Cursor is still in the body CodeMirror - press Cmd/Ctrl+Enter to send
|
||||
await pressShortcut(page, modifier, 'Enter');
|
||||
|
||||
// Verify a 200 response came back
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
|
||||
test('sends request when cursor is in response body editor', async ({ pageWithUserData: page }) => {
|
||||
await createRequest(page, 'cmd-enter-req-resp', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'cmd-enter-req-resp', { persist: true });
|
||||
|
||||
await selectRequestPaneTab(page, 'Body');
|
||||
await page.getByTestId('request-body-mode-selector').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: /^JSON$/ }).click();
|
||||
|
||||
const bodyEditor = page.getByTestId('request-body-editor').locator('.CodeMirror');
|
||||
await bodyEditor.click();
|
||||
await page.keyboard.type('{"name": "Bruno", "version": 2, "tags": ["api", "client", "http"], "active": true, "meta": {"author": "user", "created": "2025-01-01", "updated": "2025-06-01"}, "counts": {"requests": 42, "collections": 7}}');
|
||||
await expect(page.getByTestId('request-body-editor')).toContainText('"name": "Bruno"');
|
||||
|
||||
// First send to populate response
|
||||
await pressShortcut(page, modifier, 'Enter');
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
|
||||
// Focus cursor inside the response body CodeMirror
|
||||
const responseEditor = page.getByTestId('response-preview-container').locator('.CodeMirror').first();
|
||||
await responseEditor.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await responseEditor.click();
|
||||
|
||||
// Press Cmd/Ctrl+Enter again - should re-send the request
|
||||
await pressShortcut(page, modifier, 'Enter');
|
||||
|
||||
// Verify a 200 response came back (no error, status stays/refreshes to 200)
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
|
||||
test('sends request when cursor is in pre-request Vars value editor', async ({ pageWithUserData: page }) => {
|
||||
await createRequest(page, 'cmd-enter-req-vars', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'cmd-enter-req-vars', { persist: true });
|
||||
|
||||
// Open Vars tab - request Vars has a Pre Request section as the first table
|
||||
await selectRequestPaneTab(page, 'Vars');
|
||||
|
||||
// Fill the first var row: name=var-1
|
||||
const varsTable = page.getByTestId('request-pane').locator('table').first();
|
||||
const firstRow = varsTable.locator('tbody tr').first();
|
||||
const nameInput = firstRow.locator('input[type="text"]').first();
|
||||
await nameInput.click();
|
||||
await nameInput.fill('var-1');
|
||||
|
||||
// Click the value CodeMirror editor and type a multi-line value
|
||||
const valueEditor = firstRow.locator('.CodeMirror').first();
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('val-1');
|
||||
await page.keyboard.press('Enter'); // insert newline in value editor
|
||||
await page.keyboard.type('val-2');
|
||||
|
||||
// Cursor is still in the value CodeMirror - press Cmd/Ctrl+Enter to send
|
||||
// (should NOT insert a newline; should fire sendRequest)
|
||||
await pressShortcut(page, modifier, 'Enter');
|
||||
|
||||
// Verify a 200 response came back
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Send Request (customized Shift+Enter)', () => {
|
||||
test('sends request when cursor is in JSON body editor', async ({ pageWithUserData: page }) => {
|
||||
// Remap sendRequest to Shift+Enter
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-sendRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-sendRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-sendRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
// Create a POST request in the shared collection pointing to the echo server
|
||||
await createRequest(page, 'shift-enter-req-body', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'shift-enter-req-body', { persist: true });
|
||||
|
||||
// Open Body tab and select JSON mode
|
||||
await selectRequestPaneTab(page, 'Body');
|
||||
await page.getByTestId('request-body-mode-selector').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: /^JSON$/ }).click();
|
||||
|
||||
const bodyEditor = page.getByTestId('request-body-editor').locator('.CodeMirror');
|
||||
await bodyEditor.click();
|
||||
await page.keyboard.type('{"name": "Bruno", "version": 2, "tags": ["api", "client", "http"], "active": true, "meta": {"author": "user", "created": "2025-01-01", "updated": "2025-06-01"}, "counts": {"requests": 42, "collections": 7}}');
|
||||
await expect(page.getByTestId('request-body-editor')).toContainText('"name": "Bruno"');
|
||||
|
||||
// Cursor is still in the body CodeMirror - press Shift+Enter (customized) to send
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
|
||||
test('sends request when cursor is in response body editor', async ({ pageWithUserData: page }) => {
|
||||
// Remap sendRequest to Shift+Enter
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-sendRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-sendRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-sendRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
await createRequest(page, 'shift-enter-req-resp', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'shift-enter-req-resp', { persist: true });
|
||||
|
||||
await selectRequestPaneTab(page, 'Body');
|
||||
await page.getByTestId('request-body-mode-selector').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: /^JSON$/ }).click();
|
||||
|
||||
const bodyEditor = page.getByTestId('request-body-editor').locator('.CodeMirror');
|
||||
await bodyEditor.click();
|
||||
await page.keyboard.type('{"name": "Bruno", "version": 2, "tags": ["api", "client", "http"], "active": true, "meta": {"author": "user", "created": "2025-01-01", "updated": "2025-06-01"}, "counts": {"requests": 42, "collections": 7}}');
|
||||
await expect(page.getByTestId('request-body-editor')).toContainText('"name": "Bruno"');
|
||||
|
||||
// First send with Shift+Enter to populate response
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
|
||||
// Focus cursor inside the response body CodeMirror
|
||||
const responseEditor = page.getByTestId('response-preview-container').locator('.CodeMirror').first();
|
||||
await responseEditor.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await responseEditor.click();
|
||||
|
||||
// Press Shift+Enter again - should re-send the request
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
|
||||
test('sends request when cursor is in pre-request Vars value editor', async ({ pageWithUserData: page }) => {
|
||||
// Remap sendRequest to Shift+Enter
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-sendRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-sendRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-sendRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
await createRequest(page, 'shift-enter-req-vars', 'kb-collection', {
|
||||
url: 'https://echo.usebruno.com',
|
||||
method: 'POST'
|
||||
});
|
||||
await openRequest(page, 'kb-collection', 'shift-enter-req-vars', { persist: true });
|
||||
|
||||
await selectRequestPaneTab(page, 'Vars');
|
||||
|
||||
const varsTable = page.getByTestId('request-pane').locator('table').first();
|
||||
const firstRow = varsTable.locator('tbody tr').first();
|
||||
const nameInput = firstRow.locator('input[type="text"]').first();
|
||||
await nameInput.click();
|
||||
await nameInput.fill('var-1');
|
||||
|
||||
const valueEditor = firstRow.locator('.CodeMirror').first();
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('val-1');
|
||||
await page.keyboard.press('Enter'); // insert newline in value editor
|
||||
await page.keyboard.type('val-2');
|
||||
|
||||
// Cursor is still in the value CodeMirror - press Shift+Enter (customized) to send
|
||||
await pressShortcut(page, 'Shift', 'Enter');
|
||||
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Change Orientation (Cmd/Ctrl+J)', () => {
|
||||
test('default Cmd/Ctrl+J change layout orientation', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, 'kb-collection', 'req-5', { persist: true });
|
||||
|
||||
// Press Cmd/Ctrl+J to change layout
|
||||
await pressShortcut(page, modifier, 'KeyJ');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to horizontal layout');
|
||||
|
||||
// Press Cmd/Ctrl+J to change layout
|
||||
await pressShortcut(page, modifier, 'KeyJ');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to vertical layout');
|
||||
|
||||
// Press Cmd/Ctrl+J to change layout
|
||||
await pressShortcut(page, modifier, 'KeyJ');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to horizontal layout');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Change Orientation (customized Alt+Shift+Y)', () => {
|
||||
test('customized Alt+Shift+Y change layout orientation', async ({ pageWithUserData: page }) => {
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-changeLayout');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-changeLayout').click();
|
||||
await expect(page.getByTestId('keybinding-input-changeLayout')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyY');
|
||||
|
||||
await openRequest(page, 'kb-collection', 'req-5', { persist: true });
|
||||
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyY');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to horizontal layout');
|
||||
|
||||
// Press Alt+Shift+Y to change layout
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyY');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to vertical layout');
|
||||
|
||||
// Press Alt+Shift+Y to change layout
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyY');
|
||||
|
||||
await expect(
|
||||
page.getByTestId('response-layout-toggle-btn')
|
||||
).toHaveAttribute('title', 'Switch to horizontal layout');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
57
tests/shortcuts/search-actions.spec.ts
Normal file
57
tests/shortcuts/search-actions.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import {
|
||||
closePreferencesTab,
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
pressShortcut,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('SEARCH', () => {
|
||||
test.describe('SHORTCUT: Global Search (Cmd/Ctrl+K)', () => {
|
||||
test('default Cmd/Ctrl+K Global Search Modal', async ({ pageWithUserData: page }) => {
|
||||
await pressShortcut(page, modifier, 'KeyK');
|
||||
|
||||
await expect(page.getByTestId('global-search-input')).toBeVisible();
|
||||
await page.getByTestId('global-search-input').click();
|
||||
|
||||
await pressShortcut(page, 'Escape');
|
||||
});
|
||||
|
||||
test('customized Shift+K Global Search Modal', async ({ pageWithUserData: page }) => {
|
||||
// Remap globalSearch to Shift+K
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-globalSearch');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-globalSearch').click();
|
||||
await expect(page.getByTestId('keybinding-input-globalSearch')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyK');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyK');
|
||||
|
||||
await expect(page.getByTestId('global-search-input')).toBeVisible();
|
||||
await page.getByTestId('global-search-input').click();
|
||||
|
||||
await pressShortcut(page, 'Escape');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
473
tests/shortcuts/sidebar-actions.spec.ts
Normal file
473
tests/shortcuts/sidebar-actions.spec.ts
Normal file
@@ -0,0 +1,473 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections, createFolder, openCollection } from '../utils/page';
|
||||
import {
|
||||
closePreferencesTab,
|
||||
collectionName,
|
||||
modifier,
|
||||
openFolderSettingsTab,
|
||||
openKeybindingsTab,
|
||||
openRequest,
|
||||
pressShortcut,
|
||||
remapKeybinding,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor();
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('SIDEBAR', () => {
|
||||
test.describe('SHORTCUT: Sidebar search (Cmd/Ctrl+F)', () => {
|
||||
test('Sidebar search default (Cmd/Ctrl+F)', async ({ pageWithUserData: page }) => {
|
||||
await pressShortcut(page, modifier, 'KeyF');
|
||||
|
||||
await expect(page.getByTestId('sidebar-search-input')).toBeVisible();
|
||||
await page.getByTitle('Search requests').click();
|
||||
});
|
||||
|
||||
test('Sidebar search customized (Alt+F)', async ({ pageWithUserData: page }) => {
|
||||
// Remap sidebarSearch to Alt+F
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-sidebarSearch');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-sidebarSearch').click();
|
||||
await expect(page.getByTestId('keybinding-input-sidebarSearch')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyF');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyF');
|
||||
|
||||
await expect(page.getByTestId('sidebar-search-input')).toBeVisible();
|
||||
await page.getByTitle('Search requests').click();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: New request (Cmd/Ctrl+N)', () => {
|
||||
test('New request default (Cmd/Ctrl+N)', async ({ pageWithUserData: page }) => {
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-folder');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-folder', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
|
||||
test('New request customized (Alt+N)', async ({ pageWithUserData: page }) => {
|
||||
// Remap newRequest to Alt+N
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-newRequest');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-newRequest').click();
|
||||
await expect(page.getByTestId('keybinding-input-newRequest')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).click();
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyN');
|
||||
|
||||
await page.getByTestId('request-name').fill('nr-collection');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
await page.keyboard.type('https://echo.usebruno.com');
|
||||
await page.getByTestId('create-new-request-button').click();
|
||||
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('nr-collection', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item', () => {
|
||||
test.describe('SHORTCUT: Rename Item for request (Cmd/Ctrl+R)', () => {
|
||||
test('default Cmd/Ctrl+R open rename item modal for request', async ({ pageWithUserData: page }) => {
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
await openRequest(page, 'kb-collection', 'req-1', { persist: true });
|
||||
await pressShortcut(page, modifier, 'KeyR');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename request/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const requestNameInput = page.locator('#collection-item-name');
|
||||
await requestNameInput.fill('req-1-renamed');
|
||||
|
||||
// Click the rename button
|
||||
await page.getByTestId('rename-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-1-renamed', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item for folder (Cmd/Ctrl+R)', () => {
|
||||
test('default Cmd/Ctrl+R open rename item modal for folder', async ({ pageWithUserData: page }) => {
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).dblclick();
|
||||
await pressShortcut(page, modifier, 'KeyR');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename folder/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const folderNameInput = page.locator('#collection-item-name');
|
||||
await folderNameInput.fill('kb-folder-renamed');
|
||||
|
||||
// Click the rename button
|
||||
await page.getByTestId('rename-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder-renamed', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item for collection (Cmd/Ctrl+R)', () => {
|
||||
test('default Cmd/Ctrl+R open rename item modal for collection', async ({ pageWithUserData: page }) => {
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).click();
|
||||
await pressShortcut(page, modifier, 'KeyR');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename collection/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const collectionInput = page.locator('#collection-name');
|
||||
await collectionInput.fill('kb-collection-renamed');
|
||||
|
||||
// Click the rename button
|
||||
await page.locator('.submit').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection-renamed', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item for request (customized Alt+X)', () => {
|
||||
test('customized Alt+X open rename item modal for request', async ({ pageWithUserData: page }) => {
|
||||
// Remap renameItem to Alt+R
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-renameItem');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-renameItem').click();
|
||||
await expect(page.getByTestId('keybinding-input-renameItem')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyX');
|
||||
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
await pressShortcut(page, 'Alt', 'KeyX');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename request/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const requestNameInput = page.locator('#collection-item-name');
|
||||
await requestNameInput.fill('req-1-renamed-altx');
|
||||
|
||||
// Click the rename button
|
||||
await page.getByTestId('rename-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-1-renamed-altx', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item for folder (customized Alt+X)', () => {
|
||||
test('customized Alt+R open rename item modal for folder', async ({ pageWithUserData: page }) => {
|
||||
await remapKeybinding(page, 'renameItem', 'Alt', 'KeyX');
|
||||
|
||||
await createFolder(page, 'kb-folder-rename-src', collectionName, true);
|
||||
await openFolderSettingsTab(page, 'kb-folder-rename-src');
|
||||
await pressShortcut(page, 'Alt', 'KeyX');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename folder/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const folderNameInput = page.locator('#collection-item-name');
|
||||
await folderNameInput.fill('kb-folder-renamed-altx-src');
|
||||
|
||||
// Click the rename button
|
||||
await page.getByTestId('rename-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder-renamed-altx-src', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Rename Item for collection (customized Alt+X)', () => {
|
||||
test('customized Alt+R open rename item modal for collection', async ({ pageWithUserData: page }) => {
|
||||
await remapKeybinding(page, 'renameItem', 'Alt', 'KeyX');
|
||||
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText(collectionName, { exact: true }) }).click();
|
||||
await pressShortcut(page, 'Alt', 'KeyX');
|
||||
|
||||
// Verify rename modal opens
|
||||
const renameModal = page.locator('.bruno-modal-card').filter({ hasText: /rename collection/i });
|
||||
await expect(renameModal).toBeVisible();
|
||||
|
||||
// Fill in the rename req name
|
||||
const collectionInput = page.locator('#collection-name');
|
||||
await collectionInput.fill('kb-collection-renamed-altx');
|
||||
|
||||
// Click the rename button
|
||||
await page.locator('.submit').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection-renamed-altx', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Clone Item', () => {
|
||||
test.describe('SHORTCUT: Clone Item for request (Cmd/Ctrl+D)', () => {
|
||||
test('default Cmd/Ctrl+D open clone item modal for request', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, 'kb-collection', 'req-1', { persist: true });
|
||||
await pressShortcut(page, modifier, 'KeyD');
|
||||
|
||||
// Verify clone modal opens
|
||||
const cloneModal = page.locator('.bruno-modal-card').filter({ hasText: /clone request/i });
|
||||
await expect(cloneModal).toBeVisible();
|
||||
|
||||
// Fill in the clone req name
|
||||
const requestNameInput = page.locator('#collection-item-name');
|
||||
await requestNameInput.fill('req-1 clone 1');
|
||||
|
||||
// Click the clone button
|
||||
await page.getByTestId('clone-item-button').click();
|
||||
|
||||
// Verify cloned request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-1 clone 1', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Clone Item for folder (Cmd/Ctrl+D)', () => {
|
||||
test('default Cmd/Ctrl+D open clone item modal for folder', async ({ pageWithUserData: page }) => {
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).dblclick();
|
||||
await pressShortcut(page, modifier, 'KeyD');
|
||||
|
||||
// Verify clone modal opens
|
||||
const cloneModal = page.locator('.bruno-modal-card').filter({ hasText: /clone folder/i });
|
||||
await expect(cloneModal).toBeVisible();
|
||||
|
||||
// Fill in the clone kb-folder name
|
||||
const folderNameInput = page.locator('#collection-item-name');
|
||||
await folderNameInput.fill('kb-folder clone 1');
|
||||
|
||||
// Click the clone button
|
||||
await page.getByTestId('clone-item-button').click();
|
||||
|
||||
// Verify cloned request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder clone 1', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Clone Item for request (customized Alt+D)', () => {
|
||||
test('customized Alt+D open clone item modal for request', async ({ pageWithUserData: page }) => {
|
||||
// Remap cloneItem to Alt+D
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-cloneItem');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-cloneItem').click();
|
||||
await expect(page.getByTestId('keybinding-input-cloneItem')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyD');
|
||||
|
||||
await openRequest(page, 'kb-collection', 'req-2', { persist: true });
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyD');
|
||||
|
||||
// Verify clone modal opens
|
||||
const cloneModal = page.locator('.bruno-modal-card').filter({ hasText: /clone request/i });
|
||||
await expect(cloneModal).toBeVisible();
|
||||
|
||||
// Fill in the clone req name
|
||||
const requestNameInput = page.locator('#collection-item-name');
|
||||
await requestNameInput.fill('req-2 clone 1');
|
||||
|
||||
// Click the clone button
|
||||
await page.getByTestId('clone-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-2 clone 1', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Clone Item for folder (customized Alt+D)', () => {
|
||||
test('customized Alt+D open clone item modal for folder', async ({ pageWithUserData: page }) => {
|
||||
// Remap cloneItem to Alt+D (keybindings are reset after each test, so re-bind here).
|
||||
await remapKeybinding(page, 'cloneItem', 'Alt', 'KeyD');
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await createFolder(page, 'kb-folder-clone-src', collectionName, true);
|
||||
await openCollection(page, collectionName);
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder-clone-src', { exact: true }) }).first().click();
|
||||
await pressShortcut(page, 'Alt', 'KeyD');
|
||||
|
||||
// Verify clone modal opens
|
||||
const cloneModal = page.locator('.bruno-modal-card').filter({ hasText: /clone folder/i });
|
||||
await expect(cloneModal).toBeVisible();
|
||||
|
||||
// Fill in the clone req name
|
||||
const folderNameInput = page.locator('#collection-item-name');
|
||||
await folderNameInput.fill('kb-folder-clone-src copy 1');
|
||||
|
||||
// Click the clone button
|
||||
await page.getByTestId('clone-item-button').click();
|
||||
|
||||
// Verify renamed request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder-clone-src copy 1', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Copy Paste Item', () => {
|
||||
test.describe('SHORTCUT: Copy Paste Item for request (Cmd/Ctrl+C/V)', () => {
|
||||
test('default Cmd/Ctrl+C/V copy paste item for request', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, 'kb-collection', 'req-3', { persist: true });
|
||||
await pressShortcut(page, modifier, 'KeyC');
|
||||
await pressShortcut(page, modifier, 'KeyV');
|
||||
|
||||
// Verify cloned request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-3 (1)', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Copy Paste Item for folder (Cmd/Ctrl+C/V)', () => {
|
||||
test('default Cmd/Ctrl+C/V copy paste item for folder', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'kb-folder', { persist: true });
|
||||
await pressShortcut(page, modifier, 'KeyC');
|
||||
await pressShortcut(page, modifier, 'KeyV');
|
||||
|
||||
// Verify copied item appears in sidebar as child of folder
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) })).toHaveCount(2);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Copy Paste Item for request (customized Alt+C/V)', () => {
|
||||
test('customized Alt+C/V copy paste item for request', async ({ pageWithUserData: page }) => {
|
||||
// Remap copyItem to Alt+D
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-copyItem');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-copyItem').click();
|
||||
await expect(page.getByTestId('keybinding-input-copyItem')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyC');
|
||||
|
||||
// Remap pasteItem to Alt+V
|
||||
await openKeybindingsTab(page);
|
||||
const row2 = page.getByTestId('keybinding-row-pasteItem');
|
||||
await row2.hover();
|
||||
await page.getByTestId('keybinding-edit-pasteItem').click();
|
||||
await expect(page.getByTestId('keybinding-input-pasteItem')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyV');
|
||||
|
||||
await openRequest(page, 'kb-collection', 'req-4', { persist: true });
|
||||
await pressShortcut(page, 'Alt', 'KeyC');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyV');
|
||||
|
||||
// Verify cloned request appears in sidebar
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('req-4 (1)', { exact: true }) })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Copy Paste Item for folder (Cmd/Ctrl+C/V)', () => {
|
||||
test('customized Alt+C/V copy paste item for folder', async ({ pageWithUserData: page }) => {
|
||||
await remapKeybinding(page, 'copyItem', 'Alt', 'KeyC');
|
||||
await remapKeybinding(page, 'pasteItem', 'Alt', 'KeyV');
|
||||
|
||||
await createFolder(page, 'kb-folder-copy-src', collectionName, true);
|
||||
await openFolderSettingsTab(page, 'kb-folder-copy-src');
|
||||
await pressShortcut(page, 'Alt', 'KeyC');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyV');
|
||||
|
||||
// Verify copied item appears in sidebar as child of folder
|
||||
await expect(page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder-copy-src', { exact: true }) })).toHaveCount(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Collapse Sidebar', () => {
|
||||
test('Collapse sidebar & Expand using default Cmd/Ctrl+\\', async ({ pageWithUserData: page }) => {
|
||||
await expect(page.getByTestId('collections')).toBeVisible();
|
||||
await page.locator('body').click({ position: { x: 1, y: 1 } });
|
||||
|
||||
// Press Cmd/Ctrl+\ to collapse sidebar
|
||||
await pressShortcut(page, modifier, 'Backslash');
|
||||
|
||||
// Verify sidebar collapsed to 0px
|
||||
await expect.poll(
|
||||
() => page.locator('aside.sidebar').evaluate((el) => getComputedStyle(el).width)
|
||||
).toBe('0px');
|
||||
|
||||
// Press Cmd/Ctrl+\ to expand sidebar
|
||||
await pressShortcut(page, modifier, 'Backslash');
|
||||
|
||||
// Verify sidebar expanded to 250px
|
||||
await expect.poll(
|
||||
() => page.locator('aside.sidebar').evaluate((el) => getComputedStyle(el).width)
|
||||
).toBe('250px');
|
||||
});
|
||||
|
||||
test('Collapse sidebar & Expand using customized (Shift+G)', async ({ pageWithUserData: page }) => {
|
||||
// Remap collapseSidebar to Shift+G
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-collapseSidebar');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-collapseSidebar').click();
|
||||
await expect(page.getByTestId('keybinding-input-collapseSidebar')).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyG');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
// Trigger the remapped shortcut to collapse sidebar
|
||||
await pressShortcut(page, 'Shift', 'KeyG');
|
||||
|
||||
// Verify sidebar collapsed to 0px
|
||||
await expect.poll(
|
||||
() => page.locator('aside.sidebar').evaluate((el) => getComputedStyle(el).width)
|
||||
).toBe('0px');
|
||||
|
||||
// Trigger the remapped shortcut to expand sidebar
|
||||
await pressShortcut(page, 'Shift', 'KeyG');
|
||||
|
||||
// Verify sidebar expanded to 250px
|
||||
await expect.poll(
|
||||
() => page.locator('aside.sidebar').evaluate((el) => getComputedStyle(el).width)
|
||||
).toBe('250px');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
760
tests/shortcuts/tabs-actions.spec.ts
Normal file
760
tests/shortcuts/tabs-actions.spec.ts
Normal file
@@ -0,0 +1,760 @@
|
||||
import { expect, test } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
import {
|
||||
closePreferencesTab,
|
||||
closeTabByName,
|
||||
collectionName,
|
||||
getTabIndex,
|
||||
modifier,
|
||||
openKeybindingsTab,
|
||||
openRequest,
|
||||
pressShortcut,
|
||||
remapKeybinding,
|
||||
reopenClosedTab,
|
||||
resetKeybindings,
|
||||
setupBoundActionsData
|
||||
} from './helpers';
|
||||
|
||||
test.describe('Shortcut Keys - BOUND_ACTIONS', () => {
|
||||
test.beforeEach(async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
await page.locator('[data-app-state="loaded"]').waitFor(); ;
|
||||
await setupBoundActionsData(page, createTmpDir);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await resetKeybindings(page);
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test.describe('TABS', () => {
|
||||
test.describe('SHORTCUT: Close Tab', () => {
|
||||
test('Close active tab default (Cmd/Ctrl+W)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
const reqTab = page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) });
|
||||
// Click the tab to guarantee it's the focused/active tab before firing the shortcut.
|
||||
await reqTab.click();
|
||||
await expect(reqTab).toHaveClass(/active/, { timeout: 2000 });
|
||||
|
||||
await pressShortcut(page, modifier, 'KeyW');
|
||||
await expect(reqTab).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Close active tab customized (Shift+X)', async ({ pageWithUserData: page }) => {
|
||||
// Remap closeTab to Cmd/Ctrl+Shift+X
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId(`keybinding-row-closeTab`);
|
||||
await row.hover();
|
||||
await page.getByTestId(`keybinding-edit-closeTab`).click();
|
||||
// Wait for input to enter recording mode
|
||||
await expect(page.getByTestId(`keybinding-input-closeTab`)).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Remove the old keybindings
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
});
|
||||
|
||||
// Closing a tab that has unsaved changes must surface the entity-specific
|
||||
// "Unsaved changes" confirmation modal (request / folder / collection).
|
||||
test.describe('SHORTCUT: Close Tab (Unsaved changes modal)', () => {
|
||||
// These tests trigger close with the customized Shift+X binding; set it up
|
||||
// once for the group. The top-level afterEach resets it after each test.
|
||||
test.beforeEach(async ({ pageWithUserData: page }) => {
|
||||
await remapKeybinding(page, 'closeTab', 'Shift', 'KeyX');
|
||||
await closePreferencesTab(page);
|
||||
});
|
||||
|
||||
test('Close active tab customized (Shift+X) shows unsaved changes modal for request', async ({ pageWithUserData: page }) => {
|
||||
// Open a request and make an unsaved change (edit the URL → draft)
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) });
|
||||
await expect(requestTab).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.locator('#request-url .CodeMirror').click();
|
||||
await page.keyboard.type('/users');
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
// The request "Unsaved changes" modal should appear
|
||||
const modal = page.locator('.bruno-modal-card').filter({ has: page.getByText('Unsaved changes', { exact: true }) });
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await expect(modal).toContainText('You have unsaved changes in request');
|
||||
await expect(modal).toContainText('req-1');
|
||||
await expect(modal.getByRole('button', { name: 'Don\'t Save' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Save', exact: true })).toBeVisible();
|
||||
|
||||
// Cancel keeps the tab open with its draft intact
|
||||
await modal.getByRole('button', { name: 'Cancel' }).click();
|
||||
await expect(modal).not.toBeVisible();
|
||||
await expect(requestTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
// Trigger again and discard → the tab closes
|
||||
await pressShortcut(page, 'Escape');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await modal.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
await expect(requestTab).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Close active tab customized (Shift+X) shows unsaved changes modal for folder', async ({ pageWithUserData: page }) => {
|
||||
// Open folder settings and make an unsaved change (add a header → draft)
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-draft-folder', { exact: true }) }).dblclick();
|
||||
const folderTab = page.locator('.request-tab').filter({ has: page.getByText('kb-draft-folder', { exact: true }) });
|
||||
await expect(folderTab).toBeVisible({ timeout: 3000 });
|
||||
|
||||
const headerRow = page.locator('table').first().locator('tbody tr').first();
|
||||
await headerRow.locator('.CodeMirror').first().click();
|
||||
await page.keyboard.type('X-Folder-Header');
|
||||
await headerRow.locator('.CodeMirror').nth(1).click();
|
||||
await page.keyboard.type('folder-value');
|
||||
await expect(folderTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
// The folder "Unsaved changes" modal should appear
|
||||
const modal = page.locator('.bruno-modal-card').filter({ has: page.getByText('Unsaved changes', { exact: true }) });
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await expect(modal).toContainText('folder settings');
|
||||
await expect(modal).toContainText('kb-draft-folder');
|
||||
await expect(modal.getByRole('button', { name: 'Don\'t Save' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Save', exact: true })).toBeVisible();
|
||||
|
||||
// Cancel keeps the tab open with its draft intact
|
||||
await modal.getByRole('button', { name: 'Cancel' }).click();
|
||||
await expect(modal).not.toBeVisible();
|
||||
await expect(folderTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
// Trigger again and discard → the tab closes
|
||||
await pressShortcut(page, 'Escape');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await modal.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
await expect(folderTab).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Close active tab customized (Shift+X) shows unsaved changes modal for collection', async ({ pageWithUserData: page }) => {
|
||||
// Open collection settings and make an unsaved change (add a header → draft)
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) });
|
||||
await expect(collectionTab).toBeVisible({ timeout: 3000 });
|
||||
|
||||
await page.locator('.tab.headers').click();
|
||||
const headerRow = page.locator('table').first().locator('tbody tr').first();
|
||||
await headerRow.locator('.CodeMirror').first().click();
|
||||
await page.keyboard.type('X-Custom-Header');
|
||||
await headerRow.locator('.CodeMirror').nth(1).click();
|
||||
await page.keyboard.type('custom-value');
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
// The collection "Unsaved changes" modal should appear
|
||||
const modal = page.locator('.bruno-modal-card').filter({ has: page.getByText('Unsaved changes', { exact: true }) });
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await expect(modal).toContainText('collection settings');
|
||||
await expect(modal).toContainText(collectionName);
|
||||
await expect(modal.getByRole('button', { name: 'Don\'t Save' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
||||
await expect(modal.getByRole('button', { name: 'Save', exact: true })).toBeVisible();
|
||||
|
||||
// Cancel keeps the tab open with its draft intact
|
||||
await modal.getByRole('button', { name: 'Cancel' }).click();
|
||||
await expect(modal).not.toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
|
||||
// Trigger again and discard → the tab closes
|
||||
await pressShortcut(page, 'Escape');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyX');
|
||||
|
||||
await expect(modal).toBeVisible({ timeout: 3000 });
|
||||
await modal.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
await expect(collectionTab).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Close All Tabs', () => {
|
||||
test('Close all tabs default (Cmd/Ctrl+Shift+W)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-2', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-3', { persist: true });
|
||||
await page.getByTestId('runner').click();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-2', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-3', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyW');
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-2', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-3', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Close all tabs customized (Alt+Y)', async ({ pageWithUserData: page }) => {
|
||||
// Remap closeAllTabs to Alt+Y
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-closeAllTabs');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-row-closeAllTabs').click();
|
||||
await expect(page.getByTestId('keybinding-input-closeAllTabs')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyY');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-2', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-3', { persist: true });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-2', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-3', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyY');
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-2', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-3', { exact: true }) })).not.toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Save', () => {
|
||||
test('Save tab customized (Cmd/Ctrl+S)', async ({ pageWithUserData: page }) => {
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Verify initially there is NO draft indicator (close icon is present)
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) });
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
await page.locator('.tab.headers').click();
|
||||
|
||||
const headerTable = page.locator('table').first();
|
||||
const headerRow = headerTable.locator('tbody tr').first();
|
||||
|
||||
const nameEditor = headerRow.locator('.CodeMirror').first();
|
||||
await nameEditor.click();
|
||||
await page.keyboard.type('X-Custom-Header');
|
||||
|
||||
const valueEditor = headerRow.locator('.CodeMirror').nth(1);
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('custom-value');
|
||||
|
||||
// Verify draft indicator appears in the tab
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
// Save the changes
|
||||
await pressShortcut(page, modifier, 'KeyS');
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Save tab customized (Alt+S)', async ({ pageWithUserData: page }) => {
|
||||
// Remap save to Alt+S
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-save');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-save').click();
|
||||
await expect(page.getByTestId('keybinding-input-save')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyS');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Verify initially there is NO draft indicator (close icon is present)
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) });
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
await page.locator('.tab.headers').click();
|
||||
|
||||
const headerTable = page.locator('table').first();
|
||||
const headerRow = headerTable.locator('tbody tr').first();
|
||||
|
||||
const nameEditor = headerRow.locator('.CodeMirror').first();
|
||||
await nameEditor.click();
|
||||
await page.keyboard.type('X-Custom-Header');
|
||||
|
||||
const valueEditor = headerRow.locator('.CodeMirror').nth(1);
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('custom-value');
|
||||
|
||||
// Verify draft indicator appears in the tab
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
await page.locator('body').click({ position: { x: 1, y: 1 } });
|
||||
|
||||
// Save the changes
|
||||
await pressShortcut(page, 'Alt', 'KeyS');
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Save All Tabs', () => {
|
||||
test('Save all tabs default (Cmd/Ctrl+Shift+S)', async ({ pageWithUserData: page }) => {
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Verify initially there is NO draft indicator (close icon is present)
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) });
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
await page.locator('.tab.headers').click();
|
||||
|
||||
const headerTable = page.locator('table').first();
|
||||
const headerRow = headerTable.locator('tbody tr').first();
|
||||
|
||||
const nameEditor = headerRow.locator('.CodeMirror').first();
|
||||
await nameEditor.click();
|
||||
await page.keyboard.type('X-Custom-Header');
|
||||
|
||||
const valueEditor = headerRow.locator('.CodeMirror').nth(1);
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('custom-value');
|
||||
|
||||
// Verify draft indicator appears in the tab
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
// Open Folder-Settings tab (create folder + double-click)
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-draft-folder', { exact: true }) }).dblclick();
|
||||
|
||||
// Verify folder settings tab is open
|
||||
const folderTab = page.locator('.request-tab').filter({ has: page.getByText('kb-draft-folder', { exact: true }) });
|
||||
await expect(folderTab).toBeVisible();
|
||||
|
||||
await expect(folderTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
const folderHeaderTable = page.locator('table').first();
|
||||
const folderHeaderRow = folderHeaderTable.locator('tbody tr').first();
|
||||
|
||||
const folderNameEditor = folderHeaderRow.locator('.CodeMirror').first();
|
||||
await folderNameEditor.click();
|
||||
await page.keyboard.type('X-Folder-Header');
|
||||
|
||||
const folderValueEditor = folderHeaderRow.locator('.CodeMirror').nth(1);
|
||||
await folderValueEditor.click();
|
||||
await page.keyboard.type('folder-value');
|
||||
|
||||
// Verify draft indicator appears in the folder tab
|
||||
await expect(folderTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
// Save the changes
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyS');
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(folderTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Save all tabs customized (Alt+Shift+S)', async ({ pageWithUserData: page }) => {
|
||||
// Remap saveAllTabs to Alt+Shift+S
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-saveAllTabs');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-saveAllTabs').click();
|
||||
await expect(page.getByTestId('keybinding-input-saveAllTabs')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyS');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText(collectionName, { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Verify initially there is NO draft indicator (close icon is present)
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) });
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
await page.locator('.tab.headers').click();
|
||||
|
||||
const headerTable = page.locator('table').first();
|
||||
const headerRow = headerTable.locator('tbody tr').first();
|
||||
|
||||
const nameEditor = headerRow.locator('.CodeMirror').first();
|
||||
await nameEditor.click();
|
||||
await page.keyboard.type('X-Custom-Header');
|
||||
|
||||
const valueEditor = headerRow.locator('.CodeMirror').nth(1);
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('custom-value');
|
||||
|
||||
// Verify draft indicator appears in the tab
|
||||
await expect(collectionTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
// Open Folder-Settings tab (create folder + double-click)
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-draft-folder', { exact: true }) }).dblclick();
|
||||
|
||||
// Verify folder settings tab is open
|
||||
const folderTab = page.locator('.request-tab').filter({ has: page.getByText('kb-draft-folder', { exact: true }) });
|
||||
await expect(folderTab).toBeVisible();
|
||||
|
||||
await expect(folderTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
const folderHeaderTable = page.locator('table').first();
|
||||
const folderHeaderRow = folderHeaderTable.locator('tbody tr').first();
|
||||
|
||||
const folderNameEditor = folderHeaderRow.locator('.CodeMirror').first();
|
||||
await folderNameEditor.click();
|
||||
await page.keyboard.type('X-Folder-Header');
|
||||
|
||||
const folderValueEditor = folderHeaderRow.locator('.CodeMirror').nth(1);
|
||||
await folderValueEditor.click();
|
||||
await page.keyboard.type('folder-value');
|
||||
|
||||
// Verify draft indicator appears in the folder tab
|
||||
await expect(folderTab.locator('.has-changes-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.close-icon')).not.toBeVisible();
|
||||
|
||||
// Save the changes
|
||||
await pressShortcut(page, 'Alt', 'Shift', 'KeyS');
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(folderTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(folderTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
|
||||
// Verify draft indicator is gone after saving
|
||||
await expect(collectionTab.locator('.close-icon')).toBeVisible();
|
||||
await expect(collectionTab.locator('.has-changes-icon')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Switch to Previous Tab', () => {
|
||||
test('Switch to Previous Tab default (Cmd/Ctrl+Shift+[)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-4', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-5', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-6', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// req-6 is active (last opened) - press previous → req-5
|
||||
await pressShortcut(page, modifier, 'Shift', 'BracketLeft');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-5/, { timeout: 3000 });
|
||||
|
||||
// Press again → req-4
|
||||
await pressShortcut(page, modifier, 'Shift', 'BracketLeft');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-4/, { timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Switch to Previous Tab customized (Shift+P)', async ({ pageWithUserData: page }) => {
|
||||
// Remap switchToPreviousTab to Shift+P
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-switchToPreviousTab');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-switchToPreviousTab').click();
|
||||
await expect(page.getByTestId('keybinding-input-switchToPreviousTab')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyP');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
// Reuse the same requests opened in the default test
|
||||
await openRequest(page, collectionName, 'req-4', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-5', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('req-6', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// req-6 is active - press Shift+P → req-5
|
||||
await pressShortcut(page, 'Shift', 'KeyP');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-5/, { timeout: 3000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Switch to Next Tab', () => {
|
||||
test('Switch to Next Tab default (Cmd/Ctrl+Shift+])', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-4', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-5', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
|
||||
// Go back to req-4 to start from the left
|
||||
await openRequest(page, 'kb-collection', 'req-4', { persist: true });
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-4/);
|
||||
|
||||
// req-4 is active - press next → req-5
|
||||
await pressShortcut(page, modifier, 'Shift', 'BracketRight');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-5/, { timeout: 3000 });
|
||||
|
||||
// Press again → req-6
|
||||
await pressShortcut(page, modifier, 'Shift', 'BracketRight');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-6/, { timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Switch to Next Tab customized (Shift+N)', async ({ pageWithUserData: page }) => {
|
||||
// Remap switchToNextTab to Shift+N
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-switchToNextTab');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-switchToNextTab').click();
|
||||
await expect(page.getByTestId('keybinding-input-switchToNextTab')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Shift', 'KeyN');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await openRequest(page, collectionName, 'req-4', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-5', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
|
||||
// Go back to req-4
|
||||
await openRequest(page, 'kb-collection', 'req-4', { persist: true });
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-4/);
|
||||
|
||||
// req-4 is active - press Shift+N → req-5
|
||||
await pressShortcut(page, 'Shift', 'KeyN');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-5/, { timeout: 3000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Move Tab Left', () => {
|
||||
test('Move Tab Left default (Cmd/Ctrl+[)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-7', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-8', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-9', { persist: true });
|
||||
|
||||
// req-9 is active and last
|
||||
const tabs = page.locator('.request-tab');
|
||||
const totalTabs = await tabs.count();
|
||||
await expect(tabs.nth(totalTabs - 1)).toHaveText(/req-9/);
|
||||
|
||||
// Press Cmd/Ctrl+[ → req-9 moves left, req-8 becomes last
|
||||
await pressShortcut(page, modifier, 'BracketLeft');
|
||||
await expect(tabs.nth(totalTabs - 1)).toHaveText(/req-8/, { timeout: 3000 });
|
||||
await expect(tabs.nth(totalTabs - 2)).toHaveText(/req-9/);
|
||||
|
||||
// Press again → req-9 moves one more position left
|
||||
await pressShortcut(page, modifier, 'BracketLeft');
|
||||
await expect(tabs.nth(totalTabs - 3)).toHaveText(/req-9/, { timeout: 3000 });
|
||||
});
|
||||
|
||||
test('Move Tab Left customized (Alt+L)', async ({ pageWithUserData: page }) => {
|
||||
// Remap moveTabLeft to Alt+L
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-moveTabLeft');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-moveTabLeft').click();
|
||||
await expect(page.getByTestId('keybinding-input-moveTabLeft')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyL');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await openRequest(page, 'kb-collection', 'req-7', { persist: true });
|
||||
await openRequest(page, 'kb-collection', 'req-8', { persist: true });
|
||||
await openRequest(page, 'kb-collection', 'req-9', { persist: true });
|
||||
|
||||
// req-9 is active
|
||||
const tabs = page.locator('.request-tab');
|
||||
|
||||
// Press Alt+L → req-9 moves left, req-8 becomes last
|
||||
await pressShortcut(page, 'Alt', 'KeyL');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyL');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyL');
|
||||
await expect(tabs.nth(0)).toHaveText(/req-9/);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Move Tab Right', () => {
|
||||
test('Move Tab Right default (Cmd/Ctrl+])', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-7', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-8', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-9', { persist: true });
|
||||
|
||||
// Move req-9 to first position first
|
||||
await pressShortcut(page, modifier, 'BracketLeft');
|
||||
await pressShortcut(page, modifier, 'BracketLeft');
|
||||
await pressShortcut(page, modifier, 'BracketLeft');
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(/req-9/);
|
||||
const startIndex = await getTabIndex(page, 'req-9');
|
||||
expect(startIndex).toBeGreaterThanOrEqual(0);
|
||||
|
||||
await pressShortcut(page, modifier, 'BracketRight');
|
||||
const indexAfterOneMove = await getTabIndex(page, 'req-9');
|
||||
expect(indexAfterOneMove).toBeGreaterThanOrEqual(startIndex);
|
||||
|
||||
await pressShortcut(page, modifier, 'BracketRight');
|
||||
const indexAfterTwoMoves = await getTabIndex(page, 'req-9');
|
||||
expect(indexAfterTwoMoves).toBeGreaterThanOrEqual(indexAfterOneMove);
|
||||
|
||||
await pressShortcut(page, modifier, 'BracketRight');
|
||||
const indexAfterThreeMoves = await getTabIndex(page, 'req-9');
|
||||
expect(indexAfterThreeMoves).toBeGreaterThanOrEqual(indexAfterTwoMoves);
|
||||
});
|
||||
|
||||
test('Move Tab Right customized (Alt+R)', async ({ pageWithUserData: page }) => {
|
||||
// Remap moveTabRight to Alt+R
|
||||
await openKeybindingsTab(page);
|
||||
const row = page.getByTestId('keybinding-row-moveTabRight');
|
||||
await row.hover();
|
||||
await page.getByTestId('keybinding-edit-moveTabRight').click();
|
||||
await expect(page.getByTestId('keybinding-input-moveTabRight')).toBeVisible({ timeout: 2000 });
|
||||
|
||||
await page.keyboard.press('Backspace');
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyR');
|
||||
|
||||
await closePreferencesTab(page);
|
||||
|
||||
await openRequest(page, collectionName, 'req-6', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-7', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-8', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-9', { persist: true });
|
||||
|
||||
const req7Tab = page.locator('.request-tab').filter({ has: page.getByText('req-7', { exact: true }) }).first();
|
||||
await req7Tab.click();
|
||||
await expect(req7Tab).toHaveClass(/active/);
|
||||
|
||||
const startIndex = await getTabIndex(page, 'req-7');
|
||||
expect(startIndex).toBeGreaterThanOrEqual(0);
|
||||
|
||||
// Press Alt+L → req-9 moves right, req-8 becomes last
|
||||
await pressShortcut(page, 'Alt', 'KeyR');
|
||||
|
||||
const indexAfterOneMove = await getTabIndex(page, 'req-7');
|
||||
expect(indexAfterOneMove).toBeGreaterThan(startIndex);
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyR');
|
||||
|
||||
const indexAfterTwoMoves = await getTabIndex(page, 'req-7');
|
||||
expect(indexAfterTwoMoves).toBeGreaterThanOrEqual(indexAfterOneMove);
|
||||
|
||||
await pressShortcut(page, 'Alt', 'KeyR');
|
||||
|
||||
const indexAfterThreeMoves = await getTabIndex(page, 'req-7');
|
||||
expect(indexAfterThreeMoves).toBeGreaterThanOrEqual(indexAfterTwoMoves);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Switch to Tab at Position', () => {
|
||||
test('Switch to Tab at Position default (Cmd/Ctrl+1-8)', async ({ pageWithUserData: page }) => {
|
||||
// Open req-1..req-9 as persisted tabs (persist: true keeps each tab open).
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
await openRequest(page, 'kb-collection', `req-${i}`, { persist: true });
|
||||
}
|
||||
|
||||
// Cmd/Ctrl+<n> activates the tab at position n.
|
||||
for (let pos = 1; pos <= 8; pos++) {
|
||||
await pressShortcut(page, modifier, `${pos}`);
|
||||
await expect(page.locator('li.request-tab.active')).toHaveText(new RegExp(`req-${pos}`), { timeout: 3000 });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('SHORTCUT: Reopen Last Closed Tab', () => {
|
||||
test('Reopen Last Closed Tab default (Cmd/Ctrl+Shift+T)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-2', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
const req1Tab = page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) }).first();
|
||||
await req1Tab.click();
|
||||
await expect(req1Tab).toHaveClass(/active/);
|
||||
await closeTabByName(page, 'req-1');
|
||||
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyT');
|
||||
}, 'req-1');
|
||||
});
|
||||
|
||||
test('Reopen Last Closed Tab customized (Cmd/Ctrl+Shift+T)', async ({ pageWithUserData: page }) => {
|
||||
await openRequest(page, collectionName, 'req-2', { persist: true });
|
||||
|
||||
// Open Collection-Settings tab (double-click collection name)
|
||||
await page.getByTestId('sidebar-collection-row').filter({ has: page.getByText('kb-collection', { exact: true }) }).dblclick();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Collection', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Open Runner tab
|
||||
await page.getByTestId('runner').click();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Runner', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Open Variables tab
|
||||
await page.getByTestId('more-actions').click();
|
||||
await page.getByTestId('more-actions-variables').click();
|
||||
await expect(page.locator('.request-tab').filter({ has: page.getByText('Variables', { exact: true }) })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Open Folder-Settings tab (create folder + double-click)
|
||||
await page.locator('.collection-item-name').filter({ has: page.getByText('kb-folder', { exact: true }) }).dblclick();
|
||||
|
||||
// Close in order: kb-folder (first closed) → Collection → Variables → Runner (last closed)
|
||||
await closeTabByName(page, 'kb-folder');
|
||||
await closeTabByName(page, 'Collection');
|
||||
await closeTabByName(page, 'Variables');
|
||||
await closeTabByName(page, 'Runner');
|
||||
|
||||
// Reopen LIFO: Runner was closed last → reopens first
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyT');
|
||||
}, 'Runner');
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyT');
|
||||
}, /variables/i);
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyT');
|
||||
}, 'Collection');
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, modifier, 'Shift', 'KeyT');
|
||||
}, 'kb-folder');
|
||||
});
|
||||
|
||||
test('Reopen Last Closed Tab customized (Alt+Z)', async ({ pageWithUserData: page }) => {
|
||||
await remapKeybinding(page, 'reopenLastClosedTab', 'Alt', 'KeyZ');
|
||||
|
||||
await openRequest(page, collectionName, 'req-2', { persist: true });
|
||||
await openRequest(page, collectionName, 'req-1', { persist: true });
|
||||
const req1Tab = page.locator('.request-tab').filter({ has: page.getByText('req-1', { exact: true }) }).first();
|
||||
await req1Tab.click();
|
||||
await expect(req1Tab).toHaveClass(/active/);
|
||||
await closeTabByName(page, 'req-1');
|
||||
|
||||
await reopenClosedTab(page, async () => {
|
||||
await pressShortcut(page, 'Alt', 'KeyZ');
|
||||
}, 'req-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -48,9 +48,15 @@ const closeAllCollections = async (page) => {
|
||||
|
||||
for (let i = 0; i < numberOfCollections; i++) {
|
||||
const firstCollection = page.locator('[data-testid="collections"] .collection-name').first();
|
||||
await firstCollection.hover();
|
||||
await firstCollection.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').getByText('Remove').click();
|
||||
await firstCollection.scrollIntoViewIfNeeded();
|
||||
|
||||
const removeMenuItem = page.locator('.dropdown-item').getByText('Remove');
|
||||
await expect(async () => {
|
||||
await firstCollection.hover();
|
||||
await firstCollection.locator('.collection-actions .icon').click({ force: true });
|
||||
await expect(removeMenuItem).toBeVisible({ timeout: 2000 });
|
||||
}).toPass({ timeout: 15000 });
|
||||
await removeMenuItem.click();
|
||||
|
||||
// Wait for modal to appear - could be either regular remove or drafts confirmation
|
||||
const removeModal = page.locator('.bruno-modal').filter({ hasText: 'Remove Collection' });
|
||||
@@ -150,10 +156,18 @@ const createCollection = async (
|
||||
await expect(nameInput).toHaveValue(collectionName, { timeout: 2000 });
|
||||
|
||||
if (format) {
|
||||
await createCollectionModal.locator('.advanced-options .btn-advanced').click();
|
||||
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Show File Format' }).click();
|
||||
const advancedBtn = createCollectionModal.locator('.advanced-options .btn-advanced');
|
||||
const showFileFormatToggle = page.getByTestId('show-file-format-toggle');
|
||||
const formatSelect = createCollectionModal.locator('#format');
|
||||
await formatSelect.waitFor({ state: 'visible', timeout: 5000 });
|
||||
|
||||
await expect(async () => {
|
||||
if (!(await formatSelect.isVisible())) {
|
||||
await advancedBtn.click();
|
||||
await showFileFormatToggle.click({ timeout: 2000 });
|
||||
}
|
||||
await expect(formatSelect).toBeVisible({ timeout: 2000 });
|
||||
}).toPass({ timeout: 15000 });
|
||||
|
||||
await formatSelect.selectOption(format);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user