Skip to content
Open
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
115 changes: 115 additions & 0 deletions e2e/compose-test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ import { TEST_ADDRESSES } from './test-data';
export async function enableValidationBypass(page: Page): Promise<void> {
const context = page.context();

const mockPoolPosition = {
asset_a: 'XCP',
asset_b: 'PEPECASH',
lp_asset: 'A95428956661682177',
reserve_a: 500000000000,
reserve_b: 250000000000,
reserve_a_normalized: '5000',
reserve_b_normalized: '2500',
quantity: 100000000,
quantity_normalized: '1',
};

// Mock compose response - provides data for review page display
const mockComposeResponse = {
result: {
Expand Down Expand Up @@ -57,6 +69,78 @@ export async function enableValidationBypass(page: Page): Promise<void> {
const method = route.request().method();
console.log(`[E2E Debug] ${method} ${url}`);

if (url.match(/\/v2\/?(\?.*)?$/)) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
result: {
server_ready: true,
network: 'mainnet',
version: '11.1.0',
backend_height: 952500,
counterparty_height: 952500,
},
}),
});
return;
}

if (url.includes('/estimatexcpfees')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ result: 10000 }),
});
return;
}

if (url.includes('/v2/addresses/') && url.includes('/pools')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
result: [mockPoolPosition],
result_count: 1,
}),
});
return;
}

if (url.includes('/v2/pools/') && url.includes('/quote/deposit')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
result: {
asset_a: 'XCP',
asset_b: 'PEPECASH',
pool_exists: true,
first_deposit: false,
quantity_a_required: 100000000,
quantity_b_required: 50000000,
quantity_minted_estimate: 2000000,
},
}),
});
return;
}

if (url.includes('/v2/pools/') && url.includes('/quote/withdraw')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
result: {
pool_exists: true,
quantity_a_estimate: 50000000,
quantity_b_estimate: 25000000,
},
}),
});
return;
}

