From 690dedd12498c991d3df71509546af2b151f5217 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Wed, 26 Nov 2025 16:21:34 +0300 Subject: [PATCH 01/12] add user fixtures, improve balance.spec --- e2e/extendTest.ts | 2 + e2e/fixtures/index.ts | 2 + e2e/fixtures/swap/index.ts | 4 +- e2e/fixtures/swap/mockPosition.ts | 29 ++++--- e2e/fixtures/user/balances/index.ts | 2 + e2e/fixtures/user/balances/setBoostData.ts | 75 +++++++++++++++++++ e2e/fixtures/user/checkExportRewards.ts | 36 +++++++++ e2e/fixtures/user/index.ts | 47 ++++++++++++ e2e/fixtures/user/setUnboostQueue.ts | 40 ++++++++++ e2e/fixtures/user/setUnstakeQueue.ts | 65 ++++++++++++++++ e2e/fixtures/user/setUserRewards.ts | 34 +++++++++ e2e/fixtures/user/setUserStats.ts | 46 ++++++++++++ e2e/tests/balance.spec.ts | 21 +++--- e2e/types.d.ts | 2 + src/helpers/methods/apiUrls/data.ts | 12 +-- src/helpers/methods/index.ts | 2 + src/helpers/methods/insertMockE2E.ts | 18 +++++ src/helpers/requests/index.ts | 1 + src/helpers/requests/user/fetchUserRewards.ts | 32 ++++++++ src/helpers/requests/user/index.ts | 6 ++ .../ExportRewardsModal/util/useRewards.ts | 11 +-- .../RewardsChart/RewardsChart.tsx | 8 +- .../vault/useUser/useBalances/useBoost.ts | 11 ++- .../util/vault/useUser/useUnboostQueue.ts | 11 +++ .../util/vault/useUser/useUnstakeQueue.ts | 11 +++ .../util/vault/useUser/useUserChartStats.ts | 6 +- 26 files changed, 490 insertions(+), 44 deletions(-) create mode 100644 e2e/fixtures/user/balances/index.ts create mode 100644 e2e/fixtures/user/balances/setBoostData.ts create mode 100644 e2e/fixtures/user/checkExportRewards.ts create mode 100644 e2e/fixtures/user/index.ts create mode 100644 e2e/fixtures/user/setUnboostQueue.ts create mode 100644 e2e/fixtures/user/setUnstakeQueue.ts create mode 100644 e2e/fixtures/user/setUserRewards.ts create mode 100644 e2e/fixtures/user/setUserStats.ts create mode 100644 src/helpers/methods/insertMockE2E.ts create mode 100644 src/helpers/requests/user/fetchUserRewards.ts create mode 100644 src/helpers/requests/user/index.ts diff --git a/e2e/extendTest.ts b/e2e/extendTest.ts index 23d95a92..fcd84fd0 100644 --- a/e2e/extendTest.ts +++ b/e2e/extendTest.ts @@ -2,6 +2,7 @@ import { test as base } from '@guardianui/test' import { sdk, + user, swap, queue, anvil, @@ -18,6 +19,7 @@ import { const baseTest = base.extend({ sdk, + user, swap, queue, anvil, diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.ts index dc8df2e0..2f3d7549 100644 --- a/e2e/fixtures/index.ts +++ b/e2e/fixtures/index.ts @@ -1,4 +1,5 @@ export { default as sdk } from './sdk' +export { default as user } from './user' export { default as swap } from './swap' export { default as queue } from './queue' export { default as anvil } from './anvil' @@ -13,6 +14,7 @@ export { default as transactions } from './transactions' export type { SDKFixture } from './sdk' +export type { UserFixture } from './user' export type { SwapFixture } from './swap' export type { QueueFixture } from './queue' export type { AnvilFixture } from './anvil' diff --git a/e2e/fixtures/swap/index.ts b/e2e/fixtures/swap/index.ts index f5ca9211..3c82ea37 100644 --- a/e2e/fixtures/swap/index.ts +++ b/e2e/fixtures/swap/index.ts @@ -27,7 +27,7 @@ export type SwapFixture = { checkConnectButton: CheckConnectButton } -const swap: E2E.Fixture = async ({ page, graphql, transactions, element }, use) => { +const swap: E2E.Fixture = async ({ page, graphql, transactions, element, user }, use) => { await use({ tab: createTab({ page }), input: createInput({ page }), @@ -35,7 +35,7 @@ const swap: E2E.Fixture = async ({ page, graphql, transactions, ele mockApy: createMockApy({ graphql }), submitAmount: createSubmitAmount({ page }), submit: createSubmit({ page, transactions }), - mockPosition: createMockPosition({ graphql }), + mockPosition: createMockPosition({ user }), checkSwapRender: createCheckSwapRender({ page }), getSwapInfoItem: createGetSwapInfoItem({ page }), getBaseInfoItem: createGetBaseInfoItem({ page }), diff --git a/e2e/fixtures/swap/mockPosition.ts b/e2e/fixtures/swap/mockPosition.ts index 7760786a..b2917452 100644 --- a/e2e/fixtures/swap/mockPosition.ts +++ b/e2e/fixtures/swap/mockPosition.ts @@ -1,10 +1,7 @@ -import { parseEther } from 'ethers' - - -type Wrapper = E2E.FixtureMethod +type Wrapper = E2E.FixtureMethod type Input = { - isClaimable?: boolean + isClaimable: boolean } type Output = { @@ -12,23 +9,23 @@ type Output = { exitingRewards: number } -export type MockPosition = (values?: Input) => Promise +export type MockPosition = (values: Input) => Promise -export const createMockPosition: Wrapper = ({ graphql }) => ( - async (values?: Input) => { - const { isClaimable } = values || {} +export const createMockPosition: Wrapper = ({ user }) => ( + async (values: Input) => { + const { isClaimable } = values - const isExiting = typeof isClaimable === 'boolean' + const exitingShares = '10.5' + const exitingRewards = '1.5' - const exitingShares = isExiting ? '10.5' : '0' - const exitingRewards = isExiting ? '1.5' : '0' - - await graphql.mockPosition({ - exitingShares: parseEther(exitingShares).toString(), - exitingRewards: parseEther(exitingRewards).toString(), + await user.setUnboostQueue({ isClaimable, + exitingShares, + exitingRewards, }) + await user.balances.setBoostData({ shares: '0', exitingPercent: 100 }) + return { exitingShares: Number(exitingShares), exitingRewards: Number(exitingRewards), diff --git a/e2e/fixtures/user/balances/index.ts b/e2e/fixtures/user/balances/index.ts new file mode 100644 index 00000000..0e228434 --- /dev/null +++ b/e2e/fixtures/user/balances/index.ts @@ -0,0 +1,2 @@ +export { createSetBoostData } from './setBoostData' +export type { SetBoostData } from './setBoostData' diff --git a/e2e/fixtures/user/balances/setBoostData.ts b/e2e/fixtures/user/balances/setBoostData.ts new file mode 100644 index 00000000..49e6dd34 --- /dev/null +++ b/e2e/fixtures/user/balances/setBoostData.ts @@ -0,0 +1,75 @@ +import { parseEther, formatEther } from 'ethers' + + +type Input = { + shares: string + rewards?: string + vaultApy?: number + maxBoostApy?: number + exitingPercent?: number + leverageStrategyData?: { + version?: number, + isUpgradeRequired?: boolean, + }, +} + +type Output = { + totalShares: string + rewardShares: string +} + +type Payload = Store['vault']['user']['balances']['boost'] + +export type SetBoostData = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetBoostData: Wrapper = ({ page }) => ( + async (values: Input) => { + const { shares, rewards = '0', vaultApy = 0, maxBoostApy = 0, exitingPercent = 0, leverageStrategyData } = values + + const leverageStrategyVersion = leverageStrategyData?.version || 1 + const isUpgradeRequired = leverageStrategyVersion === 1 && exitingPercent === 0 + + const data = { + exitingPercent, + borrowedAssets: 0n, + shares: parseEther(shares), + vaultApy: Number(vaultApy), + allocatorMaxBoostApy: maxBoostApy, + rewardAssets: parseEther(rewards), + borrowStatus: 0, + leverageStrategyData: { + version: leverageStrategyVersion, + isUpgradeRequired: leverageStrategyData?.isUpgradeRequired || isUpgradeRequired, + }, + } + + const result = await page.evaluate(async (payload) => { + const sdk = window.e2e.sdk + + const rewardShares = await sdk.contracts.base.mintTokenController.convertToShares(payload.rewardAssets) + const totalShares = payload.shares + rewardShares + + const mockResult: Payload = { + ...payload, + totalShares, + } + + window.e2e = { + ...window.e2e, + ['user/balances/setBoostData']: mockResult, + } + + return { + totalShares, + rewardShares, + } + }, data) + + return { + totalShares: formatEther(result.totalShares), + rewardShares: formatEther(result.rewardShares), + } + } +) diff --git a/e2e/fixtures/user/checkExportRewards.ts b/e2e/fixtures/user/checkExportRewards.ts new file mode 100644 index 00000000..2f0e61a0 --- /dev/null +++ b/e2e/fixtures/user/checkExportRewards.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test' +import { createSetUserRewards } from './setUserRewards' + + +export type CheckExportRewards = () => Promise + +type Wrapper = E2E.FixtureMethod + +export const createCheckExportRewards: Wrapper = ({ page, helpers }) => ( + async () => { + const setUserRewards = createSetUserRewards({ page }) + + await page.getByTestId('user-stats-chart-tab').click() + await page.getByTestId('export-rewards-button').click() + + await page.getByTestId('export-rewards-from-input').fill('2024-01-01') + await page.getByTestId('export-rewards-to-input').fill('2024-01-31') + await page.getByTestId('export-rewards-format-select-button').click() + + const formatOptions = await page.waitForSelector('[data-testid="export-rewards-format-select-options"]') + + const options = await formatOptions.$$('> *') + expect(options.length).toBe(2) + + await page.getByTestId('export-rewards-format-select-option-xlsx').click() + + const downloadButton = page.getByTestId('export-rewards-download-button') + + await expect(downloadButton).not.toBeDisabled() + + await setUserRewards() + await downloadButton.click() + + await helpers.checkNotification('Staking stats successfully downloaded') + } +) diff --git a/e2e/fixtures/user/index.ts b/e2e/fixtures/user/index.ts new file mode 100644 index 00000000..80ffc0d6 --- /dev/null +++ b/e2e/fixtures/user/index.ts @@ -0,0 +1,47 @@ +import { createCheckExportRewards } from './checkExportRewards' +import type { CheckExportRewards } from './checkExportRewards' + +import { createSetUnstakeQueue } from './setUnstakeQueue' +import type { SetUnstakeQueue } from './setUnstakeQueue' + +import { createSetUnboostQueue } from './setUnboostQueue' +import type { SetUnboostQueue } from './setUnboostQueue' + +import { createSetUserStats } from './setUserStats' +import type { SetUserStats } from './setUserStats' + +import { + createSetBoostData, +} from './balances' + +import type { + SetBoostData, +} from './balances' + + +export type UserFixture = { + setUserStats: SetUserStats + setUnboostQueue: SetUnboostQueue + setUnstakeQueue: SetUnstakeQueue + checkExportRewards: CheckExportRewards + + balances: { + setBoostData: SetBoostData + }, +} + +const user: E2E.Fixture = async ({ page, helpers }, use) => { + await use({ + setUserStats: createSetUserStats({ page }), + setUnboostQueue: createSetUnboostQueue({ page }), + setUnstakeQueue: createSetUnstakeQueue({ page }), + checkExportRewards: createCheckExportRewards({ page, helpers }), + + balances: { + setBoostData: createSetBoostData({ page }), + }, + }) +} + + +export default user diff --git a/e2e/fixtures/user/setUnboostQueue.ts b/e2e/fixtures/user/setUnboostQueue.ts new file mode 100644 index 00000000..de966697 --- /dev/null +++ b/e2e/fixtures/user/setUnboostQueue.ts @@ -0,0 +1,40 @@ +import { parseEther } from 'ethers' + + +type Input = { + exitingShares: string + exitingRewards: string + isClaimable: boolean +} + +export type SetUnboostQueue = (values: Input) => Promise + +type Payload = Store['vault']['user']['unboostQueue']['data'] + +type Wrapper = E2E.FixtureMethod + +export const createSetUnboostQueue: Wrapper = ({ page }) => ( + async (values: Input) => { + const { exitingShares, exitingRewards, isClaimable } = values + + const data: Payload = { + version: 1, + isClaimable: Boolean(isClaimable), + exitingShares: parseEther(exitingShares), + exitingAssets: parseEther(exitingRewards), + duration: isClaimable ? 0 : 210258902756807306422, + position: isClaimable ? { + timestamp: '1730206212', + positionTicket: '210258902756807306422', + exitQueueIndex: isClaimable ? '1' : null, + } : null, + } + + await page.evaluate(async (payload: Payload) => { + window.e2e = { + ...window.e2e, + ['user/setUnboostQueue']: payload, + } + }, data) + } +) diff --git a/e2e/fixtures/user/setUnstakeQueue.ts b/e2e/fixtures/user/setUnstakeQueue.ts new file mode 100644 index 00000000..952a91a4 --- /dev/null +++ b/e2e/fixtures/user/setUnstakeQueue.ts @@ -0,0 +1,65 @@ +import { parseEther, ZeroAddress } from 'ethers' + + +type Input = { + isClaimable: boolean +} + +type Output = { + totalAssets: number + exitedAssets: number +} + +type Payload = Store['vault']['user']['unstakeQueue']['data'] + +export type SetUnstakeQueue = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetUnstakeQueue: Wrapper = ({ page }) => ( + async (values: Input) => { + const { isClaimable } = values + + const totalAssets = '9.5' + const exitedAssets = '1.5' + + const data: Payload = { + duration: 0, + total: parseEther(totalAssets), + withdrawable: isClaimable ? parseEther(exitedAssets) : 0n, + positions: [ + { + timestamp: '1727256912', + positionTicket: '156652794390067408267', + exitQueueIndex: '0', + }, + ], + requests: [ + { + isV2Position: true, + exitQueueIndex: '0', + receiver: ZeroAddress, + timestamp: '1727256912', + withdrawalTimestamp: '0', + isClaimed: !isClaimable, + isClaimable: Boolean(isClaimable), + positionTicket: '156652794390067408267', + totalAssets: parseEther(totalAssets), + exitedAssets: parseEther(exitedAssets), + }, + ], + } + + await page.evaluate((payload: Payload) => { + window.e2e = { + ...window.e2e, + ['user/setUnstakeQueue']: payload, + } + }, data) + + return { + totalAssets: Number(totalAssets), + exitedAssets: Number(exitedAssets), + } + } +) diff --git a/e2e/fixtures/user/setUserRewards.ts b/e2e/fixtures/user/setUserRewards.ts new file mode 100644 index 00000000..492caa3b --- /dev/null +++ b/e2e/fixtures/user/setUserRewards.ts @@ -0,0 +1,34 @@ +import { mergeRewardsFiat } from 'sdk' + + +export type SetUserRewards = () => Promise + +type Wrapper = E2E.FixtureMethod + +type Output = ReturnType + +export const createSetUserRewards: Wrapper = ({ page }) => ( + async () => { + const DAY = 86400 + const START = 1704091487 + + const data: Output = Array.from({ length: 30 }, (_, i) => ({ + date: START + DAY * i, + dailyRewards: 2.8604074786261133, + dailyRewardsUsd: 2.8604074786261133, + dailyRewardsEur: 2.8604074786261133, + dailyRewardsGbp: 2.8604074786261133, + dailyRewardsCny: 2.8604074786261133, + dailyRewardsJpy: 2.8604074786261133, + dailyRewardsKrw: 2.8604074786261133, + dailyRewardsAud: 2.8604074786261133, + })) + + await page.evaluate((payload) => { + window.e2e = { + ...window.e2e, + ['user/setUserRewards']: payload, + } + }, data) + } +) diff --git a/e2e/fixtures/user/setUserStats.ts b/e2e/fixtures/user/setUserStats.ts new file mode 100644 index 00000000..59bacd22 --- /dev/null +++ b/e2e/fixtures/user/setUserStats.ts @@ -0,0 +1,46 @@ +export type SetUserStats = () => Promise + +type Wrapper = E2E.FixtureMethod + +type DataPoint = { + value: number, + time: number +} + +type Output = Store['vault']['user']['rewards']['data'] + +const DAY = 86400 +const now = Math.floor(Date.now() / 1000) +const START = now - DAY * 29 + +const apyMock: DataPoint[] = Array.from({ length: 30 }, (_, i) => ({ + value: 2.8604074786261133, + time: START + DAY * i, +})) + +const balanceMock: DataPoint[] = Array.from({ length: 30 }, (_, i) => ({ + value: 16.060180934123263, + time: START + DAY * i, +})) + +const rewardsMock: DataPoint[] = Array.from({ length: 30 }, (_, i) => ({ + value: 0.001233931116484099, + time: START + DAY * i, +})) + +export const createSetUserStats: Wrapper = ({ page }) => ( + async () => { + const data: Output = { + apy: [ ...apyMock ].sort((a, b) => a.time - b.time), + balance: [ ...balanceMock ].sort((a, b) => a.time - b.time), + rewards: [ ...rewardsMock ].sort((a, b) => a.time - b.time), + } + + await page.evaluate((payload: Output) => { + window.e2e = { + ...window.e2e, + ['user/setUserStats']: payload, + } + }, data) + } +) diff --git a/e2e/tests/balance.spec.ts b/e2e/tests/balance.spec.ts index 2e4ce8e3..3447deaf 100644 --- a/e2e/tests/balance.spec.ts +++ b/e2e/tests/balance.spec.ts @@ -6,10 +6,10 @@ test.beforeEach(async ({ gui, guardian }) => { await gui.initializeChain(1) }) -test('Enabled claim', async ({ wallet, swap, graphql, queue }) => { +test('Enabled claim', async ({ wallet, swap, queue, user }) => { await swap.openPage() - const { exitedAssets, totalAssets } = await graphql.mockExitQueue({ isClaimable: true }) + const { exitedAssets, totalAssets } = await user.setUnstakeQueue({ isClaimable: true }) const { exitingShares, exitingRewards } = await swap.mockPosition({ isClaimable: true }) await wallet.connectWithBalance({ ETH: '10' }) @@ -28,11 +28,11 @@ test('Enabled claim', async ({ wallet, swap, graphql, queue }) => { }) }) -test('Disabled claim', async ({ wallet, swap, graphql, queue }) => { +test('Disabled claim', async ({ wallet, swap, user, queue }) => { await swap.openPage() - const { exitedAssets, totalAssets } = await graphql.mockExitQueue({ isClaimable: false }) const { exitingShares, exitingRewards } = await swap.mockPosition({ isClaimable: false }) + const { exitedAssets, totalAssets } = await user.setUnstakeQueue({ isClaimable: false }) await wallet.connectWithBalance({ ETH: '10' }) await swap.tab('balance') @@ -52,6 +52,7 @@ test('Disabled claim', async ({ wallet, swap, graphql, queue }) => { test('Chart', async ({ swap, page, element }) => { await swap.openPage() + await swap.tab('balance') await page.getByTestId('statistics-button').click() @@ -61,18 +62,20 @@ test('Chart', async ({ swap, page, element }) => { await element.checkVisibility({ testId: 'stake-user-stats-chart-tab', isVisible: false }) await element.checkVisibility({ testId: 'vault-stats-chart-tab' }) - await element.checkVisibility({ testId: 'stake-chart-Vault-rewards' }) + await element.checkVisibility({ testId: 'stake-chart-vault-rewards' }) }) -test('Export rewards', async ({ wallet, swap, graphql, rewards, page }) => { +test('Export rewards', async ({ wallet, swap, user, page }) => { + await swap.openPage() + await swap.mockPosition({ isClaimable: false }) - await swap.openPage() await wallet.connectWithBalance({ ETH: '10' }) await swap.tab('balance') - await graphql.mockUserRewards() + await user.setUserStats() + await page.getByTestId('statistics-button').click() - await rewards.checkExport() + await user.checkExportRewards() }) diff --git a/e2e/types.d.ts b/e2e/types.d.ts index 3bbcf7bd..f7b56cac 100644 --- a/e2e/types.d.ts +++ b/e2e/types.d.ts @@ -3,6 +3,7 @@ import type { GUI } from '@guardianui/test/dist/models/GUI' import type { SDKFixture, + UserFixture, SwapFixture, QueueFixture, AnvilFixture, @@ -25,6 +26,7 @@ declare global { gui: GUI page: Page sdk: SDKFixture + user: UserFixture swap: SwapFixture queue: QueueFixture anvil: AnvilFixture diff --git a/src/helpers/methods/apiUrls/data.ts b/src/helpers/methods/apiUrls/data.ts index 2a6d9555..a958f216 100644 --- a/src/helpers/methods/apiUrls/data.ts +++ b/src/helpers/methods/apiUrls/data.ts @@ -1,6 +1,8 @@ import { configs, Network } from 'sdk' +const defaultType = 'prod' + const apiUrls = { [Network.Gnosis]: { backend: configs[Network.Gnosis].api.backend, @@ -10,7 +12,7 @@ const apiUrls = { ], subgraph: IS_PROD ? (process.env.NEXT_PUBLIC_GNOSIS_SUBGRAPH_URL as string || configs[Network.Gnosis].api.subgraph) - : 'https://graphs.stakewise.io/gnosis/subgraphs/name/stakewise/stage', + : `https://graphs.stakewise.io/gnosis/subgraphs/name/stakewise/${defaultType}`, }, [Network.Mainnet]: { backend: configs[Network.Mainnet].api.backend, @@ -21,8 +23,8 @@ const apiUrls = { subgraph: IS_PROD ? configs[Network.Mainnet].api.subgraph : [ - 'https://graphs.stakewise.io/mainnet-a/subgraphs/name/stakewise/stage', - 'https://graphs.stakewise.io/mainnet-b/subgraphs/name/stakewise/stage', + `https://graphs.stakewise.io/mainnet-a/subgraphs/name/stakewise/${defaultType}`, + `https://graphs.stakewise.io/mainnet-b/subgraphs/name/stakewise/${defaultType}`, ], }, [Network.Chiado]: { @@ -30,14 +32,14 @@ const apiUrls = { web3: 'https://rpc.chiadochain.net/', subgraph: IS_PROD ? configs[Network.Chiado].api.subgraph - : 'https://graphs.stakewise.io/chiado/subgraphs/name/stakewise/stage', + : `https://graphs.stakewise.io/chiado/subgraphs/name/stakewise/${defaultType}`, }, [Network.Hoodi]: { backend: configs[Network.Hoodi].api.backend, web3: 'https://ethereum-hoodi-rpc.publicnode.com', subgraph: IS_PROD ? configs[Network.Hoodi].api.subgraph - : 'https://graphs.stakewise.io/hoodi/subgraphs/name/stakewise/stage', + : `https://graphs.stakewise.io/hoodi/subgraphs/name/stakewise/${defaultType}`, }, } as const diff --git a/src/helpers/methods/index.ts b/src/helpers/methods/index.ts index 8e879047..81bf9d2b 100644 --- a/src/helpers/methods/index.ts +++ b/src/helpers/methods/index.ts @@ -9,6 +9,7 @@ import getHostName from './getHostName' import getGasMargin from './getGasMargin' import getFiatValue from './getFiatValue' import downloadFile from './downloadFile' +import insertMockE2E from './insertMockE2E' import shortenAddress from './shortenAddress' import fetchWithRetry from './fetchWithRetry' import formatFiatValue from './formatFiatValue' @@ -33,6 +34,7 @@ export { getGasMargin, getFiatValue, downloadFile, + insertMockE2E, shortenAddress, fetchWithRetry, fetchFiatRates, diff --git a/src/helpers/methods/insertMockE2E.ts b/src/helpers/methods/insertMockE2E.ts new file mode 100644 index 00000000..0898f0a0 --- /dev/null +++ b/src/helpers/methods/insertMockE2E.ts @@ -0,0 +1,18 @@ +import cookie from 'helpers/cookie' +import * as constants from 'helpers/constants' + + +const insertMockE2E = (key: string): T | undefined => { + if (typeof window === 'undefined') { + return + } + + const isTestsEnabled = cookie.get(constants.cookieNames.e2e) + + if (isTestsEnabled) { + return window.e2e?.[key] + } +} + + +export default insertMockE2E diff --git a/src/helpers/requests/index.ts b/src/helpers/requests/index.ts index 118483f7..43bcbde4 100644 --- a/src/helpers/requests/index.ts +++ b/src/helpers/requests/index.ts @@ -1,3 +1,4 @@ +export { default as user } from './user' export { default as approve } from './approve' export { default as getApproveGas } from './getApproveGas' export { default as increaseDelay } from './increaseDelay' diff --git a/src/helpers/requests/user/fetchUserRewards.ts b/src/helpers/requests/user/fetchUserRewards.ts new file mode 100644 index 00000000..7a39b3dd --- /dev/null +++ b/src/helpers/requests/user/fetchUserRewards.ts @@ -0,0 +1,32 @@ +import { insertMockE2E } from '../../methods' + + +type Input = { + sdk: SDK + days: number + userAddress: string + vaultAddress: string +} + +type Output = Store['vault']['user']['rewards']['data'] + +const fetchUserRewards = async (values: Input): Promise => { + const { userAddress, vaultAddress, days, sdk } = values + + const mockE2E = insertMockE2E('user/setUserStats') + + if (mockE2E) { + return mockE2E + } + + const data = await sdk.vault.getUserStats({ + daysCount: days, + vaultAddress, + userAddress, + }) + + return data +} + + +export default fetchUserRewards diff --git a/src/helpers/requests/user/index.ts b/src/helpers/requests/user/index.ts new file mode 100644 index 00000000..10c27dbf --- /dev/null +++ b/src/helpers/requests/user/index.ts @@ -0,0 +1,6 @@ +import fetchUserRewards from './fetchUserRewards' + + +export default { + fetchUserRewards, +} diff --git a/src/layouts/modals/ExportRewardsModal/util/useRewards.ts b/src/layouts/modals/ExportRewardsModal/util/useRewards.ts index 7cc75f1d..c696b2a7 100644 --- a/src/layouts/modals/ExportRewardsModal/util/useRewards.ts +++ b/src/layouts/modals/ExportRewardsModal/util/useRewards.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { modifiers } from 'helpers' +import { modifiers, methods } from 'helpers' import { useConfig } from 'config' import { StakeWiseSDK } from 'sdk' import forms from 'modules/forms' @@ -53,10 +53,11 @@ const useRewards = (input: Input) => { dateFrom: fromInMs, } - const data: FetcherReturn = await sdk.vault.getUserRewards({ - ...params, - vaultAddress, - }) + const mockE2E = methods.insertMockE2E('user/setUserRewards') + + const data: FetcherReturn = mockE2E + ? mockE2E + : await sdk.vault.getUserRewards({ ...params, vaultAddress }) const response = data.map((values) => { const { diff --git a/src/views/SwapView/modals/StatisticsModal/RewardsChart/RewardsChart.tsx b/src/views/SwapView/modals/StatisticsModal/RewardsChart/RewardsChart.tsx index 0ba1e54b..8b55db68 100644 --- a/src/views/SwapView/modals/StatisticsModal/RewardsChart/RewardsChart.tsx +++ b/src/views/SwapView/modals/StatisticsModal/RewardsChart/RewardsChart.tsx @@ -1,4 +1,5 @@ import React, { useMemo, useCallback } from 'react' +import { requests } from 'helpers' import { useConfig } from 'config' import { swapCtx } from 'views/SwapView/util' @@ -27,10 +28,11 @@ const RewardsChart: React.FC = (props) => { return null } - const data = await sdk.vault.getUserStats({ - userAddress: address, - daysCount: days, + const data = await requests.user.fetchUserRewards({ + sdk, + days, vaultAddress, + userAddress: address, }) return data diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useBoost.ts b/src/views/SwapView/util/vault/useUser/useBalances/useBoost.ts index 144c4f1d..14f9c81a 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useBoost.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useBoost.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { initialState } from 'store/store/vault' import { useConfig } from 'config' +import { methods } from 'helpers' type Input = { @@ -8,14 +9,20 @@ type Input = { userAddress: string } +type Output = Store['vault']['user']['balances']['boost'] + const useBoost = () => { const { sdk } = useConfig() return useCallback(async (values: Input) => { try { - const response = await sdk.boost.getData(values) + const mockE2E = methods.insertMockE2E('user/balances/setBoostData') + + if (mockE2E) { + return mockE2E + } - const result: Store['vault']['user']['balances']['boost'] = response + const result: Output = await sdk.boost.getData(values) return result } diff --git a/src/views/SwapView/util/vault/useUser/useUnboostQueue.ts b/src/views/SwapView/util/vault/useUser/useUnboostQueue.ts index 26fef1ce..f17c5238 100644 --- a/src/views/SwapView/util/vault/useUser/useUnboostQueue.ts +++ b/src/views/SwapView/util/vault/useUser/useUnboostQueue.ts @@ -1,8 +1,11 @@ import { useCallback, useMemo } from 'react' import { useMountedRef, useActions } from 'hooks' +import { methods } from 'helpers' import { useConfig } from 'config' +type Output = Store['vault']['user']['unboostQueue']['data'] + const useUnboostQueue = (vaultAddress: string) => { const actions = useActions() const mountedRef = useMountedRef() @@ -13,6 +16,14 @@ const useUnboostQueue = (vaultAddress: string) => { try { actions.vault.user.unboostQueue.setFetching(true) + const mockE2E = methods.insertMockE2E('user/setUnboostQueue') + + if (mockE2E) { + actions.vault.user.unboostQueue.setData(mockE2E) + + return + } + const unboostQueue = await sdk.boost.getQueuePosition({ userAddress: address, vaultAddress, diff --git a/src/views/SwapView/util/vault/useUser/useUnstakeQueue.ts b/src/views/SwapView/util/vault/useUser/useUnstakeQueue.ts index da1cf8f3..3051ffb3 100644 --- a/src/views/SwapView/util/vault/useUser/useUnstakeQueue.ts +++ b/src/views/SwapView/util/vault/useUser/useUnstakeQueue.ts @@ -1,8 +1,11 @@ import { useCallback, useMemo } from 'react' import { useMountedRef, useActions } from 'hooks' +import { methods } from 'helpers' import { useConfig } from 'config' +type Output = Store['vault']['user']['unstakeQueue']['data'] + const useUnstakeQueue = (vaultAddress: string) => { const actions = useActions() const mountedRef = useMountedRef() @@ -13,6 +16,14 @@ const useUnstakeQueue = (vaultAddress: string) => { try { actions.vault.user.unstakeQueue.setFetching(true) + const mockE2E = methods.insertMockE2E('user/setUnstakeQueue') + + if (mockE2E) { + actions.vault.user.unstakeQueue.setData(mockE2E) + + return + } + const exitQueue = await sdk.vault.getExitQueuePositions({ userAddress: address, isClaimed: false, diff --git a/src/views/SwapView/util/vault/useUser/useUserChartStats.ts b/src/views/SwapView/util/vault/useUser/useUserChartStats.ts index 0a511443..cc796936 100644 --- a/src/views/SwapView/util/vault/useUser/useUserChartStats.ts +++ b/src/views/SwapView/util/vault/useUser/useUserChartStats.ts @@ -1,6 +1,7 @@ import { useCallback, useMemo } from 'react' import { useActions, useMountedRef } from 'hooks' import { useConfig } from 'config' +import { requests } from 'helpers' const useUserChartStats = (vaultAddress: string) => { @@ -13,9 +14,10 @@ const useUserChartStats = (vaultAddress: string) => { if (address) { actions.vault.user.rewards.setFetching(true) - const data = await sdk.vault.getUserStats({ - daysCount, + const data = await requests.user.fetchUserRewards({ + sdk, vaultAddress, + days: daysCount, userAddress: address, }) From 7fd57b324375d56ed62849da2fb972c88b8eb5a2 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Fri, 28 Nov 2025 09:19:20 +0300 Subject: [PATCH 02/12] add vault fixtures, improve boost.spec, stake.spec --- e2e/constants.ts | 4 + e2e/extendTest.ts | 4 + e2e/fixtures/index.ts | 4 + e2e/fixtures/osToken/index.ts | 15 +++ e2e/fixtures/osToken/setOsTokenApy.ts | 22 +++++ e2e/fixtures/swap/getSwapInfoItem.ts | 27 ------ e2e/fixtures/swap/helpers/getSwapInfoItem.ts | 20 ++++ e2e/fixtures/swap/helpers/index.ts | 2 + e2e/fixtures/swap/index.ts | 17 +++- e2e/fixtures/swap/setSwapStats.ts | 26 ++++++ e2e/fixtures/user/balances/index.ts | 3 + .../user/balances/setMintTokenData.ts | 57 ++++++++++++ e2e/fixtures/user/index.ts | 4 + e2e/fixtures/vault/index.ts | 15 +++ e2e/fixtures/vault/setVaultData.ts | 91 +++++++++++++++++++ e2e/tests/boost.spec.ts | 30 ++++-- e2e/tests/stake.spec.ts | 73 ++++++++------- e2e/types.d.ts | 4 + .../requests/_SSR/vault/getVaultBase.ts | 18 +--- src/helpers/requests/index.ts | 1 + src/helpers/requests/vault/fetchData.ts | 49 ++++++++++ src/helpers/requests/vault/index.ts | 6 ++ src/views/SwapView/util/useStats.ts | 7 +- src/views/SwapView/util/useTabs.ts | 82 ++++++++++------- .../SwapView/util/vault/helpers/useAssets.ts | 1 + .../vault/useUser/useBalances/useMintToken.ts | 6 ++ .../util/vault/useVault/useVaultData.ts | 20 +--- 27 files changed, 473 insertions(+), 135 deletions(-) create mode 100644 e2e/fixtures/osToken/index.ts create mode 100644 e2e/fixtures/osToken/setOsTokenApy.ts delete mode 100644 e2e/fixtures/swap/getSwapInfoItem.ts create mode 100644 e2e/fixtures/swap/helpers/getSwapInfoItem.ts create mode 100644 e2e/fixtures/swap/helpers/index.ts create mode 100644 e2e/fixtures/swap/setSwapStats.ts create mode 100644 e2e/fixtures/user/balances/setMintTokenData.ts create mode 100644 e2e/fixtures/vault/index.ts create mode 100644 e2e/fixtures/vault/setVaultData.ts create mode 100644 src/helpers/requests/vault/fetchData.ts create mode 100644 src/helpers/requests/vault/index.ts diff --git a/e2e/constants.ts b/e2e/constants.ts index 623bd643..3324d144 100644 --- a/e2e/constants.ts +++ b/e2e/constants.ts @@ -37,3 +37,7 @@ export const genesisAddress = { } export const feeRecipient = '0x16c1dc6d901abed2bac1aece138800e45d762e50' + +export const metaVault = '0x15639e82d2072fa510e5d2b5f0db361c823bcad3' + +export const maxUint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935n // (2n ** 256n) - 1n diff --git a/e2e/extendTest.ts b/e2e/extendTest.ts index fcd84fd0..e8c3c978 100644 --- a/e2e/extendTest.ts +++ b/e2e/extendTest.ts @@ -4,9 +4,11 @@ import { sdk, user, swap, + vault, queue, anvil, wallet, + osToken, graphql, rewards, element, @@ -22,8 +24,10 @@ const baseTest = base.extend({ user, swap, queue, + vault, anvil, wallet, + osToken, rewards, graphql, element, diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.ts index 2f3d7549..d1602a73 100644 --- a/e2e/fixtures/index.ts +++ b/e2e/fixtures/index.ts @@ -1,9 +1,11 @@ export { default as sdk } from './sdk' export { default as user } from './user' export { default as swap } from './swap' +export { default as vault } from './vault' export { default as queue } from './queue' export { default as anvil } from './anvil' export { default as wallet } from './wallet' +export { default as osToken } from './osToken' export { default as rewards } from './rewards' export { default as graphql } from './graphql' export { default as element } from './element' @@ -16,9 +18,11 @@ export { default as transactions } from './transactions' export type { SDKFixture } from './sdk' export type { UserFixture } from './user' export type { SwapFixture } from './swap' +export type { VaultFixture } from './vault' export type { QueueFixture } from './queue' export type { AnvilFixture } from './anvil' export type { WalletFixture } from './wallet' +export type { OsTokenFixture } from './osToken' export type { RewardsFixture } from './rewards' export type { ElementFixture } from './element' export type { HelpersFixture } from './helpers' diff --git a/e2e/fixtures/osToken/index.ts b/e2e/fixtures/osToken/index.ts new file mode 100644 index 00000000..3bf46d11 --- /dev/null +++ b/e2e/fixtures/osToken/index.ts @@ -0,0 +1,15 @@ +import { createSetOsTokenApy, SetOsTokenApy } from './setOsTokenApy' + + +export type OsTokenFixture = { + setOsTokenApy: SetOsTokenApy +} + +const osToken: E2E.Fixture = async ({ page }, use) => { + await use({ + setOsTokenApy: createSetOsTokenApy({ page }), + }) +} + + +export default osToken diff --git a/e2e/fixtures/osToken/setOsTokenApy.ts b/e2e/fixtures/osToken/setOsTokenApy.ts new file mode 100644 index 00000000..0f445952 --- /dev/null +++ b/e2e/fixtures/osToken/setOsTokenApy.ts @@ -0,0 +1,22 @@ +type Input = { + apy: string + feePercent?: number +} + +export type SetOsTokenApy = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetOsTokenApy: Wrapper = ({ page }) => ( + async (values) => { + await page.addInitScript((payload) => { + window.e2e = { + ...window.e2e, + ['fixtures/osToken/setOsTokenApy']: { + feePercent: payload.feePercent || 100, + apy: payload.apy, + }, + } + }, values) + } +) diff --git a/e2e/fixtures/swap/getSwapInfoItem.ts b/e2e/fixtures/swap/getSwapInfoItem.ts deleted file mode 100644 index 059b2bb4..00000000 --- a/e2e/fixtures/swap/getSwapInfoItem.ts +++ /dev/null @@ -1,27 +0,0 @@ -type Wrapper = E2E.FixtureMethod - -export type GetSwapInfoItem = (type: SwapInfoTypes, itemType?: SwapItemType) => Promise - -type SwapInfoTypes = ( - 'apy' - | 'fee' - | 'rate' - | 'queue' - | 'receive' - | 'apy-prev' - | 'apy-next' - | 'exiting-shares' - | 'exiting-rewards' - | 'boosted-shares-next' -) - -type SwapItemType = 'table' | 'position' - -export const createGetSwapInfoItem: Wrapper = ({ page }) => ( - async (type: SwapInfoTypes, itemType: SwapItemType = 'table') => { - const selector = await page.waitForSelector(`[data-testid="${itemType}-${type}"]`) - const value = await selector.textContent() - - return value || '' - } -) diff --git a/e2e/fixtures/swap/helpers/getSwapInfoItem.ts b/e2e/fixtures/swap/helpers/getSwapInfoItem.ts new file mode 100644 index 00000000..fb715734 --- /dev/null +++ b/e2e/fixtures/swap/helpers/getSwapInfoItem.ts @@ -0,0 +1,20 @@ +type Wrapper = E2E.FixtureMethod + +export type GetSwapInfoItem = (type: SwapInfoTypes) => Promise + +type SwapInfoTypes = ( + | 'gas' + | 'apy-prev' + | 'apy-next' + | 'asset-prev' + | 'asset-next' +) + +export const createGetSwapInfoItem: Wrapper = ({ page }) => ( + async (type: SwapInfoTypes) => { + const selector = await page.waitForSelector(`[data-testid="table-${type}"]`) + const value = await selector.textContent() + + return value || '' + } +) diff --git a/e2e/fixtures/swap/helpers/index.ts b/e2e/fixtures/swap/helpers/index.ts new file mode 100644 index 00000000..ae8c1d6e --- /dev/null +++ b/e2e/fixtures/swap/helpers/index.ts @@ -0,0 +1,2 @@ +export { createGetSwapInfoItem } from './getSwapInfoItem' +export type { GetSwapInfoItem } from './getSwapInfoItem' diff --git a/e2e/fixtures/swap/index.ts b/e2e/fixtures/swap/index.ts index 3c82ea37..3e91fecf 100644 --- a/e2e/fixtures/swap/index.ts +++ b/e2e/fixtures/swap/index.ts @@ -3,14 +3,17 @@ import { createInput, Input } from './input' import { createSubmit, Submit } from './submit' import { createMockApy, MockApy } from './mockApy' import { createOpenPage, OpenPage } from './openPage' +import { createSetSwapStats, SetSwapStats } from './setSwapStats' import { createSubmitAmount, SubmitAmount } from './submitAmount' import { createMockPosition, MockPosition } from './mockPosition' import { createCheckSwapRender, CheckSwapRender } from './checkSwapRender' import { createGetBaseInfoItem, GetBaseInfoItem } from './getBaseInfoItem' -import { createGetSwapInfoItem, GetSwapInfoItem } from './getSwapInfoItem' import { createCheckSubmitButton, CheckSubmitButton } from './checkSubmitButton' import { createCheckConnectButton, CheckConnectButton } from './checkConnectButton' +import { createGetSwapInfoItem } from './helpers' +import type { GetSwapInfoItem } from './helpers' + export type SwapFixture = { tab: Tab @@ -18,13 +21,17 @@ export type SwapFixture = { submit: Submit mockApy: MockApy openPage: OpenPage + setSwapStats: SetSwapStats mockPosition: MockPosition submitAmount: SubmitAmount checkSwapRender: CheckSwapRender getBaseInfoItem: GetBaseInfoItem - getSwapInfoItem: GetSwapInfoItem checkSubmitButton: CheckSubmitButton checkConnectButton: CheckConnectButton + + helpers: { + getSwapInfoItem: GetSwapInfoItem + } } const swap: E2E.Fixture = async ({ page, graphql, transactions, element, user }, use) => { @@ -33,14 +40,18 @@ const swap: E2E.Fixture = async ({ page, graphql, transactions, ele input: createInput({ page }), openPage: createOpenPage({ page }), mockApy: createMockApy({ graphql }), + setSwapStats: createSetSwapStats({ page }), submitAmount: createSubmitAmount({ page }), submit: createSubmit({ page, transactions }), mockPosition: createMockPosition({ user }), checkSwapRender: createCheckSwapRender({ page }), - getSwapInfoItem: createGetSwapInfoItem({ page }), getBaseInfoItem: createGetBaseInfoItem({ page }), checkConnectButton: createCheckConnectButton({ page }), checkSubmitButton: createCheckSubmitButton({ page, element }), + + helpers: { + getSwapInfoItem: createGetSwapInfoItem({ page }), + }, }) } diff --git a/e2e/fixtures/swap/setSwapStats.ts b/e2e/fixtures/swap/setSwapStats.ts new file mode 100644 index 00000000..8581e34f --- /dev/null +++ b/e2e/fixtures/swap/setSwapStats.ts @@ -0,0 +1,26 @@ +type Output = { + totalAssets: string + totalEarnedAssets: string +} + +export type SetSwapStats = () => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetSwapStats: Wrapper = ({ page }) => ( + async () => { + const stats = { + totalAssets: '100000000000000000000', + totalEarnedAssets: '10000000000000000000', + } + + await page.addInitScript((payload) => { + window.e2e = { + ...window.e2e, + ['fixtures/swap/setSwapStats']: payload, + } + }, stats) + + return stats + } +) diff --git a/e2e/fixtures/user/balances/index.ts b/e2e/fixtures/user/balances/index.ts index 0e228434..381f8ae6 100644 --- a/e2e/fixtures/user/balances/index.ts +++ b/e2e/fixtures/user/balances/index.ts @@ -1,2 +1,5 @@ export { createSetBoostData } from './setBoostData' export type { SetBoostData } from './setBoostData' + +export { createSetMintTokenData } from './setMintTokenData' +export type { SetMintTokenData } from './setMintTokenData' diff --git a/e2e/fixtures/user/balances/setMintTokenData.ts b/e2e/fixtures/user/balances/setMintTokenData.ts new file mode 100644 index 00000000..c57638f5 --- /dev/null +++ b/e2e/fixtures/user/balances/setMintTokenData.ts @@ -0,0 +1,57 @@ +import { parseEther } from 'ethers' + + +type Input = { + stakedAssets?: string + mintedShares?: string +} + +type Output = Store['vault']['user']['balances']['mintToken'] + +export type SetMintTokenData = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetMintTokenData: Wrapper = ({ page }) => ( + async (values: Input) => { + const { stakedAssets = '0', mintedShares = '0' } = values + + const data = { + stakedAssets: parseEther(stakedAssets), + mintedShares: parseEther(mintedShares), + } + + await page.evaluate(async (payload) => { + const sdk = window.e2e.sdk + + const avgRewardPerSecond = await sdk.contracts.base.mintTokenController.avgRewardPerSecond() + const ltvPercent = 999900000000000000n + + const maxMintedAssets = payload.stakedAssets * ltvPercent / 1000000000000000000n + const maxMintedAssetsHourReward = (maxMintedAssets * avgRewardPerSecond * 3600n) / 1000000000000000000n + const mintedAssets = await sdk.contracts.base.mintTokenController.convertToAssets(payload.mintedShares) + + const canMintAssets = maxMintedAssets - maxMintedAssetsHourReward - mintedAssets + + let maxMintShares = 0n + + if (canMintAssets > 0) { + maxMintShares = await sdk.contracts.base.mintTokenController.convertToShares(canMintAssets) + } + + const result: Output = { + ...payload, + mintedAssets, + maxMintShares, + isDisabled: false, + hasMintBalance: true, + mintedShares: payload.mintedShares, + } + + window.e2e = { + ...window.e2e, + ['user/balances/setMintTokenData']: result, + } + }, data) + } +) diff --git a/e2e/fixtures/user/index.ts b/e2e/fixtures/user/index.ts index 80ffc0d6..98ea2911 100644 --- a/e2e/fixtures/user/index.ts +++ b/e2e/fixtures/user/index.ts @@ -12,10 +12,12 @@ import type { SetUserStats } from './setUserStats' import { createSetBoostData, + createSetMintTokenData, } from './balances' import type { SetBoostData, + SetMintTokenData, } from './balances' @@ -27,6 +29,7 @@ export type UserFixture = { balances: { setBoostData: SetBoostData + setMintTokenData: SetMintTokenData }, } @@ -39,6 +42,7 @@ const user: E2E.Fixture = async ({ page, helpers }, use) => { balances: { setBoostData: createSetBoostData({ page }), + setMintTokenData: createSetMintTokenData({ page }), }, }) } diff --git a/e2e/fixtures/vault/index.ts b/e2e/fixtures/vault/index.ts new file mode 100644 index 00000000..0619ced5 --- /dev/null +++ b/e2e/fixtures/vault/index.ts @@ -0,0 +1,15 @@ +import { createSetVaultData, SetVaultData } from './setVaultData' + + +export type VaultFixture = { + setVaultData: SetVaultData +} + +const vault: E2E.Fixture = async ({ page }, use) => { + await use({ + setVaultData: createSetVaultData({ page }), + }) +} + + +export default vault diff --git a/e2e/fixtures/vault/setVaultData.ts b/e2e/fixtures/vault/setVaultData.ts new file mode 100644 index 00000000..27586978 --- /dev/null +++ b/e2e/fixtures/vault/setVaultData.ts @@ -0,0 +1,91 @@ +import { formatEther, getAddress, MaxUint256, ZeroAddress } from 'ethers' + +import * as constants from '../../constants' + + +export type SetVaultData = (data?: any) => Promise + +type Output = Store['vault']['base']['data'] + +type Wrapper = E2E.FixtureMethod + +export const createSetVaultData: Wrapper = ({ page }) => ( + async (data) => { + let admin = data?.admin + let address = ZeroAddress + + if (!admin) { + try { + admin = await page.evaluate('window.ethereum.signer.address') + } + catch {} + } + + if (admin) { + address = admin + } + + const vaultAddress = getAddress(data?.address || constants.metaVault) + const capacity = data?.capacity || String(constants.maxUint256) + const feePercent = (data?.feePercent || 450) / 100 + + const result: Output = { + feePercent, + vaultAddress, + isSmoothingPool: false, + imageUrl: data?.imageUrl || '', + isErc20: data?.isErc20 ?? false, + vaultAdmin: getAddress(address), + apy: Number(data?.apy || '2.42'), + tokenName: data?.tokenName || null, + isPrivate: data?.isPrivate ?? true, + description: data?.description || '', + tokenSymbol: data?.tokenSymbol || null, + queuedShares: data?.queuedShares || '0', + isBlocklist: data?.isBlocklist ?? false, + baseApy: Number(data?.baseApy || '0.80'), + depositDataRoot: data?.depositDataRoot || '', + displayName: data?.displayName || 'Mock Vault', + isCollateralized: data?.isCollateralized ?? true, + performance: Number(data?.performance || '97.65'), + lastFeePercent: (data?.lastFeePercent || 500) / 100, + blocklistCount: Number(data?.blocklistCount || '0'), + whitelistCount: Number(data?.whitelistCount || '0'), + whitelistManager: address ? getAddress(address) : '', + validatorsManager: address ? getAddress(address) : '', + depositDataManager: address ? getAddress(address) : '', + createdAt: Number(data?.createdAt || '1701031871') * 1000, + allocatorMaxBoostApy: Number(data?.allocatorMaxBoostApy || '22.11'), + lastFeeUpdateTimestamp: data?.lastFeeUpdateTimestamp || '1757670504', + totalAssets: formatEther(data?.totalAssets || '32100978717000000000'), + feeRecipient: getAddress(data?.feeRecipient || constants.feeRecipient), + isMetaVault: data?.isMetaVault ?? vaultAddress === constants.metaVault, + isGenesis: data?.isGenesis ?? vaultAddress === constants.genesisAddress.mainnet, + blocklistManager: data?.blocklistManager ? getAddress(data?.blocklistManager) : '', + mevRecipient: getAddress(data?.mevEscrow || '0x5ab14b64fb24c170671edb69b76812e4e05d558c'), + capacity: capacity !== MaxUint256.toString() + ? formatEther(capacity) + : '∞', + osTokenConfig: { + liqThresholdPercent: data?.osTokenConfig?.liqThresholdPercent || '18446744073709551615', + ltvPercent: data?.osTokenConfig?.ltvPercent || '999900000000000000', + }, + versions: { + version: Number(data?.version || '5'), + isV1Version: false, + isV2Version: false, + isMoreV1: true, + isMoreV2: true, + }, + isPostPectra: true, + protocolFeePercent: String(feePercent / 100), + } + + await page.addInitScript((payload) => { + window.e2e = { + ...window.e2e, + ['fixtures/vault/setVaultData']: payload, + } + }, result) + } +) diff --git a/e2e/tests/boost.spec.ts b/e2e/tests/boost.spec.ts index 589368c8..29047d11 100644 --- a/e2e/tests/boost.spec.ts +++ b/e2e/tests/boost.spec.ts @@ -23,10 +23,12 @@ test('Max balance', async ({ swap, wallet, page, sdk }) => { await swap.openPage() await wallet.connectWithBalance({ ETH: initialETH }) + await sdk.deposit({ vaultAddress: constants.genesisAddress.mainnet, assets: depositETH, }) + const shares = await sdk.mint({ vaultAddress: constants.genesisAddress.mainnet, assets: mintETH, @@ -42,27 +44,24 @@ test('Max balance', async ({ swap, wallet, page, sdk }) => { expect(value).toEqual(shares) }) -test('Boost info', async ({ swap, page, wallet, sdk, graphql }) => { +test('Boost info', async ({ swap, page, wallet, sdk }) => { const initialETH = '25' const depositETH = '20' const mintETH = '15' - await swap.openPage(`skipSSR=true`) + await swap.openPage() await wallet.connectWithBalance({ ETH: initialETH }) await sdk.deposit({ vaultAddress: constants.genesisAddress.mainnet, assets: depositETH, }) + await sdk.mint({ vaultAddress: constants.genesisAddress.mainnet, assets: mintETH, }) - const userAPY = '1.55' - - await graphql.mockUserApy(userAPY) - await page.reload() await swap.tab('boost') @@ -77,6 +76,25 @@ test('Boost info', async ({ swap, page, wallet, sdk, graphql }) => { expect(boostToken).toBe('osETH') }) +test('Boost not profitable', async ({ user, wallet, swap, vault }) => { + await vault.setVaultData({ + allocatorMaxBoostApy: '1', + apy: '5', + }) + + await swap.openPage('skipSSR=true') + + const amount = '10' + + await user.balances.setMintTokenData({ stakedAssets: amount, mintedShares: amount }) + + await wallet.connectWithBalance({ ETH: '50', osETH: '50' }) + await swap.tab('boost') + await swap.input.fill('1') + + await swap.checkSubmitButton({ text: 'Not profitable', isLoading: false, isDisabled: true }) +}) + test('Boost disabled', async ({ wallet, swap, page, element }) => { await swap.openPage() diff --git a/e2e/tests/stake.spec.ts b/e2e/tests/stake.spec.ts index 4192c5c1..2f9b1c0d 100644 --- a/e2e/tests/stake.spec.ts +++ b/e2e/tests/stake.spec.ts @@ -1,4 +1,4 @@ -import { parseEther, ZeroAddress } from 'ethers' +import { formatEther } from 'ethers' import { expect } from '@playwright/test' import test from '../extendTest' @@ -28,46 +28,54 @@ test('Max balance minus gas', async ({ swap, wallet, graphql }) => { expect(Number(value)).toBeGreaterThan(0) }) -test('Initial info', async ({ swap, page, graphql }) => { - const { vaultApy, maxBoostApy } = await swap.mockApy({ isProfitable: true }) +test('Initial info', async ({ swap, vault }) => { + const vaultApy = '5' + const maxApy = '10' - await graphql.mockVaultData({ - admin: ZeroAddress, - totalAssets: parseEther('401050').toString(), + const { totalAssets } = await swap.setSwapStats() + + await vault.setVaultData({ + apy: vaultApy, + allocatorMaxBoostApy: maxApy, }) await swap.openPage('skipSSR=true') - await page.waitForLoadState('networkidle') const [ maxBoostApyValue, vaultApyValue, - tvlValue, + tvl, ] = await Promise.all([ swap.getBaseInfoItem('max-boost-apy'), swap.getBaseInfoItem('vault-apy'), swap.getBaseInfoItem('stake-tvl'), ]) - expect(maxBoostApyValue).toEqual(maxBoostApy) - expect(vaultApyValue).toEqual(vaultApy) - expect(tvlValue).toEqual(401.05) + const [ currectTVL ] = formatEther(totalAssets).split('.') + + expect(maxBoostApyValue).toEqual(Number(maxApy)) + expect(vaultApyValue).toEqual(Number(vaultApy)) + expect(tvl).toEqual(Number(currectTVL)) }) -test('Initial info not profitable', async ({ swap, page, element }) => { - const { vaultApy } = await swap.mockApy({ isProfitable: false }) +test('Initial info not profitable', async ({ swap, element, vault }) => { + const vaultApy = '5' - await swap.openPage() - await page.waitForLoadState('networkidle') + await vault.setVaultData({ + apy: vaultApy, + allocatorMaxBoostApy: '1', + }) + + await swap.openPage('skipSSR=true') await element.checkVisibility({ testId: 'max-boost-apy', isVisible: false }) const osTokenApyValue = await swap.getBaseInfoItem('vault-apy') - expect(osTokenApyValue).toEqual(vaultApy) + expect(osTokenApyValue).toEqual(Number(vaultApy)) }) -test('Stake info', async ({ page, swap, wallet }) => { +test('Stake info', async ({ swap, wallet }) => { const value = 10 await swap.openPage() @@ -76,32 +84,29 @@ test('Stake info', async ({ page, swap, wallet }) => { await swap.input.fill(value.toString()) - const getPositionInfoItem = async (type: string) => { - const selector = await page.waitForSelector(`[data-testid="position-${type}"]`) - const value = await selector.textContent() - - return value || '' - } - const [ stakeToken, - apy, gas, - receive, + apyPrev, + apyNext, + assetPrev, + assetNext, ] = await Promise.all([ swap.input.token(), - getPositionInfoItem('apy-next'), - getPositionInfoItem('value-prev'), - getPositionInfoItem('assets-next'), + swap.helpers.getSwapInfoItem('gas'), + swap.helpers.getSwapInfoItem('apy-prev'), + swap.helpers.getSwapInfoItem('apy-next'), + swap.helpers.getSwapInfoItem('asset-prev'), + swap.helpers.getSwapInfoItem('asset-next'), ]) - const receiveAmount = Number(receive.replace('osETH', '')) - expect(stakeToken).toBe('ETH') - expect(parseFloat(apy)).toBeGreaterThan(0) - expect(Number(gas.replace('$ ' , ''))).toBeGreaterThan(0) + expect(parseFloat(apyPrev)).toEqual(0) + expect(parseFloat(apyNext)).toBeGreaterThan(0) + expect(parseFloat(assetPrev)).toEqual(0) + expect(parseFloat(assetNext)).toEqual(value) - expect(receiveAmount).toEqual(value) + expect(Number(gas.replace('$ ' , ''))).toBeGreaterThan(0) }) test('Stake submit', async ({ wallet, swap, transactions }) => { diff --git a/e2e/types.d.ts b/e2e/types.d.ts index f7b56cac..50cb74fd 100644 --- a/e2e/types.d.ts +++ b/e2e/types.d.ts @@ -6,8 +6,10 @@ import type { UserFixture, SwapFixture, QueueFixture, + VaultFixture, AnvilFixture, WalletFixture, + OsTokenFixture, RewardsFixture, ElementFixture, HelpersFixture, @@ -29,8 +31,10 @@ declare global { user: UserFixture swap: SwapFixture queue: QueueFixture + vault: VaultFixture anvil: AnvilFixture wallet: WalletFixture + osToken: OsTokenFixture rewards: RewardsFixture context: BrowserContext element: ElementFixture diff --git a/src/helpers/requests/_SSR/vault/getVaultBase.ts b/src/helpers/requests/_SSR/vault/getVaultBase.ts index bee0ce24..f4570161 100644 --- a/src/helpers/requests/_SSR/vault/getVaultBase.ts +++ b/src/helpers/requests/_SSR/vault/getVaultBase.ts @@ -1,8 +1,7 @@ import { cookies } from 'next/headers' import { networks } from 'config/core' -import { Network } from 'sdk' -import { constants, getters } from '../../../index' +import { constants, getters, requests } from '../../../index' import { getSDK } from '../../../methods' @@ -36,22 +35,11 @@ const getVaultBase = async () => { const chainId = networks.chainById[networkId as NetworkIds] const sdk = getSDK({ chainId }) - const data = await sdk.vault.getVault({ vaultAddress, withTime: true }) - const versions = await sdk.getVaultVersion(vaultAddress) - const feePercent = await sdk.contracts.base.mintTokenController.feePercent() - - const isEditableInGnosis = sdk.network === Network.Gnosis && versions.version >= 3 - const isEditableInEthereum = sdk.network === Network.Mainnet && versions.version >= 5 - const isPostPectra = isEditableInGnosis || isEditableInEthereum + const data = await requests.vault.fetchData({ sdk, vaultAddress, withTime: true }) return { - data: { - ...data, - versions, - isPostPectra, - protocolFeePercent: String(feePercent / 100n), - }, + data, isSSR: true, isFetching: false, } diff --git a/src/helpers/requests/index.ts b/src/helpers/requests/index.ts index 43bcbde4..308478f0 100644 --- a/src/helpers/requests/index.ts +++ b/src/helpers/requests/index.ts @@ -1,4 +1,5 @@ export { default as user } from './user' +export { default as vault } from './vault' export { default as approve } from './approve' export { default as getApproveGas } from './getApproveGas' export { default as increaseDelay } from './increaseDelay' diff --git a/src/helpers/requests/vault/fetchData.ts b/src/helpers/requests/vault/fetchData.ts new file mode 100644 index 00000000..b38ba385 --- /dev/null +++ b/src/helpers/requests/vault/fetchData.ts @@ -0,0 +1,49 @@ +import { Network } from 'sdk' + +import * as methods from '../../methods' + + +type Input = { + sdk: SDK + withTime?: boolean + vaultAddress: string +} + +type Output = Store['vault']['base']['data'] + +const fetchData = async ({ sdk, withTime, vaultAddress }: Input) => { + const mockE2E = methods.insertMockE2E('fixtures/vault/setVaultData') + + if (mockE2E) { + return mockE2E + } + + const [ + data, + versions, + feePercent, + ] = await Promise.all([ + sdk.vault.getVault({ vaultAddress, withTime }), + sdk.getVaultVersion(vaultAddress), + sdk.contracts.base.mintTokenController.feePercent(), + ]) + + const chainId = sdk.config.network.chainId + const isGnosis = chainId === Network.Gnosis || chainId === Network.Chiado + const isEthereum = chainId === Network.Mainnet || chainId === Network.Hoodi + + const isEditableInGnosis = isGnosis && versions.version >= 3 + const isEditableInEthereum = isEthereum && versions.version >= 5 + + const isPostPectra = isEditableInGnosis || isEditableInEthereum + + return { + ...data, + versions, + isPostPectra, + protocolFeePercent: String(feePercent / 100n), + } +} + + +export default fetchData diff --git a/src/helpers/requests/vault/index.ts b/src/helpers/requests/vault/index.ts new file mode 100644 index 00000000..5b6ac702 --- /dev/null +++ b/src/helpers/requests/vault/index.ts @@ -0,0 +1,6 @@ +import fetchData from './fetchData' + + +export default { + fetchData, +} diff --git a/src/views/SwapView/util/useStats.ts b/src/views/SwapView/util/useStats.ts index 19d89155..ed6e4788 100644 --- a/src/views/SwapView/util/useStats.ts +++ b/src/views/SwapView/util/useStats.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect } from 'react' import { useObjectState } from 'hooks' +import { StakeWiseSDK } from 'sdk' import { useConfig } from 'config' import { methods } from 'helpers' @@ -9,6 +10,8 @@ type State = { isStatsFetching: boolean } +type Stats = Awaited> + const useStats = () => { const { sdk } = useConfig() @@ -19,7 +22,9 @@ const useStats = () => { const fetchStats = useCallback(async () => { try { - const stats = await sdk.utils.getStakewiseStats() + const mockE2E = methods.insertMockE2E('fixtures/swap/setSwapStats') + + const stats = mockE2E ? mockE2E : await sdk.utils.getStakewiseStats() const token = sdk.config.tokens.depositToken const value = methods.formatTokenValue(BigInt(stats.totalAssets)) diff --git a/src/views/SwapView/util/useTabs.ts b/src/views/SwapView/util/useTabs.ts index 917fcc03..b1b81471 100644 --- a/src/views/SwapView/util/useTabs.ts +++ b/src/views/SwapView/util/useTabs.ts @@ -10,7 +10,6 @@ import { Tab } from './enum' type State = { tab: Tab - list: SwapView.Tabs.Data['list'] isReversed: boolean } @@ -22,53 +21,53 @@ export const tabsMock: SwapView.Tabs.Data = { } const storeSelector = (store: Store) => ({ - isMoreV2: store.vault.base.data.versions.isMoreV2, + version: store.vault.base.data.versions.version, isMintTokenDisabled: store.vault.user.balances.mintToken.isDisabled, }) const useTabs = (resetFields: () => void) => { const { isEthereum } = useConfig() - const { isMoreV2, isMintTokenDisabled } = useStore(storeSelector) + const { isMintTokenDisabled, version } = useStore(storeSelector) const withMint = !isMintTokenDisabled + const isMoreV2 = version >= 2 const withBoost = withMint && isEthereum && isMoreV2 const withToggleButton = withMint || withBoost - const [ { tab, list }, setState ] = useObjectState({ + const [ { tab, isReversed }, setState ] = useObjectState({ tab: Tab.Stake, isReversed: false, - list: getTabsList({ withMint, withBoost, isReversed: false }), }) - const getCurrentIndex = useCallback((state: State) => { - const { tab, list } = state - - return list.map(({ id }) => id).indexOf(tab) - }, []) + const list = useMemo(() => getTabsList({ withMint, withBoost, isReversed }), + [ withMint, withBoost, isReversed ] + ) const setTab = useCallback((tab: Tab) => { const isValid = Object.values(Tab).includes(tab) - if (isValid) { - setState((state) => { - const isExists = Boolean(state.list.some(({ id }) => id === tab)) + if (!isValid) { + return + } - if (!isExists) { - console.error(`Invalid tab "${tab}", on list:`, state.list) + setState((state) => { + const isExists = list.some(({ id }) => id === tab) - return state - } + if (!isExists) { + console.error(`Invalid tab "${tab}", on list:`, list) - resetFields() + return state + } - return { - ...state, - tab, - } - }) - } - }, [ setState, resetFields ]) + resetFields() + + return { + ...state, + tab, + } + }) + }, [ setState, resetFields, list ]) const toggleTabs = useCallback(() => { setState((state) => { @@ -76,23 +75,39 @@ const useTabs = (resetFields: () => void) => { const list = getTabsList({ withMint, withBoost, isReversed: false }) return { - list, + ...state, tab: list[0].id, isReversed: false, } } - const isReversed = !state.isReversed - const list = getTabsList({ withMint, withBoost, isReversed }) - const index = getCurrentIndex(state) + const nextIsReversed = !state.isReversed + + const currentList = getTabsList({ + withMint, + withBoost, + isReversed: state.isReversed, + }) + + const nextList = getTabsList({ + withMint, + withBoost, + isReversed: nextIsReversed, + }) + + const currentIndex = currentList + .map(({ id }) => id) + .indexOf(state.tab) + + const safeIndex = currentIndex === -1 ? 0 : currentIndex return { - list, - isReversed, - tab: list[index].id, + ...state, + isReversed: nextIsReversed, + tab: nextList[safeIndex].id, } }) - }, [ withBoost, withMint, withToggleButton, getCurrentIndex, setState ]) + }, [ withBoost, withMint, withToggleButton, setState ]) const resetTab = useCallback((chainId: ChainIds) => { const isEth = chainId === Network.Mainnet || chainId === Network.Hoodi @@ -101,7 +116,6 @@ const useTabs = (resetFields: () => void) => { const list = getTabsList({ withMint, withBoost: nextWithBoost, isReversed: false }) setState({ - list, tab: list[0].id, isReversed: false, }) diff --git a/src/views/SwapView/util/vault/helpers/useAssets.ts b/src/views/SwapView/util/vault/helpers/useAssets.ts index e15f12d6..d17a9863 100644 --- a/src/views/SwapView/util/vault/helpers/useAssets.ts +++ b/src/views/SwapView/util/vault/helpers/useAssets.ts @@ -49,6 +49,7 @@ const useAssets = ({ field, type, depositAmount }: Input) => { next, }, logo: `token/${depositToken}` as LogoName, + dataTestId: 'table-asset', } return result diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts b/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts index f37a24c0..d59c5fcb 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts @@ -23,6 +23,12 @@ const useMintToken = () => { return useCallback(async (values: Input) => { try { + const mockE2E = methods.insertMockE2E('user/balances/setMintTokenData') + + if (mockE2E) { + return mockE2E + } + const data = await methods.fetch(sdk.config.api.subgraph, { method: 'POST', body: JSON.stringify({ diff --git a/src/views/SwapView/util/vault/useVault/useVaultData.ts b/src/views/SwapView/util/vault/useVault/useVaultData.ts index a478ed13..e3e61a8e 100644 --- a/src/views/SwapView/util/vault/useVault/useVaultData.ts +++ b/src/views/SwapView/util/vault/useVault/useVaultData.ts @@ -1,6 +1,7 @@ import { useCallback, useMemo, useRef } from 'react' import { useStore, useActions, useMountedRef } from 'hooks' import { useConfig } from 'config' +import { requests } from 'helpers' const storeSelector = (store: Store) => ({ @@ -8,10 +9,10 @@ const storeSelector = (store: Store) => ({ }) const useVaultData = (vaultAddress: string) => { + const { sdk } = useConfig() const actions = useActions() const mountedRef = useMountedRef() const { isSSR } = useStore(storeSelector) - const { sdk, isGnosis, isEthereum } = useConfig() const isSsrRef = useRef(isSSR) isSsrRef.current = isSSR @@ -29,28 +30,17 @@ const useVaultData = (vaultAddress: string) => { try { actions.vault.base.setFetching(true) - const vault = await sdk.vault.getVault({ vaultAddress }) - const versions = await sdk.getVaultVersion(vaultAddress) - const feePercent = await sdk.contracts.base.mintTokenController.feePercent() - - const isEditableInGnosis = isGnosis && versions.version >= 3 - const isEditableInEthereum = isEthereum && versions.version >= 5 - const isPostPectra = isEditableInGnosis || isEditableInEthereum + const data = await requests.vault.fetchData({ sdk, vaultAddress }) if (mountedRef.current) { - actions.vault.base.setData({ - ...vault, - versions, - isPostPectra, - protocolFeePercent: String(feePercent / 100n), - }) + actions.vault.base.setData(data) } } catch (error: any) { console.error('Fetch vault base data fail', error) actions.vault.base.setFetching(false) } - }, [ actions, mountedRef, sdk, vaultAddress, isEthereum, isGnosis ]) + }, [ sdk, vaultAddress, actions, mountedRef ]) const resetVault = useCallback(() => { actions.vault.base.resetData() From 6a034fd4b1c470e801654338a26222ae9bc68593 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Sun, 30 Nov 2025 11:23:18 +0300 Subject: [PATCH 03/12] improve all tests (burn, stake, unstake, mint, unboost) --- e2e/constants.ts | 3 +- e2e/extendTest.ts | 2 + e2e/fixtures/api/index.ts | 15 ++ e2e/fixtures/api/mockEstimateGas.ts | 33 ++++ e2e/fixtures/helpers/addNumberSeparator.ts | 16 ++ e2e/fixtures/helpers/formatTokenValue.ts | 98 +++++++++++ e2e/fixtures/helpers/index.ts | 3 + e2e/fixtures/index.ts | 2 + e2e/fixtures/sdk/getMaxMint.ts | 41 ----- e2e/fixtures/sdk/index.ts | 3 - e2e/fixtures/swap/actions/boost.ts | 54 ++++++ e2e/fixtures/swap/actions/burn.ts | 30 ++++ e2e/fixtures/swap/actions/index.ts | 17 ++ e2e/fixtures/swap/actions/mint.ts | 41 +++++ e2e/fixtures/swap/actions/stake.ts | 34 ++++ e2e/fixtures/swap/actions/unboost.ts | 65 +++++++ e2e/fixtures/swap/actions/unstake.ts | 29 +++ .../swap/{ => helpers}/checkConnectButton.ts | 0 .../swap/{ => helpers}/checkSubmitButton.ts | 0 .../swap/{ => helpers}/checkSwapRender.ts | 0 .../swap/helpers/checkTokenDropdown.ts | 31 ++++ .../swap/{ => helpers}/getBaseInfoItem.ts | 2 +- e2e/fixtures/swap/helpers/getSwapInfoItem.ts | 7 + e2e/fixtures/swap/helpers/index.ts | 15 ++ e2e/fixtures/swap/index.ts | 101 ++++++++--- e2e/fixtures/swap/mockApy.ts | 43 ----- e2e/fixtures/swap/mocks/boostInfo.ts | 24 +++ e2e/fixtures/swap/mocks/index.ts | 8 + .../{mockPosition.ts => mocks/position.ts} | 8 +- .../{setSwapStats.ts => mocks/swapStats.ts} | 8 +- e2e/fixtures/swap/setSdkTransactions.ts | 38 ++++ e2e/fixtures/swap/submit.ts | 11 +- e2e/fixtures/swap/submitAmount.ts | 11 +- .../transactions/checkTransactionsFlow.ts | 56 ++++++ .../transactions/checkTxCompletedModal.ts | 27 ++- e2e/fixtures/transactions/index.ts | 3 + e2e/fixtures/user/balances/index.ts | 9 + .../user/balances/setMaxWithdrawAssets.ts | 22 +++ .../user/balances/setMintTokenData.ts | 1 - e2e/fixtures/user/balances/setStakeBalance.ts | 34 ++++ e2e/fixtures/user/balances/setUserApy.ts | 14 ++ e2e/fixtures/user/index.ts | 12 ++ e2e/tests/balance.spec.ts | 6 +- e2e/tests/boost.spec.ts | 66 ++++--- e2e/tests/burn.spec.ts | 107 ++++-------- e2e/tests/loader.spec.ts | 18 +- e2e/tests/mint.spec.ts | 165 ++++++------------ e2e/tests/stake.spec.ts | 49 +++--- e2e/tests/unboost.spec.ts | 62 +++++-- e2e/tests/unstake.spec.ts | 126 +++++-------- e2e/types.d.ts | 2 + .../TokenOptions/TokenOptions.tsx | 2 +- src/helpers/requests/fetchBoostSupplyCaps.ts | 2 +- .../TransactionsFlowModal/util/steps.ts | 5 + .../modals/TxCompletedModal/Token/Token.tsx | 1 + src/layouts/modals/TxCompletedModal/enum.ts | 1 + .../content/Burn/BurnInput/BurnInput.tsx | 2 +- .../content/Mint/MintInfo/util/useOptions.ts | 2 + .../content/Mint/MintInput/MintInput.tsx | 2 +- .../Unstake/UnstakeContent/util/useOptions.ts | 4 +- src/views/SwapView/util/useStats.ts | 2 +- .../actions/useUnboost/useUnboostSubmit.ts | 2 +- .../SwapView/util/vault/helpers/useShares.ts | 1 + .../useBalances/useMaxWithdrawAssets.ts | 12 +- .../vault/useUser/useBalances/useMintToken.ts | 3 +- .../vault/useUser/useBalances/useStake.ts | 15 +- .../vault/useUser/useBalances/useUserApy.ts | 7 + 67 files changed, 1122 insertions(+), 513 deletions(-) create mode 100644 e2e/fixtures/api/index.ts create mode 100644 e2e/fixtures/api/mockEstimateGas.ts create mode 100644 e2e/fixtures/helpers/addNumberSeparator.ts create mode 100644 e2e/fixtures/helpers/formatTokenValue.ts delete mode 100644 e2e/fixtures/sdk/getMaxMint.ts create mode 100644 e2e/fixtures/swap/actions/boost.ts create mode 100644 e2e/fixtures/swap/actions/burn.ts create mode 100644 e2e/fixtures/swap/actions/index.ts create mode 100644 e2e/fixtures/swap/actions/mint.ts create mode 100644 e2e/fixtures/swap/actions/stake.ts create mode 100644 e2e/fixtures/swap/actions/unboost.ts create mode 100644 e2e/fixtures/swap/actions/unstake.ts rename e2e/fixtures/swap/{ => helpers}/checkConnectButton.ts (100%) rename e2e/fixtures/swap/{ => helpers}/checkSubmitButton.ts (100%) rename e2e/fixtures/swap/{ => helpers}/checkSwapRender.ts (100%) create mode 100644 e2e/fixtures/swap/helpers/checkTokenDropdown.ts rename e2e/fixtures/swap/{ => helpers}/getBaseInfoItem.ts (79%) delete mode 100644 e2e/fixtures/swap/mockApy.ts create mode 100644 e2e/fixtures/swap/mocks/boostInfo.ts create mode 100644 e2e/fixtures/swap/mocks/index.ts rename e2e/fixtures/swap/{mockPosition.ts => mocks/position.ts} (75%) rename e2e/fixtures/swap/{setSwapStats.ts => mocks/swapStats.ts} (61%) create mode 100644 e2e/fixtures/swap/setSdkTransactions.ts create mode 100644 e2e/fixtures/transactions/checkTransactionsFlow.ts create mode 100644 e2e/fixtures/user/balances/setMaxWithdrawAssets.ts create mode 100644 e2e/fixtures/user/balances/setStakeBalance.ts create mode 100644 e2e/fixtures/user/balances/setUserApy.ts diff --git a/e2e/constants.ts b/e2e/constants.ts index 3324d144..472f69b2 100644 --- a/e2e/constants.ts +++ b/e2e/constants.ts @@ -29,6 +29,7 @@ export const walletTitles = { export const addresses = { withGNO: '0xb5afcEbCA5C1A55E149b4DDF02B9681541Dd29D6', + genesisAdmin: '0x6Da6B1EfCCb7216078B9004535941b71EeD30b0F', } export const genesisAddress = { @@ -37,7 +38,7 @@ export const genesisAddress = { } export const feeRecipient = '0x16c1dc6d901abed2bac1aece138800e45d762e50' - export const metaVault = '0x15639e82d2072fa510e5d2b5f0db361c823bcad3' +export const minimalAmount = 100000000000000n // 0.0001 export const maxUint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935n // (2n ** 256n) - 1n diff --git a/e2e/extendTest.ts b/e2e/extendTest.ts index e8c3c978..f83b89b9 100644 --- a/e2e/extendTest.ts +++ b/e2e/extendTest.ts @@ -1,6 +1,7 @@ import { test as base } from '@guardianui/test' import { + api, sdk, user, swap, @@ -20,6 +21,7 @@ import { const baseTest = base.extend({ + api, sdk, user, swap, diff --git a/e2e/fixtures/api/index.ts b/e2e/fixtures/api/index.ts new file mode 100644 index 00000000..f25adf1e --- /dev/null +++ b/e2e/fixtures/api/index.ts @@ -0,0 +1,15 @@ +import { createMockEstimateGas, MockEstimateGas } from './mockEstimateGas' + + +export type ApiFixture = { + mockEstimateGas: MockEstimateGas +} + +const api: E2E.Fixture = async ({ page }, use) => { + await use({ + mockEstimateGas: createMockEstimateGas({ page }), + }) +} + + +export default api diff --git a/e2e/fixtures/api/mockEstimateGas.ts b/e2e/fixtures/api/mockEstimateGas.ts new file mode 100644 index 00000000..3082bba8 --- /dev/null +++ b/e2e/fixtures/api/mockEstimateGas.ts @@ -0,0 +1,33 @@ +import type { Route } from '@playwright/test' + + +type Wrapper = E2E.FixtureMethod + +type Unroute = () => Promise + +export type MockEstimateGas = () => Promise + +export const createMockEstimateGas: Wrapper = ({ page }) => ( + async () => { + const handler = async (route: Route) => { + const request = route.request() + const response = await route.fetch() + const json = await response.json() + + if (json.error) { + const payload = JSON.parse(request.postData() || '{}') + + if (payload.method === 'eth_estimateGas') { + json.error = undefined + json.result = '0x5f62' + } + } + + await route.fulfill({ response, json }) + } + + await page.route(/:8545/, handler) + + return () => page.unroute(/:8545/, handler) + } +) diff --git a/e2e/fixtures/helpers/addNumberSeparator.ts b/e2e/fixtures/helpers/addNumberSeparator.ts new file mode 100644 index 00000000..fc2a37d9 --- /dev/null +++ b/e2e/fixtures/helpers/addNumberSeparator.ts @@ -0,0 +1,16 @@ +const addNumberSeparator = (value: string) => { + const reverseArray = value.split('').reverse() + + const changedArray = reverseArray.reduce((acc, number, index) => { + if (index && index % 3 === 0) { + return [ ...acc, ',', number ] + } + + return [ ...acc, number ] + }, [] as string[]) + + return changedArray.reverse().join('') +} + + +export default addNumberSeparator diff --git a/e2e/fixtures/helpers/formatTokenValue.ts b/e2e/fixtures/helpers/formatTokenValue.ts new file mode 100644 index 00000000..c4214950 --- /dev/null +++ b/e2e/fixtures/helpers/formatTokenValue.ts @@ -0,0 +1,98 @@ +import { formatEther } from 'ethers' + +import addNumberSeparator from './addNumberSeparator' + + +export type FormatTokenValue = (value: bigint | string, isMinimalValueShow?: boolean) => string + +type Wrapper = E2E.FixtureMethod + +const minimalValue = 0.0001 + +const _formatRemainder = (value: string, rest = 4) => { + if (!Number(value)) { + return '00' + } + + let shortValue = value.slice(0, rest) + shortValue = shortValue.replace(/0+$/, '') + + return shortValue.length >= 2 + ? shortValue + : value.slice(0, 2) +} + +const _reduceValue = (value: number, divider: number, postfix: string) => { + const [ integer, remainder ] = String(value / divider).split('.') + + return `${addNumberSeparator(integer)}.${_formatRemainder(remainder, 2)}${postfix}` +} + +export const createFormatTokenValue: Wrapper = () => ( + (value: bigint | string, isMinimalValueShow: boolean = false) => { + try { + const isBigInt = typeof value === 'bigint' + + let result = isBigInt ? formatEther(value) : value + + // 4999.. => 5000.. (solves the -1wei in actions problem) + result = Number(result).toFixed(5) + + if (!Number(result)) { + return '0.00' + } + + if (isMinimalValueShow && Number(result) > 0 && Number(result) < minimalValue) { + return '< 0.0001' + } + + const isNegative = /^-/.test(result) + const formattedValue = isNegative ? result.replace(/^-/, '') : result + + const [ integer, remainder ] = formattedValue.split('.') + + // 0001 => 1 + const numberInteger = Number(integer) + + let resultInteger = integer + let resultRemainder: string | null = remainder + + if (numberInteger <= 9) { + resultRemainder = _formatRemainder(remainder, 5) + } + else if (numberInteger <= 99 ) { + resultRemainder = _formatRemainder(remainder, 4) + } + else if (numberInteger <= 999 ) { + resultRemainder = _formatRemainder(remainder, 3) + } + else if (numberInteger <= 9_999 ) { + resultRemainder = _formatRemainder(remainder, 2) + resultInteger = numberInteger.toLocaleString('en-US') + } + else if (numberInteger <= 99_999 ) { + resultInteger = _reduceValue(numberInteger, 1000, 'k') + resultRemainder = null + } + else if (numberInteger <= 999_999 ) { + resultInteger = _reduceValue(numberInteger, 1000, 'k') + resultRemainder = null + } + else if (numberInteger <= 999_999_999) { + resultInteger = _reduceValue(numberInteger, 1_000_000, 'm') + resultRemainder = null + } + else { + resultInteger = _reduceValue(numberInteger, 1_000_000_000, 'b') + resultRemainder = null + } + + return `${isNegative ? '-' : ''}${resultInteger}${resultRemainder ? `.${resultRemainder}` : ''}` + } + catch (error) { + console.error(error) + + return '0.00' + } + } +) diff --git a/e2e/fixtures/helpers/index.ts b/e2e/fixtures/helpers/index.ts index c8e50864..d7d18df4 100644 --- a/e2e/fixtures/helpers/index.ts +++ b/e2e/fixtures/helpers/index.ts @@ -2,6 +2,7 @@ import { createDelay, Delay } from './delay' import { createChangeSelect, ChangeSelect } from './changeSelect' import { createCheckBalances, CheckBalances } from './checkBalances' import { createGetCookiesItem, GetCookiesItem } from './getCookiesItem' +import { createFormatTokenValue, FormatTokenValue } from './formatTokenValue' import { createCheckNotification, CheckNotification } from './checkNotification' import { createGetLocalStorageItem, GetLocalStorageItem } from './getLocalStorageItem' @@ -13,11 +14,13 @@ export type HelpersFixture = { getCookiesItem: GetCookiesItem checkNotification: CheckNotification getLocalStorageItem: GetLocalStorageItem + formatTokenValue: FormatTokenValue } const helpers: E2E.Fixture = async ({ page, element }, use) => { await use({ delay: createDelay(), + formatTokenValue: createFormatTokenValue(), changeSelect: createChangeSelect({ page }), checkBalances: createCheckBalances({ page }), getCookiesItem: createGetCookiesItem({ page }), diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.ts index d1602a73..524c4621 100644 --- a/e2e/fixtures/index.ts +++ b/e2e/fixtures/index.ts @@ -1,3 +1,4 @@ +export { default as api } from './api' export { default as sdk } from './sdk' export { default as user } from './user' export { default as swap } from './swap' @@ -15,6 +16,7 @@ export { default as guardian } from './guardian' export { default as transactions } from './transactions' +export type { ApiFixture } from './api' export type { SDKFixture } from './sdk' export type { UserFixture } from './user' export type { SwapFixture } from './swap' diff --git a/e2e/fixtures/sdk/getMaxMint.ts b/e2e/fixtures/sdk/getMaxMint.ts deleted file mode 100644 index 83d0be9d..00000000 --- a/e2e/fixtures/sdk/getMaxMint.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { formatEther, parseEther } from 'ethers' - - -type Wrapper = E2E.FixtureMethod - -export type GetMaxMint = (values: Input) => Promise - -export type Input = { - ltvPercent: string - mintedAssets: string - stakedAssets: string - vaultAddress: string -} - -export const createGetMaxMint: Wrapper = ({ page }) => ( - async (values: Input) => { - const shares = await page.evaluate(async (values) => { - const { mintedAssets, ltvPercent, stakedAssets, vaultAddress } = values - - const sdk = window.e2e.sdk - // @ts-ignore - const userAddress = window.ethereum.signer.address - - const shares = await sdk.osToken.getMaxMint({ - userAddress, - vaultAddress, - ltvPercent: BigInt(ltvPercent), - stakedAssets: BigInt(stakedAssets), - mintedAssets: BigInt(mintedAssets), - }) - - return shares.toString() - }, { - ...values, - mintedAssets: parseEther(values.mintedAssets).toString(), - stakedAssets: parseEther(values.stakedAssets).toString(), - }) - - return formatEther(shares) - } -) diff --git a/e2e/fixtures/sdk/index.ts b/e2e/fixtures/sdk/index.ts index 563db23c..82e1ff3d 100644 --- a/e2e/fixtures/sdk/index.ts +++ b/e2e/fixtures/sdk/index.ts @@ -1,19 +1,16 @@ import { createMint, Mint } from './mint' import { createDeposit, Deposit } from './deposit' -import { createGetMaxMint, GetMaxMint } from './getMaxMint' export type SDKFixture = { mint: Mint deposit: Deposit - getMaxMint: GetMaxMint } const sdk: E2E.Fixture = async ({ page }, use) => { await use({ mint: createMint({ page }), deposit: createDeposit({ page }), - getMaxMint: createGetMaxMint({ page }), }) } diff --git a/e2e/fixtures/swap/actions/boost.ts b/e2e/fixtures/swap/actions/boost.ts new file mode 100644 index 00000000..7f23b5cd --- /dev/null +++ b/e2e/fixtures/swap/actions/boost.ts @@ -0,0 +1,54 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmitAmount } from '../submitAmount' + + +type Input = { + amount?: string + withUpgrade?: boolean +} + +export type Boost = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createBoost: Wrapper = ({ page, transactions, api }) => ( + async (values: Input) => { + const { amount, withUpgrade } = values + + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmitAmount({ page }) + + await tab('boost') + + await input.fill(amount) + + const inputAmount = await input.value() + + await submit() + + const unrouteEstimateGas = await api.mockEstimateGas() + + const steps = [ 'permit', 'boost' ] + + if (withUpgrade) { + steps.unshift('boostUpgrade') + } + + await transactions.checkTransactionsFlow({ + flow: 'boost', + availableSteps: steps, + checkNotifications: false, + }) + + await page.waitForLoadState('networkidle') + + await transactions.checkTxCompletedModal({ + action: 'boost', + value: inputAmount, + }) + + await unrouteEstimateGas() + } +) diff --git a/e2e/fixtures/swap/actions/burn.ts b/e2e/fixtures/swap/actions/burn.ts new file mode 100644 index 00000000..6f500360 --- /dev/null +++ b/e2e/fixtures/swap/actions/burn.ts @@ -0,0 +1,30 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmit } from '../submit' + + +export type Burn = (amount?: string) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createBurn: Wrapper = ({ transactions, page, helpers }) => ( + async (amount?: string) => { + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmit({ page, transactions }) + + await tab('burn', true) + + await input.fill(amount) + + const inputAmount = await input.value() + const formattedAmount = helpers.formatTokenValue(inputAmount) + + await submit() + + await transactions.checkTxCompletedModal({ + action: 'burn', + value: formattedAmount, + }) + } +) diff --git a/e2e/fixtures/swap/actions/index.ts b/e2e/fixtures/swap/actions/index.ts new file mode 100644 index 00000000..ae4c5927 --- /dev/null +++ b/e2e/fixtures/swap/actions/index.ts @@ -0,0 +1,17 @@ +export { createBurn } from './burn' +export type { Burn } from './burn' + +export { createMint } from './mint' +export type { Mint } from './mint' + +export { createBoost } from './boost' +export type { Boost } from './boost' + +export { createStake } from './stake' +export type { Stake } from './stake' + +export { createUnboost } from './unboost' +export type { Unboost } from './unboost' + +export { createUnstake } from './unstake' +export type { Unstake } from './unstake' diff --git a/e2e/fixtures/swap/actions/mint.ts b/e2e/fixtures/swap/actions/mint.ts new file mode 100644 index 00000000..08ede444 --- /dev/null +++ b/e2e/fixtures/swap/actions/mint.ts @@ -0,0 +1,41 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmit } from '../submit' + + +type Input = { + amount?: string + stakedAssets: string +} + +export type Mint = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createMint: Wrapper = ({ transactions, page, helpers, user }) => ( + async (values: Input) => { + const { amount, stakedAssets } = values + + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmit({ page, transactions }) + + await tab('mint') + + await input.fill(amount) + + const inputAmount = await input.value() + const formattedAmount = helpers.formatTokenValue(inputAmount) + + await Promise.all([ + submit(), + user.balances.setMaxWithdrawAssets(stakedAssets), + user.balances.setMintTokenData({ mintedShares: inputAmount, stakedAssets }), + ]) + + await transactions.checkTxCompletedModal({ + action: 'mint', + value: formattedAmount, + }) + } +) diff --git a/e2e/fixtures/swap/actions/stake.ts b/e2e/fixtures/swap/actions/stake.ts new file mode 100644 index 00000000..6f516485 --- /dev/null +++ b/e2e/fixtures/swap/actions/stake.ts @@ -0,0 +1,34 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmit } from '../submit' + + +export type Stake = (amount?: string) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createStake: Wrapper = ({ page, transactions, user }) => ( + async (amount?: string) => { + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmit({ page, transactions }) + + await tab('stake') + + await input.fill(amount) + + const inputAmount = await input.value() + + await Promise.all([ + submit(), + user.balances.setStakeBalance(inputAmount), + user.balances.setMaxWithdrawAssets(inputAmount), + user.balances.setMintTokenData({ mintedShares: '0', stakedAssets: inputAmount }), + ]) + + await transactions.checkTxCompletedModal({ + action: 'stake', + value: inputAmount, + }) + } +) diff --git a/e2e/fixtures/swap/actions/unboost.ts b/e2e/fixtures/swap/actions/unboost.ts new file mode 100644 index 00000000..78aab87f --- /dev/null +++ b/e2e/fixtures/swap/actions/unboost.ts @@ -0,0 +1,65 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmitAmount } from '../submitAmount' + +import type { TransactionItem } from '../../transactions/checkTxCompletedModal' + + +type Input = { + percent: string + shares: string + rewards?: string + withUpgrade?: boolean +} + +export type Unboost = (values?: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createUnboost: Wrapper = ({ transactions, api, page }) => ( + async (values?: Input) => { + const { percent, withUpgrade = true, shares, rewards } = values || {} + + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmitAmount({ page }) + + await tab('unboost', true) + + await input.fill(percent) + + await submit() + + const unrouteEstimateGas = await api.mockEstimateGas() + + if (withUpgrade) { + await transactions.checkTransactionsFlow({ + flow: 'unboost', + availableSteps: [ 'upgrade', 'unboost' ], + checkNotifications: false, + }) + } + + const exitingShares = Number(shares) * (Number(percent) / 100) + + const steps: TransactionItem[] = [ + { + action: 'exiting', + value: String(exitingShares), + }, + ] + + if (rewards) { + const exitingReward = Number(rewards) * (Number(percent) / 100) + + steps.push({ + action: 'exitingReward', + value: String(exitingReward), + }) + } + + await transactions.checkTxCompletedModal(...steps) + + await unrouteEstimateGas() + } +) diff --git a/e2e/fixtures/swap/actions/unstake.ts b/e2e/fixtures/swap/actions/unstake.ts new file mode 100644 index 00000000..0c01e155 --- /dev/null +++ b/e2e/fixtures/swap/actions/unstake.ts @@ -0,0 +1,29 @@ +import { createTab } from '../tab' +import { createInput } from '../input' +import { createSubmit } from '../submit' + + +export type Unstake = (amount?: string) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createUnstake: Wrapper = ({ transactions, page }) => ( + async (amount?: string) => { + const tab = createTab({ page }) + const input = createInput({ page }) + const submit = createSubmit({ page, transactions }) + + await tab('unstake', true) + + await input.fill(amount) + + const inputAmount = await input.value() + + await submit() + + await transactions.checkTxCompletedModal({ + action: 'exitQueue', + value: inputAmount, + }) + } +) diff --git a/e2e/fixtures/swap/checkConnectButton.ts b/e2e/fixtures/swap/helpers/checkConnectButton.ts similarity index 100% rename from e2e/fixtures/swap/checkConnectButton.ts rename to e2e/fixtures/swap/helpers/checkConnectButton.ts diff --git a/e2e/fixtures/swap/checkSubmitButton.ts b/e2e/fixtures/swap/helpers/checkSubmitButton.ts similarity index 100% rename from e2e/fixtures/swap/checkSubmitButton.ts rename to e2e/fixtures/swap/helpers/checkSubmitButton.ts diff --git a/e2e/fixtures/swap/checkSwapRender.ts b/e2e/fixtures/swap/helpers/checkSwapRender.ts similarity index 100% rename from e2e/fixtures/swap/checkSwapRender.ts rename to e2e/fixtures/swap/helpers/checkSwapRender.ts diff --git a/e2e/fixtures/swap/helpers/checkTokenDropdown.ts b/e2e/fixtures/swap/helpers/checkTokenDropdown.ts new file mode 100644 index 00000000..a02e60e2 --- /dev/null +++ b/e2e/fixtures/swap/helpers/checkTokenDropdown.ts @@ -0,0 +1,31 @@ +type Wrapper = E2E.FixtureMethod + +export type CheckTokenDropdown = (network?: string) => Promise + +export const createCheckTokenDropdown: Wrapper = ({ page, element }) => ( + async (network?: string) => { + const stakeToken = network === 'gnosis' ? 'GNO' : 'ETH' + + await page.getByTestId('amount-input-token').click() + + await page.waitForLoadState('networkidle') + + await element.checkVisibility({ testId: 'token-select-input' }) + await element.checkVisibility({ testId: 'token-select-option-USDT' }) + + await page.getByTestId('token-select-option-USDT').click() + + await element.checkText({ testId: 'amount-input-token', expectedText: 'USDT' }) + + await page.getByTestId('amount-input-token').click() + + await page.waitForLoadState('networkidle') + + await page.getByTestId('token-select-input').fill(stakeToken) + + await element.checkVisibility({ testId: `token-select-option-${stakeToken}` }) + await element.checkVisibility({ testId: 'token-select-option-USDT', isVisible: false }) + + await page.getByTestId(`token-select-option-${stakeToken}`).click() + } +) diff --git a/e2e/fixtures/swap/getBaseInfoItem.ts b/e2e/fixtures/swap/helpers/getBaseInfoItem.ts similarity index 79% rename from e2e/fixtures/swap/getBaseInfoItem.ts rename to e2e/fixtures/swap/helpers/getBaseInfoItem.ts index 2997d5be..eaa033e8 100644 --- a/e2e/fixtures/swap/getBaseInfoItem.ts +++ b/e2e/fixtures/swap/helpers/getBaseInfoItem.ts @@ -2,7 +2,7 @@ type Wrapper = E2E.FixtureMethod export type GetBaseInfoItem = (type: BaseInfoTypes) => Promise -type BaseInfoTypes = 'os-token-apy' | 'max-boost-apy' | 'stake-tvl' | 'stake-users' | 'vault-apy' +type BaseInfoTypes = 'max-boost-apy' | 'stake-tvl' | 'vault-apy' export const createGetBaseInfoItem: Wrapper = ({ page }) => ( async (type: BaseInfoTypes) => { diff --git a/e2e/fixtures/swap/helpers/getSwapInfoItem.ts b/e2e/fixtures/swap/helpers/getSwapInfoItem.ts index fb715734..ece44f78 100644 --- a/e2e/fixtures/swap/helpers/getSwapInfoItem.ts +++ b/e2e/fixtures/swap/helpers/getSwapInfoItem.ts @@ -4,10 +4,17 @@ export type GetSwapInfoItem = (type: SwapInfoTypes) => Promise type SwapInfoTypes = ( | 'gas' + | 'rate' + | 'fee' | 'apy-prev' | 'apy-next' | 'asset-prev' | 'asset-next' + | 'shares-prev' + | 'shares-next' + | 'unstake-queue' + | 'exiting-shares' + | 'exiting-rewards' ) export const createGetSwapInfoItem: Wrapper = ({ page }) => ( diff --git a/e2e/fixtures/swap/helpers/index.ts b/e2e/fixtures/swap/helpers/index.ts index ae8c1d6e..d5ec4551 100644 --- a/e2e/fixtures/swap/helpers/index.ts +++ b/e2e/fixtures/swap/helpers/index.ts @@ -1,2 +1,17 @@ export { createGetSwapInfoItem } from './getSwapInfoItem' export type { GetSwapInfoItem } from './getSwapInfoItem' + +export { createGetBaseInfoItem } from './getBaseInfoItem' +export type { GetBaseInfoItem } from './getBaseInfoItem' + +export { createCheckTokenDropdown } from './checkTokenDropdown' +export type { CheckTokenDropdown } from './checkTokenDropdown' + +export { createCheckSwapRender } from './checkSwapRender' +export type { CheckSwapRender } from './checkSwapRender' + +export { createCheckSubmitButton } from './checkSubmitButton' +export type { CheckSubmitButton } from './checkSubmitButton' + +export { createCheckConnectButton } from './checkConnectButton' +export type { CheckConnectButton } from './checkConnectButton' diff --git a/e2e/fixtures/swap/index.ts b/e2e/fixtures/swap/index.ts index 3e91fecf..56e879be 100644 --- a/e2e/fixtures/swap/index.ts +++ b/e2e/fixtures/swap/index.ts @@ -1,56 +1,109 @@ import { createTab, Tab } from './tab' import { createInput, Input } from './input' import { createSubmit, Submit } from './submit' -import { createMockApy, MockApy } from './mockApy' import { createOpenPage, OpenPage } from './openPage' -import { createSetSwapStats, SetSwapStats } from './setSwapStats' import { createSubmitAmount, SubmitAmount } from './submitAmount' -import { createMockPosition, MockPosition } from './mockPosition' -import { createCheckSwapRender, CheckSwapRender } from './checkSwapRender' -import { createGetBaseInfoItem, GetBaseInfoItem } from './getBaseInfoItem' -import { createCheckSubmitButton, CheckSubmitButton } from './checkSubmitButton' -import { createCheckConnectButton, CheckConnectButton } from './checkConnectButton' +import { createSetSdkTransactions, SetSdkTransactions } from './setSdkTransactions' -import { createGetSwapInfoItem } from './helpers' -import type { GetSwapInfoItem } from './helpers' +import { + createMint, + createBurn, + createBoost, + createStake, + createUnstake, + createUnboost, +} from './actions' +import type { Stake, Unstake, Boost, Burn, Unboost, Mint } from './actions' + +import { createPosition, createSwapStats, createBoostInfo } from './mocks' +import type { Position, SwapStats, BoostInfo } from './mocks' + +import { + createGetBaseInfoItem, + createGetSwapInfoItem, + createCheckSwapRender, + createCheckSubmitButton, + createCheckConnectButton, + createCheckTokenDropdown, +} from './helpers' +import type { + CheckSwapRender, + GetBaseInfoItem, + GetSwapInfoItem, + CheckSubmitButton, + CheckTokenDropdown, + CheckConnectButton, +} from './helpers' export type SwapFixture = { tab: Tab input: Input submit: Submit - mockApy: MockApy openPage: OpenPage - setSwapStats: SetSwapStats - mockPosition: MockPosition submitAmount: SubmitAmount - checkSwapRender: CheckSwapRender - getBaseInfoItem: GetBaseInfoItem - checkSubmitButton: CheckSubmitButton - checkConnectButton: CheckConnectButton + + setSdkTransactions: SetSdkTransactions + + actions: { + burn: Burn + mint: Mint + boost: Boost + stake: Stake + unstake: Unstake + unboost: Unboost + } + + mocks: { + position: Position + swapStats: SwapStats + boostInfo: BoostInfo + } helpers: { getSwapInfoItem: GetSwapInfoItem + getBaseInfoItem: GetBaseInfoItem + + checkSwapRender: CheckSwapRender + checkSubmitButton: CheckSubmitButton + checkConnectButton: CheckConnectButton + checkTokenDropdown: CheckTokenDropdown } } -const swap: E2E.Fixture = async ({ page, graphql, transactions, element, user }, use) => { +const swap: E2E.Fixture = async ({ page, graphql, transactions, element, user, api, sdk, vault, helpers }, use) => { await use({ tab: createTab({ page }), input: createInput({ page }), openPage: createOpenPage({ page }), - mockApy: createMockApy({ graphql }), - setSwapStats: createSetSwapStats({ page }), submitAmount: createSubmitAmount({ page }), submit: createSubmit({ page, transactions }), - mockPosition: createMockPosition({ user }), - checkSwapRender: createCheckSwapRender({ page }), - getBaseInfoItem: createGetBaseInfoItem({ page }), - checkConnectButton: createCheckConnectButton({ page }), - checkSubmitButton: createCheckSubmitButton({ page, element }), + + setSdkTransactions: createSetSdkTransactions({ page, sdk, graphql }), + + actions: { + unstake: createUnstake({ page, transactions }), + boost: createBoost({ page, transactions, api }), + stake: createStake({ page, transactions, user }), + burn: createBurn({ page, transactions, helpers }), + unboost: createUnboost({ page, transactions, api }), + mint: createMint({ page, transactions, helpers,user }), + }, + + mocks: { + position: createPosition({ user }), + swapStats: createSwapStats({ page }), + boostInfo: createBoostInfo({ graphql, vault }), + }, helpers: { getSwapInfoItem: createGetSwapInfoItem({ page }), + getBaseInfoItem: createGetBaseInfoItem({ page }), + + checkSwapRender: createCheckSwapRender({ page }), + checkConnectButton: createCheckConnectButton({ page }), + checkSubmitButton: createCheckSubmitButton({ page, element }), + checkTokenDropdown: createCheckTokenDropdown({ page, element }), }, }) } diff --git a/e2e/fixtures/swap/mockApy.ts b/e2e/fixtures/swap/mockApy.ts deleted file mode 100644 index 9b5ae83b..00000000 --- a/e2e/fixtures/swap/mockApy.ts +++ /dev/null @@ -1,43 +0,0 @@ -type Wrapper = E2E.FixtureMethod - -type Input = { - userApy?: string - vaultApy?: string - isProfitable?: boolean -} - -type Output = { - userApy: number - vaultApy: number - osTokenApy: number - feePercent: number - maxBoostApy: number -} - -export type MockApy = (values: Input) => Promise - -export const createMockApy: Wrapper = ({ graphql }) => ( - async (values: Input) => { - const { userApy = '1.99', vaultApy = '1.25', isProfitable } = values - - const feePercent = 100 - const osTokenApy = '2.05' - const maxBoostApy = isProfitable ? '4.22' : osTokenApy - - await graphql.mockSwapApy({ - userApy, - vaultApy, - osTokenApy, - maxBoostApy, - feePercent, - }) - - return { - feePercent, - userApy: Number(userApy), - vaultApy: Number(vaultApy), - osTokenApy: Number(osTokenApy), - maxBoostApy: Number(maxBoostApy), - } - } -) diff --git a/e2e/fixtures/swap/mocks/boostInfo.ts b/e2e/fixtures/swap/mocks/boostInfo.ts new file mode 100644 index 00000000..1265a7ca --- /dev/null +++ b/e2e/fixtures/swap/mocks/boostInfo.ts @@ -0,0 +1,24 @@ +import type { BoostSupplyCapsQueryPayload } from 'helpers/requests/fetchBoostSupplyCaps' + +import * as constants from '../../../constants' + + +export type BoostInfo = () => Promise + +type Wrapper = E2E.FixtureMethod + +export const createBoostInfo: Wrapper = ({ graphql, vault }) => ( + async () => { + await vault.setVaultData() + + await graphql.mockCustomData({ + name: 'BoostSupplyCaps', + data: { + aave: { + osTokenTotalSupplied: '0', + osTokenSupplyCap: String(constants.maxUint256), + }, + }, + }) + } +) diff --git a/e2e/fixtures/swap/mocks/index.ts b/e2e/fixtures/swap/mocks/index.ts new file mode 100644 index 00000000..7e9dad37 --- /dev/null +++ b/e2e/fixtures/swap/mocks/index.ts @@ -0,0 +1,8 @@ +export { createPosition } from './position' +export type { Position } from './position' + +export { createSwapStats } from './swapStats' +export type { SwapStats } from './swapStats' + +export { createBoostInfo } from './boostInfo' +export type { BoostInfo } from './boostInfo' diff --git a/e2e/fixtures/swap/mockPosition.ts b/e2e/fixtures/swap/mocks/position.ts similarity index 75% rename from e2e/fixtures/swap/mockPosition.ts rename to e2e/fixtures/swap/mocks/position.ts index b2917452..45c216c2 100644 --- a/e2e/fixtures/swap/mockPosition.ts +++ b/e2e/fixtures/swap/mocks/position.ts @@ -1,5 +1,3 @@ -type Wrapper = E2E.FixtureMethod - type Input = { isClaimable: boolean } @@ -9,9 +7,11 @@ type Output = { exitingRewards: number } -export type MockPosition = (values: Input) => Promise +export type Position = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod -export const createMockPosition: Wrapper = ({ user }) => ( +export const createPosition: Wrapper = ({ user }) => ( async (values: Input) => { const { isClaimable } = values diff --git a/e2e/fixtures/swap/setSwapStats.ts b/e2e/fixtures/swap/mocks/swapStats.ts similarity index 61% rename from e2e/fixtures/swap/setSwapStats.ts rename to e2e/fixtures/swap/mocks/swapStats.ts index 8581e34f..ef01c8df 100644 --- a/e2e/fixtures/swap/setSwapStats.ts +++ b/e2e/fixtures/swap/mocks/swapStats.ts @@ -3,11 +3,11 @@ type Output = { totalEarnedAssets: string } -export type SetSwapStats = () => Promise +export type SwapStats = () => Promise -type Wrapper = E2E.FixtureMethod +type Wrapper = E2E.FixtureMethod -export const createSetSwapStats: Wrapper = ({ page }) => ( +export const createSwapStats: Wrapper = ({ page }) => ( async () => { const stats = { totalAssets: '100000000000000000000', @@ -17,7 +17,7 @@ export const createSetSwapStats: Wrapper = ({ page }) => ( await page.addInitScript((payload) => { window.e2e = { ...window.e2e, - ['fixtures/swap/setSwapStats']: payload, + ['fixtures/swap/mocks/swapStats']: payload, } }, stats) diff --git a/e2e/fixtures/swap/setSdkTransactions.ts b/e2e/fixtures/swap/setSdkTransactions.ts new file mode 100644 index 00000000..88a40875 --- /dev/null +++ b/e2e/fixtures/swap/setSdkTransactions.ts @@ -0,0 +1,38 @@ +import * as constants from '../../constants' + + +type Input = { + mint?: string + deposit: string +} + +export type SetSdkTransactions = (values: Input) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetSdkTransactions: Wrapper = ({ page, sdk, graphql }) => ( + async (values: Input) => { + const { deposit, mint } = values + + let shares + + await sdk.deposit({ + vaultAddress: constants.genesisAddress.mainnet, + assets: deposit, + }) + + if (mint) { + shares = await sdk.mint({ + vaultAddress: constants.genesisAddress.mainnet, + assets: mint, + }) + } + + await graphql.mockAllocatorsData(deposit) + + await page.reload() + await page.waitForLoadState('networkidle') + + return shares + } +) diff --git a/e2e/fixtures/swap/submit.ts b/e2e/fixtures/swap/submit.ts index 6940cb13..45a6787b 100644 --- a/e2e/fixtures/swap/submit.ts +++ b/e2e/fixtures/swap/submit.ts @@ -1,16 +1,9 @@ -import { createInput } from './input' - - type Wrapper = E2E.FixtureMethod -export type Submit = (amount?: string) => Promise +export type Submit = () => Promise export const createSubmit: Wrapper = ({ page, transactions }) => ( - async (amount?: string) => { - const input = createInput({ page }) - - await input.fill(amount) - + async () => { const submitButton = await page.waitForSelector('[data-testid="submit-button"]') await submitButton.waitForElementState('enabled') diff --git a/e2e/fixtures/swap/submitAmount.ts b/e2e/fixtures/swap/submitAmount.ts index ac17a221..1ab1cf1a 100644 --- a/e2e/fixtures/swap/submitAmount.ts +++ b/e2e/fixtures/swap/submitAmount.ts @@ -1,16 +1,9 @@ -import { createInput } from './input' - - type Wrapper = E2E.FixtureMethod -export type SubmitAmount = (amount?: string) => Promise +export type SubmitAmount = () => Promise export const createSubmitAmount: Wrapper = ({ page }) => ( - async (amount?: string) => { - const input = createInput({ page }) - - await input.fill(amount) - + async () => { const submitButton = await page.waitForSelector('[data-testid="submit-button"]') await submitButton.waitForElementState('enabled') diff --git a/e2e/fixtures/transactions/checkTransactionsFlow.ts b/e2e/fixtures/transactions/checkTransactionsFlow.ts new file mode 100644 index 00000000..f81897a5 --- /dev/null +++ b/e2e/fixtures/transactions/checkTransactionsFlow.ts @@ -0,0 +1,56 @@ +import { expect } from '@playwright/test' + + +type Wrapper = E2E.FixtureMethod + +type Input = { + flow: 'boost' | 'unboost' + token?: string + availableSteps?: string[] + checkNotifications?: boolean +} + +export type CheckTransactionsFlow = (values: Input) => Promise + +const steps = { + unboost: [ 'upgrade', 'unboost' ], + boost: [ 'boostUpgrade', 'permit', 'boost' ], +} + +const titles = { + boost: 'Boost', + unboost: 'Unboost', + permit: 'Approve osETH', + upgrade: 'Upgrade leverage strategy contract', + boostUpgrade: 'Upgrade leverage strategy contract', +} + +export const createCheckTransactionsFlow: Wrapper = ({ page, helpers }) => ( + async ({ flow, token, availableSteps, checkNotifications = true }: Input) => { + const flowSteps = availableSteps || steps[flow] + + await page.waitForSelector('[data-testid="transactions-flow-modal"]') + + const stepTitles = await Promise.all( + flowSteps.map((step) => page.getByTestId(`step-${step}-title`).textContent()) + ) + + const expectedTitles = flowSteps.map((step) => { + const expectedTitle = titles[step as keyof typeof titles] + + if (token) { + return expectedTitle.replace('osETH', token) + } + + return expectedTitle + }) + + expect(stepTitles).toEqual(expectedTitles) + + if (checkNotifications) { + for (const _step of flowSteps) { + await helpers.checkNotification('Transaction has been confirmed') + } + } + } +) diff --git a/e2e/fixtures/transactions/checkTxCompletedModal.ts b/e2e/fixtures/transactions/checkTxCompletedModal.ts index da3d78c9..4d45999d 100644 --- a/e2e/fixtures/transactions/checkTxCompletedModal.ts +++ b/e2e/fixtures/transactions/checkTxCompletedModal.ts @@ -3,9 +3,19 @@ import { expect } from '@playwright/test' type Wrapper = E2E.FixtureMethod -type Action = 'burn' | 'mint' | 'stake' | 'boost' | 'unstake' | 'exiting' | 'received' | 'redeemed' | 'exitQueue' | 'claimReward' - -type TransactionItem = { +type Action = 'burn' + | 'mint' + | 'stake' + | 'boost' + | 'unstake' + | 'exiting' + | 'received' + | 'redeemed' + | 'exitQueue' + | 'claimReward' + | 'exitingReward' + +export type TransactionItem = { action: Action value?: string more?: string @@ -13,27 +23,28 @@ type TransactionItem = { startsWith?: string } -export type CheckTxCompletedModal = (firstTransactionItem: TransactionItem, secondTransactionItem?: TransactionItem) => Promise +export type CheckTxCompletedModal = (...transactionItems: TransactionItem[]) => Promise const texts = { burn: 'osETH burned', mint: 'osETH minted', stake: 'ETH staked', boost: 'osETH boosted', - unstake: 'osETH unstaked', exiting: 'Exiting osETH', received: 'ETH received', redeemed: 'ETH redeemed', - exitQueue: 'Added to unstake queue', + unstake: 'osETH unstaked', claimReward: 'ETH unstaked', + exitingReward: 'Exiting ETH', + exitQueue: 'Added to unstake queue', } export const createCheckTxCompletedModal: Wrapper = ({ page }) => ( - async (firstTransactionItem, secondTransactionItem) => { + async (...transactionItems) => { await page.waitForSelector('[data-testid="transaction-modal"]') - const items = [ firstTransactionItem, secondTransactionItem ].filter(Boolean) as TransactionItem[] + const items = transactionItems.filter(Boolean) as TransactionItem[] for (const item of items) { const { action, value, more, less, startsWith } = item diff --git a/e2e/fixtures/transactions/index.ts b/e2e/fixtures/transactions/index.ts index 6b38d1ed..b0c401f6 100644 --- a/e2e/fixtures/transactions/index.ts +++ b/e2e/fixtures/transactions/index.ts @@ -1,15 +1,18 @@ import { createCheckTransaction, CheckTransaction } from './checkTransaction' import { createCheckTxCompletedModal, CheckTxCompletedModal } from './checkTxCompletedModal' +import { createCheckTransactionsFlow, CheckTransactionsFlow } from './checkTransactionsFlow' export type TransactionsFixture = { checkTransaction: CheckTransaction checkTxCompletedModal: CheckTxCompletedModal + checkTransactionsFlow: CheckTransactionsFlow } const transactions: E2E.Fixture = async ({ element, page, helpers, graphql }, use) => { await use({ checkTransaction: createCheckTransaction({ helpers, element, graphql }), + checkTransactionsFlow: createCheckTransactionsFlow({ page, helpers }), checkTxCompletedModal: createCheckTxCompletedModal({ page }), }) } diff --git a/e2e/fixtures/user/balances/index.ts b/e2e/fixtures/user/balances/index.ts index 381f8ae6..3475482a 100644 --- a/e2e/fixtures/user/balances/index.ts +++ b/e2e/fixtures/user/balances/index.ts @@ -1,5 +1,14 @@ +export { createSetUserApy } from './setUserApy' +export type { SetUserApy } from './setUserApy' + export { createSetBoostData } from './setBoostData' export type { SetBoostData } from './setBoostData' +export { createSetStakeBalance } from './setStakeBalance' +export type { SetStakeBalance } from './setStakeBalance' + export { createSetMintTokenData } from './setMintTokenData' export type { SetMintTokenData } from './setMintTokenData' + +export { createSetMaxWithdrawAssets } from './setMaxWithdrawAssets' +export type { SetMaxWithdrawAssets } from './setMaxWithdrawAssets' diff --git a/e2e/fixtures/user/balances/setMaxWithdrawAssets.ts b/e2e/fixtures/user/balances/setMaxWithdrawAssets.ts new file mode 100644 index 00000000..50675249 --- /dev/null +++ b/e2e/fixtures/user/balances/setMaxWithdrawAssets.ts @@ -0,0 +1,22 @@ +import { parseEther } from 'ethers' + + +type Output = Store['vault']['user']['balances']['maxWithdrawAssets'] + +export type SetMaxWithdrawAssets = (asset: string) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetMaxWithdrawAssets: Wrapper = ({ page }) => ( + async (asset: string) => { + + const data: Output = parseEther(asset) + + await page.evaluate((payload) => { + window.e2e = { + ...window.e2e, + ['user/balances/setMaxWithdrawAssets']: payload, + } + }, data) + } +) diff --git a/e2e/fixtures/user/balances/setMintTokenData.ts b/e2e/fixtures/user/balances/setMintTokenData.ts index c57638f5..15909068 100644 --- a/e2e/fixtures/user/balances/setMintTokenData.ts +++ b/e2e/fixtures/user/balances/setMintTokenData.ts @@ -40,7 +40,6 @@ export const createSetMintTokenData: Wrapper = ({ page }) => ( } const result: Output = { - ...payload, mintedAssets, maxMintShares, isDisabled: false, diff --git a/e2e/fixtures/user/balances/setStakeBalance.ts b/e2e/fixtures/user/balances/setStakeBalance.ts new file mode 100644 index 00000000..f1b492dc --- /dev/null +++ b/e2e/fixtures/user/balances/setStakeBalance.ts @@ -0,0 +1,34 @@ +import { parseEther } from 'ethers' +import * as constants from '../../../constants' + + +type Output = Pick + +export type SetStakeBalance = (amount: string) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetStakeBalance: Wrapper = ({ page }) => ( + async (amount: string) => { + const assets = Number(amount) ? parseEther(amount) : 0n + + const data: Output = { + totalEarnedAssets: parseEther('220'), + totalBoostEarnedAssets: parseEther('190'), + totalStakeEarnedAssets: parseEther('100'), + stakedAssets: assets > constants.minimalAmount ? assets : 0n, + } + + await page.evaluate((payload) => { + window.e2e = { + ...window.e2e, + ['user/balances/setStakeBalance']: payload, + } + }, data) + } +) diff --git a/e2e/fixtures/user/balances/setUserApy.ts b/e2e/fixtures/user/balances/setUserApy.ts new file mode 100644 index 00000000..b2721d9d --- /dev/null +++ b/e2e/fixtures/user/balances/setUserApy.ts @@ -0,0 +1,14 @@ +export type SetUserApy = (apy: number) => Promise + +type Wrapper = E2E.FixtureMethod + +export const createSetUserApy: Wrapper = ({ page }) => ( + async (apy: number) => { + await page.addInitScript((payload) => { + window.e2e = { + ...window.e2e, + ['user/balances/setUserApy']: payload, + } + }, apy) + } +) diff --git a/e2e/fixtures/user/index.ts b/e2e/fixtures/user/index.ts index 98ea2911..e1ce4527 100644 --- a/e2e/fixtures/user/index.ts +++ b/e2e/fixtures/user/index.ts @@ -11,13 +11,19 @@ import { createSetUserStats } from './setUserStats' import type { SetUserStats } from './setUserStats' import { + createSetUserApy, createSetBoostData, + createSetStakeBalance, createSetMintTokenData, + createSetMaxWithdrawAssets, } from './balances' import type { + SetUserApy, SetBoostData, + SetStakeBalance, SetMintTokenData, + SetMaxWithdrawAssets, } from './balances' @@ -28,8 +34,11 @@ export type UserFixture = { checkExportRewards: CheckExportRewards balances: { + setUserApy: SetUserApy setBoostData: SetBoostData + setStakeBalance: SetStakeBalance setMintTokenData: SetMintTokenData + setMaxWithdrawAssets: SetMaxWithdrawAssets }, } @@ -41,8 +50,11 @@ const user: E2E.Fixture = async ({ page, helpers }, use) => { checkExportRewards: createCheckExportRewards({ page, helpers }), balances: { + setUserApy: createSetUserApy({ page }), setBoostData: createSetBoostData({ page }), + setStakeBalance: createSetStakeBalance({ page }), setMintTokenData: createSetMintTokenData({ page }), + setMaxWithdrawAssets: createSetMaxWithdrawAssets({ page }), }, }) } diff --git a/e2e/tests/balance.spec.ts b/e2e/tests/balance.spec.ts index 3447deaf..922170d7 100644 --- a/e2e/tests/balance.spec.ts +++ b/e2e/tests/balance.spec.ts @@ -10,7 +10,7 @@ test('Enabled claim', async ({ wallet, swap, queue, user }) => { await swap.openPage() const { exitedAssets, totalAssets } = await user.setUnstakeQueue({ isClaimable: true }) - const { exitingShares, exitingRewards } = await swap.mockPosition({ isClaimable: true }) + const { exitingShares, exitingRewards } = await swap.mocks.position({ isClaimable: true }) await wallet.connectWithBalance({ ETH: '10' }) await swap.tab('balance') @@ -31,7 +31,7 @@ test('Enabled claim', async ({ wallet, swap, queue, user }) => { test('Disabled claim', async ({ wallet, swap, user, queue }) => { await swap.openPage() - const { exitingShares, exitingRewards } = await swap.mockPosition({ isClaimable: false }) + const { exitingShares, exitingRewards } = await swap.mocks.position({ isClaimable: false }) const { exitedAssets, totalAssets } = await user.setUnstakeQueue({ isClaimable: false }) await wallet.connectWithBalance({ ETH: '10' }) @@ -68,7 +68,7 @@ test('Chart', async ({ swap, page, element }) => { test('Export rewards', async ({ wallet, swap, user, page }) => { await swap.openPage() - await swap.mockPosition({ isClaimable: false }) + await swap.mocks.position({ isClaimable: false }) await wallet.connectWithBalance({ ETH: '10' }) await swap.tab('balance') diff --git a/e2e/tests/boost.spec.ts b/e2e/tests/boost.spec.ts index 29047d11..57ae21f7 100644 --- a/e2e/tests/boost.spec.ts +++ b/e2e/tests/boost.spec.ts @@ -1,6 +1,5 @@ import { expect } from '@playwright/test' -import * as constants from '../constants' import test from '../extendTest' @@ -12,10 +11,10 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() await swap.tab('boost') - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() }) -test('Max balance', async ({ swap, wallet, page, sdk }) => { +test('Max balance', async ({ swap, wallet }) => { const initialETH = '25' const depositETH = '20' const mintETH = '15' @@ -24,17 +23,8 @@ test('Max balance', async ({ swap, wallet, page, sdk }) => { await wallet.connectWithBalance({ ETH: initialETH }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - - const shares = await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: mintETH, - }) + const shares = await swap.setSdkTransactions({ deposit: depositETH, mint: mintETH }) - await page.reload() await swap.tab('boost') await swap.input.fill() @@ -44,7 +34,7 @@ test('Max balance', async ({ swap, wallet, page, sdk }) => { expect(value).toEqual(shares) }) -test('Boost info', async ({ swap, page, wallet, sdk }) => { +test('Boost info', async ({ swap, page, wallet }) => { const initialETH = '25' const depositETH = '20' const mintETH = '15' @@ -52,15 +42,7 @@ test('Boost info', async ({ swap, page, wallet, sdk }) => { await swap.openPage() await wallet.connectWithBalance({ ETH: initialETH }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - - await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: mintETH, - }) + await swap.setSdkTransactions({ deposit: depositETH, mint: mintETH }) await page.reload() await swap.tab('boost') @@ -92,20 +74,20 @@ test('Boost not profitable', async ({ user, wallet, swap, vault }) => { await swap.tab('boost') await swap.input.fill('1') - await swap.checkSubmitButton({ text: 'Not profitable', isLoading: false, isDisabled: true }) + await swap.helpers.checkSubmitButton({ text: 'Not profitable', isLoading: false, isDisabled: true }) }) test('Boost disabled', async ({ wallet, swap, page, element }) => { await swap.openPage() - await swap.mockPosition({ isClaimable: false }) + await swap.mocks.position({ isClaimable: false }) await wallet.connectWithBalance({ ETH: '50' }) await swap.tab('boost') - await swap.checkSubmitButton({ isDisabled: true }) + await swap.helpers.checkSubmitButton({ isDisabled: true }) await element.checkVisibility({ testId: 'exit-queue-note', isVisible: true }) - await swap.mockPosition({ isClaimable: false }) + await swap.mocks.position({ isClaimable: false }) await page.getByTestId('balances-link').click() await element.checkVisibility({ testId: 'unboost-queue-claim-button', isVisible: true }) @@ -114,3 +96,33 @@ test('Boost disabled', async ({ wallet, swap, page, element }) => { expect(claimButton).toBeDisabled() }) + +test('Boost with permit', async ({ wallet, swap, user }) => { + const amount = '10' + + await swap.mocks.boostInfo() + + await swap.openPage('skipSSR=true') + + await user.balances.setMintTokenData({ stakedAssets: amount, mintedShares: amount }) + + await wallet.connectWithBalance({ ETH: amount, osETH: amount }) + + await swap.actions.boost({ amount: '1' }) +}) + +test('Boost with upgrade', async ({ wallet, swap, user }) => { + const amount = '10' + + await swap.mocks.boostInfo() + + await swap.openPage('skipSSR=true') + + await user.balances.setMintTokenData({ stakedAssets: amount, mintedShares: amount }) + + await user.balances.setBoostData({ shares: '1' }) + + await wallet.connectWithBalance({ ETH: amount, osETH: amount }) + + await swap.actions.boost({ amount: '1', withUpgrade: true }) +}) diff --git a/e2e/tests/burn.spec.ts b/e2e/tests/burn.spec.ts index 7478a363..891c4213 100644 --- a/e2e/tests/burn.spec.ts +++ b/e2e/tests/burn.spec.ts @@ -1,11 +1,8 @@ import { expect } from '@playwright/test' -import * as constants from '../constants' import test from '../extendTest' -const format = (value: string) => Number(value.replace(/[^\d.]/g, '')) - test.beforeEach(async ({ gui, guardian }) => { await guardian.fixProvider() await gui.initializeChain(1) @@ -14,28 +11,14 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() await swap.tab('burn', true) - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() }) -test('Max burn', async ({ swap, wallet, graphql, sdk, page }) => { - const initialETH = '25' - const depositETH = '20' - const mintETH = '5' - +test('Max burn', async ({ swap, wallet }) => { await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - const shares = await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: mintETH, - }) + await wallet.connectWithBalance({ ETH: '50' }) - await graphql.mockAllocatorsData(depositETH) - await page.reload() + const shares = await swap.setSdkTransactions({ deposit: '20', mint: '5' }) await swap.tab('burn', true) @@ -46,84 +29,56 @@ test('Max burn', async ({ swap, wallet, graphql, sdk, page }) => { expect(value).toEqual(shares) }) -test('Burn info', async ({ wallet, swap, graphql, page, sdk }) => { - const initialETH = '25' - const depositETH = '20' +test('Burn info', async ({ wallet, swap, user, helpers }) => { + const userAPY = 5 await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) + await wallet.connectWithBalance({ ETH: '50' }) - await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: '15', - }) + await user.balances.setUserApy(userAPY) - await graphql.mockAllocatorsData(depositETH) - await page.reload() + const shares = await swap.setSdkTransactions({ deposit: '20', mint: '5' }) await swap.tab('burn', true) await swap.input.fill() - const getPositionInfoItem = async (type: string) => { - const selector = await page.waitForSelector(`[data-testid="position-${type}"]`) - const value = await selector.textContent() - - return value || '' - } - const [ mintToken, - assetsNext, - apy, - gas, + apyPrev, + apyNext, + sharesPrev, + sharesNext, ] = await Promise.all([ swap.input.token(), - getPositionInfoItem('shares-next'), - getPositionInfoItem('apy-next'), - getPositionInfoItem('value-prev'), + swap.helpers.getSwapInfoItem('apy-prev'), + swap.helpers.getSwapInfoItem('apy-next'), + swap.helpers.getSwapInfoItem('shares-prev'), + swap.helpers.getSwapInfoItem('shares-next'), ]) expect(mintToken).toEqual('osETH') - expect(format(assetsNext)).toEqual(0) - expect(format(apy)).toBeGreaterThan(0) - expect(format(gas)).toBeGreaterThanOrEqual(0) + expect(parseFloat(apyPrev)).toEqual(userAPY) + expect(parseFloat(apyNext)).toBeLessThan(userAPY) + expect(parseFloat(sharesPrev)).toEqual(Number(helpers.formatTokenValue(shares || ''))) + expect(parseFloat(sharesNext)).toEqual(0) }) -test('Burn', async ({ wallet, swap, graphql, page, sdk, transactions }) => { - const initialETH = '25' - const depositETH = '20' - +test('Burn 15', async ({ wallet, swap }) => { await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) + await wallet.connectWithBalance({ ETH: '50' }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) + const shares = await swap.setSdkTransactions({ deposit: '20', mint: '15' }) - const shares = await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: '15', - }) - - await graphql.mockAllocatorsData(depositETH) - await page.reload() - - await swap.tab('burn', true) + await swap.actions.burn(shares) +}) - await swap.submitAmount() +test('Burn max', async ({ wallet, swap }) => { + await swap.openPage() + await wallet.connectWithBalance({ ETH: '50' }) - await page.waitForSelector('text=Processing transaction') - await graphql.mockTransaction() + await swap.setSdkTransactions({ deposit: '20', mint: '15' }) - await transactions.checkTxCompletedModal({ - action: 'burn', - startsWith: shares, - }) + await swap.actions.burn() }) diff --git a/e2e/tests/loader.spec.ts b/e2e/tests/loader.spec.ts index b42c19da..8b8c0ca0 100644 --- a/e2e/tests/loader.spec.ts +++ b/e2e/tests/loader.spec.ts @@ -9,32 +9,32 @@ test('Connect wallet | Disconnect wallet', async ({ page, wallet, swap, gui, gua await gui.initializeChain(1) await page.goto('/') - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.monitorAddress(ZeroAddress) - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.disconnect({ wallet: constants.walletTitles.monitorAddress }) - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.connect() - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.disconnect() - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() }) test('Change chain', async ({ page, wallet, swap, helpers }) => { await page.goto('/') await helpers.changeSelect('network-select', 'gnosis') - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.monitorAddress(ZeroAddress) - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await helpers.changeSelect('network-select', 'mainnet') - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() await wallet.disconnect({ wallet: constants.walletTitles.monitorAddress }) - await swap.checkSwapRender() + await swap.helpers.checkSwapRender() }) diff --git a/e2e/tests/mint.spec.ts b/e2e/tests/mint.spec.ts index 2c14c058..a049b05a 100644 --- a/e2e/tests/mint.spec.ts +++ b/e2e/tests/mint.spec.ts @@ -1,11 +1,8 @@ import { expect } from '@playwright/test' -import * as constants from '../constants' import test from '../extendTest' -const format = (value: string) => Number(value.replace(/[^\d.]/g, '')) - test.beforeEach(async ({ gui, guardian }) => { await guardian.fixProvider() await gui.initializeChain(1) @@ -14,23 +11,17 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() await swap.tab('mint') - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() }) -test('Max mint', async ({ swap, wallet, graphql, sdk }) => { - const amount = '10' +test('Mint info', async ({ wallet, swap, user, helpers }) => { + const userAPY = 3 + await user.balances.setUserApy(userAPY) await swap.openPage() + await wallet.connectWithBalance({ ETH: '100' }) - await graphql.mockAllocatorsData(amount) - await wallet.connectWithBalance({ ETH: amount }) - - const shares = await sdk.getMaxMint({ - vaultAddress: constants.genesisAddress.mainnet, - ltvPercent: '999900000000000000', - mintedAssets: '0', - stakedAssets: amount, - }) + await swap.actions.stake('10') await swap.tab('mint') @@ -38,128 +29,72 @@ test('Max mint', async ({ swap, wallet, graphql, sdk }) => { const value = await swap.input.value() - expect(value).toEqual(shares) -}) - -test('Mint info', async ({ wallet, swap, graphql, page, sdk }) => { - const initialETH = '25' - const depositETH = '20' - - await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - - const shares = await sdk.getMaxMint({ - vaultAddress: constants.genesisAddress.mainnet, - ltvPercent: '999900000000000000', - mintedAssets: '0', - stakedAssets: depositETH, - }) - - await graphql.mockAllocatorsData(depositETH) - await page.reload() - - await swap.tab('mint') - - await swap.input.fill() - - const getPositionInfoItem = async (type: string) => { - const selector = await page.waitForSelector(`[data-testid="position-${type}"]`) - const value = await selector.textContent() - - return value || '' - } - const [ mintToken, - assetsNext, - apy, - gas, + apyPrev, + apyNext, + rate, + fee, + sharesPrev, + sharesNext, ] = await Promise.all([ swap.input.token(), - getPositionInfoItem('shares-next'), - getPositionInfoItem('apy-next'), - getPositionInfoItem('value-prev'), + swap.helpers.getSwapInfoItem('apy-prev'), + swap.helpers.getSwapInfoItem('apy-next'), + swap.helpers.getSwapInfoItem('rate'), + swap.helpers.getSwapInfoItem('fee'), + swap.helpers.getSwapInfoItem('shares-prev'), + swap.helpers.getSwapInfoItem('shares-next'), ]) - expect(mintToken).toEqual('osETH') - expect(shares.startsWith(assetsNext)).toBeTruthy() - expect(format(apy.replace(/.*=/, ''))).toBeGreaterThan(0) - expect(format(gas)).toBeGreaterThanOrEqual(0) -}) - -test('Mint', async ({ wallet, swap, graphql, page, sdk, transactions }) => { - const initialETH = '25' - const depositETH = '20' - - await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) + const mintTokenRate = parseFloat(rate.split('=')[1].split(' ')[1]) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - - const shares = await sdk.getMaxMint({ - vaultAddress: constants.genesisAddress.mainnet, - ltvPercent: '999900000000000000', - mintedAssets: '0', - stakedAssets: depositETH, - }) - - await graphql.mockAllocatorsData(depositETH) - await page.reload() - - await swap.tab('mint') + expect(mintToken).toBe('osETH') + expect(mintTokenRate).toBeGreaterThan(0) - await swap.submitAmount() + expect(parseFloat(apyPrev)).toEqual(userAPY) + expect(parseFloat(apyNext)).toBeLessThan(userAPY) - await page.waitForSelector('text=Processing transaction') - await graphql.mockTransaction() + expect(parseFloat(fee)).toEqual(5) - await transactions.checkTxCompletedModal({ - action: 'mint', - startsWith: shares, - }) + expect(parseFloat(sharesPrev)).toEqual(0) + expect(parseFloat(sharesNext)).toEqual(Number(helpers.formatTokenValue(value || ''))) }) -test('Mint after mint', async ({ wallet, swap, graphql, page, sdk }) => { - const initialETH = '25' - const depositETH = '20' - const mintETH = '5' +test('Mint Max', async ({ wallet, swap }) => { + const stakedAssets = '10' await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) + await wallet.connectWithBalance({ ETH: '100' }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) + await swap.actions.stake(stakedAssets) + await swap.actions.mint({ stakedAssets }) +}) - const shares = await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: mintETH, - }) +test('Mint after mint', async ({ wallet, swap, helpers }) => { + const stakedAssets = '10' + const mintAssets = '5' - const maxMint = await sdk.getMaxMint({ - vaultAddress: constants.genesisAddress.mainnet, - ltvPercent: '999900000000000000', - mintedAssets: shares, - stakedAssets: depositETH, - }) + await swap.openPage() + await wallet.connectWithBalance({ ETH: '50' }) - await graphql.mockAllocatorsData(depositETH) - await page.reload() - await page.waitForLoadState('networkidle') + await swap.actions.stake(stakedAssets) + await swap.actions.mint({ stakedAssets, amount: mintAssets }) await swap.tab('mint') await swap.input.fill() const value = await swap.input.value() - expect(Math.floor(Number(value))).toEqual(Math.floor(Number(maxMint))) + const [ + sharesPrev, + sharesNext, + ] = await Promise.all([ + swap.helpers.getSwapInfoItem('shares-prev'), + swap.helpers.getSwapInfoItem('shares-next'), + ]) + + const totalShares = String(Number(sharesPrev) + Number(value)) + + expect(parseFloat(sharesNext)).toEqual(Number(helpers.formatTokenValue(totalShares))) }) diff --git a/e2e/tests/stake.spec.ts b/e2e/tests/stake.spec.ts index 2f9b1c0d..309756cb 100644 --- a/e2e/tests/stake.spec.ts +++ b/e2e/tests/stake.spec.ts @@ -11,7 +11,15 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() +}) + +test('Token dropdown', async ({ swap, wallet }) => { + await swap.openPage() + + await swap.helpers.checkTokenDropdown() + await wallet.connectWithBalance({ ETH: '100' }) + await swap.helpers.checkTokenDropdown() }) test('Max balance minus gas', async ({ swap, wallet, graphql }) => { @@ -32,7 +40,7 @@ test('Initial info', async ({ swap, vault }) => { const vaultApy = '5' const maxApy = '10' - const { totalAssets } = await swap.setSwapStats() + const { totalAssets } = await swap.mocks.swapStats() await vault.setVaultData({ apy: vaultApy, @@ -46,9 +54,9 @@ test('Initial info', async ({ swap, vault }) => { vaultApyValue, tvl, ] = await Promise.all([ - swap.getBaseInfoItem('max-boost-apy'), - swap.getBaseInfoItem('vault-apy'), - swap.getBaseInfoItem('stake-tvl'), + swap.helpers.getBaseInfoItem('max-boost-apy'), + swap.helpers.getBaseInfoItem('vault-apy'), + swap.helpers.getBaseInfoItem('stake-tvl'), ]) const [ currectTVL ] = formatEther(totalAssets).split('.') @@ -70,18 +78,21 @@ test('Initial info not profitable', async ({ swap, element, vault }) => { await element.checkVisibility({ testId: 'max-boost-apy', isVisible: false }) - const osTokenApyValue = await swap.getBaseInfoItem('vault-apy') + const osTokenApyValue = await swap.helpers.getBaseInfoItem('vault-apy') expect(osTokenApyValue).toEqual(Number(vaultApy)) }) -test('Stake info', async ({ swap, wallet }) => { +test('Stake info', async ({ swap, wallet, user }) => { + const userAPY = 5 const value = 10 await swap.openPage() await wallet.connectWithBalance({ ETH: '100' }) + await user.balances.setUserApy(userAPY) + await swap.input.fill(value.toString()) const [ @@ -101,36 +112,24 @@ test('Stake info', async ({ swap, wallet }) => { ]) expect(stakeToken).toBe('ETH') - expect(parseFloat(apyPrev)).toEqual(0) - expect(parseFloat(apyNext)).toBeGreaterThan(0) + expect(parseFloat(apyPrev)).toEqual(apyPrev) + expect(parseFloat(apyNext)).toBeGreaterThan(userAPY) expect(parseFloat(assetPrev)).toEqual(0) expect(parseFloat(assetNext)).toEqual(value) expect(Number(gas.replace('$ ' , ''))).toBeGreaterThan(0) }) -test('Stake submit', async ({ wallet, swap, transactions }) => { +test('Stake', async ({ wallet, swap }) => { await swap.openPage() await wallet.connectWithBalance({ ETH: '100' }) - await swap.mockApy({ isProfitable: true }) - await swap.submit('50') - - await transactions.checkTxCompletedModal({ - action: 'stake', - value: '50', - }) - - await swap.tab('balance') + await swap.actions.stake('50') }) -test('Stake max', async ({ wallet, swap, transactions }) => { +test('Stake max', async ({ wallet, swap }) => { await swap.openPage() await wallet.connectWithBalance({ ETH: '100' }) - await swap.submit() - await transactions.checkTxCompletedModal({ - action: 'stake', - less: '100', - }) + await swap.actions.stake() }) diff --git a/e2e/tests/unboost.spec.ts b/e2e/tests/unboost.spec.ts index d2eb0900..e5b2b57c 100644 --- a/e2e/tests/unboost.spec.ts +++ b/e2e/tests/unboost.spec.ts @@ -11,59 +11,93 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() await swap.tab('unboost', true) - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() }) -test('Balance percent', async ({ swap, page, wallet, graphql }) => { +test('Balance percent', async ({ swap, page, wallet, user }) => { const shares = '10' + const rewards = '1' await swap.openPage() - await graphql.mockBoostData({ shares: '10' }) - const userAPY = '1.55' + await user.balances.setBoostData({ shares, rewards }) - await graphql.mockUserApy(userAPY) await wallet.connectWithBalance({ ETH: '1' }) await swap.tab('unboost', true) const percents = [ '25', '50', '75', '100' ] - const formatValue = (value: string, percent: string) => ( - Number(value) / 100 * Number(percent) - ) - for (const percent of percents) { await page.getByTestId(`percent-${percent}`).click() const [ value, exitingShares, + exitingRewards, ] = await Promise.all([ swap.input.value(), - swap.getSwapInfoItem('boosted-shares-next', 'position'), + swap.helpers.getSwapInfoItem('exiting-shares'), + swap.helpers.getSwapInfoItem('exiting-rewards'), ]) expect(value).toEqual(`${percent}%`) - const formattedShares = Number(shares) - formatValue(shares, percent) + const formatValue = (value: string) => ( + Number(value) / 100 * Number(percent) + ) + + const formattedShares = formatValue(shares) + const formattedRewards = formatValue(rewards) expect(parseFloat(exitingShares)).toEqual(formattedShares) + expect(parseFloat(exitingRewards)).toEqual(formattedRewards) } }) test('Unboost disabled', async ({ wallet, swap, page, element }) => { await swap.openPage() - await swap.mockPosition({ isClaimable: false }) + await swap.mocks.position({ isClaimable: false }) await wallet.connectWithBalance({ ETH: '50' }) await swap.tab('unboost', true) - await swap.checkSubmitButton({ isDisabled: true }) + await swap.helpers.checkSubmitButton({ isDisabled: true }) await element.checkVisibility({ testId: 'exit-queue-note', isVisible: true }) - await swap.mockPosition({ isClaimable: false }) + await swap.mocks.position({ isClaimable: false }) await page.getByTestId('balances-link').click() await element.checkVisibility({ testId: 'unboost-queue-claim-button', isVisible: true }) }) + +test('Unboost with upgrade', async ({ wallet, swap, user }) => { + const shares = '10' + const rewards = '1' + + await swap.openPage() + + await user.balances.setBoostData({ shares, rewards }) + + await wallet.connectWithBalance({ ETH: shares, osETH: shares }) + + await swap.actions.unboost({ percent: '50', rewards, shares }) +}) + +test('Unboost', async ({ wallet, swap, user }) => { + const shares = '10' + + await swap.openPage() + + await user.balances.setBoostData({ + shares, + leverageStrategyData: { + version: 2, + isUpgradeRequired: false, + }, + }) + + await wallet.connectWithBalance({ ETH: shares, osETH: shares }) + + await swap.actions.unboost({ percent: '25', withUpgrade: false, shares }) +}) diff --git a/e2e/tests/unstake.spec.ts b/e2e/tests/unstake.spec.ts index 1a94cde3..083a8365 100644 --- a/e2e/tests/unstake.spec.ts +++ b/e2e/tests/unstake.spec.ts @@ -1,11 +1,8 @@ import { expect } from '@playwright/test' -import * as constants from '../constants' import test from '../extendTest' -const format = (value: string) => Number(value.replace(/[^\d.]/g, '')) - test.beforeEach(async ({ gui, guardian }) => { await guardian.fixProvider() await gui.initializeChain(1) @@ -14,16 +11,17 @@ test.beforeEach(async ({ gui, guardian }) => { test('Connect button', async ({ swap }) => { await swap.openPage() await swap.tab('unstake', true) - await swap.checkConnectButton() + await swap.helpers.checkConnectButton() }) -test('Max balance', async ({ swap, wallet, graphql }) => { - const amount = '10' +test('Max balance', async ({ swap, wallet }) => { + const stakeAmount = '10' await swap.openPage() - await graphql.mockAllocatorsData(amount) - await wallet.connectWithBalance({ ETH: amount }) + await wallet.connectWithBalance({ ETH: '50' }) + + await swap.setSdkTransactions({ deposit: stakeAmount }) await swap.tab('unstake', true) @@ -31,109 +29,71 @@ test('Max balance', async ({ swap, wallet, graphql }) => { const value = await swap.input.value() - expect(value).toEqual(amount) + expect(value).toEqual(stakeAmount) }) -test('Unstake info', async ({ wallet, swap, graphql, page, sdk }) => { - const initialETH = '25' - const depositETH = '20' +test('Unstake info', async ({ wallet, swap, user }) => { + const stakeAmount = '10' + const userAPY = 5 await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) + await wallet.connectWithBalance({ ETH: '50' }) - await graphql.mockAllocatorsData(depositETH) - await page.reload() - await page.waitForLoadState('networkidle') + await user.balances.setUserApy(userAPY) - await swap.tab('unstake', true) + await swap.setSdkTransactions({ deposit: stakeAmount }) - await swap.input.fill() + await swap.tab('unstake', true) - const getPositionInfoItem = async (type: string) => { - const selector = await page.waitForSelector(`[data-testid="position-${type}"]`) - const value = await selector.textContent() + await swap.input.fill('5') - return value || '' - } + const value = await swap.input.value() const [ - unstakeToken, - assetsNext, - apy, - gas, + stakeToken, + apyPrev, + apyNext, + assetPrev, + assetNext, + unstakeQueue, ] = await Promise.all([ swap.input.token(), - getPositionInfoItem('assets-next'), - getPositionInfoItem('apy-next'), - getPositionInfoItem('value-prev'), + swap.helpers.getSwapInfoItem('apy-prev'), + swap.helpers.getSwapInfoItem('apy-next'), + swap.helpers.getSwapInfoItem('asset-prev'), + swap.helpers.getSwapInfoItem('asset-next'), + swap.helpers.getSwapInfoItem('unstake-queue'), ]) - expect(format(assetsNext)).toEqual(0) - expect(unstakeToken).toBe('ETH') - expect(format(apy.replace(/.*=/, ''))).toBeGreaterThan(0) - expect(format(gas)).toBeGreaterThanOrEqual(0) + expect(stakeToken).toBe('ETH') + expect(parseFloat(apyPrev)).toEqual(userAPY) + expect(parseFloat(apyNext)).toBeLessThan(userAPY) + expect(parseFloat(assetPrev)).toEqual(Number(stakeAmount)) + expect(parseFloat(assetNext)).toEqual(Number(value)) + expect(parseFloat(unstakeQueue)).toEqual(Number(value)) }) -test('Unstake', async ({ wallet, swap, graphql, page, sdk, transactions }) => { - const initialETH = '25' - const depositETH = '20' - +test('Unstake info after mint', async ({ wallet, swap }) => { await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) + await wallet.connectWithBalance({ ETH: '50' }) - await graphql.mockAllocatorsData(depositETH) - await page.reload() - await page.waitForLoadState('networkidle') + await swap.setSdkTransactions({ deposit: '20', mint: '5' }) await swap.tab('unstake', true) - await swap.submitAmount() + await swap.input.fill() - await page.waitForSelector('text=Processing transaction') - await graphql.mockTransaction() + const value = await swap.input.value() - await transactions.checkTxCompletedModal({ - action: 'exitQueue', - value: depositETH, - }) + expect(Math.round(Number(value))).toEqual(15) }) -test('Unstake after mint', async ({ wallet, swap, graphql, page, sdk }) => { - const initialETH = '25' - const depositETH = '20' - const mintETH = '5' - +test('Unstake', async ({ wallet, swap }) => { await swap.openPage() - await wallet.connectWithBalance({ ETH: initialETH }) - - await sdk.deposit({ - vaultAddress: constants.genesisAddress.mainnet, - assets: depositETH, - }) - - await sdk.mint({ - vaultAddress: constants.genesisAddress.mainnet, - assets: mintETH, - }) - - await graphql.mockAllocatorsData(depositETH) - await page.reload() - await page.waitForLoadState('networkidle') + await wallet.connectWithBalance({ ETH: '50' }) - await swap.tab('unstake', true) + await swap.setSdkTransactions({ deposit: '20' }) - await swap.input.fill() - const value = await swap.input.value() - - expect(Math.round(Number(value))).toEqual(15) + await swap.actions.unstake() }) diff --git a/e2e/types.d.ts b/e2e/types.d.ts index 50cb74fd..b8757f3d 100644 --- a/e2e/types.d.ts +++ b/e2e/types.d.ts @@ -2,6 +2,7 @@ import type { BrowserContext, Page, TestFixture } from '@playwright/test' import type { GUI } from '@guardianui/test/dist/models/GUI' import type { + ApiFixture, SDKFixture, UserFixture, SwapFixture, @@ -27,6 +28,7 @@ declare global { interface ExtendedTest { gui: GUI page: Page + api: ApiFixture sdk: SDKFixture user: UserFixture swap: SwapFixture diff --git a/src/components/TokenDropdown/TokenOptions/TokenOptions.tsx b/src/components/TokenDropdown/TokenOptions/TokenOptions.tsx index 3496a4a6..b2fcc0bc 100644 --- a/src/components/TokenDropdown/TokenOptions/TokenOptions.tsx +++ b/src/components/TokenDropdown/TokenOptions/TokenOptions.tsx @@ -59,7 +59,7 @@ const TokenOptions: React.FC = (props) => { 'w-[calc(100vw-48rem-2px)]': isMobile, })} field={field} - data-testid={`${dataTestId}-input`} + dataTestId={dataTestId} /> { isFetching ? ( diff --git a/src/helpers/requests/fetchBoostSupplyCaps.ts b/src/helpers/requests/fetchBoostSupplyCaps.ts index 6d356c03..61f1d22c 100644 --- a/src/helpers/requests/fetchBoostSupplyCaps.ts +++ b/src/helpers/requests/fetchBoostSupplyCaps.ts @@ -7,7 +7,7 @@ type Input = { type Output = bigint | null -type BoostSupplyCapsQueryPayload = { +export type BoostSupplyCapsQueryPayload = { aave: { osTokenSupplyCap: string osTokenTotalSupplied: string diff --git a/src/layouts/modals/TransactionsFlowModal/util/steps.ts b/src/layouts/modals/TransactionsFlowModal/util/steps.ts index dccd944f..720e3fe4 100644 --- a/src/layouts/modals/TransactionsFlowModal/util/steps.ts +++ b/src/layouts/modals/TransactionsFlowModal/util/steps.ts @@ -9,6 +9,11 @@ import messages from './messages' const boostSteps: Omit[] = [ + { + id: BoostStep.Upgrade, + title: commonMessages.upgradeLeverageStrategy, + testId: 'step-boostUpgrade', + }, { id: BoostStep.Permit, title: { diff --git a/src/layouts/modals/TxCompletedModal/Token/Token.tsx b/src/layouts/modals/TxCompletedModal/Token/Token.tsx index cb8bc2bd..a2139cbf 100644 --- a/src/layouts/modals/TxCompletedModal/Token/Token.tsx +++ b/src/layouts/modals/TxCompletedModal/Token/Token.tsx @@ -27,6 +27,7 @@ const titles: Record = { [Action.ClaimReward]: messages.unstaked, [Action.Exiting]: commonMessages.exitingToken, [Action.ExitQueue]: messages.addedToQueue, + [Action.ExitingReward]: commonMessages.exitingToken, } const Token: React.FC = (props) => { diff --git a/src/layouts/modals/TxCompletedModal/enum.ts b/src/layouts/modals/TxCompletedModal/enum.ts index e2a415b2..667bfcf5 100644 --- a/src/layouts/modals/TxCompletedModal/enum.ts +++ b/src/layouts/modals/TxCompletedModal/enum.ts @@ -9,4 +9,5 @@ export enum Action { Redeemed = 'redeemed', ExitQueue = 'exitQueue', ClaimReward = 'claimReward', + ExitingReward = 'exitingReward', } diff --git a/src/views/SwapView/content/Burn/BurnInput/BurnInput.tsx b/src/views/SwapView/content/Burn/BurnInput/BurnInput.tsx index 6df76dcb..5ece7eef 100644 --- a/src/views/SwapView/content/Burn/BurnInput/BurnInput.tsx +++ b/src/views/SwapView/content/Burn/BurnInput/BurnInput.tsx @@ -28,7 +28,7 @@ const BurnInput: React.FC = () => { values: { mintToken: sdk.config.tokens.mintToken }, }, }} - dataTestId="mint-amount-input" + dataTestId="amount-input" onMaxButtonClick={address ? onMaxButtonClick : undefined} /> ) diff --git a/src/views/SwapView/content/Mint/MintInfo/util/useOptions.ts b/src/views/SwapView/content/Mint/MintInfo/util/useOptions.ts index 4f87793e..6a45a8b3 100644 --- a/src/views/SwapView/content/Mint/MintInfo/util/useOptions.ts +++ b/src/views/SwapView/content/Mint/MintInfo/util/useOptions.ts @@ -51,6 +51,7 @@ const useOptions = () => { { text: messages.conversionRate, value: `1 ${sdk.config.tokens.mintToken} = ${rate} ${sdk.config.tokens.depositToken}`, + dataTestId: 'table-rate', }, { text: messages.stabilityFee, @@ -62,6 +63,7 @@ const useOptions = () => { percent, }, }, + dataTestId: 'table-fee', }, ] diff --git a/src/views/SwapView/content/Mint/MintInput/MintInput.tsx b/src/views/SwapView/content/Mint/MintInput/MintInput.tsx index bfedb15f..38142c86 100644 --- a/src/views/SwapView/content/Mint/MintInput/MintInput.tsx +++ b/src/views/SwapView/content/Mint/MintInput/MintInput.tsx @@ -35,7 +35,7 @@ const MintInput: React.FC = () => { values: { mintToken: sdk.config.tokens.mintToken }, }, }} - dataTestId="mint-amount-input" + dataTestId="amount-input" onMaxButtonClick={address ? onMaxButtonClick : undefined} /> ) diff --git a/src/views/SwapView/content/Unstake/UnstakeContent/util/useOptions.ts b/src/views/SwapView/content/Unstake/UnstakeContent/util/useOptions.ts index aa82f347..1eabd46a 100644 --- a/src/views/SwapView/content/Unstake/UnstakeContent/util/useOptions.ts +++ b/src/views/SwapView/content/Unstake/UnstakeContent/util/useOptions.ts @@ -78,7 +78,7 @@ const useOptions = () => { text: messages.immediate, value: fieldAmount, logo: `token/${sdk.config.tokens.depositToken}`, - dataTestId: 'unstake-queue', + dataTestId: 'table-unstake-queue', }) } else { @@ -87,7 +87,7 @@ const useOptions = () => { tooltip: noteMessage, value: fieldAmount, logo: `token/${sdk.config.tokens.depositToken}`, - dataTestId: 'unstake-queue', + dataTestId: 'table-unstake-queue', }) } diff --git a/src/views/SwapView/util/useStats.ts b/src/views/SwapView/util/useStats.ts index ed6e4788..8e9083d0 100644 --- a/src/views/SwapView/util/useStats.ts +++ b/src/views/SwapView/util/useStats.ts @@ -22,7 +22,7 @@ const useStats = () => { const fetchStats = useCallback(async () => { try { - const mockE2E = methods.insertMockE2E('fixtures/swap/setSwapStats') + const mockE2E = methods.insertMockE2E('fixtures/swap/mocks/swapStats') const stats = mockE2E ? mockE2E : await sdk.utils.getStakewiseStats() diff --git a/src/views/SwapView/util/vault/actions/useUnboost/useUnboostSubmit.ts b/src/views/SwapView/util/vault/actions/useUnboost/useUnboostSubmit.ts index 0b3c267d..a78a68a6 100644 --- a/src/views/SwapView/util/vault/actions/useUnboost/useUnboostSubmit.ts +++ b/src/views/SwapView/util/vault/actions/useUnboost/useUnboostSubmit.ts @@ -196,7 +196,7 @@ const useUnboostSubmit = (values: Input) => { tokens.push({ token: signSDK.config.tokens.depositToken, value: exitAssets, - action: Action.Exiting, + action: Action.ExitingReward, }) } diff --git a/src/views/SwapView/util/vault/helpers/useShares.ts b/src/views/SwapView/util/vault/helpers/useShares.ts index 308f3d71..561d5d34 100644 --- a/src/views/SwapView/util/vault/helpers/useShares.ts +++ b/src/views/SwapView/util/vault/helpers/useShares.ts @@ -50,6 +50,7 @@ const useShares = (values: Input) => { next, }, logo: `token/${mintToken}` as LogoName, + dataTestId: 'table-shares', } return result diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useMaxWithdrawAssets.ts b/src/views/SwapView/util/vault/useUser/useBalances/useMaxWithdrawAssets.ts index 34c08539..bce6d5f3 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useMaxWithdrawAssets.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useMaxWithdrawAssets.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { initialState } from 'store/store/vault' import { useConfig } from 'config' +import { methods } from 'helpers' type Input = { @@ -8,14 +9,21 @@ type Input = { vaultAddress: string } +type Output = Store['vault']['user']['balances']['maxWithdrawAssets'] + const useMaxWithdrawAssets = () => { const { sdk } = useConfig() return useCallback(async (values: Input) => { try { - const maxAssets = await sdk.vault.getMaxWithdrawAmount(values) - const result: Store['vault']['user']['balances']['maxWithdrawAssets'] = maxAssets + const mockE2E = methods.insertMockE2E('user/balances/setMaxWithdrawAssets') + + if (mockE2E) { + return mockE2E + } + + const result: Output = await sdk.vault.getMaxWithdrawAmount(values) return result } diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts b/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts index d59c5fcb..7c9af85b 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useMintToken.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react' import { initialState } from 'store/store/vault' -import * as methods from 'helpers/methods' -import { constants } from 'helpers' +import { constants, methods } from 'helpers' import { useConfig } from 'config' diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useStake.ts b/src/views/SwapView/util/vault/useUser/useBalances/useStake.ts index bf2f20d7..2a7a5244 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useStake.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useStake.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import { constants } from 'helpers' import { useConfig } from 'config' +import { constants, methods } from 'helpers' type Input = { @@ -8,11 +8,24 @@ type Input = { userAddress: string } +type Output = Pick + const useStake = () => { const { sdk } = useConfig() return useCallback(async (values: Input) => { try { + const mockE2E = methods.insertMockE2E('user/balances/setStakeBalance') + + if (mockE2E) { + return mockE2E + } + const { assets, totalEarnedAssets, diff --git a/src/views/SwapView/util/vault/useUser/useBalances/useUserApy.ts b/src/views/SwapView/util/vault/useUser/useBalances/useUserApy.ts index ea804059..1bf72574 100644 --- a/src/views/SwapView/util/vault/useUser/useBalances/useUserApy.ts +++ b/src/views/SwapView/util/vault/useUser/useBalances/useUserApy.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { initialState } from 'store/store/vault' import { useConfig } from 'config' +import { methods } from 'helpers' type Input = { @@ -15,6 +16,12 @@ const useUserApy = () => { const { userAddress, vaultAddress } = values try { + const mockE2E = methods.insertMockE2E>('user/balances/setUserApy') + + if (mockE2E) { + return mockE2E + } + const userAPY = await sdk.vault.getUserApy({ userAddress, vaultAddress, From 372f041854b06560de815bb8feaabafeb4e0a909 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Sun, 30 Nov 2025 11:33:53 +0300 Subject: [PATCH 04/12] remove unused graphql fixtures --- e2e/extendTest.ts | 2 - e2e/fixtures/graphql/helpers/allocatorMock.ts | 13 ---- e2e/fixtures/graphql/helpers/index.ts | 1 - e2e/fixtures/graphql/index.ts | 30 --------- e2e/fixtures/graphql/mockBoostData.ts | 53 --------------- e2e/fixtures/graphql/mockExitQueue.ts | 50 -------------- e2e/fixtures/graphql/mockMintTokenApy.ts | 18 ----- e2e/fixtures/graphql/mockPosition.ts | 54 --------------- e2e/fixtures/graphql/mockStakeStats.ts | 18 ----- e2e/fixtures/graphql/mockSwapApy.ts | 52 --------------- e2e/fixtures/graphql/mockSwapStats.ts | 34 ---------- e2e/fixtures/graphql/mockUserApy.ts | 22 ------- e2e/fixtures/graphql/mockUserRewards.ts | 18 ----- e2e/fixtures/graphql/mockVaultData.ts | 65 ------------------- e2e/fixtures/index.ts | 2 - e2e/fixtures/rewards.ts | 42 ------------ e2e/types.d.ts | 2 - 17 files changed, 476 deletions(-) delete mode 100644 e2e/fixtures/graphql/helpers/allocatorMock.ts delete mode 100644 e2e/fixtures/graphql/mockBoostData.ts delete mode 100644 e2e/fixtures/graphql/mockExitQueue.ts delete mode 100644 e2e/fixtures/graphql/mockMintTokenApy.ts delete mode 100644 e2e/fixtures/graphql/mockPosition.ts delete mode 100644 e2e/fixtures/graphql/mockStakeStats.ts delete mode 100644 e2e/fixtures/graphql/mockSwapApy.ts delete mode 100644 e2e/fixtures/graphql/mockSwapStats.ts delete mode 100644 e2e/fixtures/graphql/mockUserApy.ts delete mode 100644 e2e/fixtures/graphql/mockUserRewards.ts delete mode 100644 e2e/fixtures/graphql/mockVaultData.ts delete mode 100644 e2e/fixtures/rewards.ts diff --git a/e2e/extendTest.ts b/e2e/extendTest.ts index f83b89b9..87195eae 100644 --- a/e2e/extendTest.ts +++ b/e2e/extendTest.ts @@ -11,7 +11,6 @@ import { wallet, osToken, graphql, - rewards, element, helpers, settings, @@ -30,7 +29,6 @@ const baseTest = base.extend({ anvil, wallet, osToken, - rewards, graphql, element, helpers, diff --git a/e2e/fixtures/graphql/helpers/allocatorMock.ts b/e2e/fixtures/graphql/helpers/allocatorMock.ts deleted file mode 100644 index 3f90cf10..00000000 --- a/e2e/fixtures/graphql/helpers/allocatorMock.ts +++ /dev/null @@ -1,13 +0,0 @@ -const allocatorMock = new Array(30).fill(null).map((_, index) => { - const day = 86400000000 - - return { - apy: '0.3', - timestamp: String(1731888000000000 + (day * index)), - earnedAssets: '80530949104937', - totalAssets: '2031465768921153400', - } -}) - - -export default allocatorMock diff --git a/e2e/fixtures/graphql/helpers/index.ts b/e2e/fixtures/graphql/helpers/index.ts index ed8b1af3..6049db64 100644 --- a/e2e/fixtures/graphql/helpers/index.ts +++ b/e2e/fixtures/graphql/helpers/index.ts @@ -1,3 +1,2 @@ -export { default as allocatorMock } from './allocatorMock' export { default as mockCustomData } from './mockCustomData' export { default as mockCustomDataOnce } from './mockCustomDataOnce' diff --git a/e2e/fixtures/graphql/index.ts b/e2e/fixtures/graphql/index.ts index 32dc1f64..781709f7 100644 --- a/e2e/fixtures/graphql/index.ts +++ b/e2e/fixtures/graphql/index.ts @@ -1,35 +1,15 @@ -import { createMockUserApy, MockUserApy } from './mockUserApy' -import { createMockSwapApy, MockSwapApy } from './mockSwapApy' -import { createMockPosition, MockPosition } from './mockPosition' -import { createMockBoostData, MockBoostData } from './mockBoostData' -import { createMockExitQueue, MockExitQueue } from './mockExitQueue' -import { createMockSwapStats, MockSwapStats } from './mockSwapStats' -import { createMockVaultData, MockVaultData } from './mockVaultData' import { createMockCustomData, MockCustomData } from './mockCustomData' -import { createMockStakeStats, MockStakeStats } from './mockStakeStats' import { createMockTransaction, MockTransaction } from './mockTransaction' -import { createMockUserRewards, MockUserRewards } from './mockUserRewards' import { createWaitForResponse, WaitForResponse } from './waitForResponse' -import { createMockMintTokenApy, MockMintTokenApy } from './mockMintTokenApy' import { createMockAllocatorsData, MockAllocatorsData } from './mockAllocatorsData' import { createMockCustomDataOnce, MockCustomDataOnce } from './mockCustomDataOnce' import { createMockMintTokenBalance, MockMintTokenBalance } from './mockMintTokenBalance' export type GraphqlFixture = { - mockUserApy: MockUserApy - mockSwapApy: MockSwapApy - mockPosition: MockPosition - mockVaultData:MockVaultData - mockBoostData: MockBoostData - mockExitQueue: MockExitQueue - mockSwapStats: MockSwapStats - mockStakeStats: MockStakeStats mockCustomData: MockCustomData mockTransaction: MockTransaction - mockUserRewards: MockUserRewards waitForResponse: WaitForResponse - mockMintTokenApy: MockMintTokenApy mockAllocatorsData: MockAllocatorsData mockCustomDataOnce: MockCustomDataOnce mockMintTokenBalance: MockMintTokenBalance @@ -37,19 +17,9 @@ export type GraphqlFixture = { const graphql: E2E.Fixture = async ({ page }, use) => { await use({ - mockUserApy: createMockUserApy({ page }), - mockSwapApy: createMockSwapApy({ page }), - mockPosition: createMockPosition({ page }), - mockVaultData: createMockVaultData({ page }), - mockExitQueue: createMockExitQueue({ page }), - mockSwapStats: createMockSwapStats({ page }), - mockBoostData: createMockBoostData({ page }), - mockStakeStats: createMockStakeStats({ page }), mockCustomData: createMockCustomData({ page }), waitForResponse: createWaitForResponse({ page }), mockTransaction: createMockTransaction({ page }), - mockUserRewards: createMockUserRewards({ page }), - mockMintTokenApy: createMockMintTokenApy({ page }), mockCustomDataOnce: createMockCustomDataOnce({ page }), mockAllocatorsData: createMockAllocatorsData({ page }), mockMintTokenBalance: createMockMintTokenBalance({ page }), diff --git a/e2e/fixtures/graphql/mockBoostData.ts b/e2e/fixtures/graphql/mockBoostData.ts deleted file mode 100644 index 32e496f6..00000000 --- a/e2e/fixtures/graphql/mockBoostData.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { parseEther } from 'ethers' - -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -type Input = { - shares: string - rewards?: string - vaultApy?: string - maxBoostApy?: string - exitingPercent?: string -} - -export type MockBoostData = (values: Input) => Promise - -export const createMockBoostData: Wrapper = ({ page }) => ( - async (values: Input) => { - const { shares, rewards, maxBoostApy, vaultApy, exitingPercent } = values - - await mockCustomDataOnce({ - page, - name: 'BoostMainData', - data: { - leverageStrategyPositions: [ - { - proxy: '', - borrowLtv: '0', - exitingPercent: exitingPercent || '0', - osTokenShares: parseEther(shares).toString(), - boostRewardAssets: parseEther(rewards || '0').toString(), - }, - ], - vaults: [ - { - osTokenConfig: { - ltvPercent: '999900000000000000', - }, - osTokenHolderMaxBoostApy: maxBoostApy || '0', - allocatorMaxBoostApy: maxBoostApy || '0', - apy: vaultApy || '0', - }, - ], - allocators: [ - { - assets: (parseEther(shares) * 2n).toString(), - }, - ], - }, - }) - } -) diff --git a/e2e/fixtures/graphql/mockExitQueue.ts b/e2e/fixtures/graphql/mockExitQueue.ts deleted file mode 100644 index 5dcb05d5..00000000 --- a/e2e/fixtures/graphql/mockExitQueue.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { parseEther } from 'ethers' - -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -type Input = { - isClaimable?: boolean -} - -type Output = { - totalAssets: number - exitedAssets: number -} - -export type MockExitQueue = (values: Input) => Promise - -export const createMockExitQueue: Wrapper = ({ page }) => ( - async (values: Input) => { - const { isClaimable } = values - - const totalAssets = '9.5' - const exitedAssets = '1.5' - - await mockCustomDataOnce({ - page, - name: 'exitQueue', - data: { - exitRequests: [ - { - exitQueueIndex: '0', - timestamp: '1727256912', - withdrawalTimestamp: '0', - isClaimed: !isClaimable, - isClaimable: Boolean(isClaimable), - positionTicket: '156652794390067408267', - totalAssets: parseEther(totalAssets).toString(), - exitedAssets: parseEther(exitedAssets).toString(), - }, - ], - }, - }) - - return { - totalAssets: Number(totalAssets), - exitedAssets: Number(exitedAssets), - } - } -) diff --git a/e2e/fixtures/graphql/mockMintTokenApy.ts b/e2e/fixtures/graphql/mockMintTokenApy.ts deleted file mode 100644 index 79e07d94..00000000 --- a/e2e/fixtures/graphql/mockMintTokenApy.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -export type MockMintTokenApy = (apy: string) => Promise - -export const createMockMintTokenApy: Wrapper = ({ page }) => ( - async (apy: string) => { - await mockCustomDataOnce({ - page, - name: 'osTokenApy', - data: { - osTokens: [ { apy, feePercent: 1 } ], - }, - }) - } -) diff --git a/e2e/fixtures/graphql/mockPosition.ts b/e2e/fixtures/graphql/mockPosition.ts deleted file mode 100644 index 82a53082..00000000 --- a/e2e/fixtures/graphql/mockPosition.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { mockCustomDataOnce } from './helpers' -import { createMockBoostData } from './mockBoostData' - - -type Wrapper = E2E.FixtureMethod - -type Input = { - exitingShares: string - exitingRewards: string - isClaimable?: boolean -} - -export type MockPosition = (values: Input) => Promise - -export const createMockPosition: Wrapper = ({ page }) => ( - async (values: Input) => { - const { exitingShares, exitingRewards, isClaimable } = values - - const isExiting = typeof isClaimable === 'boolean' - - const exitingPosition = { - isClaimable: isClaimable as boolean, - timestamp: '1730206212', - totalAssets: '5716409381998981494', - exitedAssets: '5716409381998981494', - positionTicket: '210258902756807306422', - exitQueueIndex: isClaimable ? '1' : null, - withdrawalTimestamp: isClaimable ? '0' : '210258902756807306422', - } - - const mockBoostData = createMockBoostData({ page }) - - await Promise.all([ - mockCustomDataOnce({ - page, - name: 'BoostQueuePositions', - data: { - leverageStrategyPositions: [ - { - exitingPercent: '100', - exitingAssets: exitingRewards, - exitingOsTokenShares: exitingShares, - exitRequest: isExiting ? exitingPosition : null, - }, - ], - }, - }), - mockBoostData({ - shares: '0', - exitingPercent: '100', - }), - ]) - } -) diff --git a/e2e/fixtures/graphql/mockStakeStats.ts b/e2e/fixtures/graphql/mockStakeStats.ts deleted file mode 100644 index 56a15d87..00000000 --- a/e2e/fixtures/graphql/mockStakeStats.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { allocatorMock, mockCustomData } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -export type MockStakeStats = () => Promise - -export const createMockStakeStats: Wrapper = ({ page }) => ( - async () => { - await mockCustomData({ - page, - name: 'StakeStats', - data: { - osTokenHolder: allocatorMock, - }, - }) - } -) diff --git a/e2e/fixtures/graphql/mockSwapApy.ts b/e2e/fixtures/graphql/mockSwapApy.ts deleted file mode 100644 index 5fb8e700..00000000 --- a/e2e/fixtures/graphql/mockSwapApy.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { mockCustomDataOnce } from './helpers' -import { createMockMintTokenApy } from './mockMintTokenApy' - - -type Wrapper = E2E.FixtureMethod - -type Input = { - userApy: string - vaultApy: string - feePercent: number - osTokenApy: string - maxBoostApy: string -} - -export type MockSwapApy = (values: Input) => Promise - -export const createMockSwapApy: Wrapper = ({ page }) => ( - async (values: Input) => { - const { osTokenApy, maxBoostApy, userApy, vaultApy, feePercent } = values - - const mockMintTokenApy = createMockMintTokenApy({ page }) - - await Promise.all([ - mockMintTokenApy(osTokenApy), - mockCustomDataOnce({ - page, - name: 'Apy', - data: { - osToken: { - apy: osTokenApy, - feePercent, - }, - vaults: [ - { - apy: vaultApy, - feePercent, - osTokenHolderMaxBoostApy: maxBoostApy, - osTokenConfig: { - ltvPercent: '999900000000000000', - }, - }, - ], - osTokenHolders: [ - { - apy: userApy, - }, - ], - }, - }), - ]) - } -) diff --git a/e2e/fixtures/graphql/mockSwapStats.ts b/e2e/fixtures/graphql/mockSwapStats.ts deleted file mode 100644 index 15543dcd..00000000 --- a/e2e/fixtures/graphql/mockSwapStats.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -type Output = { - usersCount: number - totalAssets: string - totalEarnedAssets: string -} - -export type MockSwapStats = () => Promise - -export const createMockSwapStats: Wrapper = ({ page }) => ( - async () => { - const stats = { - usersCount: 1000, - totalAssets: '100000000000000000000', - totalEarnedAssets: '10000000000000000000', - } - - await mockCustomDataOnce({ - page, - name: 'Stats', - data: { - networks: [ - stats, - ], - }, - }) - - return stats - } -) diff --git a/e2e/fixtures/graphql/mockUserApy.ts b/e2e/fixtures/graphql/mockUserApy.ts deleted file mode 100644 index 63d6b117..00000000 --- a/e2e/fixtures/graphql/mockUserApy.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -export type MockUserApy = (apy: string) => Promise - -export const createMockUserApy: Wrapper = ({ page }) => ( - async (apy: string) => { - await mockCustomDataOnce({ - page, - name: 'UserApy', - data: { - allocators: [ - { - apy, - }, - ], - }, - }) - } -) diff --git a/e2e/fixtures/graphql/mockUserRewards.ts b/e2e/fixtures/graphql/mockUserRewards.ts deleted file mode 100644 index 7bb7a79c..00000000 --- a/e2e/fixtures/graphql/mockUserRewards.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { allocatorMock, mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -export type MockUserRewards = () => Promise - -export const createMockUserRewards: Wrapper = ({ page }) => ( - async () => { - await mockCustomDataOnce({ - page, - name: 'UserRewards', - data: { - allocator: allocatorMock, - }, - }) - } -) diff --git a/e2e/fixtures/graphql/mockVaultData.ts b/e2e/fixtures/graphql/mockVaultData.ts deleted file mode 100644 index 4e9e18af..00000000 --- a/e2e/fixtures/graphql/mockVaultData.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as constants from '../../constants' -import { mockCustomDataOnce } from './helpers' - - -type Wrapper = E2E.FixtureMethod - -export type MockVaultData = (data?: Record) => Promise - -export const createMockVaultData: Wrapper = ({ page }) => ( - async (data) => { - let address = '' - - if (data?.admin) { - address = data.admin - } - else { - address = await page.evaluate('window.ethereum.signer.address') - } - - await mockCustomDataOnce({ - page, - name: 'Vault', - data: { - vault: { - apy: '2.42', - version: '3', - admin: address, - isErc20: false, - imageUrl: '', - isPrivate: true, - tokenName: null, - feePercent: 450, - isGenesis: true, - description: '', - tokenSymbol: null, - queuedShares: '0', - isBlocklist: false, - blocklistCount: '0', - whitelistCount: '0', - performance: '97.65', - whitelister: address, - depositDataRoot: '', - isCollateralized: true, - blocklistManager: '', - createdAt: '1701031871', - displayName: 'Mock Vault', - validatorsManager: address, - depositDataManager: address, - allocatorMaxBoostApy: '22.11', - osTokenHolderMaxBoostApy: '22.11', - totalAssets: '32100978717000000000', - feeRecipient: constants.feeRecipient, - address: '0xac0f906e433d58fa868f936e8a43230473652885', - mevEscrow: '0x5ab14b64fb24c170671edb69b76812e4e05d558c', - capacity: '115792089237316195423570985008687907853269984665640564039457584007913129639935', - osTokenConfig: { - liqThresholdPercent: '920000000000000000', - ltvPercent: '900000000000000000', - }, - ...data, - }, - }, - }) - } -) diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.ts index 524c4621..f4af88b4 100644 --- a/e2e/fixtures/index.ts +++ b/e2e/fixtures/index.ts @@ -7,7 +7,6 @@ export { default as queue } from './queue' export { default as anvil } from './anvil' export { default as wallet } from './wallet' export { default as osToken } from './osToken' -export { default as rewards } from './rewards' export { default as graphql } from './graphql' export { default as element } from './element' export { default as helpers } from './helpers' @@ -25,7 +24,6 @@ export type { QueueFixture } from './queue' export type { AnvilFixture } from './anvil' export type { WalletFixture } from './wallet' export type { OsTokenFixture } from './osToken' -export type { RewardsFixture } from './rewards' export type { ElementFixture } from './element' export type { HelpersFixture } from './helpers' export type { GraphqlFixture } from './graphql' diff --git a/e2e/fixtures/rewards.ts b/e2e/fixtures/rewards.ts deleted file mode 100644 index efc5e622..00000000 --- a/e2e/fixtures/rewards.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from '@playwright/test' - - -export type RewardsFixture = { - checkExport: () => Promise -} - -const rewards: E2E.Fixture = async ({ page, helpers, graphql }, use) => { - - const checkExport = async () => { - await page.getByTestId('user-stats-chart-tab').click() - await page.getByTestId('export-rewards-button').click() - - await page.getByTestId('export-rewards-from-input').fill('2024-01-01') - await page.getByTestId('export-rewards-to-input').fill('2024-01-31') - await page.getByTestId('export-rewards-format-select-button').click() - - const formatOptions = await page.waitForSelector('[data-testid="export-rewards-format-select-options"]') - - const options = await formatOptions.$$('> *') - expect(options.length).toBe(2) - - await page.getByTestId('export-rewards-format-select-option-xlsx').click() - - const downloadButton = page.getByTestId('export-rewards-download-button') - - await expect(downloadButton).not.toBeDisabled() - - await graphql.mockStakeStats() - await graphql.mockUserRewards() - await downloadButton.click() - - await helpers.checkNotification('Staking stats successfully downloaded') - } - - await use({ - checkExport, - }) -} - - -export default rewards diff --git a/e2e/types.d.ts b/e2e/types.d.ts index b8757f3d..e0139472 100644 --- a/e2e/types.d.ts +++ b/e2e/types.d.ts @@ -11,7 +11,6 @@ import type { AnvilFixture, WalletFixture, OsTokenFixture, - RewardsFixture, ElementFixture, HelpersFixture, GraphqlFixture, @@ -37,7 +36,6 @@ declare global { anvil: AnvilFixture wallet: WalletFixture osToken: OsTokenFixture - rewards: RewardsFixture context: BrowserContext element: ElementFixture helpers: HelpersFixture From b2315754ac289267e125085963859a3e1c9a1a4f Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 10:10:31 +0300 Subject: [PATCH 05/12] test e2e action --- .github/workflows/e2e.yml | 17 +++++++++-------- README.md | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c454c6e7..d13704d0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -24,15 +24,16 @@ jobs: - name: Checkout 🛎️ uses: actions/checkout@v2 + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Restore Node Modules id: cache-restore uses: actions/cache@v3 with: - cache: npm - node-version: '22.14.0' - cache-dependency-path: 'package-lock.json' + path: | + node_modules key: node_modules_${{ hashFiles('package-lock.json') }} + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Setup node and npm uses: actions/setup-node@v3 @@ -40,12 +41,14 @@ jobs: cache: npm node-version: '22.14.0' cache-dependency-path: 'package-lock.json' + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Install npm dependencies run: 'npm ci --ignore-scripts || npm i --ignore-scripts --force' shell: bash env: NODE_AUTH_TOKEN: ${{ github.token }} + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Awaiting WEB deployment to be ready uses: UnlyEd/github-action-await-vercel@v1.1.1 @@ -54,9 +57,11 @@ jobs: with: deployment-url: ${{ steps.preview.outputs.preview_url }} timeout: 600 + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - name: Run tests env: @@ -68,11 +73,7 @@ jobs: GUARDIAN_UI_ALCHEMY_API_KEY: ${{ secrets.GUARDIAN_UI_ALCHEMY_API_KEY }} VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }} run: npm run e2e - - - name: Sanitize reports before upload - if: ${{ failure() }} - run: | - find ./playwright-report/ -name "*.html" -exec sed -i 's/${{ secrets.[A-Z_]* }}/***/g' {} \; + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - uses: actions/upload-artifact@v4 if: ${{ failure() }} diff --git a/README.md b/README.md index 5679a7f5..ee23761d 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,4 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. + From 5eb5a79a81500cd805744f863cd6fe2c656a7d7b Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 10:25:35 +0300 Subject: [PATCH 06/12] after merge fix --- src/helpers/requests/vault/fetchData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/requests/vault/fetchData.ts b/src/helpers/requests/vault/fetchData.ts index b38ba385..85146783 100644 --- a/src/helpers/requests/vault/fetchData.ts +++ b/src/helpers/requests/vault/fetchData.ts @@ -24,7 +24,7 @@ const fetchData = async ({ sdk, withTime, vaultAddress }: Input) => { feePercent, ] = await Promise.all([ sdk.vault.getVault({ vaultAddress, withTime }), - sdk.getVaultVersion(vaultAddress), + sdk.vault.getVaultVersion(vaultAddress), sdk.contracts.base.mintTokenController.feePercent(), ]) From ab4ebf07118f217e85bc7e5a0d3c29d14ab41032 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 10:27:37 +0300 Subject: [PATCH 07/12] add space --- e2e/fixtures/swap/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/fixtures/swap/index.ts b/e2e/fixtures/swap/index.ts index 56e879be..7e8aa04b 100644 --- a/e2e/fixtures/swap/index.ts +++ b/e2e/fixtures/swap/index.ts @@ -87,7 +87,7 @@ const swap: E2E.Fixture = async ({ page, graphql, transactions, ele stake: createStake({ page, transactions, user }), burn: createBurn({ page, transactions, helpers }), unboost: createUnboost({ page, transactions, api }), - mint: createMint({ page, transactions, helpers,user }), + mint: createMint({ page, transactions, helpers, user }), }, mocks: { From 1d22620484b7af50ef97fb8026662ee23ebb02f6 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 10:57:25 +0300 Subject: [PATCH 08/12] improve tests --- README.md | 1 - e2e/fixtures/swap/actions/stake.ts | 7 ++++--- e2e/tests/boost.spec.ts | 4 ++-- e2e/tests/stake.spec.ts | 9 +++------ package-lock.json | 2 +- src/helpers/requests/vault/fetchData.ts | 2 +- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ee23761d..5679a7f5 100644 --- a/README.md +++ b/README.md @@ -91,4 +91,3 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. - diff --git a/e2e/fixtures/swap/actions/stake.ts b/e2e/fixtures/swap/actions/stake.ts index 6f516485..cef0fa8e 100644 --- a/e2e/fixtures/swap/actions/stake.ts +++ b/e2e/fixtures/swap/actions/stake.ts @@ -5,9 +5,9 @@ import { createSubmit } from '../submit' export type Stake = (amount?: string) => Promise -type Wrapper = E2E.FixtureMethod +type Wrapper = E2E.FixtureMethod -export const createStake: Wrapper = ({ page, transactions, user }) => ( +export const createStake: Wrapper = ({ page, transactions, user, helpers }) => ( async (amount?: string) => { const tab = createTab({ page }) const input = createInput({ page }) @@ -18,6 +18,7 @@ export const createStake: Wrapper = ({ page, transactions, user }) => ( await input.fill(amount) const inputAmount = await input.value() + const formattedAmount = helpers.formatTokenValue(inputAmount) await Promise.all([ submit(), @@ -28,7 +29,7 @@ export const createStake: Wrapper = ({ page, transactions, user }) => ( await transactions.checkTxCompletedModal({ action: 'stake', - value: inputAmount, + value: formattedAmount, }) } ) diff --git a/e2e/tests/boost.spec.ts b/e2e/tests/boost.spec.ts index 57ae21f7..f81129ae 100644 --- a/e2e/tests/boost.spec.ts +++ b/e2e/tests/boost.spec.ts @@ -97,7 +97,7 @@ test('Boost disabled', async ({ wallet, swap, page, element }) => { expect(claimButton).toBeDisabled() }) -test('Boost with permit', async ({ wallet, swap, user }) => { +test.skip('Boost with permit', async ({ wallet, swap, user }) => { const amount = '10' await swap.mocks.boostInfo() @@ -111,7 +111,7 @@ test('Boost with permit', async ({ wallet, swap, user }) => { await swap.actions.boost({ amount: '1' }) }) -test('Boost with upgrade', async ({ wallet, swap, user }) => { +test.skip('Boost with upgrade', async ({ wallet, swap, user }) => { const amount = '10' await swap.mocks.boostInfo() diff --git a/e2e/tests/stake.spec.ts b/e2e/tests/stake.spec.ts index 309756cb..3c41217f 100644 --- a/e2e/tests/stake.spec.ts +++ b/e2e/tests/stake.spec.ts @@ -83,16 +83,13 @@ test('Initial info not profitable', async ({ swap, element, vault }) => { expect(osTokenApyValue).toEqual(Number(vaultApy)) }) -test('Stake info', async ({ swap, wallet, user }) => { - const userAPY = 5 +test('Stake info', async ({ swap, wallet }) => { const value = 10 await swap.openPage() await wallet.connectWithBalance({ ETH: '100' }) - await user.balances.setUserApy(userAPY) - await swap.input.fill(value.toString()) const [ @@ -112,8 +109,8 @@ test('Stake info', async ({ swap, wallet, user }) => { ]) expect(stakeToken).toBe('ETH') - expect(parseFloat(apyPrev)).toEqual(apyPrev) - expect(parseFloat(apyNext)).toBeGreaterThan(userAPY) + expect(parseFloat(apyPrev)).toEqual(0) + expect(parseFloat(apyNext)).toBeGreaterThan(0) expect(parseFloat(assetPrev)).toEqual(0) expect(parseFloat(assetNext)).toEqual(value) diff --git a/package-lock.json b/package-lock.json index acca59d0..363fc779 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@ledgerhq/hw-transport-webusb": "6.29.5", "@metamask/onboarding": "1.0.1", "@reduxjs/toolkit": "2.8.2", - "@stakewise/v3-sdk": "^4.2.0", + "@stakewise/v3-sdk": "4.2.0", "@tailwindcss/postcss": "4.1.7", "@types/react-redux": "7.1.34", "@wagmi/connectors": "5.8.3", diff --git a/src/helpers/requests/vault/fetchData.ts b/src/helpers/requests/vault/fetchData.ts index 85146783..5857585b 100644 --- a/src/helpers/requests/vault/fetchData.ts +++ b/src/helpers/requests/vault/fetchData.ts @@ -24,7 +24,7 @@ const fetchData = async ({ sdk, withTime, vaultAddress }: Input) => { feePercent, ] = await Promise.all([ sdk.vault.getVault({ vaultAddress, withTime }), - sdk.vault.getVaultVersion(vaultAddress), + sdk.vault.getVaultVersion({ vaultAddress }), sdk.contracts.base.mintTokenController.feePercent(), ]) From 53d8183084db64dd6a391c78427621c9841e4123 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 11:03:12 +0300 Subject: [PATCH 09/12] fixed build --- README.md | 1 + e2e/fixtures/swap/index.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5679a7f5..ee23761d 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,4 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. + diff --git a/e2e/fixtures/swap/index.ts b/e2e/fixtures/swap/index.ts index 7e8aa04b..3fba4629 100644 --- a/e2e/fixtures/swap/index.ts +++ b/e2e/fixtures/swap/index.ts @@ -84,10 +84,10 @@ const swap: E2E.Fixture = async ({ page, graphql, transactions, ele actions: { unstake: createUnstake({ page, transactions }), boost: createBoost({ page, transactions, api }), - stake: createStake({ page, transactions, user }), burn: createBurn({ page, transactions, helpers }), unboost: createUnboost({ page, transactions, api }), mint: createMint({ page, transactions, helpers, user }), + stake: createStake({ page, transactions, user, helpers }), }, mocks: { From aecc59c0959d3dd03e2c52c63a32760b347cbd4b Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 11:24:21 +0300 Subject: [PATCH 10/12] test action --- .github/workflows/e2e.yml | 9 --------- README.md | 1 - 2 files changed, 10 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d13704d0..39e10e8c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -26,15 +26,6 @@ jobs: uses: actions/checkout@v2 if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - - name: Restore Node Modules - id: cache-restore - uses: actions/cache@v3 - with: - path: | - node_modules - key: node_modules_${{ hashFiles('package-lock.json') }} - if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} - - name: Setup node and npm uses: actions/setup-node@v3 with: diff --git a/README.md b/README.md index ee23761d..5679a7f5 100644 --- a/README.md +++ b/README.md @@ -91,4 +91,3 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. - From 90a7112a9888a61cabd0ec146ef2bb8836fc66c2 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 11:36:44 +0300 Subject: [PATCH 11/12] remove checkLanguage --- README.md | 1 + e2e/fixtures/settings/checkLanguage.ts | 57 -------------------------- e2e/fixtures/settings/index.ts | 3 -- e2e/tests/base.spec.ts | 1 - 4 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 e2e/fixtures/settings/checkLanguage.ts diff --git a/README.md b/README.md index 5679a7f5..ee23761d 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,4 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. + diff --git a/e2e/fixtures/settings/checkLanguage.ts b/e2e/fixtures/settings/checkLanguage.ts deleted file mode 100644 index 9215d43c..00000000 --- a/e2e/fixtures/settings/checkLanguage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { expect } from '@playwright/test' - - -type Wrapper = E2E.FixtureMethod - -export type CheckLanguage = () => Promise - -const languageList = [ - { - value: 'en', - title: 'English', - }, - { - value: 'ru', - title: 'Русский', - }, - { - value: 'fr', - title: 'Français', - }, - { - value: 'es', - title: 'Español', - }, - { - value: 'pt', - title: 'Português', - }, - { - value: 'de', - title: 'Deutsch', - }, - { - value: 'zh', - title: '繁體中文', - }, -] - -export const createCheckLanguage: Wrapper = ({ page, helpers, element }) => ( - async () => { - for (const language of languageList) { - await page.getByTestId('settings-toggle-button').click() - await helpers.changeSelect('settings-language', language.value) - - const cookieLanguage = await helpers.getCookiesItem('SW_language') - expect(cookieLanguage?.value).toBe(language.value) - - await page.reload() - - expect(cookieLanguage?.value).toBe(language.value) - await page.getByTestId('settings-toggle-button').click() - await element.checkText({ testId: 'settings-language-button-subtitle', expectedText: language.title }) - - await page.getByTestId('settings-toggle-button').click() - } - } -) diff --git a/e2e/fixtures/settings/index.ts b/e2e/fixtures/settings/index.ts index 7d95d1d2..80a61cf9 100644 --- a/e2e/fixtures/settings/index.ts +++ b/e2e/fixtures/settings/index.ts @@ -1,14 +1,12 @@ import { createSetTheme, SetTheme } from './setTheme' import { createCheckTheme, CheckTheme } from './checkTheme' import { createCheckCurrency, CheckCurrency } from './checkCurrency' -import { createCheckLanguage, CheckLanguage } from './checkLanguage' export type SettingsFixture = { setTheme: SetTheme checkTheme: CheckTheme checkCurrency: CheckCurrency - checkLanguage: CheckLanguage } const settings: E2E.Fixture = async ({ page, element, helpers }, use) => { @@ -16,7 +14,6 @@ const settings: E2E.Fixture = async ({ page, element, helpers } setTheme: createSetTheme({ page, helpers }), checkTheme: createCheckTheme({ page, helpers }), checkCurrency: createCheckCurrency({ page, element, helpers }), - checkLanguage: createCheckLanguage({ page, element, helpers }), }) } diff --git a/e2e/tests/base.spec.ts b/e2e/tests/base.spec.ts index 90b2f8cb..c91ef763 100644 --- a/e2e/tests/base.spec.ts +++ b/e2e/tests/base.spec.ts @@ -6,7 +6,6 @@ test('Settings', async ({ page, settings }) => { await settings.checkTheme() await settings.checkCurrency() - await settings.checkLanguage() }) test('Select wallet', async ({ page, wallet, element }) => { From cf8942f5c80fc4bcc9c7624b415ca4b8d7595489 Mon Sep 17 00:00:00 2001 From: dfkadyr-stakewise Date: Mon, 1 Dec 2025 12:39:59 +0300 Subject: [PATCH 12/12] add Restore --- .github/workflows/e2e.yml | 9 +++++++++ README.md | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 39e10e8c..d13704d0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -26,6 +26,15 @@ jobs: uses: actions/checkout@v2 if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} + - name: Restore Node Modules + id: cache-restore + uses: actions/cache@v3 + with: + path: | + node_modules + key: node_modules_${{ hashFiles('package-lock.json') }} + if: ${{ steps.preview.outputs.deployment_state != 'CANCELED' }} + - name: Setup node and npm uses: actions/setup-node@v3 with: diff --git a/README.md b/README.md index ee23761d..5679a7f5 100644 --- a/README.md +++ b/README.md @@ -91,4 +91,3 @@ After updating the colors, run `npm run colors` — this script will generate RG ### Favicon The favicon is a 16x16 image that is displayed in the browser tab. It is located in the `public` folder. By default, osETH logo is used as the favicon. -