From 069414cb7fb009b8b06647e74757a34875ce1f8c Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:56:25 +0300 Subject: [PATCH 01/12] test: add e2e test for BTC/BTC reverse swaps --- .github/workflows/playwright.yml | 33 +++++++++++++ .gitignore | 4 ++ .gitmodules | 3 ++ e2e/reverseSwap.spec.ts | 40 +++++++++++++++ e2e/utils.ts | 34 +++++++++++++ package-lock.json | 75 +++++++++++++++++++++++++++-- package.json | 2 + playwright.config.ts | 83 ++++++++++++++++++++++++++++++++ regtest | 1 + src/components/PayInvoice.tsx | 2 +- 10 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 .gitmodules create mode 100644 e2e/reverseSwap.spec.ts create mode 100644 e2e/utils.ts create mode 100644 playwright.config.ts create mode 160000 regtest diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..341a0b23 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,33 @@ +name: E2E tests +on: + push: + branches: [ main ] + pull_request: +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Start regtest + env: + COMPOSE_PROFILES: ci + run: git submodule init && git submodule update && chmod -R 777 regtest && cd regtest && ./start.sh + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: | + npm run start & + sleep 10 + npx playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 1742bb27..4a175b55 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ ts-out/ coverage/ node_modules/ public/config.json +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f18645c2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "regtest"] + path = regtest + url = https://github.com/BoltzExchange/regtest.git diff --git a/e2e/reverseSwap.spec.ts b/e2e/reverseSwap.spec.ts new file mode 100644 index 00000000..5f9dbe7e --- /dev/null +++ b/e2e/reverseSwap.spec.ts @@ -0,0 +1,40 @@ +import { test, expect } from '@playwright/test'; +import { getBitcoinAddress, getBitcoinWalletTx, payInvoiceLnd } from "./utils"; + +test('Reverse swap BTC/BTC', async ({ page }) => { + await page.goto('https://localhost:5173'); + + const receiveAmount = "0.01"; + const inputReceiveAmount = page.locator("input[data-testid='receiveAmount']"); + await inputReceiveAmount.fill(receiveAmount); + + const inputSendAmount = page.locator("input[data-testid='sendAmount']"); + await expect(inputSendAmount).toHaveValue('0.01005558'); + + const inputOnchainAddress = page.locator("input[data-testid='onchainAddress']"); + await inputOnchainAddress.fill(await getBitcoinAddress()); + + const buttonCreateSwap = page.locator("button[data-testid='create-swap-button']"); + await buttonCreateSwap.click(); + + const payInvoiceTitle = page.locator("h2[data-testid='pay-invoice-title']"); + await expect(payInvoiceTitle).toHaveText("Pay this invoice about 0.01005558 BTC"); + + const spanLightningInvoice = page.locator("span[class='btn']"); + await spanLightningInvoice.click(); + + const lightningInvoice = await page.evaluate(() => { + return navigator.clipboard.readText(); + }); + expect(lightningInvoice).toBeDefined(); + + await payInvoiceLnd(lightningInvoice) + + const txIdLink = page.getByText("open claim transaction"); + + const txId = (await txIdLink.getAttribute("href")).split("/").pop(); + expect(txId).toBeDefined(); + + const txInfo = JSON.parse(await getBitcoinWalletTx(txId)); + expect(txInfo.amount.toString()).toEqual(receiveAmount); +}); diff --git a/e2e/utils.ts b/e2e/utils.ts new file mode 100644 index 00000000..324b6004 --- /dev/null +++ b/e2e/utils.ts @@ -0,0 +1,34 @@ +import { exec } from 'child_process'; +import { promisify } from 'util'; + +const execAsync = promisify(exec); + +const executeInScriptsContainer = 'docker exec boltz-scripts bash -c "source /etc/profile.d/utils.sh && '; + +const execCommand = async (command: string): Promise => { + try { + + const { stdout, stderr } = await execAsync(`${executeInScriptsContainer}${command}"`, { shell: '/bin/bash' }); + + if (stderr) { + throw new Error(`Error executing command: ${stderr}`); + } + + return stdout.trim(); + } catch (error) { + console.error(`Failed to execute command: ${command}`, error); + throw error; + } +}; + +export const getBitcoinAddress = async (): Promise => { + return execCommand('bitcoin-cli-sim-client getnewaddress'); +}; + +export const getBitcoinWalletTx = async (txId: string): Promise => { + return execCommand(`bitcoin-cli-sim-client gettransaction ${txId}`); +} + +export const payInvoiceLnd = async (invoice: string): Promise => { + return execCommand(`lncli-sim 1 payinvoice -f ${invoice}`); +}; diff --git a/package-lock.json b/package-lock.json index 12c5446f..a5477e50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,11 +38,13 @@ "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/preset-env": "^7.24.8", "@babel/preset-typescript": "^7.24.7", + "@playwright/test": "^1.45.3", "@solidjs/testing-library": "^0.8.9", "@testing-library/jest-dom": "^6.4.8", "@testing-library/user-event": "^14.5.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/jest": "^29.5.12", + "@types/node": "^22.0.2", "@webbtc/webln-types": "^3.0.0", "babel-jest": "^29.7.0", "babel-preset-jest": "^29.6.3", @@ -3543,6 +3545,21 @@ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz", "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==" }, + "node_modules/@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "dev": true, + "dependencies": { + "playwright": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/plugin-inject": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", @@ -4262,9 +4279,12 @@ } }, "node_modules/@types/node": { - "version": "20.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.0.tgz", - "integrity": "sha512-O+z53uwx64xY7D6roOi4+jApDGFg0qn6WHcxe5QeqjMaTezBO/mxdfFXIVAVVyNWKx84OmPB3L8kbVYOTeN34A==" + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.2.tgz", + "integrity": "sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==", + "dependencies": { + "undici-types": "~6.11.1" + } }, "node_modules/@types/offscreencanvas": { "version": "2019.7.0", @@ -10420,6 +10440,50 @@ "node": ">=10" } }, + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "dev": true, + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "dev": true, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -11796,6 +11860,11 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", + "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index c597dce8..e5babd8f 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,13 @@ "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/preset-env": "^7.24.8", "@babel/preset-typescript": "^7.24.7", + "@playwright/test": "^1.45.3", "@solidjs/testing-library": "^0.8.9", "@testing-library/jest-dom": "^6.4.8", "@testing-library/user-event": "^14.5.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/jest": "^29.5.12", + "@types/node": "^22.0.2", "@webbtc/webln-types": "^3.0.0", "babel-jest": "^29.7.0", "babel-preset-jest": "^29.6.3", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..1911b0c4 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,83 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* 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: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + ignoreHTTPSErrors: true, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + headless: true, + trace: 'on-first-retry', + permissions: ["clipboard-read", "clipboard-write"], + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + /* gfy + { + 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: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/regtest b/regtest new file mode 160000 index 00000000..c99113f1 --- /dev/null +++ b/regtest @@ -0,0 +1 @@ +Subproject commit c99113f13a3ca6acf7fd828856d6dcc99da526f2 diff --git a/src/components/PayInvoice.tsx b/src/components/PayInvoice.tsx index 35ac6365..08ef61d1 100644 --- a/src/components/PayInvoice.tsx +++ b/src/components/PayInvoice.tsx @@ -29,7 +29,7 @@ const PayInvoice = ({ return (
-

