Skip to content

Commit

Permalink
Fix send all L-BTC behavior (#443)
Browse files Browse the repository at this point in the history
* fix send flow

* add playwright test for send & receive flow

* cleaning

* increase playwrigth timeout

* regenerate lockfile

* reinstall web-ext

* nigiri in playwright.yml CI

* Update src/domain/pset.ts

Co-authored-by: João Bordalo <[email protected]>

* dynamic address placeholder & fix switch cases in pset.ts

* add with: cache: 'yarn' in CI

* confirmation page deduct fee from amount

---------

Co-authored-by: João Bordalo <[email protected]>
  • Loading branch information
louisinger and bordalix authored Mar 21, 2023
1 parent 0f07b1e commit 8e6c90c
Show file tree
Hide file tree
Showing 21 changed files with 567 additions and 290 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- name: Install Nigiri
uses: vulpemventures/nigiri-github-action@v1
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'yarn'
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build marina extension
run: yarn build:v3

- name: Install Nigiri
uses: vulpemventures/nigiri-github-action@v1

- name: Enable websocat
run: docker run --net=host solsson/websocat -b ws-l:127.0.0.1:1234 tcp:127.0.0.1:50001&

- name: Install Playwright Browsers
run: yarn playwright install --with-deps

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
"@tailwindcss/line-clamp": "^0.2.0",
"@types/chai": "^4.2.21",
"@types/chrome": "^0.0.222",
"@types/classnames": "^2.3.1",
"@types/jest": "^29.4.0",
"@types/qrcode.react": "^1.0.1",
"@types/react": "^18.0.27",
Expand Down Expand Up @@ -106,7 +105,7 @@
"ts-jest": "^29.0.5",
"ts-loader": "^9.2.4",
"typescript": "^4.3.5",
"web-ext": "^7.2.0",
"web-ext": "^7.6.0",
"webpack": "^5.47.0",
"webpack-cli": "^4.7.2",
"ws": "^8.12.0"
Expand Down
25 changes: 25 additions & 0 deletions playwright-tests/injected-script.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { MarinaProvider } from 'marina-provider';
import Marina from '../src/inject/marina/provider';
import { test as pwTest, expect as pwExpect, makeOnboardingRestore } from './utils';

const vulpemFaucetURL = 'https://faucet.vulpem.com/';

pwTest('should set up a window.marina object', async ({ page }) => {
await page.goto(vulpemFaucetURL);
// test if the window.marina object is set up
// use isEnabled() to check if the functions are available
const isEnableViaProvider = await page.evaluate(
(name: string) => (window[name as any] as unknown as MarinaProvider).isEnabled(),
Marina.PROVIDER_NAME
);
pwExpect(isEnableViaProvider).toBe(false);
});

pwTest('should be able to connect to a web app (faucet.vulpem.com)', async ({ page, extensionId, context }) => {
await makeOnboardingRestore(page, extensionId);
await page.goto(vulpemFaucetURL);
await page.getByRole('button', { name: 'Connect with Marina' }).click();
const popup = await context.waitForEvent('page');
await popup.getByRole('button', { name: 'Connect' }).click();
pwExpect(page.getByText('Wrong network, switch to the Testnet')).toBeTruthy();
});
9 changes: 9 additions & 0 deletions playwright-tests/onboarding.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { test as pwTest, makeOnboardingRestore, marinaURL, PASSWORD } from "./utils";

pwTest('should be able to create a new Marina wallet & Log in', async ({ page, extensionId }) => {
await makeOnboardingRestore(page, extensionId);
await page.goto(marinaURL(extensionId, 'popup.html'));
await page.getByPlaceholder('Enter your password').fill(PASSWORD);
await page.getByRole('button', { name: 'Log in' }).click();
await page.waitForSelector('text=Assets');
});
75 changes: 75 additions & 0 deletions playwright-tests/popup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { address } from 'liquidjs-lib';
import { faucet } from '../test/_regtest';
import {
test as pwTest,
expect as pwExpect,
makeOnboardingRestore,
marinaURL,
PASSWORD,
} from './utils';

pwTest(
'should be able to generate a new address via wallet/receive popup UI',
async ({ page, extensionId }) => {
// onboard a new wallet
await makeOnboardingRestore(page, extensionId);
// login page
await page.goto(marinaURL(extensionId, 'popup.html'));
await page.getByPlaceholder('Enter your password').fill(PASSWORD);
await page.getByRole('button', { name: 'Log in' }).click();
await page.waitForSelector('text=Assets');
// go to receive page and generate a new address
await page.getByRole('button', { name: 'Receive' }).click();
await page.getByRole('button', { name: 'New Asset' }).click();
await page.getByRole('button', { name: 'Copy' }).click();
await page.waitForSelector('text=Copied');
const clipboard = await page.evaluate('navigator.clipboard.readText()');
pwExpect(typeof clipboard).toBe('string');
pwExpect(clipboard).toContain('lq1');
pwExpect(address.isConfidential(clipboard as string)).toBe(true);
}
);

pwTest(
'should be able to send some funds via wallet/send popup UI',
async ({ page, extensionId }) => {
await makeOnboardingRestore(page, extensionId); // create a new wallet
// go to networks page and switch to regtest
await page.goto(marinaURL(extensionId, 'popup.html'));
await page.getByPlaceholder('Enter your password').fill(PASSWORD);
await page.getByRole('button', { name: 'Log in' }).click();
await page.waitForSelector('text=Assets');
await page.getByAltText('menu icon').click(); // hamburger menu
await page.getByText('Settings').click();
await page.getByRole('button', { name: 'Networks' }).click();
await page.getByRole('button', { name: 'Liquid' }).click(); // by default on Liquid, so the button contains the network name
await page.getByText('Regtest').click();
// wait some time for the network to switch
await page.waitForTimeout(2000);

// go to receive page and generate a new address, we'll use it to faucet some funds
await page.goto(marinaURL(extensionId, 'popup.html'));
await page.getByRole('button', { name: 'Receive' }).click();
await page.getByRole('button', { name: 'New Asset' }).click();
await page.getByRole('button', { name: 'Copy' }).click();
await page.waitForSelector('text=Copied');
const address = await page.evaluate("navigator.clipboard.readText()");
pwExpect(address as string).toContain('el1');
await faucet(address as string, 1); // send 1 L-BTC to the address
await page.goto(marinaURL(extensionId, 'popup.html'));
await page.waitForSelector('text=1 L-BTC');
await page.getByRole('button', { name: 'Send' }).click(); // go to send
await page.getByText('Liquid Bitcoin').click(); // select L-BTC
await page.getByPlaceholder('el1...').fill(address as string); // fill the address
await page.getByPlaceholder('0').fill('0.9'); // fill the amount
await page.getByRole('button', { name: 'Verify' }).click();
await page.getByRole('button', { name: 'L-BTC' }).click();
await page.waitForSelector('text=Fee:');
await page.getByRole('button', { name: 'Confirm' }).click(); // send
await page.getByRole('button', { name: 'Send' }).click(); // confirm
await page.waitForSelector('text=Unlock');
await page.getByPlaceholder('Password').fill(PASSWORD);
await page.getByRole('button', { name: 'Unlock' }).click();
await page.waitForSelector('text=Payment successful !');
}
);
38 changes: 0 additions & 38 deletions playwright-tests/provider.spec.ts

This file was deleted.

21 changes: 20 additions & 1 deletion playwright-tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { BrowserContext } from '@playwright/test';
import type { BrowserContext, Page } from '@playwright/test';
import { test as basePlaywrightTest, chromium } from '@playwright/test';
import { generateMnemonic } from 'bip39';
import path from 'path';

export const test = basePlaywrightTest.extend<{
Expand Down Expand Up @@ -34,4 +35,22 @@ export const test = basePlaywrightTest.extend<{
},
});

