-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
98 changed files
with
1,654 additions
and
1,049 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,81 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import { navigateTo, screenshot, waitFor } from '../utils/operators'; | ||
import { mockApi } from '../utils/mock-api'; | ||
import { setupImposter } from '../utils/debug'; | ||
import { setupImposter } from '../utils/DebugDriver'; | ||
import { CreateStrategyDriver, MyStrategyDriver } from '../utils/strategy'; | ||
import { NotificationDriver } from '../utils/NotificationDriver'; | ||
import { checkApproval } from '../utils/modal'; | ||
|
||
test.describe('Strategies', () => { | ||
test.beforeEach(async ({ page }) => { | ||
await Promise.all([mockApi(page), setupImposter(page)]); | ||
}); | ||
test('First Strategy Page', async ({ page }) => { | ||
await navigateTo(page, '/'); | ||
await page.getByTestId('first-strategy').waitFor({ state: 'visible' }); | ||
const driver = new MyStrategyDriver(page); | ||
await driver.firstStrategy().waitFor({ state: 'visible' }); | ||
await screenshot(page, 'first-strategy'); | ||
}); | ||
|
||
test('Create Limit Strategy ETH->DAI', async ({ page }) => { | ||
test.setTimeout(120_000); | ||
await waitFor(page, 'balance-DAI', 20_000); | ||
const configs = [ | ||
{ | ||
base: 'ETH', | ||
quote: 'DAI', | ||
buy: { | ||
price: '1500', | ||
budget: '10', | ||
}, | ||
sell: { | ||
price: '1700', | ||
budget: '2', | ||
}, | ||
}, | ||
]; | ||
|
||
await navigateTo(page, '/'); | ||
await page.getByTestId('create-strategy-desktop').click(); | ||
|
||
// Select Base | ||
await page.getByTestId('select-base-token').click(); | ||
await waitFor(page, 'modal-container'); | ||
await page.getByLabel('Select Token').fill('eth'); | ||
await page.getByTestId('select-token-ETH').click(); | ||
await page.getByTestId('modal-container').waitFor({ state: 'detached' }); | ||
for (const config of configs) { | ||
const { base, quote } = config; | ||
test(`Create Limit Strategy ${base}->${quote}`, async ({ page }) => { | ||
test.setTimeout(180_000); | ||
await waitFor(page, `balance-${quote}`, 30_000); | ||
|
||
// Select Quote | ||
await page.getByTestId('select-quote-token').click(); | ||
await waitFor(page, 'modal-container'); | ||
await page.getByLabel('Select Token').fill('dai'); | ||
await page.getByTestId('select-token-DAI').click(); | ||
await page.getByText('Next Step').click(); | ||
await navigateTo(page, '/'); | ||
const myStrategies = new MyStrategyDriver(page); | ||
const createForm = new CreateStrategyDriver(page, config); | ||
await myStrategies.createStrategy(); | ||
await createForm.selectBase(); | ||
await createForm.selectQuote(); | ||
const buy = await createForm.fillLimit('buy'); | ||
const sell = await createForm.fillLimit('sell'); | ||
|
||
// Fill Buy fields | ||
const buy = page.getByTestId('buy-section'); | ||
await buy.getByTestId('input-limit').fill('1500'); | ||
await buy.getByTestId('input-budget').fill('10'); | ||
await expect(buy.getByTestId('outcome-value')).toHaveText('0.006666 ETH'); | ||
await expect(buy.getByTestId('outcome-quote')).toHaveText('1,500 DAI'); | ||
// Assert 100% outcome | ||
await expect(buy.outcomeValue()).toHaveText(`0.006666 ${base}`); | ||
await expect(buy.outcomeQuote()).toHaveText(`1,500 ${quote}`); | ||
await expect(sell.outcomeValue()).toHaveText(`3,400 ${quote}`); | ||
await expect(sell.outcomeQuote()).toHaveText(`1,700 ${quote}`); | ||
|
||
// Fill Sell fields | ||
const sell = page.getByTestId('sell-section'); | ||
await sell.getByTestId('input-limit').fill('1700'); | ||
await sell.getByTestId('input-budget').fill('2'); | ||
await expect(sell.getByTestId('outcome-value')).toHaveText('3,400 DAI'); | ||
await expect(sell.getByTestId('outcome-quote')).toHaveText('1,700 DAI'); | ||
await createForm.submit(); | ||
|
||
await page.getByText('Create Strategy').click(); | ||
await checkApproval(page, [base, quote]); | ||
|
||
// Accept approval | ||
const approvalModal = await waitFor(page, 'approval-modal'); | ||
const ethMsg = approvalModal.getByTestId('msg-ETH'); | ||
await expect(ethMsg).toHaveText('Pre-Approved'); | ||
await approvalModal.getByTestId('approve-DAI').click(); | ||
const daiApprovalMsg = await waitFor(page, 'msg-DAI'); | ||
await expect(daiApprovalMsg).toHaveText('Approved'); | ||
await approvalModal.getByText('Create Strategy').click(); | ||
await page.waitForURL('/', { timeout: 10_000 }); | ||
|
||
await page.waitForURL('/', { timeout: 10_000 }); | ||
// Verfiy notification | ||
const notif = new NotificationDriver(page, 'create-strategy'); | ||
await expect(notif.getTitle()).toHaveText('Success'); | ||
await expect(notif.getDescription()).toHaveText( | ||
'New strategy was successfully created.' | ||
); | ||
|
||
// Verfiy notification | ||
const notif = page.getByTestId('notification-create-strategy'); | ||
await expect(notif.getByTestId('notif-title')).toHaveText('Success'); | ||
await expect(notif.getByTestId('notif-description')).toHaveText( | ||
'New strategy was successfully created.' | ||
); | ||
|
||
// Verify strategy data | ||
const strategies = page.locator('[data-testid="strategy-list"] > li'); | ||
await strategies.waitFor({ state: 'visible' }); | ||
await expect(strategies).toHaveCount(1); | ||
const [strategy] = await strategies.all(); | ||
await expect(strategy.getByTestId('token-pair')).toHaveText('ETH/DAI'); | ||
await expect(strategy.getByTestId('status')).toHaveText('Active'); | ||
await expect(strategy.getByTestId('total-budget')).toHaveText('$3,344'); | ||
await expect(strategy.getByTestId('buy-budget')).toHaveText('10 DAI'); | ||
await expect(strategy.getByTestId('buy-budget-fiat')).toHaveText('$10.00'); | ||
await expect(strategy.getByTestId('sell-budget-fiat')).toHaveText('$3,334'); | ||
}); | ||
// Verify strategy data | ||
const strategies = await myStrategies.getAllStrategies(); | ||
await expect(strategies).toHaveCount(1); | ||
const strategy = await myStrategies.getStrategy(1); | ||
await expect(strategy.pair()).toHaveText(`${base}/${quote}`); | ||
await expect(strategy.status()).toHaveText('Active'); | ||
await expect(strategy.totalBudget()).toHaveText('$3,344'); | ||
await expect(strategy.buyBudget()).toHaveText(`10 ${quote}`); | ||
await expect(strategy.buyBudgetFiat()).toHaveText('$10.00'); | ||
await expect(strategy.sellBudgetFiat()).toHaveText('$3,334'); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* eslint-disable prettier/prettier */ | ||
import { test, expect } from '@playwright/test'; | ||
import { mockApi } from '../utils/mock-api'; | ||
import { DebugDriver, setupImposter } from '../utils/DebugDriver'; | ||
import { TradeDriver } from '../utils/TradeDriver'; | ||
import { navigateTo } from '../utils/operators'; | ||
import { checkApproval } from '../utils/modal'; | ||
import { NotificationDriver } from '../utils/NotificationDriver'; | ||
|
||
test.describe('Trade', () => { | ||
test.beforeEach(async ({ page }) => { | ||
await Promise.all([mockApi(page), setupImposter(page)]); | ||
}); | ||
|
||
const configs = [ | ||
{ | ||
mode: 'buy' as const, | ||
source: 'USDC', | ||
target: 'ETH', | ||
sourceValue: '100', | ||
targetValue: '0.059554032010090174', | ||
}, | ||
{ | ||
mode: 'sell' as const, | ||
source: 'USDC', | ||
target: 'USDT', | ||
sourceValue: '100', | ||
targetValue: '100.047766', | ||
}, | ||
]; | ||
|
||
for (const config of configs) { | ||
const { mode, source, target, sourceValue, targetValue } = config; | ||
const testName = | ||
mode === 'buy' | ||
? `Buy ${target} with ${source}` | ||
: `Sell ${source} for ${target}`; | ||
|
||
test(testName, async ({ page }) => { | ||
test.setTimeout(120_000); | ||
// Store current balance | ||
const debug = new DebugDriver(page); | ||
const balance = { | ||
source: await debug.getBalance(config.source).textContent(), | ||
target: await debug.getBalance(config.target).textContent(), | ||
}; | ||
|
||
// Test Trade | ||
await navigateTo(page, '/trade?*'); | ||
const driver = new TradeDriver(page, config); | ||
|
||
// Select pair | ||
await driver.selectPair(); | ||
await driver.setPay(); | ||
await expect(driver.getReceiveInput()).toHaveValue(targetValue); | ||
|
||
// Verify routing | ||
const routing = await driver.openRouting(); | ||
await expect(routing.getSource()).toHaveValue(sourceValue); | ||
await expect(routing.getTarget()).toHaveValue(targetValue); | ||
await routing.close(); | ||
|
||
await driver.submit(); | ||
|
||
// Token approval | ||
await checkApproval(page, [config.source]); | ||
|
||
// Verify notification | ||
const notif = new NotificationDriver(page, 'trade'); | ||
await expect(notif.getTitle()).toHaveText('Success', { timeout: 10_000 }); | ||
await expect(notif.getDescription()).toHaveText( | ||
`Trading ${sourceValue} ${source} for ${target} was successfully completed.`, | ||
{ timeout: 10_000 } | ||
); | ||
|
||
// Verify form empty | ||
expect(driver.getPayInput()).toHaveValue(''); | ||
expect(driver.getReceiveInput()).toHaveValue(''); | ||
|
||
// Check balance diff | ||
await navigateTo(page, '/debug'); | ||
|
||
const sourceDelta = Number(balance.source) - Number(config.sourceValue); | ||
const nextSource = new RegExp(sourceDelta.toString()); | ||
await expect(debug.getBalance(config.source)).toHaveText(nextSource); | ||
const targetDelta = Number(balance.target) + Number(config.targetValue); | ||
const nextTarget = new RegExp(targetDelta.toString()); | ||
await expect(debug.getBalance(config.target)).toHaveText(nextTarget); | ||
}); | ||
} | ||
}); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Page } from '@playwright/test'; | ||
|
||
type NotificationType = | ||
| 'reject' | ||
| 'approve' | ||
| 'revoke' | ||
| 'approve-error' | ||
| 'create-strategy' | ||
| 'renew-strategy' | ||
| 'edit-strategy-name' | ||
| 'withdraw-strategy' | ||
| 'delete-strategy' | ||
| 'change-rates-strategy' | ||
| 'trade'; | ||
|
||
export class NotificationDriver { | ||
private notif = this.page.getByTestId(`notification-${this.type}`); | ||
constructor(private page: Page, private type: NotificationType) {} | ||
|
||
getTitle() { | ||
return this.notif.getByTestId('notif-title'); | ||
} | ||
getDescription() { | ||
return this.notif.getByTestId('notif-description'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* eslint-disable prettier/prettier */ | ||
import { Page } from '@playwright/test'; | ||
import { waitFor } from './operators'; | ||
import { closeModal, waitModalClose, waitModalOpen } from './modal'; | ||
|
||
interface TradeConfig { | ||
mode: 'buy' | 'sell'; | ||
source: string; | ||
target: string; | ||
sourceValue: string; | ||
targetValue: string; | ||
} | ||
|
||
export class TradeDriver { | ||
public form = this.page.getByTestId(`${this.config.mode}-form`); | ||
|
||
constructor(private page: Page, private config: TradeConfig) {} | ||
|
||
getPayInput() { | ||
return this.form.getByLabel('You Pay'); | ||
} | ||
|
||
getReceiveInput() { | ||
return this.form.getByLabel('You Receive'); | ||
} | ||
|
||
async selectPair() { | ||
const { mode, target, source } = this.config; | ||
await this.page.getByTestId('select-trade-pair').click(); | ||
await waitModalOpen(this.page); | ||
const pair = mode === 'buy' ? [target, source] : [source, target]; | ||
this.page.getByTestId('search-token-pair').fill(`${pair.join(' ')}`); | ||
const select = await waitFor(this.page, `select-${pair.join('_')}`); | ||
await select.click(); | ||
await waitModalClose(this.page); | ||
} | ||
|
||
setPay() { | ||
const { sourceValue } = this.config; | ||
return this.form.getByLabel('You Pay').fill(sourceValue); | ||
} | ||
|
||
async openRouting() { | ||
await this.form.getByTestId('routing').click(); | ||
const modal = await waitFor(this.page, 'modal-container'); | ||
return { | ||
getSource: () => modal.getByTestId('confirm-source'), | ||
getTarget: () => modal.getByTestId('confirm-target'), | ||
close: () => closeModal(this.page), | ||
}; | ||
} | ||
|
||
submit() { | ||
return this.form.getByTestId('submit').click(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Page, expect } from '@playwright/test'; | ||
import { waitFor } from './operators'; | ||
|
||
export const waitModalOpen = (page: Page) => waitFor(page, 'modal-container'); | ||
export const waitModalClose = (page: Page) => { | ||
return page.getByTestId('modal-container').waitFor({ state: 'detached' }); | ||
}; | ||
export const closeModal = async (page: Page) => { | ||
await page.getByTestId('modal-close').click(); | ||
return waitModalClose(page); | ||
}; | ||
|
||
export const checkApproval = async (page: Page, tokens: string[]) => { | ||
const modal = await waitModalOpen(page); | ||
for (const token of tokens) { | ||
if (token === 'ETH') { | ||
const msg = modal.getByTestId(`msg-${token}`); | ||
await expect(msg).toHaveText('Pre-Approved'); | ||
} else { | ||
await modal.getByTestId(`approve-${token}`).click(); | ||
const msg = await waitFor(page, `msg-${token}`); | ||
await expect(msg).toHaveText('Approved'); | ||
} | ||
} | ||
await modal.getByTestId('approve-submit').click(); | ||
await closeModal(page); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.