Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: E2E real wallet data using esbuild #193

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ web_modules/
.env.test.local
.env.production.local
.env.local
.env.mainnet
.env.signet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm run test:e2e to run the test using the network in .env.local

Screenshot 2024-10-09 at 12 50 29

When I run npm run test:e2e I see this. Is that expected behaviour?

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ where,
- `NEXT_PUBLIC_POINTS_API_URL` specifies the Points API to use for the points
system
- `NEXT_PUBLIC_NETWORK` specifies the BTC network environment
- `NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES` boolean value to indicate whether display
- `NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES` boolean value to indicate whether display
testing network related message. Default to true

Then, to start a development server:
Expand All @@ -36,3 +36,26 @@ npm run dev

Instructions for wallet integration can be found in this
[document](./docs/WalletIntegration.md).

## E2E
The end-to-end (E2E) tests are implemented using Playwright. These tests simulate user interactions with the application to ensure that all functionalities work as expected across different Bitcoin network environments (`mainnet` and `signet`). The tests utilize *some* mocked data to prevent interactions with real Bitcoin networks, ensuring safe and reliable testing.

### Prerequisites
Before running the E2E tests, ensure that you have completed the following steps:

- Environment Configuration: Set up the necessary environment variables.
- Install Dependencies: Ensure all dependencies are installed.

### Environment Configuration
The E2E tests can be run using either the .env.local file or the specific environment files .env.mainnet and .env.signet. These files should contain the following environment variables:

- `NEXT_PUBLIC_NETWORK`: Specifies the BTC network environment (mainnet or signet).
Copy link
Collaborator

@jrwbabylonlab jrwbabylonlab Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the E2E tests are using fully mocked data, do we still need all the environment setups across different env files? or maybe we can just create a single .env.test under the test directory for running the e2e test?

- `NEXT_PUBLIC_MEMPOOL_API`: Specifies the mempool.space host to use for Bitcoin node queries.
- `NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES`: Boolean value to indicate whether to display testing network-related messages.
- `NEXT_PUBLIC_API_URL`: Specifies the back-end API to use for staking system queries.

### Commands to run E2E
- `npx playwright install` to install playwright. This is *required* to do after the `npm install`
- Stop the current `dev` server in case you ran it with `npm run dev`
- `npm run test:e2e` to run the test using the network in `.env.local`
- `npm run test:e2e:full` to run the test using the networks in `.env.mainnet` and `.env.signet`
106 changes: 91 additions & 15 deletions e2e/balanceAddress.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,96 @@
import { expect, test } from "@playwright/test";
import { expect, Page, test } from "@playwright/test";

import { satoshiToBtc } from "@/utils/btcConversions";
import { maxDecimals } from "@/utils/maxDecimals";
import { trim } from "@/utils/trim";

import {
NATIVE_SEGWIT_MAINNET_ADDRESS,
NATIVE_SEGWIT_SIGNET_ADDRESS,
TAPROOT_MAINNET_ADDRESS,
TAPROOT_SIGNET_ADDRESS,
} from "./constants/wallet";
import { setupWalletConnection } from "./helper/connect";
import { mockNetwork } from "./helper/mockNetwork";
import { extractNumericBalance } from "./helper/utils";
import { nativeSegwitMainnetBalance } from "./mock/mainnet/wallet/nativeSegwit/utxos";
import { taprootMainnetBalance } from "./mock/mainnet/wallet/taproot/utxos";
import { nativeSegwitSignetBalance } from "./mock/signet/wallet/nativeSegwit/utxos";
import { taprootSignetBalance } from "./mock/signet/wallet/taproot/utxos";

type Network = "mainnet" | "signet";
type AddressType = "taproot" | "nativeSegwit";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated with the types defined in WalletType


// Determine the network from environment variable or default to 'mainnet'
const network: Network =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we not use existing network from the network.config.ts?

(process.env.NEXT_PUBLIC_NETWORK as Network) || "mainnet";

