Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v7.3.4 #2561

Merged
merged 11 commits into from
Jul 4, 2023
Next Next commit
chore: market page tests and jest environment update (#2529)
* Update market tests

* Add Github action workflow for jest tests

* Update tests CI workflow

* Add pnpm i step to workflow

* Clean up

* Fix sdk mock

* Update Jest config

* Fix warnings

* Update tests CI config

* Update market test and add codecov action

* Rename InfoBox textValueSpan prop to textValueIcon

* Remove unused type

* Correct market closed test

* Update codecov workflow

* Temp comment out test

* fix: lint warnings

* fix: jest workflow

* fix: sentry workflow

* Mock rainbowkit for all tests

* Add codecov yml to enforce coverage

---------

Co-authored-by: Ralf <[email protected]>
  • Loading branch information
avclarke and platschi authored Jun 30, 2023
commit ddc82c28f2aa7e6f938bdccdc6ed65795ad16929
21 changes: 21 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
coverage:
precision: 2
round: down
range: '50...85' # Will lift this number as coverage increases

status:
project:
default:
# basic
target: auto
threshold: 1%
base: auto
patch:
default:
# basic
target: 80%
threshold: 1%
base: auto

ignore:
- 'packages/sdk/.*' # Waiting for sdk tests
42 changes: 42 additions & 0 deletions .github/workflows/jest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: 'Jest'
on:
push:
branches:
- master
pull_request:

jobs:
sentry_release:
name: Jest tests
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: 16.x

- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false

- name: Install dependencies
run: pnpm i

- name: Run tests
run: cd packages/app && pnpm run test:jest
env:
NEXT_PUBLIC_WALLETCONNECT_V2_ID: ${{ secrets.NEXT_PUBLIC_WALLETCONNECT_V2_ID }}

- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
2 changes: 2 additions & 0 deletions .github/workflows/sentry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ jobs:

- name: Build code
run: pnpm i && cd packages/app && pnpm build
env:
NEXT_PUBLIC_WALLETCONNECT_V2_ID: ${{ secrets.NEXT_PUBLIC_WALLETCONNECT_V2_ID }}

- name: Create Sentry release
uses: getsentry/action-release@v1
Expand Down
57 changes: 0 additions & 57 deletions packages/app/__tests__/pages/market.test.tsx

This file was deleted.

27 changes: 16 additions & 11 deletions packages/app/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
const nextJest = require('next/jest');
const nextJest = require('next/jest')

const createJestConfig = nextJest({ dir: './' });
const createJestConfig = nextJest({ dir: './' })

const customJestConfig = {
roots: ['<rootDir>'],
modulePaths: ['<rootDir>'],
moduleDirectories: ['node_modules'],
roots: ['<rootDir>', 'src'],
modulePaths: ['<rootDir>', 'src'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
'@kwenta/sdk/(.+)$': '<rootDir>/../sdk/dist/$1',
'@kwenta/sdk': '<rootDir>/../sdk/dist/index.js',
},
globalSetup: './testing/unit/setup/global.js',
setupFilesAfterEnv: ['./testing/unit/setup/setup.js'],
testEnvironment: 'jest-environment-jsdom',
transform: {
'^.+\\.(svg)$': `jest-transformer-svg`,
},
};
}

const getCustomConfig = async () => {
// Delete next js module name mapper transform and use above svg
// transformer to avoid errors with svg and styled components
const config = await createJestConfig(customJestConfig)();
delete config['moduleNameMapper']['^.+\\.(svg)$'];
return config;
};
const config = await createJestConfig(customJestConfig)()
delete config['moduleNameMapper']['^.+\\.(svg)$']
config.transformIgnorePatterns = []
return config
}

module.exports = getCustomConfig();
module.exports = getCustomConfig()
2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"export": "next export",
"check-types": "tsc --noEmit",
"jest-preview": "jest-preview",
"test:unit": "jest --coverage --detectOpenHandles",
"test:jest": "jest --coverage --detectOpenHandles",
"test:e2e": "start-server-and-test 'pnpm build && pnpm start' http-get://localhost:3000 'synpress run'",
"test:e2e:only:tests": "synpress run",
"test:e2e:open:testrunner": "synpress open",
Expand Down
196 changes: 196 additions & 0 deletions packages/app/src/__tests__/pages/market.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { FuturesMarket } from '@kwenta/sdk/dist/types'
import { wei } from '@synthetixio/wei'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { ReactNode } from 'react'

import { fetchMarkets } from 'state/futures/actions'

import { mockResizeObserver } from '../../../testing/unit/mocks/app'
import { PRELOADED_STATE } from '../../../testing/unit/mocks/data/app'
import {
mockSmartMarginAccount,
preloadedStateWithSmartMarginAccount,
SDK_MARKETS,
} from '../../../testing/unit/mocks/data/futures'
import { mockUseWindowSize } from '../../../testing/unit/mocks/hooks'
import mockConnector from '../../../testing/unit/mocks/mockConnector'
import MockProviders from '../../../testing/unit/mocks/MockProviders'
import { mockReactQuery } from '../../../testing/unit/mocks/queries'
import Market from '../../pages/market'
import { selectTradePreview } from '../../state/futures/selectors'
import sdk from '../../state/sdk'
import { setupStore } from '../../state/store'

jest.mock('../../state/sdk')

jest.mock('../../queries/futures/useGetFuturesTrades', () => {
return jest.fn(() => ({
data: [],
isLoading: false,
fetchNextPage: () => {},
}))
})

