-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from khulnasoft-lab/playwright
Playwright
- Loading branch information
Showing
10 changed files
with
511 additions
and
56 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 |
---|---|---|
|
@@ -34,3 +34,7 @@ yarn-error.log* | |
|
||
|
||
components.d.ts | ||
/test-results/ | ||
/playwright-report/ | ||
/blob-report/ | ||
/playwright/.cache/ |
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,51 @@ | ||
import { Page } from '@playwright/test'; | ||
|
||
export async function setLoginInfo(page) { | ||
await page.goto('/'); | ||
await page.evaluate(() => { | ||
localStorage.setItem( | ||
'ftAuthLoginInfo', | ||
JSON.stringify({ | ||
'ftbot.0': { | ||
botName: 'TestBot', | ||
apiUrl: 'http://localhost:3000', | ||
accessToken: 'access_token_tesst', | ||
refreshToken: 'refresh_test', | ||
autoRefresh: true, | ||
}, | ||
}), | ||
); | ||
localStorage.setItem('ftSelectedBot', 'ftbot.0'); | ||
}); | ||
} | ||
|
||
export async function defaultMocks(page: Page) { | ||
page.route('**/api/v1/**', (route) => { | ||
route.fulfill({ | ||
headers: { 'access-control-allow-origin': '*' }, | ||
json: {}, | ||
}); | ||
}); | ||
|
||
await page.route('**/api/v1/ping', (route) => { | ||
return route.fulfill({ path: './cypress/fixtures/ping.json' }); | ||
}); | ||
await page.route('**/api/v1/show_config', (route) => { | ||
return route.fulfill({ path: './cypress/fixtures/show_config.json' }); | ||
}); | ||
await page.route('**/api/v1/pair_candles?*', (route) => { | ||
return route.fulfill({ path: './cypress/fixtures/pair_candles_btc_1m.json' }); | ||
}); | ||
} | ||
|
||
export function getWaitForResponse(page: Page, url: string) { | ||
const urlMapping = { | ||
'@Ping': '**/api/v1/ping', | ||
'@ShowConf': '**/api/v1/show_config', | ||
'@PairCandles': '**/api/v1/pair_candles', | ||
'@Logs': '**/api/v1/logs', | ||
}; | ||
const urlMap = urlMapping[url] ?? url; | ||
|
||
return page.waitForResponse(urlMap); | ||
} |
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,32 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
import { setLoginInfo, defaultMocks, getWaitForResponse } from './helpers'; | ||
|
||
test.describe('Logs', () => { | ||
test('Displays and reloads logs', async ({ page }) => { | ||
/// | ||
await defaultMocks(page); | ||
await setLoginInfo(page); | ||
// const pingPromise = page.route('**/*ping*', | ||
|
||
// const logsPromise = page.waitForResponse('**/api/v1/logs'); | ||
await page.route('**/api/v1/logs', (route) => { | ||
return route.fulfill({ path: './cypress/fixtures/logs.json' }); | ||
}); | ||
|
||
const logs = getWaitForResponse(page, '@Logs'); | ||
const ping = getWaitForResponse(page, '@ShowConf'); | ||
await page.goto('/logs', { waitUntil: 'networkidle' }); | ||
await Promise.all([logs, ping]); | ||
|
||
await expect(page.locator('span', { hasText: 'Checking exchange' })).toBeVisible(); | ||
await expect(page.locator('span', { hasText: 'Checking exchange' })).toHaveText( | ||
/Checking exchange.../, | ||
{}, | ||
); | ||
// const logsPromise = page.waitForResponse('**/api/v1/logs'); | ||
const logsPromise = getWaitForResponse(page, '@Logs'); | ||
await page.getByRole('button', { name: 'Reload Logs' }).click(); | ||
await logsPromise; | ||
}); | ||
}); |
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,151 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
import { setLoginInfo, defaultMocks } from './helpers'; | ||
|
||
function tradeMocks(page) { | ||
const mapping = [ | ||
{ name: '@Status', url: '**/api/v1/status', fixture: 'status_empty.json' }, | ||
{ name: '@Profit', url: '**/api/v1/profit', fixture: 'profit.json' }, | ||
{ name: '@Trades', url: '**/api/v1/trades*', fixture: 'trades.json' }, | ||
{ name: '@Balance', url: '**/api/v1/balance', fixture: 'balance.json' }, | ||
{ name: '@Whitelist', url: '**/api/v1/whitelist', fixture: 'whitelist.json' }, | ||
{ name: '@Blacklist', url: '**/api/v1/blacklist', fixture: 'blacklist.json' }, | ||
{ name: '@Locks', url: '**/api/v1/locks', fixture: 'locks_empty.json' }, | ||
{ name: '@Performance', url: '**/api/v1/performance', fixture: 'performance.json' }, | ||
{ | ||
name: '@ReloadConfig', | ||
method: 'POST', | ||
url: '**/api/v1/reload_config', | ||
fixture: 'reload_config.json', | ||
}, | ||
]; | ||
mapping.forEach((item) => { | ||
page.route(item.url, (route) => { | ||
return route.fulfill({ path: `./cypress/fixtures/${item.fixture}` }); | ||
}); | ||
}); | ||
} | ||
|
||
test.describe('Trade', () => { | ||
test.beforeEach(async ({ page }) => { | ||
await defaultMocks(page); | ||
await setLoginInfo(page); | ||
|
||
await tradeMocks(page); | ||
}); | ||
test('Trade page', async ({ page }) => { | ||
await Promise.all([ | ||
page.goto('/trade'), | ||
// Wait for network requests | ||
// page.waitForResponse('**/ping'), | ||
page.waitForResponse('**/status'), | ||
page.waitForResponse('**/profit'), | ||
page.waitForResponse('**/balance'), | ||
// page.waitForResponse('**/trades'), | ||
page.waitForResponse('**/whitelist'), | ||
page.waitForResponse('**/blacklist'), | ||
page.waitForResponse('**/locks'), | ||
]); | ||
|
||
// // Check visibility of elements | ||
await expect(page.locator('.drag-header', { hasText: 'Multi Pane' })).toBeInViewport(); | ||
await expect(page.locator('.drag-header', { hasText: 'Chart' })).toBeInViewport(); | ||
// Pairlist elements | ||
await expect(page.locator('button', { hasText: 'BTC/USDT' })).toBeInViewport(); | ||
await expect(page.locator('button', { hasText: 'ETH/USDT' })).toBeInViewport(); | ||
|
||
// // Click on Performance button and wait for response | ||
await Promise.all([ | ||
page.waitForResponse('**/performance'), | ||
page.click('button:has-text("Performance")'), | ||
]); | ||
|
||
// // Check visibility of Profit USDT | ||
await expect(page.locator('th:has-text("Profit USDT")')).toBeInViewport(); | ||
|
||
// // Test messageBox behavior | ||
|
||
const dialogModal = page.getByRole('dialog'); | ||
const modalButton = page.locator( | ||
'#MsgBoxModal .modal-dialog > .modal-content > .modal-footer > .btn-secondary:has-text("Cancel")', | ||
); | ||
await expect(dialogModal).not.toBeVisible(); | ||
await expect(dialogModal).not.toBeInViewport(); | ||
|
||
await expect(modalButton).not.toBeVisible(); | ||
|
||
await page.getByRole('button', { name: 'Stop Trading - Also stops' }).click(); | ||
|
||
// Modal open | ||
await expect(dialogModal).toBeVisible(); | ||
await expect(dialogModal).toBeInViewport(); | ||
await expect(modalButton).toBeInViewport(); | ||
|
||
// // Close modal | ||
await modalButton.click(); | ||
|
||
// // Modal closed | ||
await expect(modalButton).not.toBeVisible(); | ||
await expect(modalButton).not.toBeInViewport(); | ||
|
||
// // Click on General tab | ||
const performancePair = page.locator('td:has-text("XRP/USDT")'); | ||
await expect(performancePair).toBeInViewport(); | ||
await page.click('button[role="tab"]:has-text("General")'); | ||
|
||
// // Check visibility of elements | ||
await expect(performancePair).not.toBeInViewport(); | ||
const openTrades = page.locator('.drag-header:has-text("Open Trades")'); | ||
openTrades.scrollIntoViewIfNeeded(); | ||
await expect(openTrades).toBeInViewport(); | ||
const closedTrades = page.locator('.drag-header:has-text("Closed Trades")'); | ||
closedTrades.scrollIntoViewIfNeeded(); | ||
await expect(closedTrades).toBeInViewport(); | ||
await expect(page.locator('span:has-text("TRX/USDT")')).toBeInViewport(); | ||
await expect(page.locator('td:has-text("8070.5")')).toBeInViewport(); | ||
|
||
// Scroll to top | ||
const multiPane = page.locator('.drag-header', { hasText: 'Multi Pane' }); | ||
await expect(multiPane).toBeVisible(); | ||
await multiPane.scrollIntoViewIfNeeded(); | ||
await expect(multiPane).toBeInViewport(); | ||
|
||
// // Click on Reload Config button | ||
await page.getByRole('button', { name: 'Reload Config' }).click(); | ||
// await page.locator('button[title*="Reload Config "]').click(); | ||
await expect(dialogModal).toBeVisible(); | ||
await expect(dialogModal).toBeInViewport(); | ||
|
||
const modalOkButton = page.locator( | ||
'#MsgBoxModal .modal-dialog > .modal-content > .modal-footer > .btn-primary:has-text("Ok")', | ||
); | ||
await expect(modalOkButton).toBeVisible(); | ||
await modalOkButton.click(); | ||
|
||
await expect(page.getByText('Config reloaded successfully.')).toBeInViewport(); | ||
}); | ||
test('Trade page - drag and drop', async ({ page }) => { | ||
await page.goto('/trade'); | ||
|
||
await page.locator('#avatar-drop').click(); | ||
const multiPane = page.locator('.drag-header', { hasText: 'Multi Pane' }); | ||
|
||
await page.getByLabel('Lock layout').uncheck(); | ||
|
||
const chartHeader = await page.locator('.drag-header:has-text("Chart")'); | ||
await expect(multiPane).toBeInViewport(); | ||
await expect(chartHeader).toBeInViewport(); | ||
|
||
// Test drag and drop functionality | ||
const chartHeaderbb = await chartHeader.boundingBox(); | ||
if (chartHeaderbb) { | ||
await chartHeader.hover(); | ||
await page.mouse.down(); | ||
|
||
await page.mouse.move(chartHeaderbb?.x + chartHeaderbb.width / 2, chartHeaderbb?.y + 200); | ||
await page.mouse.up(); | ||
await expect(multiPane).toBeInViewport(); | ||
await expect(chartHeader).toBeInViewport(); | ||
} | ||
}); | ||
}); |
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,80 @@ | ||
import { defineConfig, devices } from '@playwright/test'; | ||
|
||
/** | ||
* Read environment variables from file. | ||
* https://github.com/motdotla/dotenv | ||
*/ | ||
// require('dotenv').config(); | ||
|
||
/** | ||
* See https://playwright.dev/docs/test-configuration. | ||
*/ | ||
export default defineConfig({ | ||
testDir: './e2e', | ||
/* Run tests in files in parallel */ | ||
fullyParallel: true, | ||
/* Fail the build on CI if you accidentally left test.only in the source code. */ | ||
forbidOnly: !!process.env.CI, | ||
/* Retry on CI only */ | ||
retries: process.env.CI ? 2 : 0, | ||
/* Opt out of parallel tests on CI. */ | ||
workers: process.env.CI ? 1 : undefined, | ||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ | ||
reporter: 'html', | ||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ | ||
use: { | ||
/* Base URL to use in actions like `await page.goto('/')`. */ | ||
baseURL: 'http://localhost:3000', | ||
|
||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ | ||
trace: 'on-first-retry', | ||
}, | ||
|
||
/* Configure projects for major browsers */ | ||
projects: [ | ||
{ | ||
name: 'chromium', | ||
use: { ...devices['Desktop Chrome'], channel: 'chrome' }, | ||
}, | ||
{ | ||
name: 'msedge', | ||
use: { ...devices['Desktop Edge'], channel: 'msedge' }, // or "msedge-beta" or 'msedge-dev' | ||
}, | ||
{ | ||
name: 'firefox', | ||
use: { ...devices['Desktop Firefox'] }, | ||
}, | ||
|
||
{ | ||
name: 'webkit', | ||
use: { ...devices['Desktop Safari'] }, | ||
}, | ||
|
||
/* Test against mobile viewports. */ | ||
// { | ||
// name: 'Mobile Chrome', | ||
// use: { ...devices['Pixel 5'] }, | ||
// }, | ||
// { | ||
// name: 'Mobile Safari', | ||
// use: { ...devices['iPhone 12'] }, | ||
// }, | ||
|
||
/* Test against branded browsers. */ | ||
// { | ||
// name: 'Microsoft Edge', | ||
// use: { ...devices['Desktop Edge'], channel: 'msedge' }, | ||
// }, | ||
// { | ||
// name: 'Google Chrome', | ||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' }, | ||
// }, | ||
], | ||
|
||
/* Run your local dev server before starting the tests */ | ||
webServer: { | ||
command: 'yarn dev', | ||
url: 'http://localhost:3000', | ||
reuseExistingServer: !process.env.CI, | ||
}, | ||
}); |
Oops, something went wrong.