Commit 56bbf4e6 authored by varasev's avatar varasev Committed by GitHub

Merge branch 'main' into va-tx-actions

parents 23091fa0 6c2f65d5
...@@ -57,7 +57,7 @@ jobs: ...@@ -57,7 +57,7 @@ jobs:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
push: true push: true
cache-from: type=registry,ref=ghcr.io/blockscout/frontend::buildcache cache-from: type=gha
tags: ghcr.io/blockscout/frontend:prerelease-${{ env.GITHUB_REF_NAME_SLUG }} tags: ghcr.io/blockscout/frontend:prerelease-${{ env.GITHUB_REF_NAME_SLUG }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args: | build-args: |
......
...@@ -44,8 +44,8 @@ jobs: ...@@ -44,8 +44,8 @@ jobs:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
push: true push: true
cache-from: type=registry,ref=ghcr.io/blockscout/frontend::buildcache cache-from: type=gha
cache-to: type=registry,ref=ghcr.io/blockscout/frontend::buildcache,mode=max cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args: | build-args: |
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
"detail": "start local dev server", "detail": "start local dev server",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
"detail": "start local dev server for POA network", "detail": "start local dev server for POA network",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
"detail": "start local dev server for Goerli network", "detail": "start local dev server for Goerli network",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
}, },
"presentation": { "presentation": {
"reveal": "never", "reveal": "never",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
"detail": "run eslint", "detail": "run eslint",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
"icon": { "icon": {
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
"detail": "run visual components tests for current file", "detail": "run visual components tests for current file",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"focus": true, "focus": true,
}, },
"icon": { "icon": {
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
"detail": "run visual components tests for current file", "detail": "run visual components tests for current file",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"focus": true, "focus": true,
}, },
"icon": { "icon": {
...@@ -150,7 +150,7 @@ ...@@ -150,7 +150,7 @@
"detail": "run visual components tests", "detail": "run visual components tests",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"focus": true, "focus": true,
}, },
"icon": { "icon": {
...@@ -171,7 +171,7 @@ ...@@ -171,7 +171,7 @@
"detail": "run jest tests", "detail": "run jest tests",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"focus": true, "focus": true,
}, },
"icon": { "icon": {
...@@ -190,7 +190,7 @@ ...@@ -190,7 +190,7 @@
"detail": "run jest tests in watch mode", "detail": "run jest tests in watch mode",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"focus": true, "focus": true,
}, },
...@@ -210,7 +210,7 @@ ...@@ -210,7 +210,7 @@
"detail": "run jest tests in watch mode for current file", "detail": "run jest tests in watch mode for current file",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"focus": true, "focus": true,
}, },
"icon": { "icon": {
...@@ -230,7 +230,7 @@ ...@@ -230,7 +230,7 @@
"detail": "build docker image", "detail": "build docker image",
"presentation": { "presentation": {
"reveal": "always", "reveal": "always",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
"focus": true, "focus": true,
...@@ -251,7 +251,7 @@ ...@@ -251,7 +251,7 @@
"detail": "run docker container for POA network", "detail": "run docker container for POA network",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
...@@ -271,7 +271,7 @@ ...@@ -271,7 +271,7 @@
"detail": "format svg files with svgo", "detail": "format svg files with svgo",
"presentation": { "presentation": {
"reveal": "silent", "reveal": "silent",
"panel": "dedicated", "panel": "shared",
"close": true, "close": true,
"revealProblems": "onProblem", "revealProblems": "onProblem",
}, },
......
...@@ -11,6 +11,8 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout ...@@ -11,6 +11,8 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_SMALL_LOGO=
# api config # api config
NEXT_PUBLIC_API_HOST=blockscout.com NEXT_PUBLIC_API_HOST=blockscout.com
......
...@@ -35,7 +35,31 @@ const oldUrls = [ ...@@ -35,7 +35,31 @@ const oldUrls = [
}, },
{ {
oldPath: '/block/:id/transactions', oldPath: '/block/:id/transactions',
newPath: `${ PATHS.block }?tab=txs`, newPath: `${ PATHS.block }`,
},
{
oldPath: '/address/:id/transactions',
newPath: `${ PATHS.address_index }`,
},
{
oldPath: '/address/:id/token-transfers',
newPath: `${ PATHS.address_index }?tab=token_transfers`,
},
{
oldPath: '/address/:id/tokens',
newPath: `${ PATHS.address_index }?tab=tokens`,
},
{
oldPath: '/address/:id/internal-transactions',
newPath: `${ PATHS.address_index }?tab=internal_txns`,
},
{
oldPath: '/address/:id/coin-balances',
newPath: `${ PATHS.address_index }?tab=coin_balance_history`,
},
{
oldPath: '/address/:id/validations',
newPath: `${ PATHS.address_index }?tab=blocks_validated`,
}, },
]; ];
......
...@@ -24,10 +24,10 @@ blockscout: ...@@ -24,10 +24,10 @@ blockscout:
enabled: true enabled: true
annotations: annotations:
# - 'nginx.ingress.kubernetes.io/rewrite-target: /$2' # - 'nginx.ingress.kubernetes.io/rewrite-target: /$2'
nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.blockscout-main.test.aws-k8s.blockscout.com, https://*.test.aws-k8s.blockscout.com, http://localhost:3000" - 'nginx.ingress.kubernetes.io/cors-allow-origin: "https://*.blockscout-main.test.aws-k8s.blockscout.com, https://*.test.aws-k8s.blockscout.com, http://localhost:3000"'
nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - 'nginx.ingress.kubernetes.io/cors-allow-credentials: "true"'
nginx.ingress.kubernetes.io/cors-allow-methods: PUT, GET, POST, OPTIONS, DELETE, PATCH - 'nginx.ingress.kubernetes.io/cors-allow-methods: PUT, GET, POST, OPTIONS, DELETE, PATCH'
nginx.ingress.kubernetes.io/enable-cors: "true" - 'nginx.ingress.kubernetes.io/enable-cors: "true"'
host: host:
_default: blockscout.test.blockscout.aws-k8s.blockscout.com _default: blockscout.test.blockscout.aws-k8s.blockscout.com
# enable https # enable https
......
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="m12.18 3.765-1.944-1.944a1.674 1.674 0 0 0-1.179-.488H4.333c-.92 0-1.666.746-1.666 1.667v10c0 .92.746 1.667 1.667 1.667H11c.917 0 1.667-.75 1.667-1.667V4.943c0-.44-.175-.865-.487-1.178ZM11.417 13c0 .23-.187.417-.417.417H4.334A.417.417 0 0 1 3.917 13V3.003c0-.23.186-.416.416-.416H8.5v2.08c0 .46.373.833.833.833h2.06V13h.024ZM8.273 8.938a.365.365 0 0 0-.303.162l-1.032 1.55-.305-.456a.363.363 0 0 0-.606-.001l-1.215 1.823a.364.364 0 0 0-.018.374c.063.12.186.192.297.192h5.104a.365.365 0 0 0 .304-.566L8.554 9.1c-.044-.103-.158-.162-.28-.162ZM6 8.833a.833.833 0 1 0 0-1.666.833.833 0 0 0 0 1.666Z" fill="currentColor"/>
</svg>
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.667 2A.667.667 0 0 0 2 2.667V6a.667.667 0 0 0 1.333 0V3.333H6A.667.667 0 1 0 6 2H2.667Zm0 12A.667.667 0 0 1 2 13.333V10a.667.667 0 0 1 1.333 0v2.667H6A.667.667 0 1 1 6 14H2.667ZM14 2.667A.667.667 0 0 0 13.333 2H10a.667.667 0 0 0 0 1.333h2.667V6A.667.667 0 1 0 14 6V2.667ZM13.333 14a.667.667 0 0 0 .667-.667V10a.667.667 0 0 0-1.333 0v2.667H10A.667.667 0 1 0 10 14h3.333Z" fill="currentColor"/>
</svg>
import type { Types } from 'typescript-cookie'; import Cookies from 'js-cookie';
import { Cookies } from 'typescript-cookie';
import isBrowser from './isBrowser'; import isBrowser from './isBrowser';
...@@ -14,10 +13,13 @@ export function get(name?: NAMES | undefined | null, serverCookie?: string) { ...@@ -14,10 +13,13 @@ export function get(name?: NAMES | undefined | null, serverCookie?: string) {
if (!isBrowser()) { if (!isBrowser()) {
return serverCookie ? getFromCookieString(serverCookie, name) : undefined; return serverCookie ? getFromCookieString(serverCookie, name) : undefined;
} }
return Cookies.get(name);
if (name) {
return Cookies.get(name);
}
} }
export function set(name: string, value: string, attributes: Types.CookieAttributes = {}) { export function set(name: string, value: string, attributes: Cookies.CookieAttributes = {}) {
attributes.path = '/'; attributes.path = '/';
return Cookies.set(name, value, attributes); return Cookies.set(name, value, attributes);
......
...@@ -41,46 +41,48 @@ function getUpdateParams(ts: string) { ...@@ -41,46 +41,48 @@ function getUpdateParams(ts: string) {
}; };
} }
export default function useTimeAgoIncrement(ts: string, isEnabled?: boolean) { export default function useTimeAgoIncrement(ts: string | null, isEnabled?: boolean) {
const [ value, setValue ] = React.useState(dayjs(ts).fromNow()); const [ value, setValue ] = React.useState(ts ? dayjs(ts).fromNow() : null);
React.useEffect(() => { React.useEffect(() => {
const timeouts: Array<number> = []; if (ts !== null) {
const intervals: Array<number> = []; const timeouts: Array<number> = [];
const intervals: Array<number> = [];
const startIncrement = () => { const startIncrement = () => {
const { startTimeout, interval, endTimeout } = getUpdateParams(ts); const { startTimeout, interval, endTimeout } = getUpdateParams(ts);
if (!startTimeout && !endTimeout) { if (!startTimeout && !endTimeout) {
return; return;
} }
let intervalId: number; let intervalId: number;
const startTimeoutId = window.setTimeout(() => { const startTimeoutId = window.setTimeout(() => {
setValue(dayjs(ts).fromNow());
intervalId = window.setInterval(() => {
setValue(dayjs(ts).fromNow()); setValue(dayjs(ts).fromNow());
}, interval);
intervals.push(intervalId); intervalId = window.setInterval(() => {
}, startTimeout); setValue(dayjs(ts).fromNow());
}, interval);
intervals.push(intervalId);
}, startTimeout);
const endTimeoutId = window.setTimeout(() => { const endTimeoutId = window.setTimeout(() => {
window.clearInterval(intervalId); window.clearInterval(intervalId);
startIncrement(); startIncrement();
}, endTimeout); }, endTimeout);
timeouts.push(startTimeoutId); timeouts.push(startTimeoutId);
timeouts.push(endTimeoutId); timeouts.push(endTimeoutId);
}; };
isEnabled && startIncrement(); isEnabled && startIncrement();
return () => { return () => {
timeouts.forEach(window.clearTimeout); timeouts.forEach(window.clearTimeout);
intervals.forEach(window.clearInterval); intervals.forEach(window.clearInterval);
}; };
}
}, [ isEnabled, ts ]); }, [ isEnabled, ts ]);
return value; return value;
......
...@@ -5,6 +5,7 @@ import type { NewBlockSocketResponse } from 'types/api/block'; ...@@ -5,6 +5,7 @@ import type { NewBlockSocketResponse } from 'types/api/block';
export type SocketMessageParams = SocketMessage.NewBlock | export type SocketMessageParams = SocketMessage.NewBlock |
SocketMessage.BlocksIndexStatus | SocketMessage.BlocksIndexStatus |
SocketMessage.InternalTxsIndexStatus |
SocketMessage.TxStatusUpdate | SocketMessage.TxStatusUpdate |
SocketMessage.NewTx | SocketMessage.NewTx |
SocketMessage.NewPendingTx | SocketMessage.NewPendingTx |
...@@ -23,7 +24,8 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e ...@@ -23,7 +24,8 @@ interface SocketMessageParamsGeneric<Event extends string | undefined, Payload e
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace SocketMessage { export namespace SocketMessage {
export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>; export type NewBlock = SocketMessageParamsGeneric<'new_block', NewBlockSocketResponse>;
export type BlocksIndexStatus = SocketMessageParamsGeneric<'index_status', {finished: boolean; ratio: string}>; export type BlocksIndexStatus = SocketMessageParamsGeneric<'block_index_status', {finished: boolean; ratio: string}>;
export type InternalTxsIndexStatus = SocketMessageParamsGeneric<'internal_txs_index_status', {finished: boolean; ratio: string}>;
export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>; export type TxStatusUpdate = SocketMessageParamsGeneric<'collated', NewBlockSocketResponse>;
export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>; export type NewTx = SocketMessageParamsGeneric<'transaction', { transaction: number }>;
export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>; export type NewPendingTx = SocketMessageParamsGeneric<'pending_transaction', { pending_transaction: number }>;
......
import type { FeaturedNetwork } from 'types/networks';
const FEATURED_NETWORKS: Array<FeaturedNetwork> = [
{ title: 'Gnosis Chain', url: 'https://blockscout.com/xdai/mainnet', group: 'mainnets', type: 'xdai_mainnet' },
{ title: 'Optimism on Gnosis Chain', url: 'https://blockscout.com/xdai/optimism', group: 'mainnets', type: 'xdai_optimism' },
{ title: 'Arbitrum on xDai', url: 'https://blockscout.com/xdai/aox', group: 'mainnets' },
{ title: 'Ethereum', url: 'https://blockscout.com/eth/mainnet', group: 'mainnets', type: 'eth_mainnet' },
{ title: 'Ethereum Classic', url: 'https://blockscout.com/etx/mainnet', group: 'mainnets', type: 'etc_mainnet', icon: 'https://example.com/my-logo.png' },
{ title: 'POA', url: 'https://blockscout.com/poa/core', group: 'mainnets', type: 'poa_core' },
{ title: 'RSK', url: 'https://blockscout.com/rsk/mainnet', group: 'mainnets', type: 'rsk_mainnet' },
{ title: 'Gnosis Chain Testnet', url: 'https://blockscout.com/xdai/testnet', group: 'testnets', type: 'xdai_testnet' },
{ title: 'POA Sokol', url: 'https://blockscout.com/poa/sokol', group: 'testnets', type: 'poa_sokol' },
{ title: 'ARTIS Σ1', url: 'https://blockscout.com/artis/sigma1', group: 'other', type: 'artis_sigma1' },
{ title: 'LUKSO L14', url: 'https://blockscout.com/lukso/l14', group: 'other', type: 'lukso_l14' },
{ title: 'Astar', url: 'https://blockscout.com/astar', group: 'other', type: 'astar' },
];
export const FEATURED_NETWORKS_MOCK = JSON.stringify(FEATURED_NETWORKS).replaceAll('"', '\'');
...@@ -37,6 +37,7 @@ export const erc20: TokenTransfer = { ...@@ -37,6 +37,7 @@ export const erc20: TokenTransfer = {
}, },
tx_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193', tx_hash: '0x62d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
type: 'token_transfer', type: 'token_transfer',
timestamp: '2022-10-10T14:34:30.000000Z',
}; };
export const erc721: TokenTransfer = { export const erc721: TokenTransfer = {
...@@ -75,6 +76,7 @@ export const erc721: TokenTransfer = { ...@@ -75,6 +76,7 @@ export const erc721: TokenTransfer = {
}, },
tx_hash: '0xf13bc7afe5e02b494dd2f22078381d36a4800ef94a0ccc147431db56c301e6cc', tx_hash: '0xf13bc7afe5e02b494dd2f22078381d36a4800ef94a0ccc147431db56c301e6cc',
type: 'token_transfer', type: 'token_transfer',
timestamp: '2022-10-10T14:34:30.000000Z',
}; };
export const erc1155: TokenTransfer = { export const erc1155: TokenTransfer = {
...@@ -115,6 +117,7 @@ export const erc1155: TokenTransfer = { ...@@ -115,6 +117,7 @@ export const erc1155: TokenTransfer = {
}, },
tx_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746', tx_hash: '0x05d6589367633c032d757a69c5fb16c0e33e3994b0d9d1483f82aeee1f05d746',
type: 'token_minting', type: 'token_minting',
timestamp: '2022-10-10T14:34:30.000000Z',
}; };
export const erc1155multiple: TokenTransfer = { export const erc1155multiple: TokenTransfer = {
......
export const base = {
avatar: 'https://avatars.githubusercontent.com/u/22130104',
email: 'tom@ohhhh.me',
name: 'tom goriunov',
nickname: 'tom2drum',
};
...@@ -24,7 +24,7 @@ const watchlistWithTokensHandler = async(_req: NextApiRequest, res: NextApiRespo ...@@ -24,7 +24,7 @@ const watchlistWithTokensHandler = async(_req: NextApiRequest, res: NextApiRespo
} }
const data = await Promise.all(watchlistData.map(async item => { const data = await Promise.all(watchlistData.map(async item => {
const tokens = await fetch(`?module=account&action=tokenlist&address=${ item.address_hash }`); const tokens = await fetch(`/api/?module=account&action=tokenlist&address=${ item.address_hash }`);
const tokensData = await tokens.json() as Tokenlist; const tokensData = await tokens.json() as Tokenlist;
return ({ ...item, tokens_count: Array.isArray(tokensData.result) ? tokensData.result.length : 0 }); return ({ ...item, tokens_count: Array.isArray(tokensData.result) ? tokensData.result.length : 0 });
......
import handler from 'lib/api/handler';
const getUrl = () => '/v2/main-page/indexing-status';
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
...@@ -96,6 +96,15 @@ const config: PlaywrightTestConfig = { ...@@ -96,6 +96,15 @@ const config: PlaywrightTestConfig = {
colorScheme: 'dark', colorScheme: 'dark',
}, },
}, },
{
name: 'dark color mode desktop xl',
grep: /\+@dark-mode-xl/,
use: {
...devices['Desktop Chrome'],
viewport: { width: 1600, height: 1000 },
colorScheme: 'dark',
},
},
], ],
}; };
......
...@@ -2,6 +2,8 @@ import { ChakraProvider } from '@chakra-ui/react'; ...@@ -2,6 +2,8 @@ import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import { AppContextProvider } from 'lib/appContext';
import type { Props as PageProps } from 'lib/next/getServerSideProps';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import { PORT } from 'playwright/fixtures/socketServer'; import { PORT } from 'playwright/fixtures/socketServer';
import theme from 'theme'; import theme from 'theme';
...@@ -9,9 +11,19 @@ import theme from 'theme'; ...@@ -9,9 +11,19 @@ import theme from 'theme';
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
withSocket?: boolean; withSocket?: boolean;
appContext?: {
pageProps: PageProps;
};
} }
const TestApp = ({ children, withSocket }: Props) => { const defaultAppContext = {
pageProps: {
cookies: '',
referrer: '',
},
};
const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props) => {
const [ queryClient ] = React.useState(() => new QueryClient({ const [ queryClient ] = React.useState(() => new QueryClient({
defaultOptions: { defaultOptions: {
queries: { queries: {
...@@ -25,7 +37,9 @@ const TestApp = ({ children, withSocket }: Props) => { ...@@ -25,7 +37,9 @@ const TestApp = ({ children, withSocket }: Props) => {
<ChakraProvider theme={ theme }> <ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://localhost:${ PORT }` : undefined }> <SocketProvider url={ withSocket ? `ws://localhost:${ PORT }` : undefined }>
{ children } <AppContextProvider { ...appContext }>
{ children }
</AppContextProvider>
</SocketProvider> </SocketProvider>
</QueryClientProvider> </QueryClientProvider>
</ChakraProvider> </ChakraProvider>
......
import type { BrowserContext } from '@playwright/test';
import * as cookies from 'lib/cookies';
export default function authFixture(context: BrowserContext) {
context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain: 'localhost', path: '/' } ]);
}
...@@ -25,10 +25,8 @@ do ...@@ -25,10 +25,8 @@ do
configValue="$(cut -d'=' -f2- <<<"$line")"; configValue="$(cut -d'=' -f2- <<<"$line")";
# if there is a value, escape it and add line to target file # if there is a value, escape it and add line to target file
if [ -n "$configValue" ]; then escapedConfigValue=$(echo $configValue | sed s/\'/\"/g);
escapedConfigValue=$(echo $configValue | sed s/\'/\"/g); echo "window.process.env.${configName} = localStorage.getItem('${configName}') || '${escapedConfigValue}';" >> $targetFile;
echo "window.process.env.${configName} = localStorage.getItem('${configName}') || '${escapedConfigValue}';" >> $targetFile;
fi
done < $envFile done < $envFile
done done
......
import { Button } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
[
{ variant: 'solid' },
{ variant: 'solid', colorScheme: 'gray', withDarkMode: true },
{ variant: 'outline', colorScheme: 'gray', withDarkMode: true },
{ variant: 'outline', colorScheme: 'gray-dark', withDarkMode: true },
{ variant: 'outline', colorScheme: 'blue', withDarkMode: true },
{ variant: 'simple', withDarkMode: true },
{ variant: 'ghost', withDarkMode: true },
{ variant: 'subtle' },
{ variant: 'subtle', colorScheme: 'gray', withDarkMode: true },
].forEach(({ variant, colorScheme, withDarkMode }) => {
test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => {
test('base', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>
</TestApp>,
);
await expect(component.locator('button')).toHaveScreenshot();
});
test('disabled', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme } disabled>Click me</Button>
</TestApp>,
);
await expect(component.locator('button')).toHaveScreenshot();
});
test('hovered', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>
</TestApp>,
);
await component.getByText(/click/i).hover();
await expect(component.locator('button')).toHaveScreenshot();
});
test('active', async({ mount }) => {
const component = await mount(
<TestApp>
<Button variant={ variant } colorScheme={ colorScheme } isActive>Click me</Button>
</TestApp>,
);
await expect(component.locator('button')).toHaveScreenshot();
});
});
});
import { FormControl, Input, FormLabel } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
test.use({ viewport: { width: 500, height: 300 } });
test.describe('floating label size md +@dark-mode', () => {
test('empty', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
await component.locator('input').focus();
await expect(component).toHaveScreenshot();
});
test('empty error', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
await component.locator('input').focus();
await expect(component).toHaveScreenshot();
});
test('filled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('filled disabled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isDisabled/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('filled error', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isInvalid/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
});
test.describe('floating label size lg +@dark-mode', () => {
test('empty', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value=""/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
await component.locator('input').focus();
await expect(component).toHaveScreenshot();
});
test('filled', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input required value="foo"/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
await component.locator('input').focus();
await expect(component).toHaveScreenshot();
});
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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