jest.mock('../../components/Media', () => ({
...jest.requireActual('../../components/Media'),
DesktopOnlyView: ({ children }: { children: ReactNode }) => <div>{children}</div>,
MobileOnlyView: ({ children }: { children: ReactNode }) => <div>{children}</div>,
}))

describe('Futures market page - smart margin', () => {
beforeAll(() => {
jest.setTimeout(60000)
mockUseWindowSize()
mockReactQuery()
mockResizeObserver()
mockConnector()
})

test('Calculates correct fees from trade preview', async () => {
const { findByTestId, findByText } = render(
<MockProviders
route="market/?accountType=cross_margin&asset=sETH"
preloadedState={PRELOADED_STATE}
>
<Market />
</MockProviders>
)

const marginInput = await findByTestId('set-order-margin-susd-desktop')
fireEvent.change(marginInput, { target: { value: '100' } })

const sizeInput = await findByTestId('set-order-size-amount-susd-desktop')
fireEvent.change(sizeInput, { target: { value: '1000' } })

const fees = await findByText('$1.69')
expect(fees).toBeTruthy()
})

test('Submits LONG order with correct desired fill price', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
<MockProviders route="market/?accountType=cross_margin&asset=sETH" store={store}>
<Market />
</MockProviders>
)

const marginInput = await findByTestId('set-order-margin-susd-desktop')
fireEvent.change(marginInput, { target: { value: '100' } })

const sizeInput = await findByTestId('set-order-size-amount-susd-desktop')
fireEvent.change(sizeInput, { target: { value: '1000' } })

const fees = await findByText('$1.69')
expect(fees).toBeTruthy()

const submitButton = await findByTestId('trade-panel-submit-button')
fireEvent.click(submitButton)

const confirmButton = await findByTestId('trade-confirm-order-button')
fireEvent.click(confirmButton)

// Preview generated fill price displayed in confirmation view
const fillPrice = await findByText('$1,847.76')
expect(fillPrice).toBeTruthy()

// Desired fill price is higher than fill price by 1%
// (as a long order the price is worse to account for slippage in delayed order)
expect(selectTradePreview(store.getState())?.desiredFillPrice.toString()).toBe(
'1866.234411491951332934'
)
})

test('Submits SHORT order with correct desired fill price', async () => {
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
<MockProviders route="market/?accountType=cross_margin&asset=sETH" store={store}>
<Market />
</MockProviders>
)

const shortToggle = await findByTestId('position-side-short-button')
fireEvent.click(shortToggle)

const marginInput = await findByTestId('set-order-margin-susd-desktop')
fireEvent.change(marginInput, { target: { value: '100' } })

const sizeInput = await findByTestId('set-order-size-amount-susd-desktop')
fireEvent.change(sizeInput, { target: { value: '1000' } })

const fees = await findByText('$1.69')
expect(fees).toBeTruthy()

const submitButton = await findByTestId('trade-panel-submit-button')
fireEvent.click(submitButton)

const confirmButton = await findByTestId('trade-confirm-order-button')
fireEvent.click(confirmButton)

// Preview generated fill price displayed in confirmation view
const fillPrice = await findByText('$1,847.76')
expect(fillPrice).toBeTruthy()

// Desired fill price is lower than fill price by 1%
// (as a short order the price is worse to account for slippage in delayed order)
expect(selectTradePreview(store.getState())?.desiredFillPrice.toString()).toBe(
'1829.279274630724573866'
)
})

test('Displays error when trade exceeds max OI', async () => {
// Update the mock to return some different data
sdk.futures.getMarkets = () =>
Promise.resolve([{ ...SDK_MARKETS[1], marketLimitUsd: wei(100000) } as FuturesMarket])

const store = setupStore(
preloadedStateWithSmartMarginAccount(mockSmartMarginAccount('1000000'))
)
const { findByTestId, findByText } = render(
<MockProviders route="market/?accountType=cross_margin&asset=sETH" store={store}>
<Market />
</MockProviders>
)

const marginInput = await findByTestId('set-order-margin-susd-desktop')
fireEvent.change(marginInput, { target: { value: '100000' } })

const sizeInput = await findByTestId('set-order-size-amount-susd-desktop')
fireEvent.change(sizeInput, { target: { value: '1000000' } })

// OI limit warning displayed
const fillPrice = await findByText('Open interest limit exceeded')
expect(fillPrice).toBeTruthy()
})

test('Trade panel is disabled when market is closed', async () => {
sdk.futures.getMarkets = () => Promise.resolve([...SDK_MARKETS] as FuturesMarket[])
const store = setupStore(preloadedStateWithSmartMarginAccount())
const { findByTestId, findByText } = render(
<MockProviders route="market/?accountType=cross_margin&asset=sETH" store={store}>
<Market />
</MockProviders>
)

const marginInput = await findByTestId('set-order-margin-susd-desktop')
fireEvent.change(marginInput, { target: { value: '100' } })

const sizeInput = await findByTestId('set-order-size-amount-susd-desktop')
fireEvent.change(sizeInput, { target: { value: '1000' } })

const fees = await findByText('$1.69')
expect(fees).toBeTruthy()

const submitButton = await findByTestId('trade-panel-submit-button')
expect(submitButton).toBeEnabled()

sdk.futures.getMarkets = () =>
Promise.resolve([{ ...SDK_MARKETS[1], isSuspended: true } as FuturesMarket])

waitFor(() => store.dispatch(fetchMarkets()))

const message = await findByText('Market suspended')
expect(message).toBeTruthy()

expect(submitButton).toBeDisabled()
})
})
Loading
Loading