Commit 400424a0 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into test/hook-for-paginated-queries

parents 109546a4 b2c51065
......@@ -51,6 +51,7 @@ NEXT_PUBLIC_AD_SLISE_ON=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_SLISE_ON__
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=__PLACEHOLDER_FOR_NEXT_PUBLIC_GRAPHIQL_TRANSACTION__
NEXT_PUBLIC_WEB3_DEFAULT_WALLET=__PLACEHOLDER_FOR_NEXT_PUBLIC_WEB3_DEFAULT_WALLET__
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=__PLACEHOLDER_FOR_NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET__
NEXT_PUBLIC_HIDE_INDEXING_ALERT=__PLACEHOLDER_FOR_NEXT_PUBLIC_HIDE_INDEXING_ALERT__
# api config
NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__
......
......@@ -2,15 +2,36 @@ name: Checks
on:
workflow_dispatch:
pull_request:
types: [ opened, synchronize, unlabeled ]
paths-ignore:
- '.github/ISSUE_TEMPLATE/**'
- '.husky/**'
- '.vscode/**'
- 'deploy/**'
- 'docs/**'
- 'public/**'
- 'stub/**'
push:
branches:
- main
paths-ignore:
- '.github/ISSUE_TEMPLATE/**'
- '.husky/**'
- '.vscode/**'
- 'deploy/**'
- 'docs/**'
- 'public/**'
- 'stub/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: ESLint
code_quality:
name: Code quality
runs-on: ubuntu-latest
if: "!contains(github.event.pull_request.labels.*.name, 'WIP')"
if: ${{ !contains(github.event.pull_request.labels.*.name, 'WIP') && !(github.event.action == 'unlabeled' && github.event.label.name != 'WIP') }}
steps:
- name: Checkout repo
uses: actions/checkout@v3
......@@ -21,39 +42,27 @@ jobs:
node-version: 18
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Cache node_modules
uses: actions/cache@v3
id: cache-node-modules
with:
useRollingCache: true
path: |
node_modules
key: node_modules-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Run ESLint
run: yarn lint:eslint
type_check:
name: TypeScript
runs-on: ubuntu-latest
if: "!contains(github.event.pull_request.labels.*.name, 'WIP')"
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
with:
useRollingCache: true
- name: Compile TypeScript
run: yarn lint:tsc
jest_tests:
name: Run unit tests with Jest
needs: [ lint, type_check ]
name: Jest tests
needs: [ code_quality ]
runs-on: ubuntu-latest
steps:
- name: Checkout repo
......@@ -65,20 +74,32 @@ jobs:
node-version: 18
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Cache node_modules
uses: actions/cache@v3
id: cache-node-modules
with:
useRollingCache: true
path: |
node_modules
key: node_modules-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Run Jest
run: yarn test:jest
pw_tests:
name: Run components visual tests with PlayWright
needs: [ lint, type_check ]
name: 'Playwright tests - project: ${{ matrix.project }}'
needs: [ code_quality ]
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.28.0-focal
image: mcr.microsoft.com/playwright:v1.32.0-focal
strategy:
fail-fast: false
matrix:
project: [ default, mobile, dark-color-mode ]
steps:
- name: Install git-lfs
......@@ -95,18 +116,28 @@ jobs:
node-version: 18
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
- name: Cache node_modules
uses: actions/cache@v3
id: cache-node-modules
with:
useRollingCache: true
path: |
node_modules
key: node_modules-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: yarn --frozen-lockfile
- name: Run PlayWright
run: HOME=/root yarn test:pw
run: yarn test:pw:ci
env:
HOME: /root
PW_PROJECT: ${{ matrix.project }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v2
with:
name: playwright-report
name: playwright-report-${{ matrix.project }}
path: playwright-report
retention-days: 10
\ No newline at end of file
......@@ -5,6 +5,10 @@ on:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
push_to_registry:
name: Push Docker image to registry
......
......@@ -181,6 +181,7 @@ const config = Object.freeze({
graphQL: {
defaultTxnHash: getEnvValue(process.env.NEXT_PUBLIC_GRAPHIQL_TRANSACTION) || '',
},
hideIndexingAlert: getEnvValue(process.env.NEXT_PUBLIC_HIDE_INDEXING_ALERT),
});
export default config;
......@@ -53,7 +53,7 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page | - | - | `0x69e3923eef50eada197c3336d546936d0c994211492c9f947a24c02827568f9f` |
| NEXT_PUBLIC_WEB3_DEFAULT_WALLET | `metamask` \| `coinbase`| Type of Web3 wallet which will be used by default to add tokens or chains to | - | `metamask` | `coinbase` |
| NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET | `boolean`| Set to `true` to hide icon "Add to your wallet" next to token addresses | - | `false` | `true` |
| NEXT_PUBLIC_HIDE_INDEXING_ALERT | `boolean` | Set to `true` to hide indexing alert, if the chain indexing isn't completed | - | `false` | `true` |
### Marketplace app configuration properties
| Property | Type | Description | Example value
......
......@@ -479,6 +479,9 @@ export const RESOURCES = {
csv_export_token_transfers: {
path: '/token-transfers-csv',
},
csv_export_logs: {
path: '/logs-csv',
},
};
export type ResourceName = keyof typeof RESOURCES;
......
......@@ -10,6 +10,7 @@ export function monaco(): CspDev.DirectiveDescriptor {
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/editor/editor.main.nls.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/solidity/solidity.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/basic-languages/elixir/elixir.js',
'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs/base/worker/workerMain.js',
],
'style-src': [
......
......@@ -30,6 +30,7 @@ export const verified: Partial<SmartContract> = {
{ address_hash: '0xa62744BeE8646e237441CDbfdedD3458861748A8', name: 'Sol' },
{ address_hash: '0xa62744BeE8646e237441CDbfdedD3458861748A8', name: 'math' },
],
language: 'solidity',
};
export const withMultiplePaths: Partial<SmartContract> = {
......
......@@ -28,6 +28,7 @@
"test:pw": "./playwright/make-envs-script.sh && NODE_OPTIONS=\"--max-old-space-size=4096\" ./node_modules/.bin/dotenv -e ./configs/envs/.env.pw -- playwright test -c playwright-ct.config.ts",
"test:pw:local": "export NODE_PATH=$(pwd)/node_modules && rm -rf ./playwright/.cache && yarn test:pw",
"test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.32.0-focal ./playwright/run-tests.sh",
"test:pw:ci": "yarn test:pw --project=$PW_PROJECT",
"test:jest": "jest",
"test:jest:watch": "jest --watch"
},
......
......@@ -63,7 +63,9 @@ const config: PlaywrightTestConfig = defineConfig({
},
},
/* Configure projects for major browsers */
// configured projects
// these projects are also used for sharding tests in CI
// when adding or deleting a project, make sure to update github workflow accordingly
projects: [
{
name: 'default',
......@@ -81,15 +83,7 @@ const config: PlaywrightTestConfig = defineConfig({
},
},
{
name: 'desktop xl',
grep: /\+@desktop-xl/,
use: {
...devices['Desktop Chrome'],
viewport: { width: 1600, height: 1000 },
},
},
{
name: 'dark color mode',
name: 'dark-color-mode',
grep: /\+@dark-mode/,
use: {
...devices['Desktop Chrome'],
......@@ -97,23 +91,6 @@ const config: PlaywrightTestConfig = defineConfig({
colorScheme: 'dark',
},
},
{
name: 'dark color mode mobile',
grep: /\+@dark-mode-mobile/,
use: {
...devices['iPhone 13 Pro'],
colorScheme: 'dark',
},
},
{
name: 'dark color mode desktop xl',
grep: /\+@dark-mode-xl/,
use: {
...devices['Desktop Chrome'],
viewport: { width: 1600, height: 1000 },
colorScheme: 'dark',
},
},
],
});
......
import { devices } from '@playwright/test';
export const viewport = {
mobile: devices['iPhone 13 Pro'].viewport,
xl: { width: 1600, height: 1000 },
};
......@@ -27,7 +27,6 @@ export interface SmartContract {
constructor_args: string | null;
decoded_constructor_args: Array<SmartContractDecodedConstructorArg> | null;
can_be_visualized_via_sol2uml: boolean | null;
is_vyper_contract: boolean | null;
file_path: string;
additional_sources: Array<{ file_path: string; source_code: string }>;
external_libraries: Array<SmartContractExternalLibrary> | null;
......@@ -37,6 +36,7 @@ export interface SmartContract {
};
verified_twin_address_hash: string | null;
minimal_proxy_address_hash: string | null;
language: string | null;
}
export type SmartContractDecodedConstructorArg = [
......
......@@ -22,7 +22,7 @@ export interface VerifiedContractsResponse {
export interface VerifiedContractsFilters {
q: string | undefined;
filter: 'vyper' | 'solidity' | undefined;
filter: 'vyper' | 'solidity' | 'yul' | undefined;
}
export type VerifiedContractsCounters = {
......
export type CsvExportType = 'transactions' | 'internal-transactions' | 'token-transfers';
export type CsvExportType = 'transactions' | 'internal-transactions' | 'token-transfers' | 'logs';
......@@ -10,6 +10,8 @@ import LogItem from 'ui/shared/logs/LogItem';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import AddressCsvExportLink from './AddressCsvExportLink';
const AddressLogs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}) => {
const router = useRouter();
......@@ -28,11 +30,12 @@ const AddressLogs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>
},
});
const actionBar = pagination.isVisible ? (
<ActionBar mt={ -6 } showShadow>
<Pagination ml="auto" { ...pagination }/>
const actionBar = (
<ActionBar mt={ -6 } showShadow justifyContent={{ base: 'space-between', lg: 'end' }}>
<AddressCsvExportLink address={ hash } isLoading={ pagination.isLoading } type="logs"/>
<Pagination ml={{ base: 0, lg: 8 }} { ...pagination }/>
</ActionBar>
) : null;
);
const content = data?.items ? data.items.map((item, index) => <LogItem key={ index } { ...item } type="address" isLoading={ isPlaceholderData }/>) : null;
......
import { Box } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import * as txMock from 'mocks/txs/tx';
import * as socketServer from 'playwright/fixtures/socketServer';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import AddressTxs from './AddressTxs';
......@@ -19,32 +21,45 @@ const hooksConfig = {
},
};
const test = base.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket,
});
base.describe('base view', () => {
let component: Locator;
base.beforeEach(async({ page, mount }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base, txMock.base ], next_page_params: { block: 1 } }),
}));
component = await mount(
<TestApp>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig },
);
});
base('+@mobile', async() => {
await expect(component).toHaveScreenshot();
});
// FIXME
// test cases which use socket cannot run in parallel since the socket server always run on the same port
test.describe.configure({ mode: 'serial' });
test('address txs +@mobile +@desktop-xl', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base, txMock.base ], next_page_params: { block: 1 } }),
}));
const component = await mount(
<TestApp>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
base.describe('screen xl', () => {
base.use({ viewport: configs.viewport.xl });
base('', async() => {
await expect(component).toHaveScreenshot();
});
});
});
test.describe('socket', () => {
base.describe('socket', () => {
const test = base.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket,
});
// FIXME
// test cases which use socket cannot run in parallel since the socket server always run on the same port
test.describe.configure({ mode: 'serial' });
test('without overload', async({ mount, page, createSocket }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
......
......@@ -23,9 +23,19 @@ function getEditorData(contractInfo: SmartContract | undefined) {
return undefined;
}
const defaultName = contractInfo.is_vyper_contract ? '/index.vy' : '/index.sol';
const extension = (() => {
switch (contractInfo.language) {
case 'vyper':
return 'vy';
case 'yul':
return 'yul';
default:
return 'sol';
}
})();
return [
{ file_path: formatFilePath(contractInfo.file_path || defaultName), source_code: contractInfo.source_code },
{ file_path: formatFilePath(contractInfo.file_path || `index.${ extension }`), source_code: contractInfo.source_code },
...(contractInfo.additional_sources || []).map((source) => ({ ...source, file_path: formatFilePath(source.file_path) })),
];
}
......@@ -74,7 +84,7 @@ const ContractSourceCode = ({ address, implementationAddress }: Props) => {
const heading = (
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>
<span>Contract source code</span>
<Text whiteSpace="pre" as="span" variant="secondary"> ({ activeContract?.is_vyper_contract ? 'Vyper' : 'Solidity' })</Text>
<Text whiteSpace="pre" as="span" variant="secondary" textTransform="capitalize"> ({ activeContract?.language })</Text>
</Skeleton>
);
......@@ -132,11 +142,19 @@ const ContractSourceCode = ({ address, implementationAddress }: Props) => {
return (
<>
<Box display={ sourceType === 'primary' ? 'block' : 'none' }>
<CodeEditor data={ primaryEditorData } remappings={ primaryContractQuery.data?.compiler_settings?.remappings }/>
<CodeEditor
data={ primaryEditorData }
remappings={ primaryContractQuery.data?.compiler_settings?.remappings }
language={ primaryContractQuery.data?.language ?? undefined }
/>
</Box>
{ secondaryEditorData && (
<Box display={ sourceType === 'secondary' ? 'block' : 'none' }>
<CodeEditor data={ secondaryEditorData } remappings={ secondaryContractQuery.data?.compiler_settings?.remappings }/>
<CodeEditor
data={ secondaryEditorData }
remappings={ secondaryContractQuery.data?.compiler_settings?.remappings }
language={ secondaryContractQuery.data?.language ?? undefined }
/>
</Box>
) }
</>
......
import { test, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import * as statsMock from 'mocks/stats/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import Stats from './Stats';
const API_URL = buildApiUrl('homepage_stats');
test('all items +@mobile +@dark-mode +@desktop-xl', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
test.describe('all items', () => {
let component: Locator;
const component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
test.beforeEach(async({ page, mount }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await expect(component).toHaveScreenshot();
component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
});
test('+@mobile +@dark-mode', async() => {
await expect(component).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('', async() => {
await expect(component).toHaveScreenshot();
});
});
});
test.describe('4 items', () => {
......
import { test, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import * as dailyTxsMock from 'mocks/stats/daily_txs';
......@@ -11,22 +12,36 @@ import ChainIndicators from './ChainIndicators';
const STATS_API_URL = buildApiUrl('homepage_stats');
const TX_CHART_API_URL = buildApiUrl('homepage_chart_txs');
test('daily txs chart +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(TX_CHART_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.base),
}));
const component = await mount(
<TestApp>
<ChainIndicators/>
</TestApp>,
);
await page.hover('.ChartOverlay', { position: { x: 100, y: 100 } });
await expect(component).toHaveScreenshot();
test.describe('daily txs chart', () => {
let component: Locator;
test.beforeEach(async({ page, mount }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(TX_CHART_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.base),
}));
component = await mount(
<TestApp>
<ChainIndicators/>
</TestApp>,
);
await page.hover('.ChartOverlay', { position: { x: 100, y: 100 } });
});
test('+@mobile', async() => {
await expect(component).toHaveScreenshot();
});
test.describe('dark mode', () => {
test.use({ colorScheme: 'dark' });
test('+@mobile', async() => {
await expect(component).toHaveScreenshot();
});
});
});
......@@ -39,6 +39,11 @@ const EXPORT_TYPES: Record<CsvExportType, ExportTypeEntity> = {
resource: 'csv_export_token_transfers',
fileNameTemplate: 'token_transfers',
},
logs: {
text: 'logs',
resource: 'csv_export_logs',
fileNameTemplate: 'logs',
},
};
const isCorrectExportType = (type: string): type is CsvExportType => Object.keys(EXPORT_TYPES).includes(type);
......
import { test, expect, devices } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import * as blockMock from 'mocks/blocks/block';
......@@ -8,44 +9,59 @@ import * as txMock from 'mocks/txs/tx';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import insertAdPlaceholder from 'playwright/utils/insertAdPlaceholder';
import Home from './Home';
test('default view -@default +@desktop-xl +@dark-mode', async({ mount, page }) => {
await page.route(buildApiUrl('homepage_stats'), (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(buildApiUrl('homepage_blocks'), (route) => route.fulfill({
status: 200,
body: JSON.stringify([
blockMock.base,
blockMock.base2,
]),
}));
await page.route(buildApiUrl('homepage_txs'), (route) => route.fulfill({
status: 200,
body: JSON.stringify([
txMock.base,
txMock.withContractCreation,
txMock.withTokenTransfer,
]),
}));
await page.route(buildApiUrl('homepage_chart_txs'), (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.base),
}));
const component = await mount(
<TestApp>
<Home/>
</TestApp>,
);
await insertAdPlaceholder(page);
await expect(component.locator('main')).toHaveScreenshot();
test.describe('default view', () => {
let component: Locator;
test.beforeEach(async({ page, mount }) => {
await page.route(buildApiUrl('homepage_stats'), (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(buildApiUrl('homepage_blocks'), (route) => route.fulfill({
status: 200,
body: JSON.stringify([
blockMock.base,
blockMock.base2,
]),
}));
await page.route(buildApiUrl('homepage_txs'), (route) => route.fulfill({
status: 200,
body: JSON.stringify([
txMock.base,
txMock.withContractCreation,
txMock.withTokenTransfer,
]),
}));
await page.route(buildApiUrl('homepage_chart_txs'), (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.base),
}));
component = await mount(
<TestApp>
<Home/>
</TestApp>,
);
await insertAdPlaceholder(page);
});
test('-@default +@dark-mode', async() => {
await expect(component.locator('main')).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('', async() => {
await expect(component.locator('main')).toHaveScreenshot();
});
});
});
test.describe('custom hero plate background', () => {
......
......@@ -61,14 +61,10 @@ const VerifiedContracts = () => {
return;
}
if ((value === 'vyper' || value === 'solidity')) {
onFilterChange({ q: debouncedSearchTerm, filter: value });
setType(value);
return;
}
const filter = value === 'all' ? undefined : value as VerifiedContractsFilters['filter'];
onFilterChange({ q: debouncedSearchTerm, filter: undefined });
setType(undefined);
onFilterChange({ q: debouncedSearchTerm, filter });
setType(filter);
}, [ debouncedSearchTerm, onFilterChange ]);
const handleSortToggle = React.useCallback((field: SortField) => {
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
import IndexingAlert from 'ui/home/IndexingAlert';
interface Props {
......@@ -17,7 +18,7 @@ const PageContent = ({ children, isHomePage }: Props) => {
paddingBottom={ 10 }
paddingTop={{ base: isHomePage ? '88px' : '138px', lg: 0 }}
>
<IndexingAlert display={{ base: 'block', lg: 'none' }}/>
{ !appConfig.hideIndexingAlert && <IndexingAlert display={{ base: 'block', lg: 'none' }}/> }
{ children }
</Box>
);
......
......@@ -80,7 +80,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
{ afterTitle }
</Box>
{ contentAfter }
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto"/> }
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto" w={{ base: '100%', lg: 'auto' }}/> }
</Flex>
);
};
......
/* eslint-disable max-len */
import { Flex, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/navigation';
import Script from 'next/script';
import React from 'react';
import { connectAdbutler, placeAd } from 'ui/shared/ad/adbutlerScript';
import isBrowser from 'lib/isBrowser';
import { connectAdbutler, placeAd, placeAdSPA } from 'ui/shared/ad/adbutlerScript';
const AdbutlerBanner = ({ className }: { className?: string }) => {
const router = useRouter();
React.useEffect(() => {
if (isBrowser()) {
eval(placeAdSPA);
}
}, [ router ]);
return (
<Flex className={ className } id="adBanner" h={{ base: '100px', lg: '90px' }}>
<div id="ad-banner"></div>
<Script id="ad-butler-1">{ connectAdbutler }</Script>
<Script id="ad-butler-2">{ placeAd }</Script>
<div id="ad-banner"></div>
</Flex>
);
};
......
......@@ -45,6 +45,7 @@ const CoinzillaTextAd = ({ className }: {className?: string}) => {
}
})
.finally(() => {
// setAdData(MOCK);
setIsLoading(false);
});
}
......@@ -55,7 +56,16 @@ const CoinzillaTextAd = ({ className }: {className?: string}) => {
}
if (isLoading) {
return <Skeleton className={ className } h={{ base: 12, lg: 6 }} w={{ base: '100%', lg: 'auto' }} flexGrow={ 1 } maxW="1000px" display="inline-block"/>;
return (
<Skeleton
className={ className }
h={{ base: 12, lg: 6 }}
w="100%"
flexGrow={ 1 }
maxW="800px"
display="block"
/>
);
}
if (!adData) {
......
/* eslint-disable max-len */
import appConfig from 'configs/app/config';
const ADBUTLER_ACCOUNT = 182226;
export const connectAdbutler = `if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}`;
export const placeAd = `
var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || [];
var abkw = window.abkw || '';
const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;
const isMobile = window.matchMedia("only screen and (max-width: 1000px)").matches;
if (isMobile) {
var plc${ appConfig.ad.adButlerConfigMobile?.id } = window.plc${ appConfig.ad.adButlerConfigMobile?.id } || 0;
document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+plc${ appConfig.ad.adButlerConfigMobile?.id }+'"></'+'div>';
document.getElementById("ad-banner").className = "ad-container mb-3";
AdButler.ads.push({handler: function(opt){ AdButler.register(182226, ${ appConfig.ad.adButlerConfigMobile?.id }, [${ appConfig.ad.adButlerConfigMobile?.width },${ appConfig.ad.adButlerConfigMobile?.height }], 'placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigMobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+plc${ appConfig.ad.adButlerConfigMobile?.id }+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigMobile?.id }, [${ appConfig.ad.adButlerConfigMobile?.width },${ appConfig.ad.adButlerConfigMobile?.height }], 'placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigMobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
} else {
var plc${ appConfig.ad.adButlerConfigDesktop?.id } = window.plc${ appConfig.ad.adButlerConfigDesktop?.id } || 0;
document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+plc${ appConfig.ad.adButlerConfigDesktop?.id }+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(182226, ${ appConfig.ad.adButlerConfigDesktop?.id }, [${ appConfig.ad.adButlerConfigDesktop?.width },${ appConfig.ad.adButlerConfigDesktop?.height }], 'placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigDesktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+plc${ appConfig.ad.adButlerConfigDesktop?.id }+'"></'+'div>';
AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigDesktop?.id }, [${ appConfig.ad.adButlerConfigDesktop?.width },${ appConfig.ad.adButlerConfigDesktop?.height }], 'placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigDesktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
}
`;
export const placeAdSPA = `
if (!window.AdButler.ads) {window.AdButler.ads = [];}
var abkw = window.abkw || '';
const isMobile = window.matchMedia("only screen and (max-width: 1000px)").matches;
if (isMobile) {
var plc${ appConfig.ad.adButlerConfigMobile?.id } = window.plc${ appConfig.ad.adButlerConfigMobile?.id } || 0;
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+plc${ appConfig.ad.adButlerConfigMobile?.id }+'"></'+'div>';
window.AdButler.ads.push({handler: function(opt){ AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigMobile?.id }, [${ appConfig.ad.adButlerConfigMobile?.width },${ appConfig.ad.adButlerConfigMobile?.height }], 'placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigMobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
} else {
var plc${ appConfig.ad.adButlerConfigDesktop?.id } = window.plc${ appConfig.ad.adButlerConfigDesktop?.id } || 0;
document.getElementById('ad-banner').innerHTML = '<'+'div id="placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+plc${ appConfig.ad.adButlerConfigDesktop?.id }+'"></'+'div>';
window.AdButler.ads.push({handler: function(opt){ window.AdButler.register(${ ADBUTLER_ACCOUNT }, ${ appConfig.ad.adButlerConfigDesktop?.id }, [${ appConfig.ad.adButlerConfigDesktop?.width },${ appConfig.ad.adButlerConfigDesktop?.height }], 'placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigDesktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }});
}
`;
......@@ -36,9 +36,10 @@ const EDITOR_HEIGHT = 500;
interface Props {
data: Array<File>;
remappings?: Array<string>;
language?: string;
}
const CodeEditor = ({ data, remappings }: Props) => {
const CodeEditor = ({ data, remappings, language }: Props) => {
const [ instance, setInstance ] = React.useState<Monaco | undefined>();
const [ editor, setEditor ] = React.useState<monaco.editor.IStandaloneCodeEditor | undefined>();
const [ index, setIndex ] = React.useState(0);
......@@ -53,6 +54,8 @@ const CodeEditor = ({ data, remappings }: Props) => {
const editorWidth = containerRect ? containerRect.width - (isMobile ? 0 : SIDE_BAR_WIDTH) : 0;
const editorLanguage = language === 'vyper' ? 'elixir' : 'sol';
React.useEffect(() => {
instance?.editor.setTheme(colorMode === 'light' ? 'blockscout-light' : 'blockscout-dark');
}, [ colorMode, instance?.editor ]);
......@@ -69,7 +72,7 @@ const CodeEditor = ({ data, remappings }: Props) => {
const loadedModelsPaths = loadedModels.map((model) => model.uri.path);
const newModels = data.slice(1)
.filter((file) => !loadedModelsPaths.includes(file.file_path))
.map((file) => monaco.editor.createModel(file.source_code, 'sol', monaco.Uri.parse(file.file_path)));
.map((file) => monaco.editor.createModel(file.source_code, editorLanguage, monaco.Uri.parse(file.file_path)));
loadedModels.concat(newModels).forEach(addFileImportDecorations);
......@@ -185,7 +188,7 @@ const CodeEditor = ({ data, remappings }: Props) => {
return (
<Box overflow="hidden" borderRadius="md" height={ `${ EDITOR_HEIGHT }px` }>
<MonacoEditor
language="sol"
language={ editorLanguage }
path={ data[index].file_path }
defaultValue={ data[index].source_code }
options={ EDITOR_OPTIONS }
......@@ -216,7 +219,7 @@ const CodeEditor = ({ data, remappings }: Props) => {
<MonacoEditor
className="editor-container"
height={ `${ EDITOR_HEIGHT }px` }
language="sol"
language={ editorLanguage }
path={ data[index].file_path }
defaultValue={ data[index].source_code }
options={ EDITOR_OPTIONS }
......
......@@ -5,6 +5,7 @@ import type { WindowProvider } from 'wagmi';
import { FOOTER_LINKS } from 'mocks/config/footerLinks';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs';
import Footer from './Footer';
......@@ -18,7 +19,7 @@ base.describe('with custom links, 4 cols', () => {
]) as any,
});
test('base view +@dark-mode +@mobile +@desktop-xl', async({ mount, page }) => {
test.beforeEach(async({ page, mount }) => {
await page.route(FOOTER_LINKS_URL, (route) => {
return route.fulfill({
body: JSON.stringify(FOOTER_LINKS),
......@@ -30,9 +31,19 @@ base.describe('with custom links, 4 cols', () => {
<Footer/>
</TestApp>,
);
});
test('+@mobile +@dark-mode', async({ page }) => {
await expect(page).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('', async({ page }) => {
await expect(page).toHaveScreenshot();
});
});
});
base.describe('with custom links, 2 cols', () => {
......
......@@ -5,7 +5,7 @@ import TestApp from 'playwright/TestApp';
import Header from './Header';
test('no auth +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) => {
test('no auth +@mobile', async({ mount, page }) => {
await mount(
<TestApp>
<Header/>
......@@ -14,3 +14,17 @@ test('no auth +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) =
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 150 } });
});
test.describe('dark mode', () => {
test.use({ colorScheme: 'dark' });
test('+@mobile', async({ mount, page }) => {
await mount(
<TestApp>
<Header/>
</TestApp>,
);
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 150 } });
});
});
......@@ -52,7 +52,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
paddingTop={ 9 }
display={{ base: 'none', lg: 'block' }}
>
<IndexingAlert/>
{ !appConfig.hideIndexingAlert && <IndexingAlert/> }
{ !isHomePage && (
<HStack
as="header"
......
import { Box, Flex } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import * as cookies from 'lib/cookies';
......@@ -7,6 +8,7 @@ import authFixture from 'playwright/fixtures/auth';
import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import * as configs from 'playwright/utils/configs';
import NavigationDesktop from './NavigationDesktop';
......@@ -27,18 +29,32 @@ const test = base.extend({
]) as any,
});
test('no auth +@desktop-xl +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
test.describe('no auth', () => {
let component: Locator;
test.beforeEach(async({ mount }) => {
component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
});
test('+@dark-mode', async() => {
await expect(component).toHaveScreenshot();
});
test.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async() => {
await expect(component).toHaveScreenshot();
});
});
});
base.describe('auth', () => {
......@@ -52,8 +68,10 @@ base.describe('auth', () => {
},
});
test('+@desktop-xl +@dark-mode-xl', async({ mount }) => {
const component = await mount(
let component: Locator;
test.beforeEach(async({ mount }) => {
component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
......@@ -62,41 +80,69 @@ base.describe('auth', () => {
</TestApp>,
{ hooksConfig },
);
});
test('+@dark-mode', async() => {
await expect(component).toHaveScreenshot();
});
test.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async() => {
await expect(component).toHaveScreenshot();
});
});
});
test('with tooltips +@desktop-xl -@default', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('svg[aria-label="Expand/Collapse menu"]').click();
await page.locator('a[aria-label="Tokens link"]').hover();
await expect(component).toHaveScreenshot();
test.describe('with tooltips', () => {
test.use({ viewport: configs.viewport.xl });
test('', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('svg[aria-label="Expand/Collapse menu"]').click();
await page.locator('a[aria-label="Tokens link"]').hover();
await expect(component).toHaveScreenshot();
});
});
test('with submenu +@desktop-xl +@dark-mode', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('a[aria-label="Blockchain link group"]').hover();
await expect(component).toHaveScreenshot();
test.describe('with submenu', () => {
let component: Locator;
test.beforeEach(async({ mount, page }) => {
component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('a[aria-label="Blockchain link group"]').hover();
});
test('', async() => {
await expect(component).toHaveScreenshot();
});
test.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl });
test('', async() => {
await expect(component).toHaveScreenshot();
});
});
});
base.describe('cookie set to false', () => {
......@@ -110,8 +156,10 @@ base.describe('cookie set to false', () => {
},
});
test('navbar is opened +@desktop-xl', async({ mount }) => {
const component = await mount(
let component: Locator;
test.beforeEach(async({ mount }) => {
component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
......@@ -120,9 +168,20 @@ base.describe('cookie set to false', () => {
</TestApp>,
{ hooksConfig },
);
});
test('', async() => {
const networkMenu = component.locator('button[aria-label="Network menu"]');
expect(await networkMenu.isVisible()).toBe(true);
await expect(networkMenu).toBeVisible();
});
test.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl });
test('', async() => {
const networkMenu = component.locator('button[aria-label="Network menu"]');
await expect(networkMenu).toBeVisible();
});
});
});
......
import { test, expect } from '@playwright/experimental-ct-react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs';
import NetworkLogo from './NetworkLogo';
test.describe('placeholder logo', () => {
const extendedTest = test.extend({
base.describe('placeholder logo', () => {
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: '' },
{ name: 'NEXT_PUBLIC_NETWORK_ICON', value: '' },
......@@ -15,7 +17,7 @@ test.describe('placeholder logo', () => {
]) as any,
});
extendedTest('+@desktop-xl +@dark-mode +@dark-mode-xl', async({ mount }) => {
test('+@dark-mode', async({ mount }) => {
const component = await mount(
<TestApp>
<NetworkLogo/>
......@@ -24,12 +26,26 @@ test.describe('placeholder logo', () => {
await expect(component.locator('a')).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async({ mount }) => {
const component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot();
});
});
});
test.describe('custom logo', () => {
base.describe('custom logo', () => {
const LOGO_URL = 'https://localhost:3000/my-logo.png';
const ICON_URL = 'https://localhost:3000/my-icon.png';
const extendedTest = test.extend({
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
{ name: 'NEXT_PUBLIC_NETWORK_ICON', value: ICON_URL },
......@@ -37,7 +53,9 @@ test.describe('custom logo', () => {
]) as any,
});
extendedTest('+@desktop-xl +@dark-mode +@dark-mode-xl', async({ mount, page }) => {
let component: Locator;
test.beforeEach(async({ page, mount }) => {
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
......@@ -51,20 +69,30 @@ test.describe('custom logo', () => {
});
});
const component = await mount(
component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
});
test('+@dark-mode', async() => {
await expect(component.locator('a')).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async() => {
await expect(component.locator('a')).toHaveScreenshot();
});
});
});
test.describe('custom logo with dark option', () => {
base.describe('custom logo with dark option -@default +@dark-mode', () => {
const LOGO_URL = 'https://localhost:3000/my-logo.png';
const ICON_URL = 'https://localhost:3000/my-icon.png';
const extendedTest = test.extend({
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
{ name: 'NEXT_PUBLIC_NETWORK_LOGO_DARK', value: LOGO_URL },
......@@ -74,7 +102,9 @@ test.describe('custom logo with dark option', () => {
]) as any,
});
extendedTest('-@default +@dark-mode +@dark-mode-xl', async({ mount, page }) => {
let component: Locator;
test.beforeEach(async({ page, mount }) => {
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
......@@ -88,12 +118,22 @@ test.describe('custom logo with dark option', () => {
});
});
const component = await mount(
component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
});
test('', async() => {
await expect(component.locator('a')).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('', async() => {
await expect(component.locator('a')).toHaveScreenshot();
});
});
});
import { Text } from '@chakra-ui/react';
import { Box, Text } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import _uniqBy from 'lodash/uniqBy';
import React from 'react';
......@@ -96,7 +96,11 @@ const SearchBarSuggest = ({ query, redirectCheckQuery, searchTerm, onItemClick }
return (
<>
{ !isMobile && <TextAd pb={ 4 } mb={ 5 } borderColor="divider" borderBottomWidth="1px"/> }
{ !isMobile && (
<Box pb={ 4 } mb={ 5 } borderColor="divider" borderBottomWidth="1px" _empty={{ display: 'none' }}>
<TextAd/>
</Box>
) }
{ content }
</>
);
......
......@@ -37,7 +37,7 @@ const TxRawTrace = () => {
const channel = useSocketChannel({
topic: `transactions:${ hash }`,
isDisabled: !hash || !txInfo.isPlaceholderData || !txInfo.data?.status,
isDisabled: !hash || txInfo.isPlaceholderData || !txInfo.data?.status,
onJoin: () => setIsSocketOpen(true),
});
useSocketMessage({
......
......@@ -3,16 +3,35 @@ import React from 'react';
import * as txMock from 'mocks/txs/tx';
import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs';
import TxsTable from './TxsTable';
test('base view +@dark-mode +@desktop-xl', async({ mount }) => {
const component = await mount(
<TestApp>
{ /* eslint-disable-next-line react/jsx-no-bind */ }
<TxsTable txs={ [ txMock.base, txMock.base ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
</TestApp>,
);
test.describe('base view', () => {
await expect(component).toHaveScreenshot();
test('+@dark-mode', async({ mount }) => {
const component = await mount(
<TestApp>
{ /* eslint-disable-next-line react/jsx-no-bind */ }
<TxsTable txs={ [ txMock.base, txMock.base ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl });
test('', async({ mount }) => {
const component = await mount(
<TestApp>
{ /* eslint-disable-next-line react/jsx-no-bind */ }
<TxsTable txs={ [ txMock.base, txMock.base ] } sort={ () => () => {} } top={ 0 } showBlockInfo showSocketInfo={ false }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
});
});
......@@ -35,6 +35,7 @@ const VerifiedContractsFilter = ({ onChange, defaultValue, isActive }: Props) =>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="solidity">Solidity</MenuItemOption>
<MenuItemOption value="vyper">Vyper</MenuItemOption>
<MenuItemOption value="yul">Yul</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
......
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