// Define the address types to test
const addressTypes: AddressType[] = ["taproot", "nativeSegwit"];

// Mappings for expected addresses based on network and address type
const expectedAddresses: Record<AddressType, Record<Network, string>> = {
taproot: {
mainnet: TAPROOT_MAINNET_ADDRESS,
signet: TAPROOT_SIGNET_ADDRESS,
},
nativeSegwit: {
mainnet: NATIVE_SEGWIT_MAINNET_ADDRESS,
signet: NATIVE_SEGWIT_SIGNET_ADDRESS,
},
};

// Mappings for expected balances based on network and address type
const expectedBalances: Record<AddressType, Record<Network, number>> = {
taproot: {
mainnet: taprootMainnetBalance,
signet: taprootSignetBalance,
},
nativeSegwit: {
mainnet: nativeSegwitMainnetBalance,
signet: nativeSegwitSignetBalance,
},
};
Comment on lines +32 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can move this logic together with current network to constants folder and return only taproot/nativeSegwit values depends on current network value:

const addresses = {
  mainnet: {
    taproot: 'address',
    nativeSegwit: 'address',
  },
  signet: {
    taproot: 'address',
    nativeSegwit: 'address',
  }
}[network];


// Reusable function for checking address
async function checkAddress(page: Page, expectedAddress: string) {
const address = await page.getByTestId("address").textContent();
expect(address).toBe(trim(expectedAddress));
}

// Reusable function for checking balance
async function checkBalance(page: Page, expectedBalance: number) {
const balanceText = await page.getByTestId("balance").textContent();
const balance = maxDecimals(extractNumericBalance(balanceText), 8);
expect(balance).toBe(satoshiToBtc(expectedBalance));
}

test.describe("Balance and address checks after connection", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
await setupWalletConnection(page);
});

test("balance is correct", async ({ page }) => {
const balance = await page.getByTestId("balance").textContent();
expect(balance).toBe("0.12345678 BTC");
});

test("address is correct", async ({ page }) => {
const address = await page.getByTestId("address").textContent();
expect(address).toBe("bc1p...97sd");
});
// Iterate over each address type
for (const type of addressTypes) {
test.describe(`${type} address on ${network}`, () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice the network is controlled by the env passed in.
Why not we make a testcase set here and let the for loop to cover both networks instead of passing it via env?
So something like this:

const testCases: [AddressType, Network][] = [
  ["taproot", "mainnet"],
  ["taproot", "signet"],
  ["nativeSegwit", "mainnet"],
  ["nativeSegwit", "signet"],
];

then we just do a testCase.forEach to loop through
What you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrwbabylonlab why we need test against multiple networks if everything is mocked?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically, it should be the same. The different network only matters if our code is performing BTC-specific operations. I’m not sure if our simple-staking implementation includes that (it shouldn’t), but it wouldn’t hurt to run it on multiple networks just to be safe.

// Mock the network
test.beforeEach(async ({ page }) => {
await mockNetwork(page, network);
});

// Setup wallet connection before each test
test.beforeEach(async ({ page }) => {
await setupWalletConnection(page, network, type);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s no need to use two separate test.beforeEach calls. It's the same as a single beforeEach

});

test(`should verify the ${type} address and balance on ${network}`, async ({
page,
}) => {
const expectedAddress = expectedAddresses[type][network];
const expectedBalance = expectedBalances[type][network];

// Perform address check
await checkAddress(page, expectedAddress);

// Perform balance check
await checkBalance(page, expectedBalance);
});
Comment on lines +82 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test is pretty small, no need in additional wrapper. Weird to see test without expect expression

Suggested change
test(`should verify the ${type} address and balance on ${network}`, async ({
page,
}) => {
const expectedAddress = expectedAddresses[type][network];
const expectedBalance = expectedBalances[type][network];
// Perform address check
await checkAddress(page, expectedAddress);
// Perform balance check
await checkBalance(page, expectedBalance);
});
test(`should verify the ${type} address and balance on ${network}`, async ({
page,
}) => {
const expectedAddress = expectedAddresses[type][network];
const expectedBalance = expectedBalances[type][network];
// Perform address check
const address = await page.getByTestId("address").textContent();
expect(address).toBe(trim(expectedAddress));
// Perform balance check
const balanceText = await page.getByTestId("balance").textContent();
const balance = maxDecimals(extractNumericBalance(balanceText), 8);
expect(balance).toBe(satoshiToBtc(expectedBalance));
});

});
}
});
3 changes: 1 addition & 2 deletions e2e/constants/staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ import { satoshiToBtc } from "@/utils/btcConversions";

