diff --git a/eslint.config.js b/eslint.config.js index 30d59bc0f..523623f28 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -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', diff --git a/playwright/index.ts b/playwright/index.ts index 8b476ac55..67262a357 100644 --- a/playwright/index.ts +++ b/playwright/index.ts @@ -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; launchElectronApp: (options?: { initUserDataPath?: string; userDataPath?: string; dotEnv?: Record }) => Promise; electronApp: ElectronApplication; - reuseOrLaunchElectronApp: (options?: { initUserDataPath?: string; userDataPath?: string; dotEnv?: Record }) => Promise; + reuseOrLaunchElectronApp: (options?: { initUserDataPath?: string; testFile?: string; userDataPath?: string; dotEnv?: Record }) => Promise; } >({ createTmpDir: [ @@ -150,8 +170,8 @@ export const test = baseTest.extend< reuseOrLaunchElectronApp: [ async ({ launchElectronApp }, use, testInfo) => { const apps: Record = {}; - 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(); diff --git a/tests/collection/moving-requests/cross-collection-drag-drop-folder.spec.ts b/tests/collection/moving-requests/cross-collection-drag-drop-folder.spec.ts index 13147d3e4..9295ee474 100644 --- a/tests/collection/moving-requests/cross-collection-drag-drop-folder.spec.ts +++ b/tests/collection/moving-requests/cross-collection-drag-drop-folder.spec.ts @@ -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 diff --git a/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts b/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts index e1c0c832d..78e6bf25f 100644 --- a/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts +++ b/tests/collection/moving-requests/cross-collection-drag-drop-request.spec.ts @@ -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) diff --git a/tests/collection/moving-requests/tag-persistence.spec.ts b/tests/collection/moving-requests/tag-persistence.spec.ts index d6088a0ca..56cc17c8e 100644 --- a/tests/collection/moving-requests/tag-persistence.spec.ts +++ b/tests/collection/moving-requests/tag-persistence.spec.ts @@ -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'); diff --git a/tests/collection/moving-tabs/move-tabs.spec.ts b/tests/collection/moving-tabs/move-tabs.spec.ts index c672f5435..68e221daf 100644 --- a/tests/collection/moving-tabs/move-tabs.spec.ts +++ b/tests/collection/moving-tabs/move-tabs.spec.ts @@ -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(); diff --git a/tests/environments/create-environment/collection-env-create.spec.ts b/tests/environments/create-environment/collection-env-create.spec.ts index 94c5f86a3..011f9ef03 100644 --- a/tests/environments/create-environment/collection-env-create.spec.ts +++ b/tests/environments/create-environment/collection-env-create.spec.ts @@ -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'); diff --git a/tests/environments/create-environment/global-env-create.spec.ts b/tests/environments/create-environment/global-env-create.spec.ts index 179aa4228..a32d3210d 100644 --- a/tests/environments/create-environment/global-env-create.spec.ts +++ b/tests/environments/create-environment/global-env-create.spec.ts @@ -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'); diff --git a/tests/environments/import-environment/collection-env-import.spec.ts b/tests/environments/import-environment/collection-env-import.spec.ts index b13afb02e..312dee58f 100644 --- a/tests/environments/import-environment/collection-env-import.spec.ts +++ b/tests/environments/import-environment/collection-env-import.spec.ts @@ -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'); diff --git a/tests/environments/import-environment/global-env-import.spec.ts b/tests/environments/import-environment/global-env-import.spec.ts index 5b545c896..9df911278 100644 --- a/tests/environments/import-environment/global-env-import.spec.ts +++ b/tests/environments/import-environment/global-env-import.spec.ts @@ -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'); diff --git a/tests/global-environments/non-string-values.spec.ts b/tests/global-environments/non-string-values.spec.ts index 9d0a2acd3..73fff4e83 100644 --- a/tests/global-environments/non-string-values.spec.ts +++ b/tests/global-environments/non-string-values.spec.ts @@ -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); }); diff --git a/tests/interpolation/dynamic-variable/set-var-dynamic-variable.spec.ts b/tests/interpolation/dynamic-variable/set-var-dynamic-variable.spec.ts index 4eb57fa6f..e9b24b8ca 100644 --- a/tests/interpolation/dynamic-variable/set-var-dynamic-variable.spec.ts +++ b/tests/interpolation/dynamic-variable/set-var-dynamic-variable.spec.ts @@ -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); }); diff --git a/tests/protobuf/manage-protofile.spec.ts b/tests/protobuf/manage-protofile.spec.ts index ccf88abca..b8c6418cb 100644 --- a/tests/protobuf/manage-protofile.spec.ts +++ b/tests/protobuf/manage-protofile.spec.ts @@ -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); }); diff --git a/tests/request/encoding/curl-encoding.spec.ts b/tests/request/encoding/curl-encoding.spec.ts index 28d908a8e..255d22a55 100644 --- a/tests/request/encoding/curl-encoding.spec.ts +++ b/tests/request/encoding/curl-encoding.spec.ts @@ -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(); diff --git a/tests/ssl/basic-ssl/tests/basic-ssl-success/basic-ssl-success.spec.ts b/tests/ssl/basic-ssl/tests/basic-ssl-success/basic-ssl-success.spec.ts index 00159174c..26eb6bf2f 100644 --- a/tests/ssl/basic-ssl/tests/basic-ssl-success/basic-ssl-success.spec.ts +++ b/tests/ssl/basic-ssl/tests/basic-ssl-success/basic-ssl-success.spec.ts @@ -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(); diff --git a/tests/ssl/basic-ssl/tests/self-signed-rejected/self-signed-rejected.spec.ts b/tests/ssl/basic-ssl/tests/self-signed-rejected/self-signed-rejected.spec.ts index f2165121b..0674154a4 100644 --- a/tests/ssl/basic-ssl/tests/self-signed-rejected/self-signed-rejected.spec.ts +++ b/tests/ssl/basic-ssl/tests/self-signed-rejected/self-signed-rejected.spec.ts @@ -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(); diff --git a/tests/ssl/custom-ca-certs/tests/custom-invalid-ca-cert-in-config-with-defaults/custom-invalid-ca-cert-in-config-with-defaults.spec.ts b/tests/ssl/custom-ca-certs/tests/custom-invalid-ca-cert-in-config-with-defaults/custom-invalid-ca-cert-in-config-with-defaults.spec.ts index 5ff1e72db..7b43ead19 100644 --- a/tests/ssl/custom-ca-certs/tests/custom-invalid-ca-cert-in-config-with-defaults/custom-invalid-ca-cert-in-config-with-defaults.spec.ts +++ b/tests/ssl/custom-ca-certs/tests/custom-invalid-ca-cert-in-config-with-defaults/custom-invalid-ca-cert-in-config-with-defaults.spec.ts @@ -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();