export const PASSWORD = 'passwordsupersecretonlyfortesting';

export const marinaURL = (extensionID: string, path: string) => `chrome-extension://${extensionID}/${path}`;

export const makeOnboardingRestore = async (page: Page, extensionID: string) => {
await page.goto(marinaURL(extensionID, 'home.html#initialize/welcome'));
await page.getByRole('button', { name: 'Get Started' }).click();
await page.getByRole('button', { name: 'Restore' }).click();

const mnemonic = generateMnemonic();
await page.getByRole('textbox', { name: 'mnemonic' }).fill(mnemonic);
await page.getByPlaceholder('Enter your password').fill(PASSWORD);
await page.getByPlaceholder('Confirm your password').fill(PASSWORD);
await page.getByRole('checkbox').check();
await page.getByRole('button', { name: 'Create' }).click();
await page.waitForSelector('text=Your wallet is ready');
};

export const expect = test.expect;
29 changes: 2 additions & 27 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,22 @@
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './playwright-tests',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
timeout: 60 * 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 */
permissions: ["clipboard-read", "clipboard-write"],
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
Expand Down
1 change: 1 addition & 0 deletions src/background/background-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ appRepository
.catch(console.error);

async function startBackgroundServices() {
await walletRepository.unlockUtxos(); // unlock all utxos at startup
const { isOnboardingCompleted } = await appRepository.getStatus();
if (isOnboardingCompleted) {
await Promise.allSettled([
Expand Down
8 changes: 8 additions & 0 deletions src/domain/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class CoinSelectionError extends Error {
constructor(public target: { amount: number; asset: string }, public selectedAmount: number) {
super(
`Coin selection failed for ${target.amount} with ${selectedAmount} selected (asset: ${target.asset}))`
);
this.name = 'CoinSelectionError';
}
}
Loading

0 comments on commit 8e6c90c

Please sign in to comment.