diff --git a/autotests/playwright.config.ts b/autotests/playwright.config.ts index 2b1a9bbfac..0f25f2e4a7 100644 --- a/autotests/playwright.config.ts +++ b/autotests/playwright.config.ts @@ -1,4 +1,5 @@ import { PlaywrightTestConfig } from "@playwright/test"; +import { resolve } from "path"; const config: PlaywrightTestConfig = { timeout: process.env.PW_TIMEOUT ? Number(process.env.PW_TIMEOUT) : 60_000, @@ -7,6 +8,7 @@ const config: PlaywrightTestConfig = { workers: process.env.PW_WORKERS ? Number(process.env.PW_WORKERS) : 1, use: { viewport: { width: 1600, height: 1200 }, + storageState: resolve(__dirname, "./.storage.json"), }, }; diff --git a/autotests/src/elements/base-page.ts b/autotests/src/elements/base-page.ts index 523920a2a7..231a5c27f9 100644 --- a/autotests/src/elements/base-page.ts +++ b/autotests/src/elements/base-page.ts @@ -72,6 +72,14 @@ export class BasePage { return this.wrapLocatorWithRoot(locator, locatorSelector); } + async allowSpentTokens() { + const popup = await this.wallet.page.context().waitForEvent("page"); + await popup.waitForSelector('[data-testid="page-container-footer-next"]'); + await popup.click('[data-testid="page-container-footer-next"]'); + await popup.waitForSelector('[data-testid="page-container-footer-next"]:has-text("Approve")'); + await popup.click('[data-testid="page-container-footer-next"]:has-text("Approve")'); + } + async has(locator: string | Locator, timeout = 5000) { const selector = typeof locator === "string" ? this.locator(locator).selector : locator.selector; diff --git a/autotests/src/elements/common.ts b/autotests/src/elements/common.ts index 2385c3ada5..ec86a0c663 100644 --- a/autotests/src/elements/common.ts +++ b/autotests/src/elements/common.ts @@ -16,9 +16,11 @@ export class Tabs extends BasePage { return window.getComputedStyle(el).pointerEvents; }); - if (style !== "none") { - await tab.click(); + if (style === "none") { + return; } + + await tab.click(); } } diff --git a/autotests/src/elements/page-objects.ts b/autotests/src/elements/page-objects.ts index d19c89057f..3323549b17 100644 --- a/autotests/src/elements/page-objects.ts +++ b/autotests/src/elements/page-objects.ts @@ -1,39 +1,10 @@ -import { Locator, Page, expect } from "@playwright/test"; +import { Locator, Page } from "@playwright/test"; import { Dappwright } from "@tenkeylabs/dappwright"; import { BasePage } from "./base-page"; -import { Direction, TradeMode, GmxNavigation, Leverage, EditOperation, CloseOperation } from "./types"; import { Modal, Tabs } from "./common"; - -class TradeDirectionTabs extends Tabs { - long = this.locator("trade-direction-tab-Long"); - short = this.locator("trade-direction-tab-Short"); - swap = this.locator("trade-direction-tab-Swap"); - - constructor(page: Page, wallet: Dappwright, root: Locator) { - super(page, wallet, root); - this.setTabs({ - Long: this.long, - Short: this.short, - Swap: this.swap, - }); - } -} - -class TradeModeTabs extends Tabs { - market = this.locator("trade-mode-tab-Market"); - limit = this.locator("trade-mode-tab-Limit"); - trigger = this.locator("trade-mode-tab-Trigger"); - - constructor(page: Page, wallet: Dappwright, root: Locator) { - super(page, wallet, root); - this.setTabs({ - Market: this.market, - Limit: this.limit, - Trigger: this.trigger, - }); - } -} +import { CloseOperation, EditOperation, ExchangeListTab, GmxNavigation } from "./types"; +import { Tradebox } from "./tradebox"; class PositionEditTabs extends Tabs { depositTab = this.locator("operation-tabs-tab-Deposit"); @@ -61,121 +32,6 @@ class PositionCloseTabs extends Tabs { } } -class LeverageSlider extends BasePage { - tooltip = this.locator("leverage-slider-tooltip"); - handle = this.locator("leverage-slider-handle"); - - async setLeverage(leverage: Leverage) { - const handle = await this.handle; - const leverageTextTarget = await this.page.locator( - `xpath=//*[contains(@class, "rc-slider-mark-text") and text()="${leverage}"]` - ); - await handle.dragTo(leverageTextTarget); - } -} - -class Tradebox extends BasePage { - market = this.locator("market"); - payInput = this.locator("pay-input"); - buyInput = this.locator("buy-input"); - triggerPriceInput = this.locator("trigger-price-input"); - - confirmTradeButton = this.locator("confirm-trade-button"); - - marketSelector = this.locator("market-selector"); - collateralSelector = this.locator("collateral-selector"); - - poolSelector = this.locator("pool-selector-button"); - collateralInSelector = this.locator("collateral-in-selector-button"); - - leverageSlider = new LeverageSlider(this.page, this.wallet, this.locator("leverage-slider")); - - directionTabs = new TradeDirectionTabs(this.page, this.wallet, this.locator("trade-direction")); - - modeTabs = new TradeModeTabs(this.page, this.wallet, this.locator("trade-mode")); - - async selectDirection(direction: Direction) { - await this.directionTabs.select(direction); - } - - async selectMode(mode: TradeMode) { - await this.modeTabs.select(mode); - } - - async setLeverage(leverage: Leverage) { - await this.leverageSlider.setLeverage(leverage); - } - - async selectMarket(market: string) { - await this.page.waitForSelector(this.marketSelector.selector); - await this.marketSelector.click(); - - const marketsModal = new Modal(this.page, this.wallet, "market-selector-modal"); - - await marketsModal.root.waitForVisible(); - - const item = await marketsModal.locator(`market-selector-${market}`); - - await item.focus(); - await item.waitForVisible(); - await item.click(); - - await this.page.waitForSelector(marketsModal.root.selector, { - state: "detached", - }); - } - - async selectCollateral(token: string) { - await this.page.waitForSelector(this.collateralSelector.selector); - await this.collateralSelector.click(); - - const tokensModal = new Modal(this.page, this.wallet, "collateral-selector-modal"); - - await tokensModal.root.waitForVisible(); - - const item = tokensModal.locator(`collateral-selector-token-${token}`); - - await item.focus(); - await item.waitForVisible(); - await item.click(); - - await this.page.waitForSelector(tokensModal.root.selector, { - state: "detached", - }); - } - - async selectPool(pool: string) { - const enabled = await this.has("pool-selector-button"); - - if (!enabled) { - console.log("[SelectPool]: pool selector is not enabled"); - return; - } - - await this.poolSelector.click(); - - const item = this.locator(`pool-selector-row-${pool}`, this.page.locator("body")); - await item.waitForVisible(); - await item.click(); - - await this.page.waitForSelector(`[data-qa="pool-selector-row-${pool}"]`, { - state: "detached", - }); - } - - async selectCollateralIn(token: string) { - await this.collateralInSelector.click(); - - const item = this.page.locator(`[data-qa="collateral-in-selector-row-${token}"]`); - await item; - await item.click(); - - await this.page.waitForSelector(`[data-qa="collateral-in-selector-row-${token}"]`, { - state: "detached", - }); - } -} - class Header extends BasePage { settings = this.locator("settings"); userAddress = this.locator("user-address"); @@ -185,8 +41,6 @@ class Header extends BasePage { connectWalletButton = this.locator("connect-wallet-button"); - price = this.locator("price"); - async connectWallet() { const connect = await this.connectWalletButton; @@ -200,16 +54,6 @@ class Header extends BasePage { await this.page.click('[data-testid="rk-wallet-option-io.metamask"]'); await this.wallet.approve(); } - - async getPrice(): Promise { - const result = await this.price.evaluate((el) => el.textContent); - - if (!result) { - return ""; - } - - return result; - } } class NetworkDropdown extends BasePage { @@ -237,6 +81,7 @@ class EditModal extends Modal { highPriceImpact = this.locator("high-price-impact-warning"); highSwapImpact = this.locator("high-swap-impact-warning"); + allowSpentToken = this.locator("allow-spent-token-button"); async deposit(amount: string) { await this.tabs.select("Deposit"); @@ -262,6 +107,7 @@ class EditModal extends Modal { if (agree) { const hasPriceImpactWarning = await this.has(this.highPriceImpact); const hasSwapImpactWarning = await this.has(this.highSwapImpact); + const cantSpentToken = await this.has(this.allowSpentToken); if (hasPriceImpactWarning) { await this.highPriceImpact.click(); @@ -270,6 +116,10 @@ class EditModal extends Modal { if (hasSwapImpactWarning) { await this.highSwapImpact.click(); } + + if (cantSpentToken) { + await Promise.all([this.allowSpentToken.click(), this.allowSpentTokens()]); + } } await this.confirmButton.click(); @@ -284,6 +134,7 @@ class CloseModal extends Modal { highPriceImpact = this.locator("high-price-impact-warning"); highSwapImpact = this.locator("high-swap-impact-warning"); + allowSpentToken = this.locator("allow-spent-token-button"); async closePartially(amount: string) { await this.tabs.select("Market"); @@ -311,6 +162,7 @@ class CloseModal extends Modal { if (agree) { const hasPriceImpactWarning = await this.has(this.highPriceImpact); const hasSwapImpactWarning = await this.has(this.highSwapImpact); + const cantSpentToken = await this.has(this.allowSpentToken); if (hasPriceImpactWarning) { await this.highPriceImpact.click(); @@ -319,6 +171,11 @@ class CloseModal extends Modal { if (hasSwapImpactWarning) { await this.highSwapImpact.click(); } + + if (cantSpentToken) { + await this.allowSpentToken.click(); + await this.wallet.confirmTransaction(); + } } await this.confirmButton.click(); @@ -326,9 +183,20 @@ class CloseModal extends Modal { } } +class Order extends BasePage { + editButton = this.locator("edit-order"); + closeButton = this.locator("close-order"); + + async close() { + await this.closeButton.click(); + await this.wallet.confirmTransaction(); + } +} + class Position extends BasePage { closeButton = this.locator("position-close-button"); edit = this.locator("position-edit-button"); + handle = this.locator("position-handle"); editModal?: EditModal; closeModal?: CloseModal; @@ -423,11 +291,37 @@ class Position extends BasePage { await this.waitForTransactionToBeSent(); await this.waitForLoadingEnd(); } + + async select() { + await this.handle.click(); + } +} + +class ExchangeListTabs extends Tabs { + orders = this.locator("exchange-list-tabs-tab-Orders"); + positions = this.locator("exchange-list-tabs-tab-Positions"); + trades = this.locator("exchange-list-tabs-tab-Trades"); + claims = this.locator("exchange-list-tabs-tab-Claims"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Orders: this.orders, + Positions: this.positions, + Trades: this.trades, + Claims: this.claims, + }); + } +} + +class ExchangeList extends BasePage { + tabs = new ExchangeListTabs(this.page, this.wallet, this.locator("exchange-list-tabs")); } export class GmxApp extends BasePage { header = new Header(this.page, this.wallet, this.locator("header")); tradebox = new Tradebox(this.page, this.wallet, this.locator("tradebox")); + exchangeList = new ExchangeList(this.page, this.wallet, this.locator("trade-table-large")); tradeLink = this.locator("trade"); @@ -440,6 +334,8 @@ export class GmxApp extends BasePage { buyGlpPage = this.locator("buy-glp-page"); poolsPage = this.locator("pools-page"); + price = this.locator("price"); + networksDropdown = new NetworkDropdown(this.page, this.wallet); settings = new Settings(this.page, this.wallet); baseUrl: string; @@ -450,6 +346,16 @@ export class GmxApp extends BasePage { this.root = this.page.locator("body"); } + async getPrice(): Promise { + const result = await this.price.evaluate((el) => el.textContent); + + if (!result) { + return ""; + } + + return result; + } + async closeAllToasts() { while (true) { const element = await this.page.$("[data-qa='close-toast']"); @@ -465,6 +371,7 @@ export class GmxApp extends BasePage { } async getPosition(market: string, pool: string, direction: "Long" | "Short") { + await this.exchangeList.tabs.select("Positions"); return new Position( this.page, this.wallet, @@ -472,6 +379,27 @@ export class GmxApp extends BasePage { ); } + async getOrder(fromToken: string, toTokenString: string): Promise; + async getOrder(market: string, pool: string, direction: "Long" | "Short"): Promise; + async getOrder(marketOrToken: string, poolOrToken: string, direction?: "Long" | "Short"): Promise { + await this.exchangeList.tabs.select("Orders"); + if (!direction) { + return new Order( + this.page, + this.wallet, + this.locator(`[data-qa="trade-table-large"] [data-qa="order-swap-${marketOrToken}-${poolOrToken}"]`) + ); + } + + return new Order( + this.page, + this.wallet, + this.locator( + `[data-qa="trade-table-large"] [data-qa="order-market-${marketOrToken} [${poolOrToken}]-${direction}"]` + ) + ); + } + async navigateTo(page: GmxNavigation) { await this.page.goto(this.baseUrl + "#" + page); } diff --git a/autotests/src/elements/tradebox.ts b/autotests/src/elements/tradebox.ts new file mode 100644 index 0000000000..b4aabc49fa --- /dev/null +++ b/autotests/src/elements/tradebox.ts @@ -0,0 +1,152 @@ +import { Locator, Page } from "@playwright/test"; +import { Dappwright } from "@tenkeylabs/dappwright"; + +import { BasePage } from "./base-page"; +import { Modal, Tabs } from "./common"; +import { Direction, Leverage, TradeMode } from "./types"; + +class TradeDirectionTabs extends Tabs { + long = this.locator("trade-direction-tab-Long"); + short = this.locator("trade-direction-tab-Short"); + swap = this.locator("trade-direction-tab-Swap"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Long: this.long, + Short: this.short, + Swap: this.swap, + }); + } +} + +class TradeModeTabs extends Tabs { + market = this.locator("trade-mode-tab-Market"); + limit = this.locator("trade-mode-tab-Limit"); + trigger = this.locator("trade-mode-tab-Trigger"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Market: this.market, + Limit: this.limit, + Trigger: this.trigger, + }); + } +} + +class LeverageSlider extends BasePage { + tooltip = this.locator("leverage-slider-tooltip"); + handle = this.locator("leverage-slider-handle"); + + async setLeverage(leverage: Leverage) { + const handle = await this.handle; + const leverageTextTarget = await this.page.locator( + `xpath=//*[contains(@class, "rc-slider-mark-text") and text()="${leverage}"]` + ); + await handle.dragTo(leverageTextTarget); + } +} + +export class Tradebox extends BasePage { + market = this.locator("market"); + payInput = this.locator("pay-input"); + buyInput = this.locator("buy-input"); + triggerPriceInput = this.locator("trigger-price-input"); + + confirmTradeButton = this.locator("confirm-trade-button"); + + marketSelector = this.locator("market-selector"); + collateralSelector = this.locator("collateral-selector"); + + poolSelector = this.locator("pool-selector-button"); + collateralInSelector = this.locator("collateral-in-selector-button"); + + leverageSlider = new LeverageSlider(this.page, this.wallet, this.locator("leverage-slider")); + + directionTabs = new TradeDirectionTabs(this.page, this.wallet, this.locator("trade-direction")); + modeTabs = new TradeModeTabs(this.page, this.wallet, this.locator("trade-mode")); + + limits = this.locator("info-row-tradebox-limit-entries"); + + async selectDirection(direction: Direction) { + await this.directionTabs.select(direction); + } + + async selectMode(mode: TradeMode) { + await this.modeTabs.select(mode); + } + + async setLeverage(leverage: Leverage) { + await this.leverageSlider.setLeverage(leverage); + } + + async selectMarket(market: string) { + await this.page.waitForSelector(this.marketSelector.selector); + await this.marketSelector.click(); + + const marketsModal = new Modal(this.page, this.wallet, "market-selector-modal"); + + await marketsModal.root.waitForVisible(); + + const item = await marketsModal.locator(`market-selector-${market}`); + + await item.focus(); + await item.waitForVisible(); + await item.click(); + + await this.page.waitForSelector(marketsModal.root.selector, { + state: "detached", + }); + } + + async selectCollateral(token: string) { + await this.page.waitForSelector(this.collateralSelector.selector); + await this.collateralSelector.click(); + + const tokensModal = new Modal(this.page, this.wallet, "collateral-selector-modal"); + + await tokensModal.root.waitForVisible(); + + const item = tokensModal.locator(`collateral-selector-token-${token}`); + + await item.focus(); + await item.waitForVisible(); + await item.click(); + + await this.page.waitForSelector(tokensModal.root.selector, { + state: "detached", + }); + } + + async selectPool(pool: string) { + const enabled = await this.has("pool-selector-button"); + + if (!enabled) { + console.log("[SelectPool]: pool selector is not enabled"); + return; + } + + await this.poolSelector.click(); + + const item = this.locator(`pool-selector-row-${pool}`, this.page.locator("body")); + await item.waitForVisible(); + await item.click(); + + await this.page.waitForSelector(`[data-qa="pool-selector-row-${pool}"]`, { + state: "detached", + }); + } + + async selectCollateralIn(token: string) { + await this.collateralInSelector.click(); + + const item = this.page.locator(`[data-qa="collateral-in-selector-row-${token}"]`); + await item; + await item.click(); + + await this.page.waitForSelector(`[data-qa="collateral-in-selector-row-${token}"]`, { + state: "detached", + }); + } +} diff --git a/autotests/src/elements/types.ts b/autotests/src/elements/types.ts index 10905e1488..ecf7b08607 100644 --- a/autotests/src/elements/types.ts +++ b/autotests/src/elements/types.ts @@ -14,3 +14,4 @@ export type GmxNavigation = | "/referrals"; export type EditOperation = "Deposit" | "Withdraw"; export type CloseOperation = "Market" | "TP/SL"; +export type ExchangeListTab = "Positions" | "Orders" | "Trades" | "Claims"; diff --git a/autotests/src/tests/nanvigation.spec.ts b/autotests/src/tests/nanvigation.spec.ts index aca5ecc0bd..100bf4b217 100644 --- a/autotests/src/tests/nanvigation.spec.ts +++ b/autotests/src/tests/nanvigation.spec.ts @@ -2,6 +2,7 @@ import { expect } from "@playwright/test"; import { test } from "../base"; test.describe("Should navigate across all pages with no crashes", () => { + test.describe.configure({ retries: 2 }); test.afterEach(async ({ gmx }) => { await gmx.page.close(); }); diff --git a/autotests/src/tests/trade.spec.ts b/autotests/src/tests/trade.spec.ts index 37c358a89e..6860a35e95 100644 --- a/autotests/src/tests/trade.spec.ts +++ b/autotests/src/tests/trade.spec.ts @@ -1,35 +1,58 @@ import { expect } from "@playwright/test"; import { test } from "../base"; +/** + * Increased to 30 seconds because of the slow performance on fuji + */ +const POSITION_CHANGE_TIMEOUT = 30_000; // 30 seconds; +const isFuji = process.env.CHAIN === "fuji"; + test.describe.serial("Trades", () => { test.afterEach(async ({ page }) => { await page.close(); }); - test.describe("Market", () => { + test.describe.skip("Market", () => { + const TEST_CONFIG = isFuji + ? { + direction: "Long" as const, + market: "WBTC/USD", + collateral: "AVAX", + pool: "WBTC-USDC", + collateralIn: "USDC", + pay: "0.1", + } + : { + direction: "Long" as const, + market: "WETH/USD", + collateral: "ETH", + pool: "WETH-USDC", + collateralIn: "USDC", + pay: "0.001", + }; + test("increase market position", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); await gmx.tradebox.selectDirection("Long"); await gmx.tradebox.selectMode("Market"); - await gmx.tradebox.selectMarket("WBTC/USD"); - await gmx.tradebox.selectCollateral("AVAX"); + await gmx.tradebox.selectMarket(TEST_CONFIG.market); + await gmx.tradebox.selectCollateral(TEST_CONFIG.collateral); - await gmx.tradebox.selectPool("WBTC-USDC"); - await gmx.tradebox.selectCollateralIn("USDC"); + await gmx.tradebox.selectPool(TEST_CONFIG.pool); + await gmx.tradebox.selectCollateralIn(TEST_CONFIG.collateralIn); - await gmx.tradebox.payInput.fill("0.1"); + await gmx.tradebox.payInput.fill(TEST_CONFIG.pay); await gmx.tradebox.setLeverage("2x"); expect(gmx.tradebox.confirmTradeButton).toBeEnabled(); await gmx.tradebox.confirmTradeButton.click(); await gmx.wallet.confirmTransaction(); - await gmx.page.waitForTimeout(1000); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); - const isPositionPresent = await gmx.has(position.root); + const position = await gmx.getPosition(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + const isPositionPresent = await gmx.has(position.root, POSITION_CHANGE_TIMEOUT); expect(isPositionPresent).toBeTruthy(); }); @@ -37,8 +60,8 @@ test.describe.serial("Trades", () => { test("edit position deposit", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); - position.root.waitForVisible(); + const position = await gmx.getPosition(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + await position.root.waitForVisible(); expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); @@ -52,8 +75,8 @@ test.describe.serial("Trades", () => { test("edit position withdraw 25%", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); - position.root.waitForVisible(); + const position = await gmx.getPosition(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + await position.root.waitForVisible(); expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); @@ -67,8 +90,8 @@ test.describe.serial("Trades", () => { test("close position partially", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); - position.root.waitForVisible(); + const position = await gmx.getPosition(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + await position.root.waitForVisible(); expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); @@ -82,8 +105,8 @@ test.describe.serial("Trades", () => { test("close position full", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); - position.root.waitForVisible(); + const position = await gmx.getPosition(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + await position.root.waitForVisible(); expect(position.root).toBeVisible(); await position.closeFull(); @@ -93,25 +116,47 @@ test.describe.serial("Trades", () => { }); }); - test.describe.skip("Limit", () => { + test.describe("Limit", () => { + const TEST_CONFIG = isFuji + ? { + direction: "Long" as const, + market: "WBTC/USD", + collateral: "AVAX", + pool: "WBTC-USDC", + collateralIn: "USDC", + limitRatio: 0.9, + pay: "0.1", + } + : { + direction: "Long" as const, + market: "WETH/USD", + collateral: "ETH", + pool: "WETH-USDC", + collateralIn: "USDC", + limitRatio: 0.9, + pay: "0.001", + }; + test("create limit position", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); await gmx.tradebox.selectDirection("Long"); - await gmx.tradebox.selectMode("Limit"); - await gmx.tradebox.selectMarket("BTC/USD"); - await gmx.tradebox.selectCollateral("AVAX"); + await gmx.tradebox.selectMarket(TEST_CONFIG.market); + await gmx.tradebox.selectCollateral(TEST_CONFIG.collateral); - await gmx.tradebox.selectPool("WBTC-USDC"); - await gmx.tradebox.selectCollateralIn("USDC"); + await gmx.tradebox.selectPool(TEST_CONFIG.pool); + await gmx.tradebox.selectCollateralIn(TEST_CONFIG.collateralIn); - await gmx.tradebox.payInput.fill("0.1"); + await gmx.tradebox.payInput.fill(TEST_CONFIG.pay); await gmx.tradebox.setLeverage("2x"); - const price = await gmx.header.getPrice(); + await gmx.tradebox.selectMode("Limit"); + await gmx.tradebox.triggerPriceInput.waitForSelector(); + + const price = await gmx.getPrice(); - const limitPrice = Number(price.replace(/[\$,]/g, "")) * 0.9; + const limitPrice = Number(price.replace(/[\$,]/g, "")) * TEST_CONFIG.limitRatio; await gmx.tradebox.triggerPriceInput.fill(limitPrice.toString()); @@ -120,10 +165,21 @@ test.describe.serial("Trades", () => { await gmx.tradebox.confirmTradeButton.click(); await gmx.wallet.confirmTransaction(); - const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + const order = await gmx.getOrder(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + const isOrderPresent = await gmx.has(order.root, POSITION_CHANGE_TIMEOUT); + expect(isOrderPresent).toBeTruthy(); + }); + + test("close Limit order", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + const order = await gmx.getOrder(TEST_CONFIG.market, TEST_CONFIG.pool, TEST_CONFIG.direction); + await order.root.waitForVisible(); + + await order.close(); - await position.root.waitForSelector(); - expect(position.root).toBeAttached(); + await order.root.waitForDetached(); + expect(order.root).not.toBeAttached(); }); }); }); diff --git a/src/components/ApproveTokenButton/ApproveTokenButton.tsx b/src/components/ApproveTokenButton/ApproveTokenButton.tsx index 2e3a517d8d..2347fca373 100644 --- a/src/components/ApproveTokenButton/ApproveTokenButton.tsx +++ b/src/components/ApproveTokenButton/ApproveTokenButton.tsx @@ -45,7 +45,7 @@ export function ApproveTokenButton(p: Props) { const isLoading = isApproving || (isApproveSubmitted && !p.isApproved); return ( -
+
{p.customLabel ?? Allow {p.tokenSymbol} to be spent}
diff --git a/src/components/SuggestionInput/SuggestionInput.tsx b/src/components/SuggestionInput/SuggestionInput.tsx index d51f60ec30..0ac0f1f7a7 100644 --- a/src/components/SuggestionInput/SuggestionInput.tsx +++ b/src/components/SuggestionInput/SuggestionInput.tsx @@ -11,6 +11,7 @@ type Props = { symbol?: string; isError?: boolean; inputClassName?: string; + qa?: string; }; export default function SuggestionInput({ @@ -21,6 +22,7 @@ export default function SuggestionInput({ symbol, isError, inputClassName, + qa, }: Props) { const [isPanelVisible, setIsPanelVisible] = useState(false); @@ -46,6 +48,7 @@ export default function SuggestionInput({ value={value ?? ""} placeholder={placeholder} onValueChange={handleChange} + qa={qa} />