// Handle compose endpoints - return mock transaction data
if (url.includes('/compose/')) {
// Parse compose type and params from URL
Expand Down Expand Up @@ -116,6 +200,36 @@ export async function enableValidationBypass(page: Page): Promise<void> {
console.log(`[E2E Mock] Order: ${giveQuantity} ${giveAsset} for ${getQuantity} ${getAsset}`);
}

if (composeType === 'pooldeposit') {
responseParams = {
source: mockComposeResponse.result.params.source,
asset_a: urlParams.get('asset_a') || 'XCP',
asset_b: urlParams.get('asset_b') || 'PEPECASH',
quantity_a: parseInt(urlParams.get('quantity_a') || '100000000', 10),
quantity_b: parseInt(urlParams.get('quantity_b') || '50000000', 10),
min_lp_quantity: parseInt(urlParams.get('min_lp_quantity') || '1900000', 10),
lp_asset: urlParams.get('lp_asset') || mockPoolPosition.lp_asset,
quantity_a_normalized: '1',
quantity_b_normalized: '0.5',
min_lp_quantity_normalized: '0.019',
};
}

if (composeType === 'poolwithdraw') {
responseParams = {
source: mockComposeResponse.result.params.source,
asset_a: urlParams.get('asset_a') || mockPoolPosition.asset_a,
asset_b: urlParams.get('asset_b') || mockPoolPosition.asset_b,
lp_asset: urlParams.get('lp_asset') || mockPoolPosition.lp_asset,
quantity: parseInt(urlParams.get('quantity') || '10000000', 10),
min_quantity_a: parseInt(urlParams.get('min_quantity_a') || '47500000', 10),
min_quantity_b: parseInt(urlParams.get('min_quantity_b') || '23750000', 10),
quantity_normalized: '0.1',
min_quantity_a_normalized: '0.475',
min_quantity_b_normalized: '0.2375',
};
}

// Build dynamic response with params from request
const dynamicResponse = {
...mockComposeResponse,
Expand Down Expand Up @@ -144,6 +258,7 @@ export async function enableValidationBypass(page: Page): Promise<void> {
XCP: { divisible: true, supply: 2648755823622677, locked: true },
BTC: { divisible: true, supply: 0, locked: true },
PEPECASH: { divisible: true, supply: 1000000000000000, locked: true },
A95428956661682177: { divisible: true, supply: 10000000000, locked: true },
// TESTUNLOCKED is an unlocked asset for testing issue-supply and lock-supply
TESTUNLOCKED: { divisible: true, supply: 100000000000, locked: false },
};
Expand Down
12 changes: 12 additions & 0 deletions e2e/pages/assets/[asset]/balance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import { walletTest, expect } from '@e2e/fixtures';
import { enableValidationBypass } from '../../../compose-test-helpers';

walletTest.describe('View Balance Page (/assets/:asset/balance)', () => {
// Helper to navigate to balance page and wait for content to load
Expand Down Expand Up @@ -151,6 +152,17 @@ walletTest.describe('View Balance Page (/assets/:asset/balance)', () => {
await expect(page).toHaveURL(/compose\/dispenser\/XCP/, { timeout: 5000 });
});

walletTest('LP asset shows Manage Pool action and navigates to pool page', async ({ page }) => {
await enableValidationBypass(page);
await navigateToBalance(page, 'A95428956661682177');

const managePoolAction = page.locator('button:has-text("Manage Pool"), div[role="button"]:has-text("Manage Pool")').first();
await expect(managePoolAction).toBeVisible({ timeout: 10000 });
await managePoolAction.click();

await expect(page).toHaveURL(/\/pools\/A95428956661682177/, { timeout: 5000 });
});

walletTest('Attach action navigates to compose/utxo/attach for XCP', async ({ page }) => {
await navigateToBalance(page, 'XCP');

Expand Down
67 changes: 67 additions & 0 deletions e2e/pages/compose/pool/deposit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Compose Pool Deposit Page Tests (/compose/pool/deposit)
*/

import { walletTest, expect } from '@e2e/fixtures';
import { compose } from '@e2e/selectors';
import {
clickBack,
enableDryRun,
enableValidationBypass,
waitForReview,
} from '../../../compose-test-helpers';

walletTest.describe('Pool Deposit Flow - Full Compose Flow', () => {
walletTest.beforeEach(async ({ page }) => {
await enableValidationBypass(page);
await enableDryRun(page);
});

walletTest('prefilled pair can quote and reach review', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/compose/pool/deposit/XCP/PEPECASH'));
await page.waitForLoadState('networkidle');

await expect(compose.pool.assetAAmountInput(page)).toBeVisible({ timeout: 10000 });
await compose.pool.assetAAmountInput(page).fill('1');
await compose.pool.assetBAmountInput(page).fill('0.5');

await expect(page.getByText(/Quoted partner amount/i)).toBeVisible({ timeout: 10000 });
await expect(page.getByText(/Minimum LP tokens/i)).toBeVisible({ timeout: 10000 });

const submitBtn = compose.pool.depositButton(page);
await expect(submitBtn).toBeEnabled({ timeout: 10000 });
await submitBtn.click();

await waitForReview(page);
await expect(page.getByText('PEPECASH / XCP')).toBeVisible();
await expect(page.getByText(/Minimum LP/i)).toBeVisible();
});

walletTest('form data is preserved after returning from review', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/compose/pool/deposit/XCP/PEPECASH'));
await page.waitForLoadState('networkidle');

await compose.pool.assetAAmountInput(page).fill('1');
await compose.pool.assetBAmountInput(page).fill('0.5');

await expect(compose.pool.depositButton(page)).toBeEnabled({ timeout: 10000 });
await compose.pool.depositButton(page).click();
await waitForReview(page);

await clickBack(page);

await expect(compose.pool.assetAAmountInput(page)).toHaveValue('1');
await expect(compose.pool.assetBAmountInput(page)).toHaveValue('0.5');
});

walletTest('Use quote fills the partner asset amount', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/compose/pool/deposit/XCP/PEPECASH'));
await page.waitForLoadState('networkidle');

await compose.pool.assetAAmountInput(page).fill('1');
await expect(compose.pool.useQuoteButton(page)).toBeVisible({ timeout: 10000 });
await compose.pool.useQuoteButton(page).click();

await expect(compose.pool.assetBAmountInput(page)).toHaveValue('0.5');
});
});
53 changes: 53 additions & 0 deletions e2e/pages/compose/pool/withdraw.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Compose Pool Withdraw Page Tests (/compose/pool/withdraw/:lpAsset)
*/

import { walletTest, expect } from '@e2e/fixtures';
import { compose } from '@e2e/selectors';
import {
clickBack,
enableDryRun,
enableValidationBypass,
waitForReview,
} from '../../../compose-test-helpers';

walletTest.describe('Pool Withdraw Flow - Full Compose Flow', () => {
walletTest.beforeEach(async ({ page }) => {
await enableValidationBypass(page);
await enableDryRun(page);
});

walletTest('LP position can quote and reach review', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/compose/pool/withdraw/A95428956661682177'));
await page.waitForLoadState('networkidle');

await expect(page.getByText('PEPECASH / XCP')).toBeVisible({ timeout: 10000 });
await expect(compose.pool.lpWithdrawInput(page)).toBeVisible({ timeout: 10000 });
await compose.pool.lpWithdrawInput(page).fill('0.1');

await expect(page.getByText(/Estimated receive/i)).toBeVisible({ timeout: 10000 });
await expect(page.getByText(/Minimum received/i)).toBeVisible({ timeout: 10000 });

const submitBtn = compose.pool.withdrawButton(page);
await expect(submitBtn).toBeEnabled({ timeout: 10000 });
await submitBtn.click();

await waitForReview(page);
await expect(page.getByText('PEPECASH / XCP')).toBeVisible();
await expect(page.getByText(/Minimum Receive/i)).toBeVisible();
});

walletTest('form data is preserved after returning from review', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/compose/pool/withdraw/A95428956661682177'));
await page.waitForLoadState('networkidle');

await compose.pool.lpWithdrawInput(page).fill('0.1');
await expect(compose.pool.withdrawButton(page)).toBeEnabled({ timeout: 10000 });
await compose.pool.withdrawButton(page).click();
await waitForReview(page);

await clickBack(page);

await expect(compose.pool.lpWithdrawInput(page)).toHaveValue('0.1');
});
});
39 changes: 39 additions & 0 deletions e2e/pages/pools/[lpAsset].spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Pool Position Page Tests (/pools/:lpAsset)
*/

import { walletTest, expect } from '@e2e/fixtures';
import { enableValidationBypass } from '../../compose-test-helpers';

walletTest.describe('Pool Position Page (/pools/:lpAsset)', () => {
walletTest.beforeEach(async ({ page }) => {
await enableValidationBypass(page);
});

walletTest('shows pool details and action buttons', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/pools/A95428956661682177'));
await page.waitForLoadState('networkidle');

await expect(page.getByRole('heading', { name: 'PEPECASH / XCP' })).toBeVisible({ timeout: 10000 });
await expect(page.getByText('Reserve PEPECASH')).toBeVisible();
await expect(page.getByText('Reserve XCP')).toBeVisible();
await expect(page.getByRole('button', { name: 'Deposit' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Withdraw' })).toBeVisible();
});

walletTest('Deposit opens the prefilled pool deposit flow', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/pools/A95428956661682177'));
await page.waitForLoadState('networkidle');

await page.getByRole('button', { name: 'Deposit' }).click();
await expect(page).toHaveURL(/compose\/pool\/deposit\/XCP\/PEPECASH/, { timeout: 5000 });
});

walletTest('Withdraw opens the pool withdraw flow', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/pools/A95428956661682177'));
await page.waitForLoadState('networkidle');

await page.getByRole('button', { name: 'Withdraw' }).click();
await expect(page).toHaveURL(/compose\/pool\/withdraw\/A95428956661682177/, { timeout: 5000 });
});
});
44 changes: 44 additions & 0 deletions e2e/pages/pools/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Manage Pools Page Tests (/pools)
*
* Tests the pool entry point and position list using mocked Counterparty pool data.
*/

import { walletTest, expect, navigateTo } from '@e2e/fixtures';
import { actions } from '@e2e/selectors';
import { enableValidationBypass } from '../../compose-test-helpers';

walletTest.describe('Manage Pools Page (/pools)', () => {
walletTest.beforeEach(async ({ page }) => {
await enableValidationBypass(page);
});

walletTest('can navigate to Manage Pools from actions', async ({ page }) => {
await navigateTo(page, 'actions');

await expect(actions.managePoolsOption(page)).toBeVisible({ timeout: 5000 });
await actions.managePoolsOption(page).click();

await expect(page).toHaveURL(/\/pools/, { timeout: 5000 });
await expect(page.getByRole('button', { name: /Enter Pool/i }).first()).toBeVisible();
});

walletTest('lists LP positions and opens the position page', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/pools'));
await page.waitForLoadState('networkidle');

await expect(page.getByText('PEPECASH / XCP')).toBeVisible({ timeout: 10000 });
await expect(page.getByText('A95428956661682177')).toBeVisible();

await page.getByRole('button').filter({ hasText: 'PEPECASH / XCP' }).first().click();
await expect(page).toHaveURL(/\/pools\/A95428956661682177/, { timeout: 5000 });
});

walletTest('Enter Pool opens the pool deposit flow', async ({ page }) => {
await page.goto(page.url().replace(/\/index.*/, '/pools'));
await page.waitForLoadState('networkidle');

await page.getByRole('button', { name: /Enter Pool/i }).first().click();
await expect(page).toHaveURL(/compose\/pool\/deposit/, { timeout: 5000 });
});
});
Loading
Loading