From 6455b007420918294df942a5ae435edd8d7e7232 Mon Sep 17 00:00:00 2001 From: ramki-bruno Date: Mon, 14 Apr 2025 13:33:22 +0530 Subject: [PATCH 1/2] Removed old Playwright setup --- package-lock.json | 64 ---------------------- package.json | 3 -- playwright.config.js | 110 -------------------------------------- tests/home.spec.js | 48 ----------------- tests/pages/home.page.js | 86 ----------------------------- tests/utils/data-faker.js | 6 --- 6 files changed, 317 deletions(-) delete mode 100644 playwright.config.js delete mode 100644 tests/home.spec.js delete mode 100644 tests/pages/home.page.js delete mode 100644 tests/utils/data-faker.js diff --git a/package-lock.json b/package-lock.json index 8bf023cfd..1fa51c2c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,6 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", - "@playwright/test": "^1.27.1", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", "concurrently": "^8.2.2", @@ -6104,22 +6103,6 @@ "node": ">=14" } }, - "node_modules/@playwright/test": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", - "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.49.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -21299,53 +21282,6 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", "license": "MIT" }, - "node_modules/playwright": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", - "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.49.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", - "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", diff --git a/package.json b/package.json index 30d41bb01..4bd03c657 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", - "@playwright/test": "^1.27.1", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", "concurrently": "^8.2.2", @@ -59,8 +58,6 @@ "build:electron:snap": "./scripts/build-electron.sh snap", "watch:common": "npm run watch --workspace=packages/bruno-common", "test:codegen": "npm run dev:web & node ./scripts/playwright-codegen.js", - "test:e2e": "npx playwright test", - "test:report": "npx playwright show-report", "test:prettier:web": "npm run test:prettier --workspace=packages/bruno-app", "prepare": "husky install", "lint": "npx eslint ./" diff --git a/playwright.config.js b/playwright.config.js deleted file mode 100644 index 63afd88d4..000000000 --- a/playwright.config.js +++ /dev/null @@ -1,110 +0,0 @@ -// @ts-check -const { devices } = require('@playwright/test'); - -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// require('dotenv').config(); - -process.env.PLAYWRIGHT = "1"; - -/** - * @see https://playwright.dev/docs/test-configuration - * @type {import('@playwright/test').PlaywrightTestConfig} - */ -const config = { - testDir: './tests', - /* Maximum time one test can run for. */ - timeout: 30 * 1000, - expect: { - /** - * Maximum time expect() should wait for the condition to be met. - * For example in `await expect(locator).toHaveText();` - */ - timeout: 5000 - }, - /* Run tests in files in parallel */ - fullyParallel: true, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ - actionTimeout: 0, - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: 'http://localhost:3000', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'retain-on-failure', - }, - - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - - { - name: 'firefox', - use: { - ...devices['Desktop Firefox'], - }, - }, - - { - name: 'webkit', - use: { - ...devices['Desktop Safari'], - }, - }, - - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { - // ...devices['Pixel 5'], - // }, - // }, - // { - // name: 'Mobile Safari', - // use: { - // ...devices['iPhone 12'], - // }, - // }, - - /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { - // channel: 'msedge', - // }, - // }, - // { - // name: 'Google Chrome', - // use: { - // channel: 'chrome', - // }, - // }, - ], - - /* Folder for test artifacts such as screenshots, videos, traces, etc. */ - // outputDir: 'test-results/', - - /* Run your local dev server before starting the tests */ - webServer: { - command: 'npm run dev:web', - port: 3000, - }, -}; - -module.exports = config; diff --git a/tests/home.spec.js b/tests/home.spec.js deleted file mode 100644 index 0ab8a5652..000000000 --- a/tests/home.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -const { test, expect } = require('@playwright/test'); -const { HomePage } = require('../tests/pages/home.page'); -const { faker } = require('./utils/data-faker'); - -test.describe('bruno e2e test', () => { - let homePage; - - test.beforeEach(async ({ page }) => { - homePage = new HomePage(page); - - await homePage.open(); - await expect(page).toHaveURL('/'); - await expect(page).toHaveTitle(/bruno/); - }); - - test('user should be able to create new collection & new request', async () => { - await homePage.createNewCollection(faker.randomWords); - await expect(homePage.createNewCollectionSuccessToast).toBeVisible(); - - // using fake data to simulate negative case - await homePage.createNewRequest(faker.randomVerb, faker.randomHttpMethod, faker.randomUrl); - await expect(homePage.networkErrorToast).toBeVisible(); - - // using real data to simulate positive case - await homePage.createNewRequest('Single User', 'GET', 'https://reqres.in/api/users/2'); - await expect(homePage.statusRequestSuccess).toBeVisible(); - }); - - test('user should be able to load & use sample collection', async () => { - await homePage.loadSampleCollection(); - await expect(homePage.loadSampleCollectionSuccessToast).toBeVisible(); - - await homePage.getUsers(); - await expect(homePage.statusRequestSuccess).toBeVisible(); - - await homePage.getSingleUser(); - await expect(homePage.statusRequestSuccess).toBeVisible(); - - await homePage.getUserNotFound(); - await expect(homePage.statusRequestNotFound).toBeVisible(); - - await homePage.createUser(); - await expect(homePage.statusRequestCreated).toBeVisible(); - - await homePage.updateUser(); - await expect(homePage.statusRequestSuccess).toBeVisible(); - }); -}); diff --git a/tests/pages/home.page.js b/tests/pages/home.page.js deleted file mode 100644 index 4aff24ce1..000000000 --- a/tests/pages/home.page.js +++ /dev/null @@ -1,86 +0,0 @@ -exports.HomePage = class HomePage { - constructor(page) { - this.page = page; - - // welcome - this.createCollectionSelector = page.locator('#create-collection'); - this.addCollectionSelector = page.locator('#add-collection'); - this.importCollectionSelector = page.locator('#import-collection'); - this.loadSampleCollectionSelector = page.locator('#load-sample-collection'); - - // sample collection - this.loadSampleCollectionSuccessToast = page.getByText('Sample Collection loaded successfully'); - this.sampleCollectionSelector = page.locator('#sidebar-collection-name'); - this.getUsersSelector = page.getByText('Users'); - this.getSingleUserSelector = page.getByText('Single User'); - this.getUserNotFoundSelector = page.getByText('User Not Found'); - this.postCreateSelector = page.getByText('Create'); - this.putUpdateSelector = page.getByText('Update'); - - // request panel - this.sendRequestButton = page.locator('#send-request'); - this.statusRequestSuccess = page.getByText('200 OK'); - this.statusRequestNotFound = page.getByText('404 Not Found'); - this.statusRequestCreated = page.getByText('201 Created'); - - // create collection - this.collectionNameField = page.locator('#collection-name'); - this.submitButton = page.locator(`button[type='submit']`); - this.createNewCollectionSuccessToast = page.getByText('Collection created'); - this.createNewTab = page.locator('#create-new-tab'); - this.requestNameField = page.locator('input[name="requestName"]'); - this.methodName = page.locator('#create-new-request-method').first(); - this.requestUrlField = page.locator('#request-url'); - this.networkErrorToast = page.getByText('Network Error'); - } - - async open() { - await this.page.goto('/'); - } - - async loadSampleCollection() { - await this.loadSampleCollectionSelector.click(); - } - - async getUsers() { - await this.sampleCollectionSelector.click(); - await this.getUsersSelector.click(); - await this.sendRequestButton.click(); - } - - async getSingleUser() { - await this.getSingleUserSelector.click(); - await this.sendRequestButton.click(); - } - - async getUserNotFound() { - await this.getUserNotFoundSelector.click(); - await this.sendRequestButton.click(); - } - - async createUser() { - await this.postCreateSelector.click(); - await this.sendRequestButton.click(); - } - - async updateUser() { - await this.putUpdateSelector.click(); - await this.sendRequestButton.click(); - } - - async createNewCollection(collectionName) { - await this.createCollectionSelector.click(); - await this.collectionNameField.fill(collectionName); - await this.submitButton.click(); - } - - async createNewRequest(name, method, endpoint) { - await this.createNewTab.click(); - await this.requestNameField.fill(name); - await this.methodName.click(); - await this.page.click(`text=${method}`); - await this.requestUrlField.fill(endpoint); - await this.submitButton.click(); - await this.sendRequestButton.click(); - } -}; diff --git a/tests/utils/data-faker.js b/tests/utils/data-faker.js deleted file mode 100644 index 2674b6244..000000000 --- a/tests/utils/data-faker.js +++ /dev/null @@ -1,6 +0,0 @@ -const { faker } = require('@faker-js/faker'); - -export let randomWords = faker.random.words(); -export let randomVerb = faker.hacker.verb(); -export let randomHttpMethod = faker.internet.httpMethod(); -export let randomUrl = faker.internet.url(); From aa911f88f2afa770558997a0d2a3d37b29bf78de Mon Sep 17 00:00:00 2001 From: ramki-bruno Date: Mon, 14 Apr 2025 14:24:50 +0530 Subject: [PATCH 2/2] Playwright Codegen and CI setup - Improved the Codegen setup - Removed the app-launch related boilerplate from tests - Enable recording mode by default - Option to provide the test file name to save the recording - Added GitHub workflow to run Playwright tests with Electron in Headless mode(mocking display using `xvfb`). --- .github/workflows/playwright.yml | 44 ++++++++++++++++ .gitignore | 5 +- e2e-tests/test-app-start.spec.ts | 5 ++ package-lock.json | 86 +++++++++++++++++++++++++++++--- package.json | 6 ++- playwright.config.ts | 25 ++++++++++ playwright/codegen.ts | 13 +++++ playwright/electron.ts | 13 +++++ playwright/index.ts | 23 +++++++++ scripts/playwright-codegen.js | 17 ------- 10 files changed, 210 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 e2e-tests/test-app-start.spec.ts create mode 100644 playwright.config.ts create mode 100644 playwright/codegen.ts create mode 100644 playwright/electron.ts create mode 100644 playwright/index.ts delete mode 100644 scripts/playwright-codegen.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 000000000..f630b56cb --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,44 @@ +name: Playwright E2E Tests +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + e2e-test: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: v22.11.x + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get --no-install-recommends install -y \ + libglib2.0-0 libnss3 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libgtk-3-0 libasound2t64 \ + xvfb + npm ci --legacy-peer-deps + sudo chown root /home/runner/work/bruno/bruno/node_modules/electron/dist/chrome-sandbox + sudo chmod 4755 /home/runner/work/bruno/bruno/node_modules/electron/dist/chrome-sandbox + + - name: Build libraries + run: | + npm run build:graphql-docs + npm run build:bruno-query + npm run build:bruno-common + npm run sandbox:bundle-libraries --workspace=packages/bruno-js + npm run build:bruno-converters + npm run build:bruno-requests + + - name: Run Playwright tests + run: | + xvfb-run npm run test:e2e + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index b97cd17e3..9331b13ff 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,7 @@ yarn-error.log* #dev editor bruno.iml .idea -.vscode \ No newline at end of file +.vscode + +# Playwright +/blob-report/ diff --git a/e2e-tests/test-app-start.spec.ts b/e2e-tests/test-app-start.spec.ts new file mode 100644 index 000000000..891c7ce3b --- /dev/null +++ b/e2e-tests/test-app-start.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from '../playwright'; + +test('test-app-start', async ({ page }) => { + await expect(page.getByRole('button', { name: 'bruno' })).toBeVisible(); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1fa51c2c1..15809857d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,10 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", + "@playwright/test": "^1.51.1", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", + "@types/node": "^22.14.1", "concurrently": "^8.2.2", "eslint": "^9.26.0", "fs-extra": "^11.1.1", @@ -33,6 +35,7 @@ "jest": "^29.2.0", "lint-staged": "^15.5.2", "lodash-es": "^4.17.21", + "playwright": "^1.51.1", "pretty-quick": "^3.1.3", "randomstring": "^1.2.2", "rimraf": "^6.0.1", @@ -6103,6 +6106,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -8273,6 +8292,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, "license": "MIT" }, "node_modules/@types/lodash": { @@ -8295,6 +8315,7 @@ "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/linkify-it": "*", @@ -8305,6 +8326,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, "license": "MIT" }, "node_modules/@types/ms": { @@ -8315,13 +8337,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "version": "22.15.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", + "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/plist": { @@ -13246,6 +13268,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -21282,6 +21305,53 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", "license": "MIT" }, + "node_modules/playwright": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -26410,7 +26480,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -26436,9 +26506,9 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "devOptional": true, "license": "MIT" }, diff --git a/package.json b/package.json index 4bd03c657..8d7ee6287 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,10 @@ "devDependencies": { "@faker-js/faker": "^7.6.0", "@jest/globals": "^29.2.0", + "@playwright/test": "^1.51.1", "@types/jest": "^29.5.11", "@types/lodash-es": "^4.17.12", + "@types/node": "^22.14.1", "concurrently": "^8.2.2", "eslint": "^9.26.0", "fs-extra": "^11.1.1", @@ -30,6 +32,7 @@ "jest": "^29.2.0", "lint-staged": "^15.5.2", "lodash-es": "^4.17.21", + "playwright": "^1.51.1", "pretty-quick": "^3.1.3", "randomstring": "^1.2.2", "rimraf": "^6.0.1", @@ -57,7 +60,8 @@ "build:electron:rpm": "./scripts/build-electron.sh rpm", "build:electron:snap": "./scripts/build-electron.sh snap", "watch:common": "npm run watch --workspace=packages/bruno-common", - "test:codegen": "npm run dev:web & node ./scripts/playwright-codegen.js", + "test:codegen": "node playwright/codegen.ts", + "test:e2e": "playwright test", "test:prettier:web": "npm run test:prettier --workspace=packages/bruno-app", "prepare": "husky install", "lint": "npx eslint ./" diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 000000000..b0adb030f --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e-tests', + fullyParallel: false, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? undefined : 1, + reporter: 'html', + use: { + trace: 'on-first-retry' + }, + + projects: [ + { + name: 'Bruno Electron App' + } + ], + + webServer: { + command: 'npm run dev:web', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI + } +}); diff --git a/playwright/codegen.ts b/playwright/codegen.ts new file mode 100644 index 000000000..da2bfcb1f --- /dev/null +++ b/playwright/codegen.ts @@ -0,0 +1,13 @@ +const path = require('path'); +const { startApp } = require('./electron.ts'); + +async function main() { + const { app, context } = await startApp(); + let outputFile = process.argv[2]?.trim(); + if (outputFile && !/\.(ts|js)$/.test(outputFile)) { + outputFile = path.join(__dirname, '../e2e-tests/', outputFile + '.spec.ts'); + } + await context._enableRecorder({ language: 'playwright-test', mode: 'recording', outputFile }); +} + +main(); diff --git a/playwright/electron.ts b/playwright/electron.ts new file mode 100644 index 000000000..bc49363f1 --- /dev/null +++ b/playwright/electron.ts @@ -0,0 +1,13 @@ +const path = require('path'); +const { _electron: electron } = require('playwright'); + +const electronAppPath = path.join(__dirname, '../packages/bruno-electron'); + +exports.startApp = async () => { + const app = await electron.launch({ args: [electronAppPath] }); + const context = await app.context(); + + app.process().stdout.on('data', (data) => console.log(data.toString())); + app.process().stderr.on('data', (error) => console.error(error.toString())); + return { app, context }; +}; diff --git a/playwright/index.ts b/playwright/index.ts new file mode 100644 index 000000000..ca865437d --- /dev/null +++ b/playwright/index.ts @@ -0,0 +1,23 @@ +import { test as baseTest, ElectronApplication, Page } from '@playwright/test'; + +const { startApp } = require('./electron.ts'); + +export const test = baseTest.extend<{ page: Page }, { electronApp: ElectronApplication }>({ + electronApp: [ + async ({}, use) => { + const { app: electronApp, context } = await startApp(); + + await use(electronApp); + await context.close(); + await electronApp.close(); + }, + { scope: 'worker' } + ], + page: async ({ electronApp }, use) => { + const page = await electronApp.firstWindow(); + await use(page); + await page.reload(); + } +}); + +export * from '@playwright/test' diff --git a/scripts/playwright-codegen.js b/scripts/playwright-codegen.js deleted file mode 100644 index ae96c7a41..000000000 --- a/scripts/playwright-codegen.js +++ /dev/null @@ -1,17 +0,0 @@ -const path = require('path'); -const timer = require('node:timers/promises'); -const { _electron: electron } = require('playwright'); - -const electronAppPath = path.join(__dirname, '../packages/bruno-electron'); - -(async () => { - const browser = await electron.launch({ args: [electronAppPath] }); - const context = await browser.context(); - await context.route('**/*', (route) => route.continue()); - - while (true) { - if(browser.windows().length) break; - await timer.setTimeout(200); - } - await browser.windows()[0].pause(); -})();