Commit 3e8c18a2 authored by Sharafitdinova's avatar Sharafitdinova Committed by GitHub

Merge branch 'main' into fix-ci-cd

parents 3268d85d 11eb4feb
__snapshots__/** filter=lfs diff=lfs merge=lfs -text
name: playwright
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.27.0-focal
steps:
- run: apt-get update && apt-get install git-lfs
- uses: actions/checkout@v3
with:
lfs: 'true'
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: yarn
- name: Run your tests
run: HOME=/root yarn test-ct
- name: Upload test results
if: always()
uses: actions/upload-artifact@v2
with:
name: playwright-report
path: playwright-report
\ No newline at end of file
...@@ -42,3 +42,6 @@ yarn-error.log* ...@@ -42,3 +42,6 @@ yarn-error.log*
.sentryclirc .sentryclirc
**.decrypted~** **.decrypted~**
/test-results/
/playwright-report/
/playwright/.cache/
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-checkout'.\n"; exit 2; }
git lfs post-checkout "$@"
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-commit'.\n"; exit 2; }
git lfs post-commit "$@"
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/post-merge'.\n"; exit 2; }
git lfs post-merge "$@"
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; }
git lfs pre-push "$@"
...@@ -9,6 +9,7 @@ Core technologies what used in the project are ...@@ -9,6 +9,7 @@ Core technologies what used in the project are
- [Next.js](https://nextjs.org/) as application framework - [Next.js](https://nextjs.org/) as application framework
- [Chakra](https://chakra-ui.com/) as component library; our theme customization could be found in `/theme` folder - [Chakra](https://chakra-ui.com/) as component library; our theme customization could be found in `/theme` folder
- [css-modules](https://github.com/css-modules/css-modules) as lib for styling custom components - [css-modules](https://github.com/css-modules/css-modules) as lib for styling custom components
- [playwright](https://playwright.dev/) as a tool for components visual testing
And of course our premier language is [Typescript](https://www.typescriptlang.org/) And of course our premier language is [Typescript](https://www.typescriptlang.org/)
...@@ -26,6 +27,11 @@ For local development please follow next steps: ...@@ -26,6 +27,11 @@ For local development please follow next steps:
- for custom network setup create `.env.local` file with all required environment variables from the [list](#environment-variables) and run `yarn dev` - for custom network setup create `.env.local` file with all required environment variables from the [list](#environment-variables) and run `yarn dev`
- navigate to the host from logs output - navigate to the host from logs output
## Components visual testing
We use [playwright experimental components testing](https://playwright.dev/docs/test-components) for visual (screenshots) CI check. Test renders a single component in headless browser in docker, generates screenshots and then compares this screenshot with a reference one.
To perform testing locally you need to install docker and run `yarn test-docker`
## Environment variables ## Environment variables
### Variables list ### Variables list
The app instance could be customized by passing following variables to NodeJS environment at runtime. The app instance could be customized by passing following variables to NodeJS environment at runtime.
......
export const WEI = BigInt(10 ** 18); import BigNumber from 'bignumber.js';
export const GWEI = BigInt(10 ** 9);
export const WEI = new BigNumber(10 ** 18);
export const GWEI = new BigNumber(10 ** 9);
export const WEI_IN_GWEI = WEI.dividedBy(GWEI);
...@@ -16,7 +16,9 @@ ...@@ -16,7 +16,9 @@
"start:docker:poa_core": "docker run -p 3000:3000 --env-file ./configs/envs/.env.poa_core --env-file ./configs/envs/.env.common --env-file ./configs/envs/.env.secrets blockscout", "start:docker:poa_core": "docker run -p 3000:3000 --env-file ./configs/envs/.env.poa_core --env-file ./configs/envs/.env.common --env-file ./configs/envs/.env.secrets blockscout",
"lint:eslint": "./node_modules/.bin/eslint . --ext .js,.jsx,.ts,.tsx", "lint:eslint": "./node_modules/.bin/eslint . --ext .js,.jsx,.ts,.tsx",
"prepare": "husky install", "prepare": "husky install",
"format-svg": "./node_modules/.bin/svgo -r ./icons" "format-svg": "./node_modules/.bin/svgo -r ./icons",
"test-ct": "playwright test -c playwright-ct.config.ts",
"test-docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.27.0-focal ./run-tests.sh"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/react": "2.3.1", "@chakra-ui/react": "2.3.1",
...@@ -29,6 +31,7 @@ ...@@ -29,6 +31,7 @@
"@tanstack/react-query": "^4.0.10", "@tanstack/react-query": "^4.0.10",
"@tanstack/react-query-devtools": "^4.0.10", "@tanstack/react-query-devtools": "^4.0.10",
"@types/react-scroll": "^1.8.4", "@types/react-scroll": "^1.8.4",
"bignumber.js": "^9.1.0",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"ethers": "^5.7.1", "ethers": "^5.7.1",
"framer-motion": "^6", "framer-motion": "^6",
...@@ -46,6 +49,7 @@ ...@@ -46,6 +49,7 @@
"use-font-face-observer": "^1.2.1" "use-font-face-observer": "^1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@playwright/experimental-ct-react": "^1.26.1",
"@types/node": "17.0.36", "@types/node": "17.0.36",
"@types/react": "18.0.9", "@types/react": "18.0.9",
"@types/react-dom": "18.0.5", "@types/react-dom": "18.0.5",
...@@ -58,6 +62,7 @@ ...@@ -58,6 +62,7 @@
"eslint-plugin-regexp": "^1.7.0", "eslint-plugin-regexp": "^1.7.0",
"husky": "^8.0.0", "husky": "^8.0.0",
"lint-staged": ">=10", "lint-staged": ">=10",
"playwright": "^1.26.1",
"svgo": "^2.8.0", "svgo": "^2.8.0",
"typescript": "4.7.2" "typescript": "4.7.2"
}, },
......
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
import { devices } from '@playwright/experimental-ct-react';
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: './',
testMatch: /.*\.pw\.tsx/,
/* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */
snapshotDir: './__snapshots__',
/* Maximum time one test can run for. */
timeout: 10 * 1000,
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: Boolean(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: {
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
/* Port to use for Playwright component endpoint. */
ctPort: 3100,
headless: true,
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
],
};
export default config;
import { ChakraProvider } from '@chakra-ui/react';
import type { ColorMode } from '@chakra-ui/react';
import React from 'react';
import theme from '../theme';
type Props = {
children: React.ReactNode;
colorMode?: ColorMode;
}
const RenderWithChakra = ({ children, colorMode = 'light' }: Props) => {
return (
<ChakraProvider theme={{ ...theme, config: { ...theme.config, initialColorMode: colorMode } }}>
{ children }
</ChakraProvider>
);
};
export default RenderWithChakra;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Testing Page</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/playwright/index.ts"></script>
</body>
</html>
// Import styles, initialize component theme here.
// import '../src/common.css';
export {};
#!/bin/sh
yarn install
yarn test-ct
\ No newline at end of file
import { Box, Text, chakra } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import { utils, constants } from 'ethers'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import { WEI, GWEI } from 'lib/consts';
interface Props { interface Props {
value: string; value: string;
unit?: 'wei' | 'gwei' | 'ether'; unit?: 'wei' | 'gwei' | 'ether';
currency?: string; currency?: string;
exchangeRate?: string; exchangeRate?: string;
className?: string; className?: string;
accuracy?: number;
accuracyUsd?: number;
} }
const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className }: Props) => { const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className, accuracy, accuracyUsd }: Props) => {
const valueBn = utils.parseUnits(value, unit); let unitBn: BigNumber.Value;
const exchangeRateBn = utils.parseUnits(exchangeRate || '0', 'ether'); switch (unit) {
const usdBn = valueBn.mul(exchangeRateBn).div(constants.WeiPerEther); case 'wei':
unitBn = WEI;
break;
case 'gwei':
unitBn = GWEI;
break;
default:
unitBn = new BigNumber(1);
}
const valueBn = new BigNumber(value);
const valueCurr = valueBn.dividedBy(unitBn);
const exchangeRateBn = new BigNumber(exchangeRate || 0);
const usdBn = valueCurr.times(exchangeRateBn);
return ( return (
<Box as="span" className={ className }> <Box as="span" className={ className }>
<Text as="span"> <Text as="span">
{ Number(utils.formatUnits(valueBn)).toLocaleString() }{ currency ? ` ${ currency }` : '' }</Text> { accuracy ? valueCurr.toFixed(accuracy) : valueCurr.toFixed() }{ currency ? ` ${ currency }` : '' }
</Text>
{ exchangeRate !== undefined && exchangeRate !== null && { exchangeRate !== undefined && exchangeRate !== null &&
<Text as="span" variant="secondary" whiteSpace="pre" fontWeight={ 400 }> (${ utils.formatUnits(usdBn) })</Text> } // TODO: mb need to implement rounding to the first significant digit
<Text as="span" variant="secondary" whiteSpace="pre" fontWeight={ 400 }> (${ accuracyUsd ? usdBn.toFixed(accuracyUsd) : usdBn.toFixed() })</Text>
}
</Box> </Box>
); );
}; };
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import RenderWithChakra from '../../playwright/RenderWithChakra';
import Utilization from './Utilization';
test.use({ viewport: { width: 100, height: 50 } });
test('Utilization light', async({ mount }) => {
const component = await mount(
<RenderWithChakra>
<Utilization value={ 0.1 }/>
</RenderWithChakra>,
);
await expect(component).toHaveScreenshot();
});
test('Utilization dark', async({ mount }) => {
const component = await mount(
<RenderWithChakra colorMode="dark">
<Utilization value={ 0.1 }/>
</RenderWithChakra>,
);
await expect(component).toHaveScreenshot();
});
import { Grid, GridItem, Text, Box, Icon, Link, Flex } from '@chakra-ui/react'; import { Grid, GridItem, Text, Box, Icon, Link, Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import appConfig from 'configs/app/config'; import BigNumber from 'bignumber.js';
import { utils } from 'ethers';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
...@@ -10,6 +9,7 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -10,6 +9,7 @@ import type { Transaction } from 'types/api/transaction';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
// import errorIcon from 'icons/status/error.svg'; // import errorIcon from 'icons/status/error.svg';
// import successIcon from 'icons/status/success.svg'; // import successIcon from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -164,17 +164,17 @@ const TxDetails = () => { ...@@ -164,17 +164,17 @@ const TxDetails = () => {
title="Gas price" title="Gas price"
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage." hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
> >
<Text mr={ 1 }>{ utils.formatUnits(utils.parseUnits(String(data.gas_price), 'wei')) } { appConfig.network.currency }</Text> <Text mr={ 1 }>{ BigNumber(data.gas_price).dividedBy(WEI).toFixed() } { selectedNetwork?.currency }</Text>
<Text variant="secondary">({ Number(utils.formatUnits(utils.parseUnits(String(data.gas_price), 'wei'), 'gwei')) } Gwei)</Text> <Text variant="secondary">({ BigNumber(data.gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Gas limit & usage by txn" title="Gas limit & usage by txn"
hint="Actual gas amount used by the transaction." hint="Actual gas amount used by the transaction."
> >
<Text>{ utils.commify(data.gas_used) }</Text> <Text>{ BigNumber(data.gas_used).toFormat() }</Text>
<TextSeparator/> <TextSeparator/>
<Text >{ utils.commify(data.gas_limit) }</Text> <Text >{ BigNumber(data.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ utils.parseUnits(data.gas_used).mul(10_000).div(utils.parseUnits(data.gas_limit)).toNumber() / 10_000 }/> <Utilization ml={ 4 } value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas_limit)).toNumber() }/>
</DetailsInfoItem> </DetailsInfoItem>
{ (data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && ( { (data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && (
<DetailsInfoItem <DetailsInfoItem
...@@ -185,21 +185,21 @@ const TxDetails = () => { ...@@ -185,21 +185,21 @@ const TxDetails = () => {
{ data.base_fee_per_gas && ( { data.base_fee_per_gas && (
<Box> <Box>
<Text as="span" fontWeight="500">Base: </Text> <Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.base_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
{ data.max_fee_per_gas && ( { data.max_fee_per_gas && (
<Box> <Box>
<TextSeparator/> <TextSeparator/>
<Text as="span" fontWeight="500">Max: </Text> <Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.max_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
{ data.max_priority_fee_per_gas && ( { data.max_priority_fee_per_gas && (
<Box> <Box>
<TextSeparator/> <TextSeparator/>
<Text as="span" fontWeight="500">Max priority: </Text> <Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.max_priority_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
</DetailsInfoItem> </DetailsInfoItem>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment