diff --git a/src/pages/position/manage.ts b/src/pages/position/manage.ts index c6a7f158..405ae614 100644 --- a/src/pages/position/manage.ts +++ b/src/pages/position/manage.ts @@ -26,7 +26,7 @@ export class Manage { } @step - async getLoanToValue(protocol?: 'Ajna'): Promise { + async getLoanToValue(protocol?: 'Ajna' | 'Morpho Blue'): Promise { if (protocol) { return await this.base.getLoanToValue('Ajna'); } diff --git a/src/pages/position/orderInformation.ts b/src/pages/position/orderInformation.ts index a2b327ef..5d38866f 100644 --- a/src/pages/position/orderInformation.ts +++ b/src/pages/position/orderInformation.ts @@ -22,11 +22,11 @@ export class OrderInformation { tokenAmount: string; token: string; dollarsAmount: string; - protocol?: 'Maker' | 'Ajna'; + protocol?: 'Maker' | 'Ajna' | 'Morpho Blue'; }) { const regExp = new RegExp( `${tokenAmount} ${token}${ - protocol === 'Maker' ? ' \\(' : protocol === 'Ajna' ? '\\(' : ' ' + protocol === 'Maker' ? ' \\(' : ['Ajna', 'Morpho Blue'].includes(protocol) ? '\\(' : ' ' }\\$${dollarsAmount}${protocol ? '\\)' : ''}` ); await expect( @@ -61,7 +61,7 @@ export class OrderInformation { }: { amount: string; percentage: string; - protocol?: 'Ajna'; + protocol?: 'Ajna' | 'Morpho Blue'; pair?: string; }) { const regExp = new RegExp(`${amount} ${protocol ? `${pair}` : ''}\\(${percentage}%\\)`); diff --git a/src/pages/position/overview.ts b/src/pages/position/overview.ts index 25507bbd..ecf7ac9d 100644 --- a/src/pages/position/overview.ts +++ b/src/pages/position/overview.ts @@ -216,7 +216,7 @@ export class Overview { token, timeout, protocol, - }: { amount: string; token: string; timeout?: number; protocol?: 'Ajna' } = { + }: { amount: string; token: string; timeout?: number; protocol?: 'Ajna' | 'Morpho Blue' } = { amount: '', token: '', timeout: expectDefaultTimeout, @@ -239,7 +239,7 @@ export class Overview { }: { amount: string; token: string; - protocol?: 'Ajna'; + protocol?: 'Ajna' | 'Morpho Blue'; }) { const debtElement = protocol ? this.page.getByText('Position debt').locator('..') @@ -297,13 +297,13 @@ export class Overview { protocol, }: { amount: string; - protocol?: 'Maker' | 'Ajna'; + protocol?: 'Maker' | 'Ajna' | 'Morpho Blue'; }) { let regexObj = new RegExp(`${protocol ? '\\$' : ''}${amount}${protocol ? '' : ' USD'}`); const locator = protocol === 'Maker' ? this.page.getByText('Buying Power').locator('..') - : protocol === 'Ajna' + : ['Ajna', 'Morpho Blue'].includes(protocol) ? this.page.locator('li:has-text("Buying Power")').locator('div') : this.page.locator('li:has-text("Buying Power")'); diff --git a/src/pages/position/setup.ts b/src/pages/position/setup.ts index 5a3c4ec1..e93da5d2 100644 --- a/src/pages/position/setup.ts +++ b/src/pages/position/setup.ts @@ -111,7 +111,7 @@ export class Setup { * @param value should be between '0' and '1' both included | 0: far left | 1: far right */ @step - async moveSlider({ protocol, value }: { protocol?: 'Ajna'; value: number }) { + async moveSlider({ protocol, value }: { protocol?: 'Ajna' | 'Morpho Blue'; value: number }) { if (protocol) { await this.base.moveSlider({ value }); } else { diff --git a/tests/withWallet/ajna/base/ajnaMultiplyBase.spec.ts b/tests/withWallet/ajna/base/ajnaMultiplyBase.spec.ts index d6e21040..c091430f 100644 --- a/tests/withWallet/ajna/base/ajnaMultiplyBase.spec.ts +++ b/tests/withWallet/ajna/base/ajnaMultiplyBase.spec.ts @@ -119,7 +119,7 @@ test.describe('Ajna Base Multiply - Wallet connected', async () => { }); }); - test('It should allow to simulate risk adjustment (Up & Down) with slider in an Ajna Multiply position before opening it @regression', async () => { + test('It should allow to simulate risk adjustment (Up & Down) with slider in an Ajna Base Multiply position before opening it @regression', async () => { test.info().annotations.push({ type: 'Test case', description: 'xxx', @@ -151,7 +151,7 @@ test.describe('Ajna Base Multiply - Wallet connected', async () => { expect(updatedLoanToValue2).toBeLessThan(updatedLoanToValue); }); - test('It should open an Ajna Ethreum Multiply position @regression', async () => { + test('It should open an Ajna Base Multiply position @regression', async () => { test.info().annotations.push({ type: 'Test case', description: 'xxx', diff --git a/tests/withWallet/ajna/ethereum/ajnaMultiplyEthereum.spec.ts b/tests/withWallet/ajna/ethereum/ajnaMultiplyEthereum.spec.ts index 28fca12b..00866e74 100644 --- a/tests/withWallet/ajna/ethereum/ajnaMultiplyEthereum.spec.ts +++ b/tests/withWallet/ajna/ethereum/ajnaMultiplyEthereum.spec.ts @@ -121,7 +121,7 @@ test.describe('Ajna Ethereum Multiply - Wallet connected', async () => { }); }); - test('It should allow to simulate risk adjustment (Up & Down) with slider in an Ajna Multiply position before opening it @regression', async () => { + test('It should allow to simulate risk adjustment (Up & Down) with slider in an Ajna Ethereum Multiply position before opening it @regression', async () => { test.info().annotations.push({ type: 'Test case', description: '12730', @@ -153,7 +153,7 @@ test.describe('Ajna Ethereum Multiply - Wallet connected', async () => { expect(updatedLoanToValue2).toBeLessThan(updatedLoanToValue); }); - test('It should open an Ajna Ethreum Multiply position @regression', async () => { + test('It should open an Ajna Ethereum Multiply position @regression', async () => { test.info().annotations.push({ type: 'Test case', description: '12104', diff --git a/tests/withWallet/morphoBlue/morphoBlueMultiply.spec.ts b/tests/withWallet/morphoBlue/morphoBlueMultiply.spec.ts new file mode 100644 index 00000000..4a93224e --- /dev/null +++ b/tests/withWallet/morphoBlue/morphoBlueMultiply.spec.ts @@ -0,0 +1,319 @@ +import { BrowserContext, test } from '@playwright/test'; +import { expect, metamaskSetUp } from 'utils/setup'; +import { resetState } from '@synthetixio/synpress/commands/synpress'; +import * as metamask from '@synthetixio/synpress/commands/metamask'; +import * as tenderly from 'utils/tenderly'; +import { setup } from 'utils/setup'; +import { veryLongTestTimeout, longTestTimeout, positionTimeout } from 'utils/config'; +import { App } from 'src/app'; + +let context: BrowserContext; +let app: App; +let forkId: string; +let walletAddress: string; + +test.describe.configure({ mode: 'serial' }); + +test.describe('Morpho Blue Multiply - Wallet connected', async () => { + test.afterAll(async () => { + await tenderly.deleteFork(forkId); + + await app.page.close(); + + await context.close(); + + await resetState(); + }); + + test('It should allow to simulate a Morpho Blue Multiply position before opening it @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxx', + }); + + test.setTimeout(veryLongTestTimeout); + + await test.step('Test setup', async () => { + ({ context } = await metamaskSetUp({ network: 'mainnet' })); + let page = await context.newPage(); + app = new App(page); + + ({ forkId, walletAddress } = await setup({ app, network: 'mainnet' })); + + await tenderly.setTokenBalance({ + forkId, + network: 'mainnet', + walletAddress, + token: 'WSTETH', + balance: '100', + }); + }); + + await app.page.goto('/ethereum/morphoblue/multiply/WSTETH-USDC#setup'); + await app.position.setup.deposit({ token: 'WSTETH', amount: '15.12345' }); + + await app.position.overview.shouldHaveLiquidationPriceAfterPill( + '[0-9]{3}.[0-9]{2} WSTETH/USDC' + ); + await app.position.overview.shouldHaveLoanToValueAfterPill('[0-9]{1,2}.[0-9]{2}%'); + await app.position.overview.shouldHaveNetValueAfterPill('\\$[0-9]{2},[0-9]{3}.[0-9]{2}'); + await app.position.overview.shouldHaveBuyingPowerAfterPill({ + amount: '[0-9]{2},[0-9]{3}(.[0-9]{1,2})?', + protocol: 'Morpho Blue', + }); + await app.position.overview.shouldHaveExposureAfterPill({ + amount: '1[5-7].[0-9]{1,2}', + token: 'WSTETH', + }); + await app.position.overview.shouldHaveDebtAfterPill({ + amount: '[1-9],[0-9]{3}.[0-9]{2}', + token: 'USDC', + protocol: 'Morpho Blue', + }); + await app.position.overview.shouldHaveMultipleAfterPill('1(.[0-9]{1,2})?'); + + await app.position.setup.shouldHaveLiquidationPrice({ + amount: '[0-9]{3}(.[0-9]{1,2})? WSTETH/USDC', + }); + // Morphho Blue LTV displays both current and future values: 0.00% -> 10.00% + await app.position.setup.shouldHaveLoanToValue('0.00%10.00'); + await app.position.setup.orderInformation.shouldHaveBuyingAmount({ + tokenAmount: '[0-2].[0-9]{4}', + token: 'WSTETH', + dollarsAmount: '([1-9]{1,2},)?[0-9]{3}.[0-9]{2}', + protocol: 'Morpho Blue', + }); + await app.position.setup.orderInformation.shouldHaveTotalExposure({ + token: 'WSTETH', + current: '0.00', + future: '1[5-7].[0-9]{1,2}', + }); + await app.position.setup.orderInformation.shouldHavePriceImpact({ + amount: '[1-5],[0-9]{3}.[0-9]{2}', + percentage: '[0-9].[0-9]{2}', + protocol: 'Morpho Blue', + pair: 'WSTETH/USDC', + }); + await app.position.setup.orderInformation.shouldHaveMultiple({ + current: '1.00', + future: '1(.[0-9]{1,2})?', + }); + await app.position.setup.orderInformation.shouldHaveSlippageLimit('0.50'); + await app.position.setup.orderInformation.shouldHaveDebt({ + token: 'USDC', + current: '0.00', + future: '[1-9],[0-9]{3}.[0-9]{2}', + }); + await app.position.setup.orderInformation.shouldHaveLTV({ + current: '0.00', + future: '[0-9]{1,2}.[0-9]{2}', + protocol: 'Morpho Blue', + }); + }); + + test('It should allow to simulate risk adjustment (Up & Down) with slider in a Morpho Blue Multiply position before opening it @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxx', + }); + + const initialLiqPrice = await app.position.manage.getLiquidationPrice(); + const initialLoanToValue = await app.position.manage.getLoanToValue('Morpho Blue'); + + // RISK UP + await app.position.setup.moveSlider({ protocol: 'Morpho Blue', value: 0.5 }); + + // Wait for simulation to update with new risk + await app.position.setup.shouldHaveLiquidationPrice({ amount: '.', pair: 'WSTETH/USDC' }); + + const updatedLiqPrice = await app.position.manage.getLiquidationPrice(); + const updatedLoanToValue = await app.position.manage.getLoanToValue('Morpho Blue'); + expect(updatedLiqPrice).toBeGreaterThan(initialLiqPrice); + expect(updatedLoanToValue).toBeGreaterThan(initialLoanToValue); + + // RISK DOWN + await app.position.setup.moveSlider({ protocol: 'Morpho Blue', value: 0.3 }); + + // Wait for simulation to update with new risk + await app.position.setup.shouldHaveLiquidationPrice({ amount: '.', pair: 'WSTETH/USDC' }); + + const updatedLiqPrice2 = await app.position.manage.getLiquidationPrice(); + const updatedLoanToValue2 = await app.position.manage.getLoanToValue('Morpho Blue'); + expect(updatedLiqPrice2).toBeLessThan(updatedLiqPrice); + expect(updatedLoanToValue2).toBeLessThan(updatedLoanToValue); + }); + + test('It should open a Morpho Blue Multiply position @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxx', + }); + + test.setTimeout(veryLongTestTimeout); + + await app.page.goto('/ethereum/morphoblue/multiply/WSTETH-ETH#setup'); + + await app.position.setup.deposit({ token: 'WSTETH', amount: '30.12345' }); + await app.position.setup.createSmartDeFiAccount(); + + // Smart DeFi Acount creation randomly fails - Retry until it's created. + await expect(async () => { + await app.position.setup.createSmartDeFiAccount(); + await test.step('Metamask: ConfirmAddToken', async () => { + await metamask.confirmAddToken(); + }); + await app.position.setup.continueShouldBeVisible(); + }).toPass({ timeout: longTestTimeout }); + + await app.position.setup.continue(); + + // Setting up allowance randomly fails - Retry until it's set. + await expect(async () => { + await app.position.setup.approveAllowance(); + await test.step('Metamask: ConfirmAddToken', async () => { + await metamask.confirmAddToken(); + }); + await app.position.setup.continueShouldBeVisible(); + }).toPass({ timeout: longTestTimeout }); + + await app.position.setup.continue(); + + // ====================================================================== + + // UI sometimes gets stuck after confirming position creation + // - 'Reload' added to avoid flakines + + await app.position.setup.confirm(); + await test.step('Metamask: ConfirmPermissionToSpend', async () => { + await metamask.confirmPermissionToSpend(); + }); + await app.position.setup.shouldShowCreatingPosition(); + + await app.page.reload(); + await app.position.setup.goToPosition(); + + // ====================================================================== + + await app.position.manage.shouldBeVisible('Manage your Morpho Multiply'); + }); + + test('It should adjust risk of an existing Morpho Blue Multiply position - Up @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxx', + }); + + test.setTimeout(longTestTimeout); + + const initialLiqPrice = await app.position.manage.getLiquidationPrice(); + const initialLoanToValue = await app.position.manage.getLoanToValue(); + + await app.position.setup.moveSlider({ protocol: 'Morpho Blue', value: 0.6 }); + + await app.position.manage.confirm(); + + // ====================================================================== + + // UI sometimes gets stuck after confirming position creation + // - 'Reload' added to avoid flakines + + await app.position.setup.confirm(); + await test.step('Metamask: ConfirmPermissionToSpend', async () => { + await metamask.confirmPermissionToSpend(); + }); + await app.position.setup.shouldShowUpdatingPosition(); + + await app.page.reload(); + + // ====================================================================== + + await app.position.manage.shouldBeVisible('Manage your Morpho Multiply'); + const updatedLiqPrice = await app.position.manage.getLiquidationPrice(); + const updatedLoanToValue = await app.position.manage.getLoanToValue(); + expect(updatedLiqPrice).toBeGreaterThan(initialLiqPrice); + expect(updatedLoanToValue).toBeGreaterThan(initialLoanToValue); + }); + + test('It should adjust risk of an existing Morpho Blue Multiply position - Down @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxxx', + }); + + test.setTimeout(longTestTimeout); + + const initialLiqPrice = await app.position.manage.getLiquidationPrice(); + const initialLoanToValue = await app.position.manage.getLoanToValue(); + + await app.position.setup.moveSlider({ protocol: 'Morpho Blue', value: 0.5 }); + + await app.position.manage.confirm(); + + // ====================================================================== + + // UI sometimes gets stuck after confirming position creation + // - 'Reload' added to avoid flakines + + await app.position.setup.confirm(); + await test.step('Metamask: ConfirmPermissionToSpend', async () => { + await metamask.confirmPermissionToSpend(); + }); + await app.position.setup.shouldShowUpdatingPosition(); + + await app.page.reload(); + + // ====================================================================== + + await app.position.manage.shouldBeVisible('Manage your Morpho Multiply'); + const updatedLiqPrice = await app.position.manage.getLiquidationPrice(); + const updatedLoanToValue = await app.position.manage.getLoanToValue(); + expect(updatedLiqPrice).toBeLessThan(initialLiqPrice); + expect(updatedLoanToValue).toBeLessThan(initialLoanToValue); + }); + + test('It should Close to collateral an existing Morpho Blue Multiply position @regression', async () => { + test.info().annotations.push({ + type: 'Test case', + description: 'xxxx', + }); + + test.setTimeout(longTestTimeout); + + await app.position.manage.openManageOptions({ currentLabel: 'Adjust' }); + await app.position.manage.select('Close position'); + await app.position.manage.shouldHaveTokenAmountAfterClosing({ + token: 'WSTETH', + amount: '[0-9]{1,2}.[0-9]{1,2}', + }); + + await app.position.setup.confirm(); + + // ============================================================ + + // UI sometimes gets stuck after confirming position update + // - 'Reload' added to avoid flakines + await app.position.setup.confirm(); + await test.step('Metamask: ConfirmPermissionToSpend', async () => { + await metamask.confirmPermissionToSpend(); + }); + await app.position.setup.shouldShowUpdatingPosition(); + await app.page.reload(); + + // ============================================================ + + await app.position.overview.shouldHaveLiquidationPrice({ + price: '0.00', + timeout: positionTimeout, + }); + await app.position.overview.shouldHaveLoanToValue('0.00'); + await app.position.overview.shouldHaveNetValue({ value: '0.00' }); + await app.position.overview.shouldHaveBuyingPower('0.00'); + await app.position.overview.shouldHaveExposure({ token: 'WSTETH', amount: '0.00' }); + await app.position.overview.shouldHaveDebt({ + amount: '0.00', + token: 'ETH', + protocol: 'Morpho Blue', + }); + await app.position.overview.shouldHaveMultiple('1.00'); + }); +});