mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
Refactor: Change how test runner handles pageWithUserData tests (#5922)
* refactor: change how test runner opens pageWithUserData instances * fix: test move tabs * fix: custom ca cert tests * fix: update file patterns and improve error messages * fix: improve electron app launch logic * fix: update temporary directory handling for Electron app * fix: ensure newline at end of file in index.ts This change adds a newline at the end of the file to comply with coding standards. * fix: improve error handling in recursiveCopy function - Simplified error message when source path does not exist. - Enhanced error handling to provide clearer guidance on usage of `page` fixture. * fix(e2e): close collections after each tests * fix: reuse the worker instance per file instead of per user data dir * fix: revert ssl tests as serial run is fixed * fix: change afterEach to afterAll for cleanup fix: change afterEach to afterAll for cleanup --------- Co-authored-by: Bijin Bruno <bijin@usebruno.com>
This commit is contained in:
@@ -26,6 +26,7 @@ module.exports = runESMImports().then(() => defineConfig([
|
||||
files: [
|
||||
'./eslint.config.js',
|
||||
'tests/**/*.{ts,js}',
|
||||
'playwright/**/*.{js,ts}',
|
||||
'packages/bruno-app/**/*.{js,jsx,ts}',
|
||||
'packages/bruno-app/src/test-utils/mocks/codemirror.js',
|
||||
'packages/bruno-cli/**/*.js',
|
||||
|
||||
@@ -5,6 +5,26 @@ import * as fs from 'fs';
|
||||
|
||||
const electronAppPath = path.join(__dirname, '../packages/bruno-electron');
|
||||
|
||||
const existsAsync = (filepath: string) => fs.promises.access(filepath).then(() => true).catch(() => false);
|
||||
|
||||
async function recursiveCopy(src: string, dest: string) {
|
||||
if (!await existsAsync(src)) {
|
||||
throw new Error(`${src} doesn't exist`);
|
||||
}
|
||||
|
||||
const files = await fs.promises.readdir(src, {
|
||||
recursive: true,
|
||||
withFileTypes: true
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.isFile()) continue;
|
||||
const fullPath = path.join(src, file.name);
|
||||
const fullDestPath = path.join(dest, file.name);
|
||||
await fs.promises.copyFile(fullPath, fullDestPath);
|
||||
}
|
||||
}
|
||||
|
||||
export const test = baseTest.extend<
|
||||
{
|
||||
context: BrowserContext;
|
||||
@@ -17,7 +37,7 @@ export const test = baseTest.extend<
|
||||
createTmpDir: (tag?: string) => Promise<string>;
|
||||
launchElectronApp: (options?: { initUserDataPath?: string; userDataPath?: string; dotEnv?: Record<string, string> }) => Promise<ElectronApplication>;
|
||||
electronApp: ElectronApplication;
|
||||
reuseOrLaunchElectronApp: (options?: { initUserDataPath?: string; userDataPath?: string; dotEnv?: Record<string, string> }) => Promise<ElectronApplication>;
|
||||
reuseOrLaunchElectronApp: (options?: { initUserDataPath?: string; testFile?: string; userDataPath?: string; dotEnv?: Record<string, string> }) => Promise<ElectronApplication>;
|
||||
}
|
||||
>({
|
||||
createTmpDir: [
|
||||
@@ -150,8 +170,8 @@ export const test = baseTest.extend<
|
||||
reuseOrLaunchElectronApp: [
|
||||
async ({ launchElectronApp }, use, testInfo) => {
|
||||
const apps: Record<string, ElectronApplication> = {};
|
||||
await use(async ({ initUserDataPath, userDataPath, dotEnv = {} } = {}) => {
|
||||
const key = userDataPath || initUserDataPath;
|
||||
await use(async ({ initUserDataPath, testFile, userDataPath, dotEnv = {} } = {}) => {
|
||||
const key = testFile || userDataPath || initUserDataPath;
|
||||
if (key && apps[key]) {
|
||||
return apps[key];
|
||||
}
|
||||
@@ -191,13 +211,21 @@ export const test = baseTest.extend<
|
||||
}
|
||||
},
|
||||
|
||||
pageWithUserData: async ({ reuseOrLaunchElectronApp }, use, testInfo) => {
|
||||
pageWithUserData: async ({ reuseOrLaunchElectronApp, createTmpDir }, use, testInfo) => {
|
||||
const testDir = path.dirname(testInfo.file);
|
||||
const initUserDataPath = path.join(testDir, 'init-user-data');
|
||||
|
||||
const app = await reuseOrLaunchElectronApp(
|
||||
(await fs.promises.stat(initUserDataPath).catch(() => false)) ? { initUserDataPath } : {}
|
||||
);
|
||||
const tmpAppDataDir = await createTmpDir();
|
||||
try {
|
||||
await recursiveCopy(initUserDataPath, tmpAppDataDir);
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message.includes('doesn\'t exist')) {
|
||||
throw new Error(`${initUserDataPath} doesn't exist, either add one or if you don't need an initial state then use the \`page\` fixture instead of \`pageWithUserData\`.`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
const app = await reuseOrLaunchElectronApp({ initUserDataPath: tmpAppDataDir, testFile: testInfo.file });
|
||||
|
||||
const context = await app.context();
|
||||
const page = await app.firstWindow();
|
||||
|
||||
@@ -2,12 +2,12 @@ import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Cross-Collection Drag and Drop for folder', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Verify cross-collection folder drag and drop', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('Verify cross-collection folder drag and drop', async ({ page, createTmpDir }) => {
|
||||
// Create first collection - click dropdown menu first
|
||||
await page.locator('.dropdown-icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
|
||||
@@ -121,7 +121,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
|
||||
});
|
||||
|
||||
test('Verify cross-collection folder drag and drop, a duplicate folder exist. expected to throw error toast', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
// Create first collection (source) - use unique names for this test
|
||||
|
||||
@@ -2,12 +2,12 @@ import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Cross-Collection Drag and Drop', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Verify request drag and drop', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('Verify request drag and drop', async ({ page, createTmpDir }) => {
|
||||
// Create first collection - click dropdown menu first
|
||||
await page.locator('.dropdown-icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
|
||||
@@ -79,7 +79,7 @@ test.describe('Cross-Collection Drag and Drop', () => {
|
||||
});
|
||||
|
||||
test('Expected to show error toast message, when duplicate request found in drop location', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
// Create first collection (source-collection)
|
||||
|
||||
@@ -2,12 +2,12 @@ import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Tag persistence', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Verify tag persistence while moving requests within a collection', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('Verify tag persistence while moving requests within a collection', async ({ page, createTmpDir }) => {
|
||||
// Create first collection - click dropdown menu first
|
||||
await page.getByLabel('Create Collection').click();
|
||||
await page.getByLabel('Name').fill('test-collection');
|
||||
@@ -72,7 +72,7 @@ test.describe('Tag persistence', () => {
|
||||
await expect(page.getByRole('button', { name: 'smoke' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('verify tag persistence while moving requests between folders', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('verify tag persistence while moving requests between folders', async ({ page, createTmpDir }) => {
|
||||
// Create first collection - click dropdown menu first
|
||||
await page.getByLabel('Create Collection').click();
|
||||
await page.getByLabel('Name').fill('test-collection');
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Move tabs', () => {
|
||||
test('Verify tab move by drag and drop', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('Verify tab move by drag and drop', async ({ page, createTmpDir }) => {
|
||||
// Create a collection
|
||||
await page.locator('.dropdown-icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
|
||||
await page.getByLabel('Name').fill('source-collection');
|
||||
await page.getByLabel('Location').fill(await createTmpDir('source-collection'));
|
||||
await page.getByLabel('Name').fill('source-collection-drag-drop');
|
||||
await page.getByLabel('Location').fill(await createTmpDir('source-collection-drag-drop'));
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
|
||||
// Wait for collection to appear and click on it
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' })).toBeVisible();
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' }).click();
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection-drag-drop' })).toBeVisible();
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection-drag-drop' }).click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// Create a folder in the collection
|
||||
const sourceCollection = page.locator('.collection-name').filter({ hasText: 'source-collection' });
|
||||
const sourceCollection = page.locator('.collection-name').filter({ hasText: 'source-collection-drag-drop' });
|
||||
await sourceCollection.locator('.collection-actions').hover();
|
||||
await sourceCollection.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Folder' }).click();
|
||||
@@ -89,22 +95,22 @@ test.describe('Move tabs', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('Verify tab move by keyboard shortcut', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('Verify tab move by keyboard shortcut', async ({ page, createTmpDir }) => {
|
||||
// Create a collection
|
||||
await page.locator('.dropdown-icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Create Collection' }).click();
|
||||
await page.getByLabel('Name').fill('source-collection');
|
||||
await page.getByLabel('Location').fill(await createTmpDir('source-collection'));
|
||||
await page.getByLabel('Name').fill('source-collection-keyboard-shortcut');
|
||||
await page.getByLabel('Location').fill(await createTmpDir('source-collection-keyboard-shortcut'));
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
|
||||
// Wait for collection to appear and click on it
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' })).toBeVisible();
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection' }).click();
|
||||
await expect(page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection-keyboard-shortcut' })).toBeVisible();
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'source-collection-keyboard-shortcut' }).click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// Create a folder in the collection
|
||||
const sourceCollection = page.locator('.collection-name').filter({ hasText: 'source-collection' });
|
||||
const sourceCollection = page.locator('.collection-name').filter({ hasText: 'source-collection-keyboard-shortcut' });
|
||||
await sourceCollection.locator('.collection-actions').hover();
|
||||
await sourceCollection.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Folder' }).click();
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
|
||||
test.describe('Collection Environment Create Tests', () => {
|
||||
test('should import collection and create environment for request usage', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
|
||||
test.describe('Global Environment Create Tests', () => {
|
||||
test('should import collection and create global environment for request usage', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'bruno-collection.json');
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
|
||||
test.describe('Collection Environment Import Tests', () => {
|
||||
test('should import collection environment from file', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('should import collection environment from file', async ({ page, createTmpDir }) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'collection.json');
|
||||
const envFile = path.join(__dirname, 'fixtures', 'collection-env.json');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../../playwright';
|
||||
import path from 'path';
|
||||
|
||||
test.describe('Global Environment Import Tests', () => {
|
||||
test('should import global environment from file', async ({ pageWithUserData: page, createTmpDir }) => {
|
||||
test('should import global environment from file', async ({ page, createTmpDir }) => {
|
||||
const openApiFile = path.join(__dirname, 'fixtures', 'collection.json');
|
||||
const globalEnvFile = path.join(__dirname, 'fixtures', 'global-env.json');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../playwright';
|
||||
import { openCollectionAndAcceptSandbox, closeAllCollections } from '../utils/page';
|
||||
|
||||
test.describe('Global Environment Variables - Non-string Values', () => {
|
||||
test.afterAll(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
// Cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections, openCollectionAndAcceptSandbox } from '../../utils/page';
|
||||
|
||||
test.describe.serial('Dynamic Variable Interpolation', () => {
|
||||
test.afterAll(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../playwright';
|
||||
import { closeAllCollections } from '../utils/page';
|
||||
|
||||
test.describe('manage protofile', () => {
|
||||
test.afterAll(async ({ page }) => {
|
||||
test.afterAll(async ({ pageWithUserData: page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Code Generation URL Encoding', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
try {
|
||||
const modalCloseButton = page.locator('[data-test-id="modal-close-button"]');
|
||||
if (await modalCloseButton.isVisible()) {
|
||||
@@ -15,7 +15,7 @@ test.describe('Code Generation URL Encoding', () => {
|
||||
});
|
||||
|
||||
test('Should generate code with proper URL encoding for unencoded input', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
await page.locator('.dropdown-icon').click();
|
||||
@@ -57,7 +57,7 @@ test.describe('Code Generation URL Encoding', () => {
|
||||
});
|
||||
|
||||
test('Should generate code with proper URL encoding for encoded input', async ({
|
||||
pageWithUserData: page,
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
await page.locator('.dropdown-icon').click();
|
||||
|
||||
@@ -30,7 +30,7 @@ test.describe.serial('basic ssl success', () => {
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
|
||||
@@ -2,7 +2,6 @@ import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('self signed rejected', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('self-signed-badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
@@ -30,7 +29,6 @@ test.describe.serial('self signed rejected', () => {
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
|
||||
@@ -2,7 +2,6 @@ import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('custom invalid ca cert added to the config and keep default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
@@ -29,7 +28,6 @@ test.describe.serial('custom invalid ca cert added to the config and keep defaul
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
|
||||
Reference in New Issue
Block a user