diff --git a/packages/e2e/pages/trade-page.ts b/packages/e2e/pages/trade-page.ts index 9f64cd8951..4065c52525 100644 --- a/packages/e2e/pages/trade-page.ts +++ b/packages/e2e/pages/trade-page.ts @@ -53,7 +53,9 @@ export class TradePage extends BasePage { this.limitTabBtn = page.locator('//div[@class="w-full"]/button[.="Limit"]'); this.orderHistoryLink = page.getByText("Order history"); this.limitPrice = page.locator("//div/input[@type='text']"); - this.slippageInput = page.locator('input[type="text"][inputmode="decimal"]').first(); + this.slippageInput = page + .locator('input[type="text"][inputmode="decimal"]') + .first(); } async goto() { @@ -172,7 +174,7 @@ export class TradePage extends BasePage { ).toBeFalsy(); console.log("Swap and Sign now.."); await expect(this.swapBtn, "Swap button is disabled!").toBeEnabled({ - timeout: 7000, + timeout: 15000, }); await this.swapBtn.click({ timeout: 4000 }); // Handle 1-click by default @@ -219,7 +221,7 @@ export class TradePage extends BasePage { ) .first(); // Wait for token to be visible before clicking - await fromLocator.waitFor({ state: 'visible', timeout: 10000 }); + await fromLocator.waitFor({ state: "visible", timeout: 10000 }); await fromLocator.click({ timeout: 10000 }); // Select To Token await toToken.click({ timeout: 10000 }); @@ -234,7 +236,7 @@ export class TradePage extends BasePage { ) .first(); // Wait for token to be visible before clicking - await toLocator.waitFor({ state: 'visible', timeout: 10000 }); + await toLocator.waitFor({ state: "visible", timeout: 10000 }); await toLocator.click({ timeout: 10000 }); // we expect that after 2 seconds exchange rate is populated. await this.page.waitForTimeout(2000); @@ -326,104 +328,424 @@ export class TradePage extends BasePage { return `${fromTokenText}/${toTokenText}`; } - async buyAndGetWalletMsg(context: BrowserContext, limit = false) { - await expect(this.buyBtn, "Buy button is disabled!").toBeEnabled({ - timeout: this.buySellTimeout, - }); - // Handle Pop-up page -> - await this.buyBtn.click(); - const pageApprove = context.waitForEvent("page"); - await this.confirmSwapBtn.click(); - await this.page.waitForTimeout(200); - const approvePage = await pageApprove; - await approvePage.waitForLoadState(); - const approveBtn = approvePage.getByRole("button", { - name: "Approve", - }); - await expect(approveBtn).toBeEnabled(); - let msgTextLocator = "type: osmosis/poolmanager/"; - if (limit) { - msgTextLocator = "Execute contract"; + /** + * Initiates a buy transaction and retrieves the wallet message content from Keplr popup. + * Supports both market orders and limit orders with configurable retry logic and slippage tolerance. + * + * @param context - Browser context to handle Keplr popup windows + * @param options - Configuration options for the buy operation + * @param options.maxRetries - Maximum number of retry attempts on failure (default: 2, meaning 3 total attempts) + * @param options.slippagePercent - Slippage tolerance percentage as string (e.g., "3" for 3%). Applied after buy button click. + * @param options.limit - Whether this is a limit order (default: false). Affects message validation logic. + * + * @returns Object containing msgContentAmount (string | undefined) + * - Returns message content if Keplr popup appears (standard wallet approval flow) + * - Returns undefined if no popup appears within 20s (1-click trading or pre-approved) + * + * @throws Error if buy operation fails after all retry attempts or if insufficient balance + * + * @remarks + * - Checks for sufficient balance before attempting transaction + * - Implements automatic retry logic with 2s delay between attempts + * - Gracefully handles 1-click trading scenarios (no Keplr popup) + * - Automatically waits for blockchain confirmation before returning (40s timeout) + * - Each retry attempt gets a fresh success listener to avoid timeout issues + */ + async buyAndGetWalletMsg( + context: BrowserContext, + options?: { maxRetries?: number; slippagePercent?: string; limit?: boolean } + ) { + const maxRetries = options?.maxRetries ?? 2; + const slippagePercent = options?.slippagePercent; + const limit = options?.limit ?? false; + + // Check for insufficient balance BEFORE retry loop - no point retrying if balance is low + expect( + await this.isInsufficientBalance(), + "Insufficient balance for buy! Please top up your wallet." + ).toBeFalsy(); + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + if (attempt > 0) { + console.log( + `🔄 Retry attempt ${attempt}/${maxRetries} for buy operation...` + ); + } + + await expect(this.buyBtn, "Buy button is disabled!").toBeEnabled({ + timeout: this.buySellTimeout, + }); + + // IMPORTANT: Start listening for transaction success BEFORE any UI interactions + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect(this.trxSuccessful).toBeVisible({ + timeout: 40000, + }); + + // Handle Pop-up page -> + // IMPORTANT: waitForEvent MUST have a timeout to prevent hanging indefinitely + // If Keplr popup doesn't appear (1-click trading enabled), this will timeout gracefully + const pageApprovePromise = context.waitForEvent("page", { + timeout: 20000, + }); + await this.buyBtn.click(); + // Small wait to let UI settle before triggering popup + await this.page.waitForTimeout(500); + + // Set slippage tolerance if specified (after buy clicked, before confirm) + if (slippagePercent) { + await this.setSlippageTolerance(slippagePercent); + } + + await this.confirmSwapBtn.click(); + + let msgContentAmount: string | undefined; + + try { + const approvePage = await pageApprovePromise; + await approvePage.waitForLoadState(); + const approveBtn = approvePage.getByRole("button", { + name: "Approve", + }); + await expect(approveBtn).toBeEnabled(); + let msgTextLocator = "type: osmosis/poolmanager/"; + if (limit) { + msgTextLocator = "Execute contract"; + } + msgContentAmount = + (await approvePage.getByText(msgTextLocator).textContent()) ?? + undefined; + console.log(`Wallet is approving this msg: \n${msgContentAmount}`); + // Approve trx + await approveBtn.click(); + // Handle Pop-up page <- + } catch (error: any) { + // IMPORTANT: Gracefully handle timeout errors for 1-click trading scenarios + // When 1-click trading is enabled, no Keplr popup appears and waitForEvent times out + // This is expected behavior, not an error - transaction is still submitted on-chain + if ( + error.name === "TimeoutError" || + (error instanceof Error && /timeout/i.test(error.message)) + ) { + console.log( + "✓ Keplr approval popup did not appear within 20s; assuming 1-click trading is enabled or transaction was pre-approved." + ); + msgContentAmount = undefined; + } else { + // Other errors (button not found, page closed, etc.) should be retried + console.error( + "Failed to get Keplr approval popup:", + error.message ?? "Unknown error" + ); + throw error; // Re-throw to be caught by outer try-catch + } + } + + // Successfully submitted! Now wait for transaction success + if (attempt > 0) { + console.log( + `✓ Buy transaction submitted after ${attempt} retry(ies)` + ); + } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Each retry gets a fresh 40s timeout to avoid timeout exhaustion + await successPromise; + + return { msgContentAmount }; + } catch (error: any) { + const isLastAttempt = attempt === maxRetries; + + if (isLastAttempt) { + console.error( + `❌ Buy operation failed after ${maxRetries + 1} attempts:`, + error.message ?? "Unknown error" + ); + throw new Error( + `Failed to complete buy operation after ${ + maxRetries + 1 + } attempts. ` + + `Last error: ${ + error.message ?? "Unknown error" + }. Check if wallet extension is properly configured.` + ); + } + + console.warn( + `⚠️ Buy operation failed on attempt ${attempt + 1}/${ + maxRetries + 1 + }. ` + `Error: ${error.message ?? "Unknown error"}. Retrying...` + ); + + // Wait before retry to let things settle + await this.page.waitForTimeout(2000); + } } - const msgContentAmount = await approvePage - .getByText(msgTextLocator) - .textContent(); - console.log(`Wallet is approving this msg: \n${msgContentAmount}`); - // Approve trx - await approveBtn.click(); - // wait for trx confirmation - await this.page.waitForTimeout(2000); - // Handle Pop-up page <- - return { msgContentAmount }; + + // TypeScript needs this but it should never reach here + throw new Error("Buy operation failed unexpectedly"); } - async sellAndGetWalletMsg(context: BrowserContext, limit = false) { - // Make sure Sell button is enabled - await expect(this.sellBtn, "Sell button is disabled!").toBeEnabled({ - timeout: this.buySellTimeout, - }); - // Handle Pop-up page -> - await this.sellBtn.click(); - const pageApprove = context.waitForEvent("page"); - await this.confirmSwapBtn.click(); - await this.page.waitForTimeout(200); - const approvePage = await pageApprove; - await approvePage.waitForLoadState(); - const approveBtn = approvePage.getByRole("button", { - name: "Approve", - }); - await expect(approveBtn).toBeEnabled(); - let msgTextLocator = "type: osmosis/poolmanager/"; - if (limit) { - msgTextLocator = "Execute contract"; + /** + * Initiates a sell transaction and retrieves the wallet message content from Keplr popup. + * Supports both market orders and limit orders with configurable retry logic and slippage tolerance. + * + * @param context - Browser context to handle Keplr popup windows + * @param options - Configuration options for the sell operation + * @param options.maxRetries - Maximum number of retry attempts on failure (default: 2, meaning 3 total attempts) + * @param options.slippagePercent - Slippage tolerance percentage as string (e.g., "3" for 3%). Applied after sell button click. + * @param options.limit - Whether this is a limit order (default: false). Affects message validation logic. + * + * @returns Object containing msgContentAmount (string | undefined) + * - Returns message content if Keplr popup appears (standard wallet approval flow) + * - Returns undefined if no popup appears within 20s (1-click trading or pre-approved) + * + * @throws Error if sell operation fails after all retry attempts or if insufficient balance + * + * @remarks + * - Checks for sufficient balance before attempting transaction + * - Implements automatic retry logic with 2s delay between attempts + * - Gracefully handles 1-click trading scenarios (no Keplr popup) + * - Automatically waits for blockchain confirmation before returning (40s timeout) + * - Each retry attempt gets a fresh success listener to avoid timeout issues + */ + async sellAndGetWalletMsg( + context: BrowserContext, + options?: { maxRetries?: number; slippagePercent?: string; limit?: boolean } + ) { + const maxRetries = options?.maxRetries ?? 2; + const slippagePercent = options?.slippagePercent; + const limit = options?.limit ?? false; + + // Check for insufficient balance BEFORE retry loop - no point retrying if balance is low + expect( + await this.isInsufficientBalance(), + "Insufficient balance for sell! Please top up your wallet." + ).toBeFalsy(); + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + if (attempt > 0) { + console.log( + `🔄 Retry attempt ${attempt}/${maxRetries} for sell operation...` + ); + } + + // Make sure Sell button is enabled + await expect(this.sellBtn, "Sell button is disabled!").toBeEnabled({ + timeout: this.buySellTimeout, + }); + + // IMPORTANT: Start listening for transaction success BEFORE any UI interactions + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect(this.trxSuccessful).toBeVisible({ + timeout: 40000, + }); + + // Handle Pop-up page -> + // IMPORTANT: waitForEvent MUST have a timeout to prevent hanging indefinitely + // If Keplr popup doesn't appear (1-click trading enabled), this will timeout gracefully + const pageApprovePromise = context.waitForEvent("page", { + timeout: 20000, + }); + await this.sellBtn.click(); + // Small wait to let UI settle before triggering popup + await this.page.waitForTimeout(500); + + // Set slippage tolerance if specified (after sell clicked, before confirm) + if (slippagePercent) { + await this.setSlippageTolerance(slippagePercent); + } + + await this.confirmSwapBtn.click(); + + let msgContentAmount: string | undefined; + + try { + const approvePage = await pageApprovePromise; + await approvePage.waitForLoadState(); + const approveBtn = approvePage.getByRole("button", { + name: "Approve", + }); + await expect(approveBtn).toBeEnabled(); + let msgTextLocator = "type: osmosis/poolmanager/"; + if (limit) { + msgTextLocator = "Execute contract"; + } + msgContentAmount = + (await approvePage.getByText(msgTextLocator).textContent()) ?? + undefined; + console.log(`Wallet is approving this msg: \n${msgContentAmount}`); + // Approve trx + await approveBtn.click(); + // Handle Pop-up page <- + } catch (error: any) { + // IMPORTANT: Gracefully handle timeout errors for 1-click trading scenarios + // When 1-click trading is enabled, no Keplr popup appears and waitForEvent times out + // This is expected behavior, not an error - transaction is still submitted on-chain + if ( + error.name === "TimeoutError" || + (error instanceof Error && /timeout/i.test(error.message)) + ) { + console.log( + "✓ Keplr approval popup did not appear within 20s; assuming 1-click trading is enabled or transaction was pre-approved." + ); + msgContentAmount = undefined; + } else { + // Other errors (button not found, page closed, etc.) should be retried + console.error( + "Failed to get Keplr approval popup:", + error.message ?? "Unknown error" + ); + throw error; // Re-throw to be caught by outer try-catch + } + } + + // Successfully submitted! Now wait for transaction success + if (attempt > 0) { + console.log( + `✓ Sell transaction submitted after ${attempt} retry(ies)` + ); + } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Each retry gets a fresh 40s timeout to avoid timeout exhaustion + await successPromise; + + return { msgContentAmount }; + } catch (error: any) { + const isLastAttempt = attempt === maxRetries; + + if (isLastAttempt) { + console.error( + `❌ Sell operation failed after ${maxRetries + 1} attempts:`, + error.message ?? "Unknown error" + ); + throw new Error( + `Failed to complete sell operation after ${ + maxRetries + 1 + } attempts. ` + + `Last error: ${ + error.message ?? "Unknown error" + }. Check if wallet extension is properly configured.` + ); + } + + console.warn( + `⚠️ Sell operation failed on attempt ${attempt + 1}/${ + maxRetries + 1 + }. ` + `Error: ${error.message ?? "Unknown error"}. Retrying...` + ); + + // Wait before retry to let things settle + await this.page.waitForTimeout(2000); + } } - const msgContentAmount = await approvePage - .getByText(msgTextLocator) - .textContent(); - console.log(`Wallet is approving this msg: \n${msgContentAmount}`); - // Approve trx - await approveBtn.click(); - // wait for trx confirmation - await this.page.waitForTimeout(2000); - // Handle Pop-up page <- - return { msgContentAmount }; + + // TypeScript needs this but it should never reach here + throw new Error("Sell operation failed unexpectedly"); } - async sellAndApprove(context: BrowserContext, options?: { slippagePercent?: string }) { + /** + * Initiates a sell transaction with simplified approval flow. + * Automatically waits for transaction confirmation on blockchain. + * + * @param context - Browser context to handle potential Keplr popup windows + * @param options - Configuration options for the sell operation + * @param options.slippagePercent - Slippage tolerance percentage as string (e.g., "3" for 3%). Applied after sell button click. + * + * @remarks + * - Waits for sell button to be enabled before proceeding + * - Automatically approves transaction in Keplr if popup appears (7s timeout) + * - Gracefully handles 1-click trading (no popup scenario) + * - Waits for blockchain confirmation before returning (40s timeout) + * - No retry logic - for retry support, use sellAndGetWalletMsg() + */ + async sellAndApprove( + context: BrowserContext, + options?: { slippagePercent?: string } + ) { const slippagePercent = options?.slippagePercent; - + // Make sure Sell button is enabled await expect(this.sellBtn, "Sell button is disabled!").toBeEnabled({ timeout: this.buySellTimeout, }); + + // IMPORTANT: Start listening for transaction success BEFORE any UI interactions + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect(this.trxSuccessful).toBeVisible({ + timeout: 40000, + }); + await this.sellBtn.click(); - + // Set slippage tolerance if specified (after sell clicked, before confirm) if (slippagePercent) { await this.setSlippageTolerance(slippagePercent); } - + await this.confirmSwapBtn.click(); await this.justApproveIfNeeded(context); await this.page.waitForTimeout(1000); + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + await successPromise; } - async buyAndApprove(context: BrowserContext, options?: { slippagePercent?: string }) { + /** + * Initiates a buy transaction with simplified approval flow. + * Automatically waits for transaction confirmation on blockchain. + * + * @param context - Browser context to handle potential Keplr popup windows + * @param options - Configuration options for the buy operation + * @param options.slippagePercent - Slippage tolerance percentage as string (e.g., "3" for 3%). Applied after buy button click. + * + * @remarks + * - Waits for buy button to be enabled before proceeding + * - Automatically approves transaction in Keplr if popup appears (7s timeout) + * - Gracefully handles 1-click trading (no popup scenario) + * - Waits for blockchain confirmation before returning (40s timeout) + * - No retry logic - for retry support, use buyAndGetWalletMsg() + */ + async buyAndApprove( + context: BrowserContext, + options?: { slippagePercent?: string } + ) { const slippagePercent = options?.slippagePercent; - + await expect(this.buyBtn, "Buy button is disabled!").toBeEnabled({ timeout: this.buySellTimeout, }); + + // IMPORTANT: Start listening for transaction success BEFORE any UI interactions + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect(this.trxSuccessful).toBeVisible({ + timeout: 40000, + }); + await this.buyBtn.click(); - + // Set slippage tolerance if specified (after buy clicked, before confirm) if (slippagePercent) { await this.setSlippageTolerance(slippagePercent); } - + await this.confirmSwapBtn.click(); await this.justApproveIfNeeded(context); await this.page.waitForTimeout(1000); + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + await successPromise; } /** @@ -433,20 +755,22 @@ export class TradePage extends BasePage { */ async setSlippageTolerance(slippagePercent: string) { console.log(`⚙️ Setting slippage tolerance to ${slippagePercent}%...`); - + try { // Wait for review modal and slippage input to be visible - await this.slippageInput.waitFor({ state: 'visible', timeout: 5000 }); - + await this.slippageInput.waitFor({ state: "visible", timeout: 5000 }); + // Click to focus the input await this.slippageInput.click(); - + // Clear and set new value await this.slippageInput.fill(slippagePercent); - + // Verify the value was actually set - await expect(this.slippageInput).toHaveValue(slippagePercent, { timeout: 2000 }); - + await expect(this.slippageInput).toHaveValue(slippagePercent, { + timeout: 2000, + }); + console.log(`✓ Slippage tolerance confirmed set to ${slippagePercent}%`); } catch (error: any) { console.warn(`⚠️ Could not set slippage tolerance: ${error.message}`); @@ -454,13 +778,30 @@ export class TradePage extends BasePage { } } + /** + * Initiates a swap transaction with retry logic and automatic confirmation. + * Handles race conditions where quote refreshes temporarily disable the swap button. + * + * @param context - Browser context to handle potential Keplr popup windows + * @param options - Configuration options for the swap operation + * @param options.maxRetries - Maximum number of retry attempts on failure (default: 3, meaning 4 total attempts) + * @param options.slippagePercent - Slippage tolerance percentage as string (e.g., "3" for 3%). Applied after swap button click. + * + * @remarks + * - Checks for sufficient balance before attempting swap + * - Implements automatic retry logic with 1.5s delay for quote refresh race conditions + * - Automatically approves transaction in Keplr if popup appears (7s timeout) + * - Gracefully handles 1-click trading (no popup scenario) + * - Waits for blockchain confirmation before returning (40s timeout) + * - Retries only on swap button disabled errors; other errors fail immediately + */ async swapAndApprove( - context: BrowserContext, + context: BrowserContext, options?: { maxRetries?: number; slippagePercent?: string } ) { const maxRetries = options?.maxRetries ?? 3; const slippagePercent = options?.slippagePercent; - + // Retry logic to handle race conditions where quote refreshes and temporarily disables swap button for (let attempt = 0; attempt <= maxRetries; attempt++) { try { @@ -473,43 +814,59 @@ export class TradePage extends BasePage { await expect(this.swapBtn, "Swap button is disabled!").toBeEnabled({ timeout: this.buySellTimeout, }); + + // IMPORTANT: Start listening for transaction success BEFORE any UI interactions + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect(this.trxSuccessful).toBeVisible({ + timeout: 40000, + }); + await this.swapBtn.click({ timeout: 4000 }); - + // Set slippage tolerance if specified (after swap clicked, before confirm) if (slippagePercent) { await this.setSlippageTolerance(slippagePercent); } - + await this.confirmSwapBtn.click({ timeout: 5000 }); await this.justApproveIfNeeded(context); - - // Success! Exit retry loop + + // Successfully submitted! Now wait for transaction success if (attempt > 0) { - console.log(`✓ Swap succeeded after ${attempt} retry(ies)`); + console.log( + `✓ Swap transaction submitted after ${attempt} retry(ies)` + ); } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Each retry gets a fresh 40s timeout to avoid timeout exhaustion + await successPromise; + return; - } catch (error: any) { - const isDisabledError = error.message?.includes('disabled') || - error.message?.includes('toBeEnabled'); - + const isDisabledError = + error.message?.includes("disabled") || + error.message?.includes("toBeEnabled"); + if (attempt < maxRetries && isDisabledError) { console.warn( `⚠️ RACE CONDITION DETECTED: Swap button disabled ` + - `(attempt ${attempt + 1}/${maxRetries + 1}). ` + - `Waiting for quote to stabilize and retrying...` + `(attempt ${attempt + 1}/${maxRetries + 1}). ` + + `Waiting for quote to stabilize and retrying...` ); - + // Wait for quote/route to finish refreshing await this.page.waitForTimeout(1500); - + // Log exchange rate to see if it changed - const rate = await this.getExchangeRate().catch(() => 'N/A'); + const rate = await this.getExchangeRate().catch(() => "N/A"); console.log(`Exchange rate after wait: ${rate}`); - + continue; // Retry } - + // Final attempt failed or different error - throw it throw error; } diff --git a/packages/e2e/pages/transactions-page.ts b/packages/e2e/pages/transactions-page.ts index 89c60b8216..555cc1f1ea 100644 --- a/packages/e2e/pages/transactions-page.ts +++ b/packages/e2e/pages/transactions-page.ts @@ -3,175 +3,320 @@ import { type Locator, type Page, expect, -} from '@playwright/test' +} from "@playwright/test"; -import { BasePage } from './base-page' -const TRANSACTION_CONFIRMATION_TIMEOUT = 2000 +import { BasePage } from "./base-page"; +const TRANSACTION_CONFIRMATION_TIMEOUT = 2000; export class TransactionsPage extends BasePage { - readonly transactionRow: Locator - readonly viewExplorerLink: Locator - readonly closeTransactionBtn: Locator - readonly page: Page - readonly claimAndClose: Locator - readonly claimAllBtn: Locator + readonly transactionRow: Locator; + readonly viewExplorerLink: Locator; + readonly closeTransactionBtn: Locator; + readonly page: Page; + readonly claimAndClose: Locator; + readonly claimAllBtn: Locator; constructor(page: Page) { - super(page) - this.page = page - this.transactionRow = page.locator('//div/p[.="Swapped"]') - this.viewExplorerLink = page.locator('//a/span["View on explorer"]/..') - this.closeTransactionBtn = page.locator('//button[@data-testid="close"]') - this.claimAndClose = page.getByRole('button', { - name: 'Claim and close', + super(page); + this.page = page; + this.transactionRow = page.locator('//div/p[.="Swapped"]'); + this.viewExplorerLink = page.locator('//a/span["View on explorer"]/..'); + this.closeTransactionBtn = page.locator('//button[@data-testid="close"]'); + this.claimAndClose = page.getByRole("button", { + name: "Claim and close", exact: true, - }) - this.claimAllBtn = page.getByRole('button', { - name: 'Claim all', + }); + this.claimAllBtn = page.getByRole("button", { + name: "Claim all", exact: true, - }) + }); } async open() { - await this.page.goto('/transactions') - return this + await this.page.goto("/transactions"); + return this; } async viewTransactionByNumber(number: number) { - await this.transactionRow.nth(number).click() - await this.page.waitForTimeout(1000) + await this.transactionRow.nth(number).click(); + await this.page.waitForTimeout(1000); } async viewBySwapAmount(amount: string | number) { // Transactions need some time to get loaded, wait for 30 seconds. - await this.page.waitForTimeout(30000) - await this.page.reload() - const loc = `//div/div[@class="subtitle1 text-osmoverse-100" and contains(text(), "${amount}")]` + await this.page.waitForTimeout(30000); + await this.page.reload(); + const loc = `//div/div[@class="subtitle1 text-osmoverse-100" and contains(text(), "${amount}")]`; const isTransactionVisible = await this.page .locator(loc) - .isVisible({ timeout: 3000 }) + .isVisible({ timeout: 3000 }); if (!isTransactionVisible) { - await this.page.waitForTimeout(30000) - await this.page.reload() + await this.page.waitForTimeout(30000); + await this.page.reload(); } - await this.page.waitForTimeout(1000) - await this.page.locator(loc).click({ timeout: 3000 }) + await this.page.waitForTimeout(1000); + await this.page.locator(loc).click({ timeout: 3000 }); } async closeTransaction() { - await this.closeTransactionBtn.click() + await this.closeTransactionBtn.click(); } async viewOnExplorerIsVisible() { - await expect(this.viewExplorerLink).toBeVisible() + await expect(this.viewExplorerLink).toBeVisible(); } async getOnExplorerLink() { - const trxUrl = await this.viewExplorerLink.getAttribute('href') - console.log(`Trx url: ${trxUrl}`) - return trxUrl + const trxUrl = await this.viewExplorerLink.getAttribute("href"); + console.log(`Trx url: ${trxUrl}`); + return trxUrl; } async takeScreenshot(name: string) { await this.page.screenshot({ path: `screenshot-transactions-${name}.png`, fullPage: true, - }) + }); } + /** + * Cancels a limit order by locating and clicking the cancel button, then approving in Keplr. + * Automatically waits for transaction confirmation on blockchain. + * + * @param amount - The order amount to locate (e.g., "Sell $1.01 of") + * @param price - The limit price to locate (e.g., "5.234") + * @param context - Browser context to handle Keplr popup window + * + * @remarks + * - Locates cancel button using xpath with amount and price selectors + * - Checks button visibility first to fail fast if not found (avoids hanging promises) + * - Starts success listener BEFORE clicking to catch immediate confirmations (1-click trading) + * - Waits for Keplr approval popup with 20s timeout (gracefully handles 1-click scenarios) + * - Validates that the transaction message contains 'cancel_limit' + * - Waits for actual blockchain confirmation (40s timeout) instead of arbitrary delays + */ async cancelLimitOrder( amount: string, price: string, - context: BrowserContext, + context: BrowserContext ) { - const cancelBtn = `//td//span[.='${amount}']/../../../../..//td//p[.='$${price}']/../../..//button` - console.log(`Use locator for a cancel btn: ${cancelBtn}`) - await this.page.locator(cancelBtn).first().click() - const pageApprove = context.waitForEvent('page') - const approvePage = await pageApprove - await approvePage.waitForLoadState() - const approveBtn = approvePage.getByRole('button', { - name: 'Approve', - }) - await expect(approveBtn).toBeEnabled() - const msgContentAmount = await approvePage - .getByText('Execute contract') - .textContent() - console.log(`Wallet is approving this msg: \n${msgContentAmount}`) - // Approve trx - await approveBtn.click() - // Expect that this is a cancel limit call - expect(msgContentAmount).toContain('cancel_limit') - // wait for trx confirmation - await this.page.waitForTimeout(TRANSACTION_CONFIRMATION_TIMEOUT) + const cancelBtn = `//td//span[.='${amount}']/../../../../..//td//p[.='$${price}']/../../..//button`; + console.log(`Use locator for a cancel btn: ${cancelBtn}`); + + // IMPORTANT: Check button exists first before starting success listener + // This prevents hanging promises if button is not found (fail-fast pattern) + const cancelBtnLocator = this.page.locator(cancelBtn).first(); + await expect(cancelBtnLocator, "Cancel button not found!").toBeVisible({ + timeout: 5000, + }); + + // IMPORTANT: Start listening for transaction success BEFORE clicking cancel button + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect( + this.page.getByText("Transaction Successful") + ).toBeVisible({ + timeout: 40000, + }); + + await cancelBtnLocator.click(); + + // IMPORTANT: waitForEvent MUST have a timeout to prevent hanging indefinitely + // If Keplr popup doesn't appear (1-click trading enabled), this will timeout gracefully + const pageApprovePromise = context.waitForEvent("page", { + timeout: 20000, + }); + + try { + const approvePage = await pageApprovePromise; + await approvePage.waitForLoadState(); + const approveBtn = approvePage.getByRole("button", { + name: "Approve", + }); + await expect(approveBtn).toBeEnabled(); + const msgContentAmount = await approvePage + .getByText("Execute contract") + .textContent(); + console.log(`Wallet is approving this msg: \n${msgContentAmount}`); + // Approve trx + await approveBtn.click(); + // Expect that this is a cancel limit call + expect(msgContentAmount).toContain("cancel_limit"); + } catch (error: any) { + // IMPORTANT: Gracefully handle timeout errors for 1-click trading scenarios + // When 1-click trading is enabled, no Keplr popup appears and waitForEvent times out + // This is expected behavior, not an error - transaction is still submitted on-chain + if ( + error.name === "TimeoutError" || + error.message?.includes("Timeout") || + error.message?.includes("timeout") + ) { + console.log( + "✓ Keplr approval popup did not appear within 20s for cancel operation; assuming 1-click trading is enabled or transaction was pre-approved." + ); + } else { + // Other errors (button not found, page closed, etc.) should fail the test + console.error( + "Failed to get Keplr approval popup for cancel:", + error.message ?? "Unknown error" + ); + throw error; + } + } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Replaces old pattern: await this.page.waitForTimeout(2000) + await successPromise; } async isFilledByLimitPrice(price: string | number) { - const loc = `//td//span[.='Filled']/../../..//td//p[.='$${price}']` - console.log(`Use Limit Order locator: ${loc}`) + const loc = `//td//span[.='Filled']/../../..//td//p[.='$${price}']`; + console.log(`Use Limit Order locator: ${loc}`); await expect(this.page.locator(loc).first()).toBeVisible({ timeout: 120_000, visible: true, - }) + }); } async claimAndCloseAny(context: BrowserContext) { const isClaimable = await this.claimAndClose .first() - .isVisible({ timeout: 3000 }) + .isVisible({ timeout: 3000 }); if (!isClaimable) { - console.log('No partially filled orders to claim.') - return + console.log("No partially filled orders to claim."); + return; } - await this.claimAndClose.first().click() - const pageApprove = context.waitForEvent('page') - const approvePage = await pageApprove - await approvePage.waitForLoadState() - const approveBtn = approvePage.getByRole('button', { - name: 'Approve', - }) - await expect(approveBtn).toBeEnabled() - const msgContentAmount1 = await approvePage - .getByText('Execute contract') - .first() - .textContent() - const msgContentAmount2 = await approvePage - .getByText('Execute contract') - .last() - .textContent() - console.log( - `Wallet is approving this msg: \n${msgContentAmount1}---- \n${msgContentAmount2}`, - ) - // Approve trx - await approveBtn.click() - expect(msgContentAmount1).toContain('claim_limit') - expect(msgContentAmount2).toContain('cancel_limit') - // wait for trx confirmation - await this.page.waitForTimeout(TRANSACTION_CONFIRMATION_TIMEOUT) + + // IMPORTANT: Start listening for transaction success BEFORE clicking button + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect( + this.page.getByText("Transaction Successful") + ).toBeVisible({ + timeout: 40000, + }); + + await this.claimAndClose.first().click(); + + // IMPORTANT: waitForEvent MUST have a timeout to prevent hanging indefinitely + // If Keplr popup doesn't appear (1-click trading enabled), this will timeout gracefully + const pageApprovePromise = context.waitForEvent("page", { + timeout: 20000, + }); + + try { + const approvePage = await pageApprovePromise; + await approvePage.waitForLoadState(); + const approveBtn = approvePage.getByRole("button", { + name: "Approve", + }); + await expect(approveBtn).toBeEnabled(); + const msgContentAmount1 = await approvePage + .getByText("Execute contract") + .first() + .textContent(); + const msgContentAmount2 = await approvePage + .getByText("Execute contract") + .last() + .textContent(); + console.log( + `Wallet is approving this msg: \n${msgContentAmount1}---- \n${msgContentAmount2}` + ); + // Approve trx + await approveBtn.click(); + expect(msgContentAmount1).toContain("claim_limit"); + expect(msgContentAmount2).toContain("cancel_limit"); + } catch (error: any) { + // IMPORTANT: Gracefully handle timeout errors for 1-click trading scenarios + // When 1-click trading is enabled, no Keplr popup appears and waitForEvent times out + // This is expected behavior, not an error - transaction is still submitted on-chain + if ( + error.name === "TimeoutError" || + error.message?.includes("Timeout") || + error.message?.includes("timeout") + ) { + console.log( + "✓ Keplr approval popup did not appear within 20s for claim and close; assuming 1-click trading is enabled or transaction was pre-approved." + ); + } else { + // Other errors (button not found, page closed, etc.) should fail the test + console.error( + "Failed to get Keplr approval popup for claim and close:", + error.message ?? "Unknown error" + ); + throw error; + } + } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Replaces old pattern: await this.page.waitForTimeout(TRANSACTION_CONFIRMATION_TIMEOUT) + await successPromise; } async claimAll(context: BrowserContext) { - await this.claimAllBtn.click() - const pageApprove = context.waitForEvent('page') - const approvePage = await pageApprove - await approvePage.waitForLoadState() - const approveBtn = approvePage.getByRole('button', { - name: 'Approve', - }) - await expect(approveBtn).toBeEnabled() - // Approve trx - await approveBtn.click() - // wait for trx confirmation - await this.page.waitForTimeout(TRANSACTION_CONFIRMATION_TIMEOUT) + // IMPORTANT: Start listening for transaction success BEFORE clicking button + // This ensures we don't miss immediate confirmations (1-click trading can be very fast) + // The promise runs in parallel with subsequent operations to minimize total wait time + const successPromise = expect( + this.page.getByText("Transaction Successful") + ).toBeVisible({ + timeout: 40000, + }); + + await this.claimAllBtn.click(); + + // IMPORTANT: waitForEvent MUST have a timeout to prevent hanging indefinitely + // If Keplr popup doesn't appear (1-click trading enabled), this will timeout gracefully + const pageApprovePromise = context.waitForEvent("page", { + timeout: 20000, + }); + + try { + const approvePage = await pageApprovePromise; + await approvePage.waitForLoadState(); + const approveBtn = approvePage.getByRole("button", { + name: "Approve", + }); + await expect(approveBtn).toBeEnabled(); + // Approve trx + await approveBtn.click(); + } catch (error: any) { + // IMPORTANT: Gracefully handle timeout errors for 1-click trading scenarios + // When 1-click trading is enabled, no Keplr popup appears and waitForEvent times out + // This is expected behavior, not an error - transaction is still submitted on-chain + if ( + error.name === "TimeoutError" || + error.message?.includes("Timeout") || + error.message?.includes("timeout") + ) { + console.log( + "✓ Keplr approval popup did not appear within 20s for claim all; assuming 1-click trading is enabled or transaction was pre-approved." + ); + } else { + // Other errors (button not found, page closed, etc.) should fail the test + console.error( + "Failed to get Keplr approval popup for claim all:", + error.message ?? "Unknown error" + ); + throw error; + } + } + + // IMPORTANT: Wait for actual blockchain confirmation instead of arbitrary timeout + // This ensures transaction is actually confirmed on-chain (or fails) before proceeding + // Replaces old pattern: await this.page.waitForTimeout(TRANSACTION_CONFIRMATION_TIMEOUT) + await successPromise; } async claimAllIfPresent(context: BrowserContext) { - const isClaimable = await this.claimAllBtn.isVisible({ timeout: 4000 }) + const isClaimable = await this.claimAllBtn.isVisible({ timeout: 4000 }); if (isClaimable) { - console.log('Claim All filled limit orders!') - await this.claimAll(context) + console.log("Claim All filled limit orders!"); + await this.claimAll(context); } else { - console.log('No Claim All button.') + console.log("No Claim All button."); } } } diff --git a/packages/e2e/playwright.config.ts b/packages/e2e/playwright.config.ts index f884e49d59..9c044b683c 100644 --- a/packages/e2e/playwright.config.ts +++ b/packages/e2e/playwright.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ ["junit", { outputFile: "./playwright-report/test-results.xml" }], ["json", { outputFile: "./playwright-report/test-results.json" }], ], - timeout: 90000, // Increased from 60s to 90s for slippage tolerance + balance checks + network latency + timeout: 90000, // Increased from default 60s to 90s for slippage tolerance + balance checks + network latency testDir: "./tests", /* Run tests in files in parallel. */ fullyParallel: true, diff --git a/packages/e2e/setup-keplr.ts b/packages/e2e/setup-keplr.ts index 1d7a48e542..980c410a94 100644 --- a/packages/e2e/setup-keplr.ts +++ b/packages/e2e/setup-keplr.ts @@ -6,7 +6,7 @@ import { WalletPage } from "./pages/keplr-page"; export class SetupKeplr { async setupWalletKeplr(privateKey: string, headless = false) { - const pathToKeplrExtension = new UnzipExtension().getPathToExtension(); + const pathToKeplrExtension = await new UnzipExtension().getPathToExtension(); const testConfig = new TestConfig().getBrowserExtensionConfig( headless, pathToKeplrExtension diff --git a/packages/e2e/tests/monitoring.limit.wallet.spec.ts b/packages/e2e/tests/monitoring.limit.wallet.spec.ts index 4ffa71cb72..082b0e6977 100644 --- a/packages/e2e/tests/monitoring.limit.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.limit.wallet.spec.ts @@ -1,76 +1,77 @@ -import * as core from '@actions/core' -import { type BrowserContext, expect, test } from '@playwright/test' -import { TradePage } from '../pages/trade-page' -import { SetupKeplr } from '../setup-keplr' -import { ensureBalances } from '../utils/balance-checker' +import * as core from "@actions/core"; +import { type BrowserContext, expect, test } from "@playwright/test"; +import { TradePage } from "../pages/trade-page"; +import { SetupKeplr } from "../setup-keplr"; +import { ensureBalances } from "../utils/balance-checker"; -test.describe('Test Filled Limit Order feature', () => { - let context: BrowserContext - const privateKey = process.env.PRIVATE_KEY ?? 'private_key' - const walletId = process.env.WALLET_ID ?? 'wallet_id' - let tradePage: TradePage +test.describe("Test Filled Limit Order feature", () => { + let context: BrowserContext; + const privateKey = process.env.PRIVATE_KEY ?? "private_key"; + const walletId = process.env.WALLET_ID ?? "wallet_id"; + let tradePage: TradePage; test.beforeAll(async () => { - context = await new SetupKeplr().setupWallet(privateKey) - + context = await new SetupKeplr().setupWallet(privateKey); + // Check balances before running tests - warn only mode - await ensureBalances(walletId, [ - { token: 'OSMO', amount: 1.1 }, // For limit sell OSMO - { token: 'USDC', amount: 1.1 }, // For limit buy OSMO - ], { warnOnly: true }) - - tradePage = new TradePage(context.pages()[0]) - await tradePage.goto() - }) + await ensureBalances( + walletId, + [ + { token: "OSMO", amount: 1.1 }, // For limit sell OSMO + { token: "USDC", amount: 1.1 }, // For limit buy OSMO + ], + { warnOnly: true } + ); + + tradePage = new TradePage(context.pages()[0]); + await tradePage.goto(); + }); test.beforeEach(async () => { - await tradePage.connectWallet() - expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() - }) + await tradePage.connectWallet(); + expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); + }); test.afterEach(async () => { - await tradePage.logOut() - }) + await tradePage.logOut(); + }); // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) - if (testInfo.status === 'failed') { - const name = testInfo.title - core.notice(`Test ${name} failed.`) + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); + if (testInfo.status === "failed") { + const name = testInfo.title; + core.notice(`Test ${name} failed.`); } - }) + }); - test('User should be able to limit sell OSMO', async () => { - await tradePage.goto() - await tradePage.openSellTab() - await tradePage.openLimit() - await tradePage.selectAsset('OSMO') - await tradePage.enterAmount('1.08') - await tradePage.setLimitPriceChange('Market') - await tradePage.sellAndApprove(context) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + test("User should be able to limit sell OSMO", async () => { + await tradePage.goto(); + await tradePage.openSellTab(); + await tradePage.openLimit(); + await tradePage.selectAsset("OSMO"); + await tradePage.enterAmount("1.08"); + await tradePage.setLimitPriceChange("Market"); + await tradePage.sellAndApprove(context); + await tradePage.getTransactionUrl(); + }); - test('User should be able to limit buy OSMO', async () => { - const PRICE_INCREASE_FACTOR = 1.07 // 7% increase for limit price - const _ORDER_HISTORY_TIMEOUT = 30 // Seconds to wait for order history - await tradePage.goto() - await tradePage.openBuyTab() - await tradePage.openLimit() - await tradePage.selectAsset('OSMO') - await tradePage.enterAmount('1.04') - await tradePage.setLimitPriceChange('Market') - const limitPrice = Number(await tradePage.getLimitPrice()) - const highLimitPrice = (limitPrice * PRICE_INCREASE_FACTOR).toFixed(4) - await tradePage.setLimitPrice(String(highLimitPrice)) - await tradePage.buyAndApprove(context) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() + test("User should be able to limit buy OSMO", async () => { + const PRICE_INCREASE_FACTOR = 1.07; // 7% increase for limit price + await tradePage.goto(); + await tradePage.openBuyTab(); + await tradePage.openLimit(); + await tradePage.selectAsset("OSMO"); + await tradePage.enterAmount("1.04"); + await tradePage.setLimitPriceChange("Market"); + const limitPrice = Number(await tradePage.getLimitPrice()); + const highLimitPrice = (limitPrice * PRICE_INCREASE_FACTOR).toFixed(4); + await tradePage.setLimitPrice(String(highLimitPrice)); + await tradePage.buyAndApprove(context); + await tradePage.getTransactionUrl(); //await tradePage.gotoOrdersHistory(ORDER_HISTORY_TIMEOUT); //const p = context.pages()[0] //const trxPage = new TransactionsPage(p) //await trxPage.isFilledByLimitPrice(highLimitPrice) - }) -}) + }); +}); diff --git a/packages/e2e/tests/monitoring.market.wallet.spec.ts b/packages/e2e/tests/monitoring.market.wallet.spec.ts index 807c3b9cd9..5884fbf526 100644 --- a/packages/e2e/tests/monitoring.market.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.market.wallet.spec.ts @@ -1,89 +1,89 @@ -import * as core from '@actions/core' -import { type BrowserContext, expect, test } from '@playwright/test' -import { TradePage } from '../pages/trade-page' -import { SetupKeplr } from '../setup-keplr' -import { ensureBalances } from '../utils/balance-checker' +import * as core from "@actions/core"; +import { type BrowserContext, expect, test } from "@playwright/test"; +import { TradePage } from "../pages/trade-page"; +import { SetupKeplr } from "../setup-keplr"; +import { ensureBalances } from "../utils/balance-checker"; -test.describe('Test Market Buy/Sell Order feature', () => { - let context: BrowserContext - const privateKey = process.env.PRIVATE_KEY ?? 'private_key' - const walletId = process.env.WALLET_ID ?? 'wallet_id' - let tradePage: TradePage - const TRX_SUCCESS_TIMEOUT = 10000 +test.describe("Test Market Buy/Sell Order feature", () => { + let context: BrowserContext; + const privateKey = process.env.PRIVATE_KEY ?? "private_key"; + const walletId = process.env.WALLET_ID ?? "wallet_id"; + let tradePage: TradePage; test.beforeAll(async () => { - context = await new SetupKeplr().setupWallet(privateKey) - + context = await new SetupKeplr().setupWallet(privateKey); + // Check balances before running tests - warn only mode - await ensureBalances(walletId, [ - { token: 'USDC', amount: 3.2 }, // For market buy BTC and OSMO (1.55 each) - { token: 'BTC', amount: 1.6 }, // For market sell BTC - { token: 'OSMO', amount: 1.6 }, // For market sell OSMO - ], { warnOnly: true }) - - tradePage = new TradePage(context.pages()[0]) - await tradePage.goto() - }) + await ensureBalances( + walletId, + [ + { token: "USDC", amount: 3.2 }, // For market buy BTC and OSMO (1.55 each) + { token: "BTC", amount: 1.6 }, // For market sell BTC + { token: "OSMO", amount: 1.6 }, // For market sell OSMO + ], + { warnOnly: true } + ); + + tradePage = new TradePage(context.pages()[0]); + await tradePage.goto(); + }); test.afterAll(async () => { - await context.close() - }) + await context.close(); + }); test.beforeEach(async () => { - await tradePage.connectWallet() - expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() - }) + await tradePage.connectWallet(); + expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); + }); test.afterEach(async () => { - await tradePage.logOut() - }) + await tradePage.logOut(); + }); // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) - if (testInfo.status === 'failed') { - const name = testInfo.title - core.notice(`Test ${name} failed.`) + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); + if (testInfo.status === "failed") { + const name = testInfo.title; + core.notice(`Test ${name} failed.`); } - }) + }); // biome-ignore lint/complexity/noForEach: - ;[{ name: 'BTC' }, { name: 'OSMO' }].forEach(({ name }) => { + [{ name: "BTC" }, { name: "OSMO" }].forEach(({ name }) => { test(`User should be able to Market Buy ${name}`, async () => { - await tradePage.goto() - await tradePage.openBuyTab() - await tradePage.selectAsset(name) - await tradePage.enterAmount('1.55') - await tradePage.isSufficientBalanceForTrade() - await tradePage.showSwapInfo() - await tradePage.buyAndApprove(context, { slippagePercent: '3' }) - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) - await tradePage.getTransactionUrl() - }) - }) + await tradePage.goto(); + await tradePage.openBuyTab(); + await tradePage.selectAsset(name); + await tradePage.enterAmount("1.55"); + await tradePage.isSufficientBalanceForTrade(); + await tradePage.showSwapInfo(); + await tradePage.buyAndApprove(context, { slippagePercent: "3" }); + await tradePage.getTransactionUrl(); + }); + }); // unwrapped market sell tests just in case this affects anything. - test('User should be able to Market Sell BTC', async () => { - await tradePage.goto() - await tradePage.openSellTab() - await tradePage.selectAsset('BTC') - await tradePage.enterAmount('1.54') - await tradePage.isSufficientBalanceForTrade() - await tradePage.showSwapInfo() - await tradePage.sellAndApprove(context, { slippagePercent: '3' }) - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) - await tradePage.getTransactionUrl() - }) + test("User should be able to Market Sell BTC", async () => { + await tradePage.goto(); + await tradePage.openSellTab(); + await tradePage.selectAsset("BTC"); + await tradePage.enterAmount("1.54"); + await tradePage.isSufficientBalanceForTrade(); + await tradePage.showSwapInfo(); + await tradePage.sellAndApprove(context, { slippagePercent: "3" }); + await tradePage.getTransactionUrl(); + }); - test('User should be able to Market Sell OSMO', async () => { - await tradePage.goto() - await tradePage.openSellTab() - await tradePage.selectAsset('OSMO') - await tradePage.enterAmount('1.54') - await tradePage.isSufficientBalanceForTrade() - await tradePage.showSwapInfo() - await tradePage.sellAndApprove(context, { slippagePercent: '3' }) - await tradePage.isTransactionSuccesful(TRX_SUCCESS_TIMEOUT) - await tradePage.getTransactionUrl() - }) -}) + test("User should be able to Market Sell OSMO", async () => { + await tradePage.goto(); + await tradePage.openSellTab(); + await tradePage.selectAsset("OSMO"); + await tradePage.enterAmount("1.54"); + await tradePage.isSufficientBalanceForTrade(); + await tradePage.showSwapInfo(); + await tradePage.sellAndApprove(context, { slippagePercent: "3" }); + await tradePage.getTransactionUrl(); + }); +}); diff --git a/packages/e2e/tests/monitoring.swap.wallet.spec.ts b/packages/e2e/tests/monitoring.swap.wallet.spec.ts index e92d22e62f..812ca3931f 100644 --- a/packages/e2e/tests/monitoring.swap.wallet.spec.ts +++ b/packages/e2e/tests/monitoring.swap.wallet.spec.ts @@ -1,60 +1,68 @@ -import * as core from '@actions/core' -import { type BrowserContext, expect, test } from '@playwright/test' -import { TradePage } from '../pages/trade-page' -import { SetupKeplr } from '../setup-keplr' -import { ensureBalances } from '../utils/balance-checker' +import * as core from "@actions/core"; +import { type BrowserContext, expect, test } from "@playwright/test"; +import { TradePage } from "../pages/trade-page"; +import { SetupKeplr } from "../setup-keplr"; +import { ensureBalances } from "../utils/balance-checker"; -test.describe('Test Swap Stables feature', () => { - let context: BrowserContext - const privateKey = process.env.PRIVATE_KEY ?? 'private_key' - const walletId = process.env.WALLET_ID ?? 'wallet_id' - let tradePage: TradePage - const swapAmount = '0.55' +test.describe("Test Swap Stables feature", () => { + let context: BrowserContext; + const privateKey = process.env.PRIVATE_KEY ?? "private_key"; + const walletId = process.env.WALLET_ID ?? "wallet_id"; + let tradePage: TradePage; + const swapAmount = "0.55"; test.beforeAll(async () => { - context = await new SetupKeplr().setupWallet(privateKey) - + context = await new SetupKeplr().setupWallet(privateKey); + // Check balances before running tests - warn only mode - await ensureBalances(walletId, [ - { token: 'USDC', amount: 1.2 }, // Total for USDC swaps - { token: 'USDC.eth.axl', amount: 0.6 }, // For USDC.eth.axl swap - { token: 'USDT', amount: 0.6 }, // For USDT swap - ], { warnOnly: true }) - - tradePage = new TradePage(context.pages()[0]) - await tradePage.goto() - await tradePage.connectWallet() - expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() - }) + await ensureBalances( + walletId, + [ + { token: "USDC", amount: 1.2 }, // Total for USDC swaps + { token: "USDC.eth.axl", amount: 0.6 }, // For USDC.eth.axl swap + { token: "USDT", amount: 0.6 }, // For USDT swap + ], + { warnOnly: true } + ); + + tradePage = new TradePage(context.pages()[0]); + await tradePage.goto(); + await tradePage.connectWallet(); + expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); + }); test.afterAll(async () => { - await context.close() - }) + await context.close(); + }); // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) - if (testInfo.status === 'failed') { - const name = testInfo.title - core.notice(`Test ${name} failed.`) + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); + if (testInfo.status === "failed") { + const name = testInfo.title; + core.notice(`Test ${name} failed.`); } - }) + }); // biome-ignore lint/complexity/noForEach: - ;[ - { from: 'USDC', to: 'USDC.eth.axl' }, - { from: 'USDC.eth.axl', to: 'USDC' }, - { from: 'USDC', to: 'USDT' }, - { from: 'USDT', to: 'USDC' }, + [ + { from: "USDC", to: "USDC.eth.axl" }, + { from: "USDC.eth.axl", to: "USDC" }, + { from: "USDC", to: "USDT" }, + { from: "USDT", to: "USDC" }, ].forEach(({ from, to }) => { test(`User should be able to swap ${from} to ${to}`, async () => { - await tradePage.goto() - await tradePage.selectPair(from, to) - await tradePage.enterAmount(swapAmount) - await tradePage.showSwapInfo() - await tradePage.swapAndApprove(context, { slippagePercent: '3' }) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) - }) -}) + await tradePage.goto(); + await tradePage.selectPair(from, to); + await tradePage.enterAmount(swapAmount); + await tradePage.showSwapInfo(); + // Slippage tolerance set to 1% for stablecoin swaps. + // Stablecoin pairs typically have tighter spreads and more predictable pricing, + // so a lower slippage improves trade execution. However, this may increase the risk + // of transaction failures during periods of high volatility or network congestion. + // Adjust if swap failures become frequent. + await tradePage.swapAndApprove(context, { slippagePercent: "1" }); + await tradePage.getTransactionUrl(); + }); + }); +}); diff --git a/packages/e2e/tests/swap.osmo.wallet.spec.ts b/packages/e2e/tests/swap.osmo.wallet.spec.ts index 22361a0c61..269cd5c361 100644 --- a/packages/e2e/tests/swap.osmo.wallet.spec.ts +++ b/packages/e2e/tests/swap.osmo.wallet.spec.ts @@ -1,82 +1,89 @@ -import * as core from '@actions/core' -import { type BrowserContext, expect, test } from '@playwright/test' -import { TradePage } from '../pages/trade-page' -import { SetupKeplr } from '../setup-keplr' -import { ensureBalances } from '../utils/balance-checker' +import * as core from "@actions/core"; +import { type BrowserContext, expect, test } from "@playwright/test"; +import { TradePage } from "../pages/trade-page"; +import { SetupKeplr } from "../setup-keplr"; +import { ensureBalances } from "../utils/balance-checker"; -test.describe('Test Swap to/from OSMO feature', () => { - let context: BrowserContext +test.describe("Test Swap to/from OSMO feature", () => { + let context: BrowserContext; const _walletId = - process.env.WALLET_ID ?? 'osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l' - const privateKey = process.env.PRIVATE_KEY ?? 'private_key' - let tradePage: TradePage + process.env.WALLET_ID ?? "osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l"; + const privateKey = process.env.PRIVATE_KEY ?? "private_key"; + let tradePage: TradePage; const _ATOM = - 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2' + "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; test.beforeAll(async () => { - context = await new SetupKeplr().setupWallet(privateKey) - + context = await new SetupKeplr().setupWallet(privateKey); + // Check balances before running tests - fail fast if insufficient - await ensureBalances(_walletId, [ - { token: 'OSMO', amount: 0.2 }, // Max needed in single test - { token: 'ATOM', amount: 0.01 }, // Max needed in single test - ], { warnOnly: true }) - - tradePage = new TradePage(context.pages()[0]) - await tradePage.goto() - await tradePage.connectWallet() - expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() - }) + await ensureBalances( + _walletId, + [ + { token: "OSMO", amount: 0.2 }, // Max needed in single test + { token: "ATOM", amount: 0.01 }, // Max needed in single test + ], + { warnOnly: true } + ); + + tradePage = new TradePage(context.pages()[0]); + await tradePage.goto(); + await tradePage.connectWallet(); + expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); + }); test.afterAll(async () => { - await tradePage.logOut() - await context.close() - }) + await tradePage.logOut(); + await context.close(); + }); test.beforeEach(async () => { - await tradePage.goto() - }) + await tradePage.goto(); + }); // biome-ignore lint/correctness/noEmptyPattern: test.afterEach(async ({}, testInfo) => { - console.log(`Test [${testInfo.title}] status: ${testInfo.status}`) - if (testInfo.status === 'failed') { - const name = testInfo.title - core.notice(`Test ${name} failed.`) + console.log(`Test [${testInfo.title}] status: ${testInfo.status}`); + if (testInfo.status === "failed") { + const name = testInfo.title; + core.notice(`Test ${name} failed.`); } - }) + }); - test.skip('User should be able to swap OSMO to WBTC', async () => { - await tradePage.selectPair('OSMO', 'WBTC') - await tradePage.enterAmount('0.9') - await tradePage.showSwapInfo() - await tradePage.swapAndApprove(context) + test.skip("User should be able to swap OSMO to WBTC", async () => { + await tradePage.selectPair("OSMO", "WBTC"); + await tradePage.enterAmount("0.9"); + await tradePage.showSwapInfo(); + await tradePage.swapAndApprove(context); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("denom: uosmo"); - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + await tradePage.getTransactionUrl(); + }); - test('User should be able to swap OSMO to ATOM', async () => { - await tradePage.selectPair('OSMO', 'ATOM') - await tradePage.enterAmount('0.2') - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }) + test("User should be able to swap OSMO to ATOM", async () => { + await tradePage.selectPair("OSMO", "ATOM"); + await tradePage.enterAmount("0.2"); + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); //expect(msgContent).toContain(`token_out_denom: ${ATOM}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("denom: uosmo"); - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + await tradePage.getTransactionUrl(); + }); - test('User should be able to swap ATOM to OSMO', async () => { - await tradePage.selectPair('ATOM', 'OSMO') - await tradePage.enterAmount('0.01') - await tradePage.showSwapInfo() - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }) + test("User should be able to swap ATOM to OSMO", async () => { + await tradePage.selectPair("ATOM", "OSMO"); + await tradePage.enterAmount("0.01"); + await tradePage.showSwapInfo(); + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); //expect(msgContent).toContain(`denom: ${ATOM}`); //expect(msgContent).toContain(`sender: ${walletId}`); //expect(msgContent).toContain("token_out_denom: uosmo"); - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) -}) + await tradePage.getTransactionUrl(); + }); +}); diff --git a/packages/e2e/tests/swap.usdc.wallet.spec.ts b/packages/e2e/tests/swap.usdc.wallet.spec.ts index 3367c34b08..6126b5e5f5 100644 --- a/packages/e2e/tests/swap.usdc.wallet.spec.ts +++ b/packages/e2e/tests/swap.usdc.wallet.spec.ts @@ -23,16 +23,20 @@ test.describe("Test Swap to/from USDC feature", () => { test.beforeAll(async () => { context = await new SetupKeplr().setupWallet(privateKey); - + // Check balances before running tests - fail fast if insufficient - await ensureBalances(_walletId, [ - { token: "USDC", amount: 0.5 }, // Total needed: 0.1 + 0.1 + 0.2 + 0.1 for swaps to other tokens - { token: "ATOM", amount: 0.015 }, // Max needed in single test - { token: "TIA", amount: 0.02 }, // Max needed in single test - { token: "INJ", amount: 0.01 }, // Max needed in single test - { token: "AKT", amount: 0.025 }, // Max needed in single test - ], { warnOnly: true }); - + await ensureBalances( + _walletId, + [ + { token: "USDC", amount: 0.5 }, // Total needed: 0.1 + 0.1 + 0.2 + 0.1 for swaps to other tokens + { token: "ATOM", amount: 0.015 }, // Max needed in single test + { token: "TIA", amount: 0.02 }, // Max needed in single test + { token: "INJ", amount: 0.01 }, // Max needed in single test + { token: "AKT", amount: 0.025 }, // Max needed in single test + ], + { warnOnly: true } + ); + // Switch to Application tradePage = new TradePage(context.pages()[0]); await tradePage.goto(); @@ -62,8 +66,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("ATOM", "USDC"); await tradePage.enterAmount("0.015"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -71,8 +77,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("USDC", "ATOM"); await tradePage.enterAmount("0.1"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -80,8 +88,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("USDC", "TIA"); await tradePage.enterAmount("0.1"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -89,8 +99,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("TIA", "USDC"); await tradePage.enterAmount("0.02"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -98,8 +110,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("USDC", "INJ"); await tradePage.enterAmount("0.2"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -107,8 +121,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("INJ", "USDC"); await tradePage.enterAmount("0.01"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -116,8 +132,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("USDC", "AKT"); await tradePage.enterAmount("0.1"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); @@ -125,8 +143,10 @@ test.describe("Test Swap to/from USDC feature", () => { await tradePage.selectPair("AKT", "USDC"); await tradePage.enterAmount("0.025"); await tradePage.showSwapInfo(); - await tradePage.swapAndApprove(context, { maxRetries: 3, slippagePercent: "3" }); - await tradePage.isTransactionSuccesful(15); // Increased timeout for USDC swaps + await tradePage.swapAndApprove(context, { + maxRetries: 3, + slippagePercent: "3", + }); await tradePage.getTransactionUrl(); }); }); diff --git a/packages/e2e/tests/trade.wallet.spec.ts b/packages/e2e/tests/trade.wallet.spec.ts index 4a0c4a7820..ce6157fc3f 100644 --- a/packages/e2e/tests/trade.wallet.spec.ts +++ b/packages/e2e/tests/trade.wallet.spec.ts @@ -1,123 +1,135 @@ -import { type BrowserContext, expect, test } from '@playwright/test' -import { TradePage } from '../pages/trade-page' -import { TransactionsPage } from '../pages/transactions-page' -import { SetupKeplr } from '../setup-keplr' -import { ensureBalances } from '../utils/balance-checker' +import { type BrowserContext, expect, test } from "@playwright/test"; +import { TradePage } from "../pages/trade-page"; +import { TransactionsPage } from "../pages/transactions-page"; +import { SetupKeplr } from "../setup-keplr"; +import { ensureBalances } from "../utils/balance-checker"; -test.describe('Test Trade feature', () => { - let context: BrowserContext - const privateKey = process.env.PRIVATE_KEY ?? 'private_key' +test.describe("Test Trade feature", () => { + let context: BrowserContext; + const privateKey = process.env.PRIVATE_KEY ?? "private_key"; const walletId = - process.env.WALLET_ID ?? 'osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l' - let tradePage: TradePage + process.env.WALLET_ID ?? "osmo1qyc8u7cn0zjxcu9dvrjz5zwfnn0ck92v62ak9l"; + let tradePage: TradePage; const USDC = - 'ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4' + "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4"; const ATOM = - 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2' + "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; test.beforeAll(async () => { - context = await new SetupKeplr().setupWallet(privateKey) - + context = await new SetupKeplr().setupWallet(privateKey); + // Balance pre-check before running tests (warnOnly keeps monitoring runs non-blocking) - await ensureBalances(walletId, [ - { token: 'USDC', amount: 1.12 }, // Max needed for buy test - { token: 'ATOM', amount: 2.12 }, // Total needed: 1.11 + 1.01 for sell tests - { token: 'OSMO', amount: 1.01 }, // Max needed for limit sell OSMO test - ], { warnOnly: true }) - tradePage = new TradePage(context.pages()[0]) - await tradePage.goto() - }) + await ensureBalances( + walletId, + [ + { token: "USDC", amount: 1.12 }, // Max needed for buy test + { token: "ATOM", amount: 2.12 }, // Total needed: 1.11 + 1.01 for sell tests + { token: "OSMO", amount: 1.01 }, // Max needed for limit sell OSMO test + ], + { warnOnly: true } + ); + tradePage = new TradePage(context.pages()[0]); + await tradePage.goto(); + }); test.afterAll(async () => { - await context.close() - }) + await context.close(); + }); test.beforeEach(async () => { - await tradePage.connectWallet() - expect(await tradePage.isError(), 'Swap is not available!').toBeFalsy() - }) + await tradePage.connectWallet(); + expect(await tradePage.isError(), "Swap is not available!").toBeFalsy(); + }); test.afterEach(async () => { - await tradePage.logOut() - }) + await tradePage.logOut(); + }); - test('User should be able to Buy ATOM', async () => { - await tradePage.goto() - await tradePage.openBuyTab() - await tradePage.selectAsset('ATOM') - await tradePage.enterAmount('1.12') - const { msgContentAmount } = await tradePage.buyAndGetWalletMsg(context) - expect(msgContentAmount).toBeTruthy() - expect(msgContentAmount).toContain(`denom: ${ATOM}`) - expect(msgContentAmount).toContain('type: osmosis/poolmanager/') - expect(msgContentAmount).toContain(`denom: ${USDC}`) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + test("User should be able to Buy ATOM", async () => { + await tradePage.goto(); + await tradePage.openBuyTab(); + await tradePage.selectAsset("ATOM"); + await tradePage.enterAmount("1.12"); + const { msgContentAmount } = await tradePage.buyAndGetWalletMsg(context, { + maxRetries: 2, + slippagePercent: "3", + }); + // Only validate message content if Keplr popup appeared (not 1-click trading) + if (msgContentAmount) { + expect(msgContentAmount).toContain(`denom: ${ATOM}`); + expect(msgContentAmount).toContain("type: osmosis/poolmanager/"); + expect(msgContentAmount).toContain(`denom: ${USDC}`); + } + await tradePage.getTransactionUrl(); + }); - test('User should be able to Sell ATOM', async () => { - await tradePage.goto() - await tradePage.openSellTab() - await tradePage.selectAsset('ATOM') - await tradePage.enterAmount('1.11') - const { msgContentAmount } = await tradePage.sellAndGetWalletMsg(context) - expect(msgContentAmount).toBeTruthy() - expect(msgContentAmount).toContain(`denom: ${USDC}`) - expect(msgContentAmount).toContain('type: osmosis/poolmanager/') - expect(msgContentAmount).toContain(`denom: ${ATOM}`) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + test("User should be able to Sell ATOM", async () => { + await tradePage.goto(); + await tradePage.openSellTab(); + await tradePage.selectAsset("ATOM"); + await tradePage.enterAmount("1.11"); + const { msgContentAmount } = await tradePage.sellAndGetWalletMsg(context, { + maxRetries: 2, + slippagePercent: "3", + }); + // Only validate message content if Keplr popup appeared (not 1-click trading) + if (msgContentAmount) { + expect(msgContentAmount).toContain(`denom: ${USDC}`); + expect(msgContentAmount).toContain("type: osmosis/poolmanager/"); + expect(msgContentAmount).toContain(`denom: ${ATOM}`); + } + await tradePage.getTransactionUrl(); + }); - test('User should be able to limit sell ATOM', async () => { - await tradePage.goto() - const amount = '1.01' - await tradePage.openSellTab() - await tradePage.openLimit() - await tradePage.selectAsset('ATOM') - await tradePage.enterAmount(amount) - await tradePage.setLimitPriceChange('5%') - const limitPrice = await tradePage.getLimitPrice() - const { msgContentAmount } = await tradePage.sellAndGetWalletMsg( - context, - true, - ) - expect(msgContentAmount).toBeTruthy() - //expect(msgContentAmount).toContain(amount + " ATOM (Cosmos Hub/channel-0)"); - expect(msgContentAmount).toContain('place_limit') - expect(msgContentAmount).toContain('"order_direction": "ask"') - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - await tradePage.gotoOrdersHistory() - const trxPage = new TransactionsPage(context.pages()[0]) - await trxPage.cancelLimitOrder(`Sell $${amount} of`, limitPrice, context) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) + test("User should be able to limit sell ATOM", async () => { + await tradePage.goto(); + const amount = "1.01"; + await tradePage.openSellTab(); + await tradePage.openLimit(); + await tradePage.selectAsset("ATOM"); + await tradePage.enterAmount(amount); + await tradePage.setLimitPriceChange("5%"); + const limitPrice = await tradePage.getLimitPrice(); + const { msgContentAmount } = await tradePage.sellAndGetWalletMsg(context, { + maxRetries: 2, + limit: true, + }); + // Only validate message content if Keplr popup appeared (not 1-click trading) + if (msgContentAmount) { + //expect(msgContentAmount).toContain(amount + " ATOM (Cosmos Hub/channel-0)"); + expect(msgContentAmount).toContain("place_limit"); + expect(msgContentAmount).toContain('"order_direction": "ask"'); + } + await tradePage.getTransactionUrl(); + await tradePage.gotoOrdersHistory(); + const trxPage = new TransactionsPage(context.pages()[0]); + await trxPage.cancelLimitOrder(`Sell $${amount} of`, limitPrice, context); + await tradePage.getTransactionUrl(); + }); - test('User should be able to cancel limit sell OSMO', async () => { - await tradePage.goto() - const amount = '1.01' - await tradePage.openSellTab() - await tradePage.openLimit() - await tradePage.selectAsset('OSMO') - await tradePage.enterAmount(amount) - await tradePage.setLimitPriceChange('10%') - const limitPrice = await tradePage.getLimitPrice() - const { msgContentAmount } = await tradePage.sellAndGetWalletMsg( - context, - true, - ) - expect(msgContentAmount).toBeTruthy() - //expect(msgContentAmount).toContain(`${amount} OSMO`); - expect(msgContentAmount).toContain('place_limit') - expect(msgContentAmount).toContain('"order_direction": "ask"') - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - await tradePage.gotoOrdersHistory() - const trxPage = new TransactionsPage(context.pages()[0]) - await trxPage.cancelLimitOrder(`Sell $${amount} of`, limitPrice, context) - await tradePage.isTransactionSuccesful() - await tradePage.getTransactionUrl() - }) -}) + test("User should be able to cancel limit sell OSMO", async () => { + await tradePage.goto(); + const amount = "1.01"; + await tradePage.openSellTab(); + await tradePage.openLimit(); + await tradePage.selectAsset("OSMO"); + await tradePage.enterAmount(amount); + await tradePage.setLimitPriceChange("10%"); + const limitPrice = await tradePage.getLimitPrice(); + const { msgContentAmount } = await tradePage.sellAndGetWalletMsg(context, { + maxRetries: 2, + limit: true, + }); + // Only validate message content if Keplr popup appeared (not 1-click trading) + if (msgContentAmount) { + //expect(msgContentAmount).toContain(`${amount} OSMO`); + expect(msgContentAmount).toContain("place_limit"); + expect(msgContentAmount).toContain('"order_direction": "ask"'); + } + await tradePage.getTransactionUrl(); + await tradePage.gotoOrdersHistory(); + const trxPage = new TransactionsPage(context.pages()[0]); + await trxPage.cancelLimitOrder(`Sell $${amount} of`, limitPrice, context); + await tradePage.getTransactionUrl(); + }); +}); diff --git a/packages/e2e/unzip-extension.ts b/packages/e2e/unzip-extension.ts index 552070248c..5fa5ffed38 100644 --- a/packages/e2e/unzip-extension.ts +++ b/packages/e2e/unzip-extension.ts @@ -3,7 +3,7 @@ import path from "node:path"; import fs from "node:fs"; export class UnzipExtension { - getPathToExtension() { + async getPathToExtension() { console.log("Unzip Wallet Extension before tests."); // Unzip keplr-extension-manifest const pathToZip = path.join( @@ -11,7 +11,8 @@ export class UnzipExtension { "./keplr-extension-manifest-v3-v0.12.280.zip" ); const pathToExtension = path.join(__dirname, "./keplr-extension-manifest"); - decompress(pathToZip, pathToExtension); + await decompress(pathToZip, pathToExtension); + console.log("✓ Keplr extension extracted successfully"); return pathToExtension; }