export const STAKING_AMOUNT_SAT = 50000;
export const STAKING_AMOUNT_BTC = satoshiToBtc(STAKING_AMOUNT_SAT);
export const STAKING_TX_HASH =
"47af61d63bcc6c513561d9a1198d082052cc07a81f50c6f130653f0a6ecc0fc1";
export const STAKING_DURATION_BLOCKS = 150;
8 changes: 8 additions & 0 deletions e2e/constants/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Constants for selectors and texts

export const BUTTON_TEXT_CONNECT_TO_BTC = "Connect to BTC";
export const LABEL_TEXT_ACCEPT_TERMS = "I certify that I have read";
export const LABEL_TEXT_NO_INSCRIPTIONS = "I certify that there are no";
export const LABEL_TEXT_HW_WALLET = "I acknowledge that Keystone via QR code";
export const BUTTON_TEXT_BROWSER = "Browser";
export const INPUT_DURATION_BLOCKS = "Blocks";
32 changes: 32 additions & 0 deletions e2e/constants/wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Don't use real funds with these addresses
export const MNEMONIC =
"baby baby baby baby baby baby baby rocket rocket rocket rocket science";

// The following addresses are derived from the above mnemonic
export const TAPROOT_MAINNET_ADDRESS =
"bc1p620x2d0wvycj0y7jmc0lxytk0m64swcx2lp430grtn0klqwugpcsgwalan";
export const TAPROOT_MAINNET_PK =
"038f561718d7f3ead37bd57e7b68360325db2263acfbbc8cb6b129e768d27aa46d";
export const TAPROOT_MAINNET_SCRIPT_PUBKEY =
"5120d29e6535ee61312793d2de1ff311767ef5583b0657c358bd035cdf6f81dc4071";

export const NATIVE_SEGWIT_MAINNET_ADDRESS =
"bc1q7w09t8gl84c7ksus5nt3r2eg0qexk987hk9n23";
export const NATIVE_SEGWIT_MAINNET_PK =
"02ed32cf86745ea2c7a13f72b3194a576c249736e0b773e1a9f837767d60a3adcf";
export const NATIVE_SEGWIT_MAINNET_SCRIPT_PUBKEY =
"0014f39e559d1f3d71eb4390a4d711ab2878326b14fe";

export const TAPROOT_SIGNET_ADDRESS =
"tb1pc29r075qcgypvxzaq34680sk3epwv3xrqu4fnkskrqesd65d9yjqun5x5j";
export const TAPROOT_SIGNET_PK =
"025bf662d62fdfaa781c6ecb6b533d50f5a8a1b6892e9fe5609ccccd07e041b20a";
export const TAPROOT_SIGNET_SCRIPT_PUBKEY =
"5120c28a37fa80c20816185d046ba3be168e42e644c3072a99da16183306ea8d2924";

export const NATIVE_SEGWIT_SIGNET_ADDRESS =
"tb1qrspcmpwh4k05xlflx7fqz5dmgpt06fydfmyzyr";
export const NATIVE_SEGWIT_SIGNET_PK =
"026b4ebd2b5ec63b8e9260c540921e46e75bbdcc42cf14bc3c0274e8d6a544bda1";
export const NATIVE_SEGWIT_SIGNET_SCRIPT_PUBKEY =
"00141c038d85d7ad9f437d3f37920151bb4056fd248d";
Loading