fix: flaky tests - standardize save keyboard shortcut across tests (#7141)

This commit is contained in:
Bijin A B
2026-02-14 03:58:02 +05:30
committed by GitHub
parent 0c3b828b09
commit 1d126dcb65
27 changed files with 66 additions and 54 deletions

View File

@@ -99,7 +99,8 @@ test.describe('Tag persistence', () => {
await locators.tags.input().fill('smoke');
await locators.tags.input().press('Enter');
await expect(locators.tags.item('smoke')).toBeVisible();
await page.keyboard.press('Meta+s');
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
// Create another folder
await locators.sidebar.collectionRow('test-collection').hover();

View File

@@ -33,7 +33,8 @@ test.describe('EditableTable - Focus and Placeholder', () => {
await expect(nameInput).toBeFocused();
// Save the request
await page.keyboard.press('Meta+s');
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
// Wait for save toast
await expect(page.getByText('Request saved successfully').last()).toBeVisible();

View File

@@ -27,6 +27,7 @@ test.describe('Collection Environment Import Tests', () => {
// Select a location and import
await page.locator('#collection-location').fill(await createTmpDir('collection-env-import-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
await expect(
page.locator('#sidebar-collection-name').filter({ hasText: 'Environment Test Collection' })).toBeVisible({ timeout: 10000 });

View File

@@ -1,6 +1,8 @@
import { test, expect } from '../../../playwright';
import { buildGrpcCommonLocators } from '../../utils/page/locators';
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
test.describe('make grpc requests', () => {
const setupGrpcTest = async (page) => {
const locators = buildGrpcCommonLocators(page);
@@ -53,7 +55,7 @@ test.describe('make grpc requests', () => {
/* TODO: Reflection fetching incorrectly marks requests as modified, causing save indicators to appear. This save step prevents test timeouts by clearing the modified state. This is a temporary workaround until the reflection fetching issue is resolved. */
await test.step('save request via shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
});
});
@@ -94,7 +96,7 @@ test.describe('make grpc requests', () => {
/* TODO: Reflection fetching incorrectly marks requests as modified, causing save indicators to appear. This save step prevents test timeouts by clearing the modified state. This is a temporary workaround until the reflection fetching issue is resolved. */
await test.step('save request via shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
});
});
@@ -144,7 +146,7 @@ test.describe('make grpc requests', () => {
/* TODO: Reflection fetching incorrectly marks requests as modified, causing save indicators to appear. This save step prevents test timeouts by clearing the modified state. This is a temporary workaround until the reflection fetching issue is resolved. */
await test.step('save request via shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
});
});
@@ -196,7 +198,7 @@ test.describe('make grpc requests', () => {
/* TODO: Reflection fetching incorrectly marks requests as modified, causing save indicators to appear. This save step prevents test timeouts by clearing the modified state. This is a temporary workaround until the reflection fetching issue is resolved. */
await test.step('save request via shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
});
});
});

View File

@@ -26,7 +26,8 @@ test.describe('grpc metadata', () => {
/* TODO: Reflection fetching incorrectly marks requests as modified, causing save indicators to appear. This save step prevents test timeouts by clearing the modified state. This is a temporary workaround until the reflection fetching issue is resolved. */
await test.step('save request via shortcut', async () => {
await page.keyboard.press('Meta+s');
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
});
});
});

View File

@@ -18,8 +18,8 @@ test.describe('Invalid File Handling', () => {
// Wait for the loader to disappear
await page.locator('#import-collection-loader').waitFor({ state: 'hidden' });
const hasError = await page.getByText('Failed to parse the file ensure it is valid JSON or YAML').first().isVisible();
expect(hasError).toBe(true);
// Use auto-retrying assertion instead of snapshot isVisible() check
await expect(page.getByText('Failed to parse the file ensure it is valid JSON or YAML').first()).toBeVisible();
// Cleanup: close any open modals
await page.getByTestId('modal-close-button').click();

View File

@@ -39,6 +39,7 @@ test.describe('Import Insomnia v4 Collection - Environment Import', () => {
await page.locator('#collection-location').fill(await createTmpDir('insomnia-v4-env-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
await expect(page.locator('#sidebar-collection-name').getByText('Test API Collection v4 with Environments')).toBeVisible();

View File

@@ -38,6 +38,7 @@ test.describe('Import Insomnia v5 Collection - Environment Import', () => {
await page.locator('#collection-location').fill(await createTmpDir('insomnia-v5-env-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
await openCollection(page, 'Test API Collection v5 with Environments');
});

View File

@@ -16,8 +16,8 @@ test.describe('Invalid Insomnia Collection - Malformed Structure', () => {
await page.setInputFiles('input[type="file"]', insomniaFile);
// Check for error message - this should fail during JSON parsing
const hasError = await page.getByText('Failed to parse the file').first().isVisible();
expect(hasError).toBe(true);
// Use auto-retrying assertion instead of snapshot isVisible() check
await expect(page.getByText('Failed to parse the file').first()).toBeVisible();
// Cleanup: close any open modals
await page.getByTestId('modal-close-button').click();

View File

@@ -31,6 +31,7 @@ test.describe('OpenAPI Duplicate Names Handling', () => {
// select a location
await page.locator('#collection-location').fill(await createTmpDir('duplicate-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// verify the collection was imported successfully
await expect(page.locator('#sidebar-collection-name').getByText('Duplicate Test Collection')).toBeVisible();

View File

@@ -75,6 +75,7 @@ test.describe('Import OpenAPI Collection with Examples', () => {
await test.step('Complete import by clicking import button', async () => {
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
});
await test.step('Handle sandbox modal', async () => {
@@ -203,6 +204,7 @@ test.describe('Import OpenAPI Collection with Examples', () => {
await test.step('Complete import by clicking import button', async () => {
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
});
await test.step('Handle sandbox modal', async () => {

View File

@@ -30,6 +30,7 @@ test.describe('OpenAPI Newline Handling', () => {
// select a location
await page.locator('#collection-location').fill(await createTmpDir('newline-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// verify the collection was imported successfully
await expect(page.locator('#sidebar-collection-name').getByText('Newline Test Collection')).toBeVisible();

View File

@@ -34,6 +34,7 @@ test.describe('OpenAPI Path-Based Grouping', () => {
// Select a location and import
await page.locator('#collection-location').fill(await createTmpDir('path-grouping-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// Verify the collection was imported successfully
await expect(page.locator('#sidebar-collection-name').getByText('Path Grouping Test API')).toBeVisible();

View File

@@ -79,6 +79,7 @@ test.describe('Import Postman Collection with Examples', () => {
await test.step('Complete import by clicking import button', async () => {
const locationModal = page.locator('[data-testid="import-collection-location-modal"]');
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
});
await test.step('Open collection', async () => {

View File

@@ -16,9 +16,7 @@ test.describe('Invalid Postman Collection - Invalid JSON', () => {
await page.setInputFiles('input[type="file"]', postmanFile);
// Check for error message
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
expect(hasError).toBe(true);
await expect(page.getByText('Unsupported collection format').first()).toBeVisible();
// Cleanup: close any open modals
await page.getByTestId('modal-close-button').click();

View File

@@ -16,8 +16,7 @@ test.describe('Invalid Postman Collection - Missing Info', () => {
await page.setInputFiles('input[type="file"]', postmanFile);
// Check for error message
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
expect(hasError).toBe(true);
await expect(page.getByText('Unsupported collection format').first()).toBeVisible();
// Cleanup: close any open modals
await page.getByTestId('modal-close-button').click();

View File

@@ -16,8 +16,7 @@ test.describe('Invalid Postman Collection - Invalid Schema', () => {
await page.setInputFiles('input[type="file"]', postmanFile);
// Check for error message
const hasError = await page.getByText('Unsupported collection format').first().isVisible();
expect(hasError).toBe(true);
await expect(page.getByText('Unsupported collection format').first()).toBeVisible();
// Cleanup: close any open modals
await page.getByTestId('modal-close-button').click();

View File

@@ -32,6 +32,7 @@ test.describe('Insomnia URL Import', () => {
// Select a location and import
await page.locator('#collection-location').fill(await createTmpDir('test-api-collection-v5'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// Verify the collection was imported successfully and configure it
await expect(page.locator('#sidebar-collection-name').getByText('Test API Collection v5')).toBeVisible();

View File

@@ -36,6 +36,7 @@ test.describe('OpenAPI URL Import', () => {
// Select a location and import with default grouping (tags)
await page.locator('#collection-location').fill(await createTmpDir('swagger-petstore'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// Verify the collection was imported successfully and configure it
await expect(page.locator('#sidebar-collection-name').getByText('Swagger Petstore')).toBeVisible();
@@ -82,6 +83,7 @@ test.describe('OpenAPI URL Import', () => {
// Select a location and import with path-based grouping
await page.locator('#collection-location').fill(await createTmpDir('swagger-petstore-path'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// Verify the collection was imported successfully and configure it
await expect(page.locator('#sidebar-collection-name').getByText('Swagger Petstore')).toBeVisible();

View File

@@ -32,6 +32,7 @@ test.describe('Postman URL Import', () => {
// Select a location and import
await page.locator('#collection-location').fill(await createTmpDir('postman-v21-collection'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
// Verify the collection was imported successfully and configure it
await expect(page.locator('#sidebar-collection-name').getByText('Postman v2.1 Collection')).toBeVisible();

View File

@@ -37,6 +37,7 @@ test.describe('Import WSDL Collection', () => {
// select a location
await page.locator('#collection-location').fill(await createTmpDir('wsdl-xml-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
await expect(page.locator('#sidebar-collection-name').getByText('TestWSDLServiceXML')).toBeVisible();
});
@@ -98,6 +99,7 @@ test.describe('Import WSDL Collection', () => {
// select a location
await page.locator('#collection-location').fill(await createTmpDir('wsdl-json-test'));
await locationModal.getByRole('button', { name: 'Import' }).click();
await locationModal.waitFor({ state: 'hidden' });
});
await test.step('Verify that the collection was imported successfully', async () => {

View File

@@ -127,8 +127,8 @@ test.describe('manage protofile', () => {
// Use more specific selector for proto file selection
await page.locator('div').filter({ hasText: /^product\.proto\.\.\/protos\/services\/product\.proto$/ }).first().click();
const loadedMethodsMessage = await page.getByText('Failed to load gRPC methods: Unknown error').first().isVisible();
expect(loadedMethodsMessage).toBe(true);
// Verify the error message is visible (auto-retrying)
await expect(page.getByText('Failed to load gRPC methods: Unknown error').first()).toBeVisible();
// Check that methods dropdown is not visible when loading fails
const methodsDropdown = page.getByTestId('grpc-methods-dropdown');

View File

@@ -47,7 +47,8 @@ test('should persist request with newlines across app restarts', async ({ create
await getTableCell(postResRow, 1).locator('.CodeMirror').click();
await getTableCell(postResRow, 1).locator('textarea').fill('post\nResponse\nValue');
await page.keyboard.press('Meta+s');
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
await app1.close();
// Verify persistence after restart

View File

@@ -1,5 +1,7 @@
import { test, expect } from '../../../../playwright';
const findShortcut = process.platform === 'darwin' ? 'Meta+f' : 'Control+f';
test.describe('Custom Search Functionality in Scripts Tab', () => {
test('should open search box when Cmd+F or Ctrl+F is pressed in scripts tab', async ({ pageWithUserData: page }) => {
await page.getByTestId('collections').locator('#sidebar-collection-name').filter({ hasText: 'custom-search' }).click();
@@ -22,7 +24,7 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
const preContent = await preRequestEditor.textContent();
console.log('Pre Request content loaded:', preContent?.substring(0, 100));
await page.keyboard.press('Meta+f');
await page.keyboard.press(findShortcut);
// Verify search box appears
const preEditorSearchBar = page.getByTestId('pre-request-script-editor');
@@ -74,7 +76,7 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
const preRequestEditor = page.getByTestId('pre-request-script-editor').locator('.CodeMirror').first();
const preTextarea = preRequestEditor.locator('textarea[tabindex="0"]');
await preTextarea.focus();
await page.keyboard.press('Meta+f');
await page.keyboard.press(findShortcut);
const preSearchInput = page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await preSearchInput.fill('uniquePreVar');
@@ -87,7 +89,7 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
const postResponseEditor = page.getByTestId('post-response-script-editor').locator('.CodeMirror').first();
const postTextarea = postResponseEditor.locator('textarea[tabindex="0"]');
await postTextarea.focus();
await page.keyboard.press('Meta+f');
await page.keyboard.press(findShortcut);
const postSearchInput = page.getByTestId('post-response-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await postSearchInput.fill('uniquePostVar');
@@ -108,7 +110,7 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
const preRequestEditor = page.getByTestId('pre-request-script-editor').locator('.CodeMirror').first();
const preTextarea = preRequestEditor.locator('textarea[tabindex="0"]');
await preTextarea.focus();
await page.keyboard.press('Meta+f');
await page.keyboard.press(findShortcut);
const preSearchInput = page.getByTestId('pre-request-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await preSearchInput.fill('commonVar');
@@ -121,7 +123,7 @@ test.describe('Custom Search Functionality in Scripts Tab', () => {
const postResponseEditor = page.getByTestId('post-response-script-editor').locator('.CodeMirror').first();
const postTextarea = postResponseEditor.locator('textarea[tabindex="0"]');
await postTextarea.focus();
await page.keyboard.press('Meta+f');
await page.keyboard.press(findShortcut);
const postSearchInput = page.getByTestId('post-response-script-editor').locator('.bruno-search-bar input[placeholder="Search..."]');
await postSearchInput.fill('postVar');

View File

@@ -176,7 +176,8 @@ test.describe.serial('Edit Response Examples', () => {
await page.getByTestId('response-example-edit-btn').click();
await page.getByTestId('response-example-name-input').clear();
await page.getByTestId('response-example-name-input').fill('Keyboard Shortcut Test');
await page.keyboard.press('Meta+s');
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
await page.keyboard.press(saveShortcut);
await expect(page.getByTestId('response-example-title')).toHaveText('edit-example / Keyboard Shortcut Test');
});
});

View File

@@ -2,6 +2,8 @@ import { test, expect } from '../../playwright';
import { createTransientRequest, fillRequestUrl, closeAllCollections, createCollection, sendRequest, clickResponseAction, selectRequestPaneTab } from '../utils/page';
import { buildCommonLocators, buildWebsocketCommonLocators } from '../utils/page/locators';
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
test.describe.serial('Transient Requests', () => {
let locators: ReturnType<typeof buildCommonLocators>;
@@ -123,7 +125,7 @@ test.describe.serial('Transient Requests', () => {
await test.step('Trigger save action using keyboard shortcut', async () => {
// Try to save using Cmd+S (Mac) or Ctrl+S (other platforms)
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
await page.waitForTimeout(500);
});
@@ -171,7 +173,7 @@ test.describe.serial('Transient Requests', () => {
});
await test.step('Trigger save action using keyboard shortcut', async () => {
await page.keyboard.press('Meta+s');
await page.keyboard.press(saveShortcut);
await page.waitForTimeout(500);
});

View File

@@ -2,9 +2,9 @@ import { expect, Locator, test } from '../../playwright';
import { buildWebsocketCommonLocators } from '../utils/page/locators';
import { readFile, writeFile } from 'fs/promises';
import { join } from 'path';
import { waitForPredicate } from '../utils/wait';
const BRU_REQ_NAME = /^base$/;
const BRU_PATH = join(__dirname, 'fixtures/collection/base.bru');
// TODO: reaper move to someplace common
const isRequestSaved = async (saveButton: Locator) => {
@@ -14,53 +14,43 @@ const isRequestSaved = async (saveButton: Locator) => {
test.describe.serial('persistence', () => {
let originalUrl = '';
let originalContext = {
path: join(__dirname, 'fixtures/collection/base.bru'),
data: ''
};
let originalData = '';
test.beforeAll(async () => {
// Store original request data to simplify test consistency
originalContext.data = await readFile(originalContext.path, 'utf8');
const originalUrlMatch = originalContext.data.match(`(url)\s*\:\s*(.+)`);
originalData = await readFile(BRU_PATH, 'utf8');
const originalUrlMatch = originalData.match(`(url)\s*\:\s*(.+)`);
if (!originalUrlMatch) {
throw new Error('url not found in bru file for websocket');
}
originalUrl = originalUrlMatch[0].replace(/url\:/, '');
// Trim to remove leading/trailing whitespace from the regex capture
originalUrl = originalUrlMatch[0].replace(/url\:/, '').trim();
});
test.afterAll(async () => {
// Write back the original request information
await writeFile(originalContext.path, originalContext.data, 'utf8');
// Restore original fixture since pageWithUserData does not isolate collection files
await writeFile(BRU_PATH, originalData, 'utf8');
});
test('save new websocket url', async ({ pageWithUserData: page }) => {
const replacementUrl = 'ws://localhost:8083';
const locators = buildWebsocketCommonLocators(page);
const clearText = async (text: string) => {
for (let i = text.length; i > 0; i--) {
await page.keyboard.press('Backspace');
}
};
const selectAllShortcut = process.platform === 'darwin' ? 'Meta+a' : 'Control+a';
await page.locator('#sidebar-collection-name').click();
await page.getByTitle(BRU_REQ_NAME).click();
// remove the original url from the request
// Select all text in the URL input and replace with new URL
await page.locator('.input-container').filter({ hasText: originalUrl }).first().click();
await clearText(originalUrl);
// replace it with an arbritrary url
await page.keyboard.press(selectAllShortcut);
await page.keyboard.insertText(replacementUrl);
// check if the request is now unsaved
await expect(await isRequestSaved(locators.saveButton())).toBe(false);
// Use auto-retrying assertion to check if the request is now unsaved
await expect.poll(() => isRequestSaved(locators.saveButton())).toBe(false);
await locators.saveButton().click();
const result = await waitForPredicate(() => isRequestSaved(locators.saveButton()));
await expect(result).toBe(true);
// Use auto-retrying assertion to verify save completed
await expect.poll(() => isRequestSaved(locators.saveButton())).toBe(true);
// check if the replacementUrl is now visually available
await expect(page.locator('.input-container').filter({ hasText: replacementUrl }).first()).toBeAttached();