+

{t("pay_invoice_to", { amount: formatAmount( BigNumber(sendAmount), From 8c1da4fced0a0710837fed1f6fa882f92a5f1e0e Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:55:16 +0300 Subject: [PATCH 02/12] rework webserver start --- .github/workflows/playwright.yml | 7 +++---- playwright.config.ts | 12 +++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 341a0b23..de5760ac 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -21,10 +21,9 @@ jobs: - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run Playwright tests - run: | - npm run start & - sleep 10 - npx playwright test + env: + CI: true + run: npx playwright test - uses: actions/upload-artifact@v4 if: always() with: diff --git a/playwright.config.ts b/playwright.config.ts index 1911b0c4..84e962dc 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -75,9 +75,11 @@ export default defineConfig({ ], /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, + webServer: { + command: 'npm run start', + port: 5173, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + stderr: 'pipe', + }, }); From 10b47b9c49993e5f0985ccb753e62d7e8833f837 Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:19:06 +0300 Subject: [PATCH 03/12] enable firefox --- e2e/reverseSwap.spec.ts | 100 ++++++++++++++++++++++++---------------- e2e/utils.ts | 21 ++++++--- package.json | 4 +- playwright.config.ts | 51 +++++--------------- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/e2e/reverseSwap.spec.ts b/e2e/reverseSwap.spec.ts index 5f9dbe7e..cce23ba3 100644 --- a/e2e/reverseSwap.spec.ts +++ b/e2e/reverseSwap.spec.ts @@ -1,40 +1,62 @@ -import { test, expect } from '@playwright/test'; -import { getBitcoinAddress, getBitcoinWalletTx, payInvoiceLnd } from "./utils"; - -test('Reverse swap BTC/BTC', async ({ page }) => { - await page.goto('https://localhost:5173'); - - const receiveAmount = "0.01"; - const inputReceiveAmount = page.locator("input[data-testid='receiveAmount']"); - await inputReceiveAmount.fill(receiveAmount); - - const inputSendAmount = page.locator("input[data-testid='sendAmount']"); - await expect(inputSendAmount).toHaveValue('0.01005558'); - - const inputOnchainAddress = page.locator("input[data-testid='onchainAddress']"); - await inputOnchainAddress.fill(await getBitcoinAddress()); - - const buttonCreateSwap = page.locator("button[data-testid='create-swap-button']"); - await buttonCreateSwap.click(); - - const payInvoiceTitle = page.locator("h2[data-testid='pay-invoice-title']"); - await expect(payInvoiceTitle).toHaveText("Pay this invoice about 0.01005558 BTC"); - - const spanLightningInvoice = page.locator("span[class='btn']"); - await spanLightningInvoice.click(); - - const lightningInvoice = await page.evaluate(() => { - return navigator.clipboard.readText(); - }); - expect(lightningInvoice).toBeDefined(); - - await payInvoiceLnd(lightningInvoice) - - const txIdLink = page.getByText("open claim transaction"); - - const txId = (await txIdLink.getAttribute("href")).split("/").pop(); - expect(txId).toBeDefined(); - - const txInfo = JSON.parse(await getBitcoinWalletTx(txId)); - expect(txInfo.amount.toString()).toEqual(receiveAmount); +import { expect, test } from "@playwright/test"; + +import { + generateBitcoinBlock, + getBitcoinAddress, + getBitcoinWalletTx, + payInvoiceLnd, +} from "./utils"; + +test.describe("reverseSwap", () => { + test.beforeEach(async () => { + await generateBitcoinBlock(); + }); + + test("Reverse swap BTC/BTC", async ({ page }) => { + await page.goto("/"); + + const receiveAmount = "0.01"; + const inputReceiveAmount = page.locator( + "input[data-testid='receiveAmount']", + ); + await inputReceiveAmount.fill(receiveAmount); + + const inputSendAmount = page.locator("input[data-testid='sendAmount']"); + await expect(inputSendAmount).toHaveValue("0.01005558"); + + const inputOnchainAddress = page.locator( + "input[data-testid='onchainAddress']", + ); + await inputOnchainAddress.fill(await getBitcoinAddress()); + + const buttonCreateSwap = page.locator( + "button[data-testid='create-swap-button']", + ); + await buttonCreateSwap.click(); + + const payInvoiceTitle = page.locator( + "h2[data-testid='pay-invoice-title']", + ); + await expect(payInvoiceTitle).toHaveText( + "Pay this invoice about 0.01005558 BTC", + ); + + const spanLightningInvoice = page.locator("span[class='btn']"); + await spanLightningInvoice.click(); + + const lightningInvoice = await page.evaluate(() => { + return navigator.clipboard.readText(); + }); + expect(lightningInvoice).toBeDefined(); + + await payInvoiceLnd(lightningInvoice); + + const txIdLink = page.getByText("open claim transaction"); + + const txId = (await txIdLink.getAttribute("href")).split("/").pop(); + expect(txId).toBeDefined(); + + const txInfo = JSON.parse(await getBitcoinWalletTx(txId)); + expect(txInfo.amount.toString()).toEqual(receiveAmount); + }); }); diff --git a/e2e/utils.ts b/e2e/utils.ts index 324b6004..abc01e25 100644 --- a/e2e/utils.ts +++ b/e2e/utils.ts @@ -1,14 +1,17 @@ -import { exec } from 'child_process'; -import { promisify } from 'util'; +import { exec } from "child_process"; +import { promisify } from "util"; const execAsync = promisify(exec); -const executeInScriptsContainer = 'docker exec boltz-scripts bash -c "source /etc/profile.d/utils.sh && '; +const executeInScriptsContainer = + 'docker exec boltz-scripts bash -c "source /etc/profile.d/utils.sh && '; const execCommand = async (command: string): Promise => { try { - - const { stdout, stderr } = await execAsync(`${executeInScriptsContainer}${command}"`, { shell: '/bin/bash' }); + const { stdout, stderr } = await execAsync( + `${executeInScriptsContainer}${command}"`, + { shell: "/bin/bash" }, + ); if (stderr) { throw new Error(`Error executing command: ${stderr}`); @@ -22,12 +25,16 @@ const execCommand = async (command: string): Promise => { }; export const getBitcoinAddress = async (): Promise => { - return execCommand('bitcoin-cli-sim-client getnewaddress'); + return execCommand("bitcoin-cli-sim-client getnewaddress"); +}; + +export const generateBitcoinBlock = async (): Promise => { + return execCommand("bitcoin-cli-sim-client -generate"); }; export const getBitcoinWalletTx = async (txId: string): Promise => { return execCommand(`bitcoin-cli-sim-client gettransaction ${txId}`); -} +}; export const payInvoiceLnd = async (invoice: string): Promise => { return execCommand(`lncli-sim 1 payinvoice -f ${invoice}`); diff --git a/package.json b/package.json index e5babd8f..8912b574 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "mainnet": "cp src/configs/mainnet.json public/config.json", "beta": "cp src/configs/beta.json public/config.json", "testnet": "cp src/configs/testnet.json public/config.json", - "prettier": "prettier --write src tests", - "prettier-check": "prettier --check src tests", + "prettier": "prettier --write src tests e2e", + "prettier-check": "prettier --check src tests e2e", "tsc": "tsc", "changelog": "git-cliff -o CHANGELOG.md" }, diff --git a/playwright.config.ts b/playwright.config.ts index 84e962dc..66932ed1 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,12 +1,5 @@ import { defineConfig, devices } from '@playwright/test'; -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// import dotenv from 'dotenv'; -// dotenv.config({ path: path.resolve(__dirname, '.env') }); - /** * See https://playwright.dev/docs/test-configuration. */ @@ -25,59 +18,37 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:3000', + baseURL: 'https://localhost:4173', ignoreHTTPSErrors: true, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - headless: true, + headless: false, trace: 'on-first-retry', - permissions: ["clipboard-read", "clipboard-write"], }, /* Configure projects for major browsers */ projects: [ { name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + use: { + ...devices['Desktop Chrome'], + contextOptions: { + // chromium-specific permissions + permissions: ['clipboard-read', 'clipboard-write'], + }, + }, }, - /* gfy { 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: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, ], /* Run your local dev server before starting the tests */ webServer: { - command: 'npm run start', - port: 5173, + command: 'npm run build && npm run preview', + port: 4173, reuseExistingServer: !process.env.CI, stdout: 'pipe', stderr: 'pipe', From 8da1bbffcd2e0cf030051daa9b40c00d23490aa7 Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:24:30 +0300 Subject: [PATCH 04/12] headless mode --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index 66932ed1..ae8508d9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -22,7 +22,7 @@ export default defineConfig({ ignoreHTTPSErrors: true, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - headless: false, + headless: true, trace: 'on-first-retry', }, From ab027f9d2bf12ccdf68c8a33ac93b4568b0e03c0 Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:16:24 +0300 Subject: [PATCH 05/12] submarine and chain swaps --- e2e/chainSwaps.spec.ts | 60 +++++++++++++++++++++++++++++++++++++++ e2e/submarineSwap.spec.ts | 55 +++++++++++++++++++++++++++++++++++ e2e/utils.ts | 26 +++++++++++++++++ playwright.config.ts | 2 ++ 4 files changed, 143 insertions(+) create mode 100644 e2e/chainSwaps.spec.ts create mode 100644 e2e/submarineSwap.spec.ts diff --git a/e2e/chainSwaps.spec.ts b/e2e/chainSwaps.spec.ts new file mode 100644 index 00000000..970dbc1e --- /dev/null +++ b/e2e/chainSwaps.spec.ts @@ -0,0 +1,60 @@ +import { expect, test } from "@playwright/test"; + +import { + elementsSendToAddress, + generateBitcoinBlock, + generateLiquidBlock, + getBitcoinAddress, +} from "./utils"; + +test.describe("Chain swap", () => { + test.beforeEach(async () => { + await generateBitcoinBlock(); + }); + + test("BTC/L-BTC", async ({ page }) => { + await page.goto("/"); + + const assetSelector = page.locator("div[class='asset asset-LN'] div"); + await assetSelector.click(); + + const lbtcAsset = page.locator("div[data-testid='select-L-BTC']"); + await lbtcAsset.click(); + + const receiveAmount = "0.01"; + const inputReceiveAmount = page.locator( + "input[data-testid='receiveAmount']", + ); + await inputReceiveAmount.fill(receiveAmount); + + const inputSendAmount = page.locator("input[data-testid='sendAmount']"); + const sendAmount = "0.0100168"; + await expect(inputSendAmount).toHaveValue(sendAmount); + + const inputOnchainAddress = page.locator( + "input[data-testid='onchainAddress']", + ); + await inputOnchainAddress.fill(await getBitcoinAddress()); + + const buttonCreateSwap = page.locator( + "button[data-testid='create-swap-button']", + ); + await buttonCreateSwap.click(); + + const skipDownload = page.getByText("Skip download"); + await skipDownload.click(); + + const buttons = page.locator("div[data-testid='pay-onchain-buttons']"); + const copyAddressButton = buttons.getByText("address"); + expect(copyAddressButton).toBeDefined(); + await copyAddressButton.click(); + + const sendAddress = await page.evaluate(() => { + return navigator.clipboard.readText(); + }); + expect(sendAddress).toBeDefined(); + + await elementsSendToAddress(sendAddress, sendAmount); + await generateLiquidBlock(); + }); +}); diff --git a/e2e/submarineSwap.spec.ts b/e2e/submarineSwap.spec.ts new file mode 100644 index 00000000..c237da1f --- /dev/null +++ b/e2e/submarineSwap.spec.ts @@ -0,0 +1,55 @@ +import { expect, test } from "@playwright/test"; + +import { + bitcoinSendToAddress, + generateBitcoinBlock, + generateInvoiceLnd, +} from "./utils"; + +test.describe("Submarine swap", () => { + test.beforeEach(async () => { + await generateBitcoinBlock(); + }); + + test("Submarine swap BTC/BTC", async ({ page }) => { + await page.goto("/"); + + const divFlipAssets = page.locator("#flip-assets"); + await divFlipAssets.click(); + + const receiveAmount = "0.01"; + const inputReceiveAmount = page.locator( + "input[data-testid='receiveAmount']", + ); + await inputReceiveAmount.fill(receiveAmount); + + const inputSendAmount = page.locator("input[data-testid='sendAmount']"); + const sendAmount = "0.01005302"; + await expect(inputSendAmount).toHaveValue(sendAmount); + + const invoiceInput = page.locator("textarea[data-testid='invoice']"); + await invoiceInput.fill( + JSON.parse(await generateInvoiceLnd(1000000)).payment_request, + ); + const buttonCreateSwap = page.locator( + "button[data-testid='create-swap-button']", + ); + await buttonCreateSwap.click(); + + const skipDownload = page.getByText("Skip download"); + await skipDownload.click(); + + const copyAddressButton = page.getByText("address"); + expect(copyAddressButton).toBeDefined(); + await copyAddressButton.click(); + + const sendAddress = await page.evaluate(() => { + return navigator.clipboard.readText(); + }); + expect(sendAddress).toBeDefined(); + await bitcoinSendToAddress(sendAddress, sendAmount); + + await generateBitcoinBlock(); + // TODO: verify amounts + }); +}); diff --git a/e2e/utils.ts b/e2e/utils.ts index abc01e25..15a5caa7 100644 --- a/e2e/utils.ts +++ b/e2e/utils.ts @@ -28,10 +28,32 @@ export const getBitcoinAddress = async (): Promise => { return execCommand("bitcoin-cli-sim-client getnewaddress"); }; +export const bitcoinSendToAddress = async ( + address: string, + amount: string, +): Promise => { + return execCommand( + `bitcoin-cli-sim-client sendtoaddress "${address}" ${amount}`, + ); +}; + +export const elementsSendToAddress = async ( + address: string, + amount: string, +): Promise => { + return execCommand( + `elements-cli-sim-client sendtoaddress "${address}" ${amount}`, + ); +}; + export const generateBitcoinBlock = async (): Promise => { return execCommand("bitcoin-cli-sim-client -generate"); }; +export const generateLiquidBlock = async (): Promise => { + return execCommand("elements-cli-sim-client -generate"); +}; + export const getBitcoinWalletTx = async (txId: string): Promise => { return execCommand(`bitcoin-cli-sim-client gettransaction ${txId}`); }; @@ -39,3 +61,7 @@ export const getBitcoinWalletTx = async (txId: string): Promise => { export const payInvoiceLnd = async (invoice: string): Promise => { return execCommand(`lncli-sim 1 payinvoice -f ${invoice}`); }; + +export const generateInvoiceLnd = async (amount: number): Promise => { + return execCommand(`lncli-sim 1 addinvoice --amt ${amount}`); +}; diff --git a/playwright.config.ts b/playwright.config.ts index ae8508d9..d1595345 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -39,10 +39,12 @@ export default defineConfig({ }, }, + /* { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, + */ ], /* Run your local dev server before starting the tests */ From 6e282adf4e47bebedcf522f3265b96dc2b3f94f9 Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:24:50 +0300 Subject: [PATCH 06/12] no parallel --- e2e/chainSwaps.spec.ts | 1 + playwright.config.ts | 84 +++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/e2e/chainSwaps.spec.ts b/e2e/chainSwaps.spec.ts index 970dbc1e..8ba592f5 100644 --- a/e2e/chainSwaps.spec.ts +++ b/e2e/chainSwaps.spec.ts @@ -56,5 +56,6 @@ test.describe("Chain swap", () => { await elementsSendToAddress(sendAddress, sendAmount); await generateLiquidBlock(); + // TODO: verify amounts }); }); diff --git a/playwright.config.ts b/playwright.config.ts index d1595345..5bccc841 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,58 +1,58 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test"; /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './e2e', - /* 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: { - /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: 'https://localhost:4173', - ignoreHTTPSErrors: true, + testDir: "./e2e", + /* Run tests in files in parallel */ + fullyParallel: false, + /* 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: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "https://localhost:4173", + ignoreHTTPSErrors: true, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - headless: true, - trace: 'on-first-retry', - }, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + headless: true, + trace: "on-first-retry", + }, - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - contextOptions: { - // chromium-specific permissions - permissions: ['clipboard-read', 'clipboard-write'], + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { + ...devices["Desktop Chrome"], + contextOptions: { + // chromium-specific permissions + permissions: ["clipboard-read", "clipboard-write"], + }, + }, }, - }, - }, - /* + /* { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, */ - ], + ], - /* Run your local dev server before starting the tests */ - webServer: { - command: 'npm run build && npm run preview', - port: 4173, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - stderr: 'pipe', - }, + /* Run your local dev server before starting the tests */ + webServer: { + command: "npm run build && npm run preview", + port: 4173, + reuseExistingServer: !process.env.CI, + stdout: "pipe", + stderr: "pipe", + }, }); From 14e93778a9c2d502bf967b5817f6c7234a2c62cb Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:29:29 +0300 Subject: [PATCH 07/12] try again --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index 5bccc841..1bc6a028 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -49,7 +49,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { - command: "npm run build && npm run preview", + command: "npm run build && npm run preview -- --host 0.0.0.0", port: 4173, reuseExistingServer: !process.env.CI, stdout: "pipe", From 2a815f1a6a55ad5e02d36e40b39481d2c769fef4 Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:38:37 +0300 Subject: [PATCH 08/12] pls work --- playwright.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index 1bc6a028..f627bec4 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -50,7 +50,8 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { command: "npm run build && npm run preview -- --host 0.0.0.0", - port: 4173, + url: 'https://localhost:4173', + // port: 4173, reuseExistingServer: !process.env.CI, stdout: "pipe", stderr: "pipe", From 440151999937542b6a53a4dddf9017c1dbdbb4ce Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:44:28 +0300 Subject: [PATCH 09/12] leeroy --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index f627bec4..3c8e7b25 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -50,7 +50,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { command: "npm run build && npm run preview -- --host 0.0.0.0", - url: 'https://localhost:4173', + url: 'https://127.0.0.1:4173', // port: 4173, reuseExistingServer: !process.env.CI, stdout: "pipe", From ed130db59e2f8f36f87ae762274cc0ed34817bec Mon Sep 17 00:00:00 2001 From: maybeast <78227110+maybeast@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:53:39 +0300 Subject: [PATCH 10/12] works --- playwright.config.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index 3c8e7b25..33573d15 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - baseURL: "https://localhost:4173", + baseURL: "https://localhost:5173", ignoreHTTPSErrors: true, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ @@ -47,13 +47,11 @@ export default defineConfig({ */ ], - /* Run your local dev server before starting the tests */ webServer: { - command: "npm run build && npm run preview -- --host 0.0.0.0", - url: 'https://127.0.0.1:4173', - // port: 4173, - reuseExistingServer: !process.env.CI, - stdout: "pipe", - stderr: "pipe", + command: 'npm run start', + port: 5173, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + stderr: 'pipe', }, }); From 5bfe7051f1cac5bd1ec8dceb2ac47521d671bf10 Mon Sep 17 00:00:00 2001 From: michael1011 Date: Thu, 1 Aug 2024 17:39:53 +0200 Subject: [PATCH 11/12] refactor github action files --- .github/workflows/CI.yml | 69 ++++++++++++++++++++++++++------ .github/workflows/playwright.yml | 32 --------------- 2 files changed, 56 insertions(+), 45 deletions(-) delete mode 100644 .github/workflows/playwright.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 20424ac0..e7e470da 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -3,16 +3,59 @@ name: CI on: [push, pull_request] jobs: - ci: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: "npm" - - run: npm ci - - run: npm run prettier-check - - run: npm run test - - run: npm run tsc + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - run: npm ci + + - run: npm run prettier-check + + - run: npm run test + + - run: npm run tsc + + e2e: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Start regtest + env: + COMPOSE_PROFILES: ci + run: | + git submodule init + git submodule update + chmod -R 777 regtest + cd regtest + ./start.sh + + - name: Install dependencies + run: npm ci + + - name: Install Playwright Browsers + run: npx playwright install --with-deps + + - name: Run Playwright tests + env: + CI: true + run: npx playwright test + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index de5760ac..00000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: E2E tests -on: - push: - branches: [ main ] - pull_request: -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - name: Start regtest - env: - COMPOSE_PROFILES: ci - run: git submodule init && git submodule update && chmod -R 777 regtest && cd regtest && ./start.sh - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - env: - CI: true - run: npx playwright test - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 From db80758428b2d5c679cccf84bc5699dd7fe1e76f Mon Sep 17 00:00:00 2001 From: michael1011 Date: Thu, 1 Aug 2024 17:49:07 +0200 Subject: [PATCH 12/12] move playwright scripts to package.json --- .github/workflows/CI.yml | 4 ++-- package.json | 2 ++ playwright.config.ts | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e7e470da..0213927d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,12 +46,12 @@ jobs: run: npm ci - name: Install Playwright Browsers - run: npx playwright install --with-deps + run: npm run playwright:install - name: Run Playwright tests env: CI: true - run: npx playwright test + run: npm run test:e2e - uses: actions/upload-artifact@v4 if: always() diff --git a/package.json b/package.json index 8912b574..013d18fe 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "scripts": { "start": "npm run regtest && vite", "test": "jest --runInBand ./tests", + "test:e2e": "playwright test", + "playwright:install": "playwright install --with-deps chromium", "dev": "vite", "regtest": "cp src/configs/regtest.json public/config.json", "preview": "vite preview", diff --git a/playwright.config.ts b/playwright.config.ts index 33573d15..7b9e32f9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -48,10 +48,10 @@ export default defineConfig({ ], webServer: { - command: 'npm run start', - port: 5173, - reuseExistingServer: !process.env.CI, - stdout: 'pipe', - stderr: 'pipe', + command: "npm run start", + port: 5173, + reuseExistingServer: !process.env.CI, + stdout: "pipe", + stderr: "pipe", }, });