Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f51d3ce
merge unit tests back into release branch (#925)
jrwbabylonlab Apr 30, 2025
e3fea26
Merge pull request #926 from babylonlabs-io/main
jrwbabylonlab Apr 30, 2025
f2171f8
chore: use APR instead of APY (#937)
jrwbabylonlab May 1, 2025
a2b421d
Merge pull request #942 from babylonlabs-io/main
jrwbabylonlab May 1, 2025
6eb96a8
feat: add workflow for unit tests (#939)
May 1, 2025
2a3b1aa
Updates FAQ with new copy (#946)
May 2, 2025
88a139a
fix: look up staking output instead of hardcoded 0 (#947)
jrwbabylonlab May 2, 2025
57126e3
Merge pull request #948 from babylonlabs-io/main
jrwbabylonlab May 2, 2025
570b96b
fix: use APR instead of APY from API (#950)
jrwbabylonlab May 2, 2025
8b6b3e9
Merge pull request #951 from babylonlabs-io/main
jrwbabylonlab May 2, 2025
a211e7c
Merge pull request #952 from babylonlabs-io/add-wallet-logs
0xDazzer May 5, 2025
1755423
define clientError Class (#954)
jeremy-babylonlabs May 5, 2025
d28fc3c
feat: implement logger for observability with Sentry integration (#953)
jeremy-babylonlabs May 5, 2025
5ae95bf
merge main into release branch (#956)
jrwbabylonlab May 6, 2025
471d16e
update to devnet-11 (#960)
liam-icheng-lai May 8, 2025
f4037be
Merge pull request #963 from babylonlabs-io/feat/disable-certain-wallets
jrwbabylonlab May 9, 2025
524ba3a
fix: fix flaky test (StakingState) (#966)
May 9, 2025
0d71c9b
Merge branch 'release/v1.x' into main
jrwbabylonlab May 9, 2025
5c9861c
feat: add custom fingerprinting group by error code (#962)
jeremy-babylonlabs May 9, 2025
295318b
fix: exclude cause error code from sentry fingerprint (#967)
0xDazzer May 9, 2025
4d0a377
chore: remove NEXT_PUBLIC_DISABLED_WALLETS from .env.example (#971)
jrwbabylonlab May 12, 2025
36e9158
fix user count not tracked (#957)
jeremy-babylonlabs May 12, 2025
f5d0ff6
Update devnet.ts (#983)
maiquanghiep May 13, 2025
c55231c
refactor: standardize all client error handling (#969)
jeremy-babylonlabs May 13, 2025
e689b89
feat: add e2e tests (#914)
May 13, 2025
7fa666e
feat: add loggers for btc staking manager bug (#984)
May 13, 2025
faa7afc
Merge remote-tracking branch 'upstream/main' into sync-repo
apple-juice May 13, 2025
0df1c5b
Merge remote-tracking branch 'upstream/main' into sync-repo
apple-juice May 13, 2025
e050e57
chore: fix merge issue
apple-juice May 13, 2025
2b755c5
chore: remove e2e workflow
apple-juice May 13, 2025
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
9 changes: 9 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
NEXT_PUBLIC_API_URL=/
NEXT_PUBLIC_NETWORK=mainnet
NEXT_PUBLIC_DISPLAY_TESTING_MESSAGES=false
NEXT_PUBLIC_FIXED_STAKING_TERM=true
NEXT_BUILD_E2E=true
NEXT_PUBLIC_BBN_GAS_PRICE=0.007
NEXT_PUBLIC_DEFAULT_FP_FILTER=inactive
NEXT_PUBLIC_BABY_RPC_URL=/
NEXT_PUBLIC_BABY_LCD_URL=/
3 changes: 3 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ ENV NEXT_PUBLIC_FIXED_STAKING_TERM=${NEXT_PUBLIC_FIXED_STAKING_TERM}
ARG NEXT_PUBLIC_BBN_GAS_PRICE
ENV NEXT_PUBLIC_BBN_GAS_PRICE=${NEXT_PUBLIC_BBN_GAS_PRICE}

ARG NEXT_PUBLIC_DISABLED_WALLETS
ENV NEXT_PUBLIC_DISABLED_WALLETS=${NEXT_PUBLIC_DISABLED_WALLETS}

ARG NEXT_PUBLIC_SIDECAR_API_URL
ENV NEXT_PUBLIC_SIDECAR_API_URL=${NEXT_PUBLIC_SIDECAR_API_URL}

Expand Down
20 changes: 0 additions & 20 deletions e2e/balanceAddress.spec.ts

This file was deleted.

3 changes: 3 additions & 0 deletions e2e/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./page_navigation";
export * from "./wallet_balance";
export * from "./wallet_connect";
18 changes: 18 additions & 0 deletions e2e/fixtures/page_navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Page } from "@playwright/test";

import { injectBBNQueries } from "../mocks/blockchain";

export class PageNavigationActions {
constructor(private page: Page) {}

async navigateToHomePage(page: Page) {
await injectBBNQueries(page);
await this.page.goto("/");
await this.waitForPageLoad();
}

async waitForPageLoad() {
await this.page.waitForLoadState("domcontentloaded");
await this.page.waitForLoadState("networkidle");
}
}
14 changes: 14 additions & 0 deletions e2e/fixtures/wallet_balance.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Note: These should be replaced by data-testid selectors

export const SPINNER_SELECTOR =
'[data-testid="staked-balance"] .bbn-loader, [data-testid="stakable-balance"] .bbn-loader, [data-testid="baby-balance"] .bbn-loader, [data-testid="baby-rewards"] .bbn-loader';
export const STAKED_BALANCE_ITEM_SELECTOR =
'.bbn-list-item:has-text("Staked Balance")';
export const STAKED_BALANCE_VALUE_SELECTOR =
'.bbn-list-item:has-text("Staked Balance") .bbn-list-value';
export const STAKABLE_BALANCE_VALUE_SELECTOR =
'.bbn-list-item:has-text("Stakable Balance") .bbn-list-value';
export const BABY_BALANCE_VALUE_SELECTOR =
'.bbn-list-item:has-text("BABY Balance") .bbn-list-value';
export const BABY_REWARDS_VALUE_SELECTOR =
'.bbn-list-item:has-text("BABY Rewards") .bbn-list-value';
50 changes: 50 additions & 0 deletions e2e/fixtures/wallet_balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Page } from "@playwright/test";

import {
BABY_BALANCE_VALUE_SELECTOR,
BABY_REWARDS_VALUE_SELECTOR,
SPINNER_SELECTOR,
STAKABLE_BALANCE_VALUE_SELECTOR,
STAKED_BALANCE_ITEM_SELECTOR,
STAKED_BALANCE_VALUE_SELECTOR,
} from "./wallet_balance.selectors";

export class WalletBalanceActions {
constructor(private page: Page) {}

async waitForBalanceLoadingComplete() {
await this.page.waitForFunction(
(sel) => document.querySelectorAll(sel).length === 0,
SPINNER_SELECTOR,
{ timeout: 30_000 },
);

try {
await this.page.waitForSelector(STAKED_BALANCE_ITEM_SELECTOR, {
timeout: 30000,
state: "attached",
});
} catch (error: unknown) {
await this.page.reload({ waitUntil: "domcontentloaded" });
await this.page.waitForLoadState("networkidle");
await this.page.waitForTimeout(10000);
}
}

async getStakedBalance(): Promise<string | null> {
const stakedBalance = this.page.locator(STAKED_BALANCE_VALUE_SELECTOR);
return stakedBalance.textContent();
}

async getStakableBalance(): Promise<string | null> {
return this.page.locator(STAKABLE_BALANCE_VALUE_SELECTOR).textContent();
}

async getBabyBalance(): Promise<string | null> {
return this.page.locator(BABY_BALANCE_VALUE_SELECTOR).textContent();
}

async getBabyRewards(): Promise<string | null> {
return this.page.locator(BABY_REWARDS_VALUE_SELECTOR).textContent();
}
}
40 changes: 40 additions & 0 deletions e2e/fixtures/wallet_connect.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Note: These should be replaced by data-testid selectors

export const CONNECT_BUTTON_SELECTOR = 'button:has-text("Connect")';

export const DIALOG_SELECTORS = {
TERMS_DIALOG_HEADER: '[class*="bbn-dialog-header"]',
ANY_DIALOG: 'dialog, [role="dialog"]',
ERROR_DIALOG: '[role="dialog"]:has-text("The wallet cannot be connected")',
ERROR_DIALOG_DONE_BUTTON:
'[role="dialog"]:has-text("The wallet cannot be connected") button:has-text("Done")',
};

export const BUTTON_SELECTORS = {
NEXT: 'button:has-text("Next")',
ACCEPT: 'button:has-text("Accept")',
CONTINUE: 'button:has-text("Continue")',
OK: 'button:has-text("OK")',
SAVE: '.bbn-dialog-footer button:has-text("Save")',
DONE: '.bbn-dialog-footer button:has-text("Done")',
};

export const CHECKBOX_SELECTOR = '.bbn-switcher-input[type="checkbox"]';

export const WALLET_SELECTORS = {
BITCOIN: 'button:has-text("Select Bitcoin Wallet")',
OKX: [
'button:has-text("OKX")',
'div[role="button"]:has-text("OKX")',
'[data-testid="wallet-option-okx"]',
'button:has-img[alt="OKX"]',
],
BABYLON: [
'button:has-text("Select Babylon Chain Wallet")',
'button:has-img[alt="Babylon Chain"]',
"button:has(.bbn-avatar)",
],
};

export const createGenericWalletSelector = (walletType: string) =>
`button:has(.h-10.w-10.object-contain):has-text("${walletType}")`;
210 changes: 210 additions & 0 deletions e2e/fixtures/wallet_connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { Page } from "@playwright/test";

import { injectBBNWallet, injectBTCWallet } from "../mocks/blockchain";
import { mockVerifyBTCAddress } from "../mocks/handlers";

import {
BUTTON_SELECTORS,
CHECKBOX_SELECTOR,
CONNECT_BUTTON_SELECTOR,
DIALOG_SELECTORS,
WALLET_SELECTORS,
createGenericWalletSelector,
} from "./wallet_connect.selectors";

export class WalletConnectActions {
constructor(private page: Page) {}

async clickConnectButton() {
await this.page.waitForSelector(CONNECT_BUTTON_SELECTOR, {
state: "visible",
timeout: 3000,
});

const connectButtons = await this.page
.locator(CONNECT_BUTTON_SELECTOR)
.all();

for (const button of connectButtons) {
const isDisabled = await button.isDisabled();
if (!isDisabled) {
await button.click({ force: true });
return;
}
}

if (connectButtons.length > 0) {
await connectButtons[0].click({ force: true });
}
}

async acceptTermsAndConditions() {
await Promise.race([
this.page
.locator(DIALOG_SELECTORS.TERMS_DIALOG_HEADER)
.waitFor({ state: "visible", timeout: 3000 })
.catch(() => {}),
this.page
.locator(DIALOG_SELECTORS.ANY_DIALOG)
.first()
.waitFor({ state: "visible", timeout: 3000 })
.catch(() => {}),
]);

const termsDialogVisible = await this.page
.locator(DIALOG_SELECTORS.TERMS_DIALOG_HEADER)
.isVisible()
.catch(() => false);

if (!termsDialogVisible) {
await this.handleAlternativeDialog();
return;
}

const checkboxes = this.page.locator(CHECKBOX_SELECTOR);
const count = await checkboxes.count();

for (let i = 0; i < count; i++) {
await checkboxes.nth(i).click();
}

await this.page.locator(BUTTON_SELECTORS.NEXT).click();
}

private async handleAlternativeDialog() {
const anyDialog = await this.page
.locator(DIALOG_SELECTORS.ANY_DIALOG)
.first()
.isVisible()
.catch(() => false);

if (!anyDialog) return;

const buttonSelectorsList = [
BUTTON_SELECTORS.NEXT,
BUTTON_SELECTORS.ACCEPT,
BUTTON_SELECTORS.CONTINUE,
BUTTON_SELECTORS.OK,
];

for (const selector of buttonSelectorsList) {
const button = this.page.locator(selector).first();
if (await button.isVisible().catch(() => false)) {
await button
.click()
.catch((e) => console.error(`Error clicking ${selector}:`, e));
return;
}
}

const anyButton = this.page
.locator(`${DIALOG_SELECTORS.ANY_DIALOG} button`)
.first();
if (await anyButton.isVisible().catch(() => false)) {
await anyButton
.click()
.catch((e) => console.error("Error clicking dialog button:", e));
}
}

async clickInjectableWalletButton() {
const bitcoinWalletButton = this.page
.locator(WALLET_SELECTORS.BITCOIN)
.first();

await bitcoinWalletButton.waitFor({ state: "visible", timeout: 10_000 });
await bitcoinWalletButton.click();
}

async clickConnectWalletButton() {
const saveButton = this.page.locator(BUTTON_SELECTORS.SAVE);
await saveButton.waitFor({ state: "visible", timeout: 3000 });
await saveButton.click();
}

async clickOKXWalletButton() {
for (const selector of WALLET_SELECTORS.OKX) {
const okxButton = this.page.locator(selector).first();
if (await okxButton.isVisible().catch(() => false)) {
await okxButton.click();
break;
}
}

await this.clickConnectWalletButton();
}

async clickBabylonChainWalletButton() {
for (const selector of WALLET_SELECTORS.BABYLON) {
const babylonButton = this.page.locator(selector).first();
if (await babylonButton.isVisible().catch(() => false)) {
await babylonButton.click();
break;
}
}
}

async clickGenericWalletButton(walletType: string = "Leap") {
const walletSelector = createGenericWalletSelector(walletType);
const walletButton = this.page.locator(walletSelector);
await walletButton.waitFor({ state: "visible", timeout: 3000 });
await walletButton.click();
}

async clickDoneButton() {
const doneButton = this.page.locator(BUTTON_SELECTORS.DONE);
await doneButton.waitFor({ state: "visible", timeout: 3000 });
await doneButton.click();
}

async setupMocks() {
await mockVerifyBTCAddress(this.page);
}

async handleVerificationErrorIfPresent() {
await this.page
.locator(DIALOG_SELECTORS.ERROR_DIALOG)
.waitFor({
state: "visible",
timeout: 1000,
})
.catch(() => {});

const isErrorVisible = await this.page
.locator(DIALOG_SELECTORS.ERROR_DIALOG)
.isVisible()
.catch(() => false);

if (isErrorVisible) {
const doneButton = this.page.locator(
DIALOG_SELECTORS.ERROR_DIALOG_DONE_BUTTON,
);

if (await doneButton.isVisible().catch(() => false)) {
await doneButton.click();

await this.page
.locator(DIALOG_SELECTORS.ERROR_DIALOG)
.waitFor({
state: "hidden",
timeout: 1000,
})
.catch(() => {});
}
}
}

async setupWalletConnection() {
await this.setupMocks();
await injectBTCWallet(this.page);
await injectBBNWallet(this.page);
await this.clickConnectButton();
await this.acceptTermsAndConditions();
await this.clickInjectableWalletButton();
await this.clickOKXWalletButton();
await this.handleVerificationErrorIfPresent();
await this.clickBabylonChainWalletButton();
await this.clickGenericWalletButton();
await this.clickDoneButton();
}
}
Loading