Merge branch 'main' into dapp-rating
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable max-len */
|
||||
import type { RpcBlock } from 'viem';
|
||||
|
||||
import type { Block, BlocksResponse } from 'types/api/block';
|
||||
|
||||
export const base: Block = {
|
||||
@@ -144,6 +146,11 @@ export const withBlobTxs: Block = {
|
||||
blob_tx_count: 1,
|
||||
};
|
||||
|
||||
export const withWithdrawals: Block = {
|
||||
...base,
|
||||
withdrawals_count: 2,
|
||||
};
|
||||
|
||||
export const baseListResponse: BlocksResponse = {
|
||||
items: [
|
||||
base,
|
||||
@@ -151,3 +158,107 @@ export const baseListResponse: BlocksResponse = {
|
||||
],
|
||||
next_page_params: null,
|
||||
};
|
||||
|
||||
export const rpcBlockBase: RpcBlock = {
|
||||
difficulty: '0x37fcc04bef8',
|
||||
extraData: '0x476574682f76312e302e312d38326566323666362f6c696e75782f676f312e34',
|
||||
gasLimit: '0x2fefd8',
|
||||
gasUsed: '0x0',
|
||||
hash: '0xfbafb4b7b6f6789338d15ff046f40dc608a42b1a33b093e109c6d7a36cd76f61',
|
||||
logsBloom: '0x0',
|
||||
miner: '0xe6a7a1d47ff21b6321162aea7c6cb457d5476bca',
|
||||
mixHash: '0x038956b9df89d0c1f980fd656d045e912beafa515cff7d7fd3c5f34ffdcb9e4b',
|
||||
nonce: '0xd8d3392f340bbb22',
|
||||
number: '0x1869f',
|
||||
parentHash: '0x576fd45e598c9f86835f50fe2c6e6d11df2d4c4b01f19e4241b7e793d852f9e4',
|
||||
receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
|
||||
sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
|
||||
size: '0x225',
|
||||
stateRoot: '0x32356228651d64cc5e6e7be87a556ecdbf40e876251dc867ba9e4bb82a0124a3',
|
||||
timestamp: '0x55d19741',
|
||||
totalDifficulty: '0x259e89748daae17',
|
||||
transactions: [
|
||||
'0x0e70849f10e22fe2e53fe6755f86a572aa6bb2fc472f0b87d9e561efa1fc2e1f',
|
||||
'0xae5624c77f06d0164301380afa7780ebe49debe77eb3d5167004d69bd188a09f',
|
||||
],
|
||||
transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
|
||||
uncles: [],
|
||||
baseFeePerGas: null,
|
||||
blobGasUsed: `0x0`,
|
||||
excessBlobGas: `0x0`,
|
||||
sealFields: [],
|
||||
withdrawals: [
|
||||
{ address: '0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f', amount: '0x12128cd', index: '0x3216bbb', validatorIndex: '0x4dca3' },
|
||||
{ address: '0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f', amount: '0x12027dd', index: '0x3216bbc', validatorIndex: '0x4dca4' },
|
||||
],
|
||||
};
|
||||
|
||||
export const rpcBlockWithTxsInfo: RpcBlock = {
|
||||
...rpcBlockBase,
|
||||
transactions: [
|
||||
{
|
||||
accessList: [
|
||||
{
|
||||
address: '0x7af661a6463993e05a171f45d774cf37e761c83f',
|
||||
storageKeys: [
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000007',
|
||||
'0x000000000000000000000000000000000000000000000000000000000000000c',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000008',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000006',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000009',
|
||||
'0x000000000000000000000000000000000000000000000000000000000000000a',
|
||||
],
|
||||
},
|
||||
{
|
||||
address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
storageKeys: [
|
||||
'0x0d726f311404f8052d44e7004a6ffb747709a6d3666a62ce3f5aad13374680ab',
|
||||
'0x1a824a6850dcbd9223afea4418727593881e2911ed2e734272a263153159fe26',
|
||||
'0xfae3a383c82daf853bbd8bbcd21280410599b135c274c01354ea7d3a5e09f43c',
|
||||
],
|
||||
},
|
||||
],
|
||||
blockHash: '0xeb37ebc94e31773e5c5703073fd3911b2ab596f099d00d18b55ae3ac8203c1d5',
|
||||
blockNumber: '0x136058d',
|
||||
chainId: '0x1',
|
||||
from: '0x111527f1386c6725a2f5986230f3060bdcac041f',
|
||||
gas: '0xf4240',
|
||||
gasPrice: '0x1780b2ff9',
|
||||
hash: '0x0e70849f10e22fe2e53fe6755f86a572aa6bb2fc472f0b87d9e561efa1fc2e1f',
|
||||
input: '0x258d7af661a6463993e05a171f45d774cf37e761c83f402ab3277301b3574863a151d042dc870fb1b3f0c72cbbdd53a85898f62415fe124406f6608d8802269d1283cdb2a5a329649e5cb4cdcee91ab6',
|
||||
// maxFeePerGas: '0x3ac1bf7ee',
|
||||
// maxPriorityFeePerGas: '0x0',
|
||||
nonce: '0x127b2',
|
||||
r: '0x3c47223f880a3fb7b1eca368d9d7320d2278f0b679109d9ed0af4080ee386f23',
|
||||
s: '0x587a441f9472b312ff302d7132547aa250ea06c6203c76831d56a46ec188e664',
|
||||
to: '0x000000d40b595b94918a28b27d1e2c66f43a51d3',
|
||||
transactionIndex: '0x0',
|
||||
type: '0x1',
|
||||
v: '0x1',
|
||||
value: '0x31',
|
||||
yParity: '0x1',
|
||||
},
|
||||
{
|
||||
accessList: [],
|
||||
blockHash: '0xeb37ebc94e31773e5c5703073fd3911b2ab596f099d00d18b55ae3ac8203c1d5',
|
||||
blockNumber: '0x136058d',
|
||||
chainId: '0x1',
|
||||
from: '0xe25d2cb47b606bb6fd9272125457a7230e26f956',
|
||||
gas: '0x47bb0',
|
||||
gasPrice: '0x1ba875cb6',
|
||||
hash: '0xae5624c77f06d0164301380afa7780ebe49debe77eb3d5167004d69bd188a09f',
|
||||
input: '0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006696237b00000000000000000000000000000000000000000000000000000000000000040b080604000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000006d1aaedfab0f00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d422990000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d42299000000000000000000000000000000fee13a103a10d593b9ae06b3e05f2e7e1c00000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000d84d4e8e1e8f268e027c29fa4d48c4b7e4d42299000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000006cd4db3c8c8d',
|
||||
// maxFeePerGas: '0x23493c9cd',
|
||||
// maxPriorityFeePerGas: '0x427c2cbd',
|
||||
nonce: '0x32b',
|
||||
r: '0x6566181b3cfd01702b24a2124ea7698b8cc815c7f37d1ea55800f176ca7a94cf',
|
||||
s: '0x34f8dd837f37746ccd18f4fa71e05de98a2212f1c931f740598e491518616bb3',
|
||||
to: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad',
|
||||
transactionIndex: '0x1',
|
||||
type: '0x1',
|
||||
v: '0x1',
|
||||
value: '0xb1a2bc2ec50000',
|
||||
yParity: '0x1',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ interface Options<R extends ResourceName> {
|
||||
pathParams?: Parameters<typeof buildUrl<R>>[1];
|
||||
queryParams?: Parameters<typeof buildUrl<R>>[2];
|
||||
times?: number;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export type MockApiResponseFixture = <R extends ResourceName>(resourceName: R, responseMock: ResourcePayload<R>, options?: Options<R>) => Promise<string>;
|
||||
@@ -16,8 +17,8 @@ const fixture: TestFixture<MockApiResponseFixture, { page: Page }> = async({ pag
|
||||
const apiUrl = buildUrl(resourceName, options?.pathParams, options?.queryParams);
|
||||
|
||||
await page.route(apiUrl, (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify(responseMock),
|
||||
status: options?.status ?? 200,
|
||||
json: responseMock,
|
||||
}), { times: options?.times });
|
||||
|
||||
return apiUrl;
|
||||
|
||||
@@ -8,9 +8,8 @@ type Params = PublicRpcSchema[number];
|
||||
|
||||
export type MockRpcResponseFixture = (params: Params) => Promise<void>;
|
||||
|
||||
// WIP
|
||||
const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ page }, use) => {
|
||||
await use(async({ Method, ReturnType }) => {
|
||||
await use(async(rpcMock) => {
|
||||
const rpcUrl = getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL');
|
||||
|
||||
if (!rpcUrl) {
|
||||
@@ -31,18 +30,18 @@ const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ pag
|
||||
const payload = {
|
||||
id,
|
||||
jsonrpc: '2.0',
|
||||
method: Method,
|
||||
// TODO: add params to match actual payload
|
||||
method: rpcMock.Method,
|
||||
...(rpcMock.Parameters ? { params: rpcMock.Parameters } : {}),
|
||||
};
|
||||
|
||||
if (_isEqual(json, payload) && id !== undefined) {
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
json: {
|
||||
id,
|
||||
jsonrpc: '2.0',
|
||||
result: ReturnType,
|
||||
}),
|
||||
result: rpcMock.ReturnType,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 100 KiB |
@@ -45,7 +45,7 @@ const ContractVerificationFieldCode = ({ isVyper }: Props) => {
|
||||
{ isVyper ? null : (
|
||||
<>
|
||||
<span>We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the </span>
|
||||
<Link href="https://github.com/poanetwork/solidity-flattener" target="_blank">POA solidity flattener</Link>
|
||||
<Link href="https://hardhat.org/hardhat-runner/docs/advanced/flattening" target="_blank">Hardhat flattener</Link>
|
||||
<span> or the </span>
|
||||
<Link href="https://www.npmjs.com/package/truffle-flattener" target="_blank">Truffle flattener</Link>
|
||||
</>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React from 'react';
|
||||
import { numberToHex } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
import * as addressMock from 'mocks/address/address';
|
||||
import * as addressCountersMock from 'mocks/address/counters';
|
||||
import * as addressTabCountersMock from 'mocks/address/tabCounters';
|
||||
import * as socketServer from 'playwright/fixtures/socketServer';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
import * as pwConfig from 'playwright/utils/config';
|
||||
|
||||
import Address from './Address';
|
||||
|
||||
@@ -31,3 +34,23 @@ test.describe('fetched bytecode', () => {
|
||||
expect(request).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test('degradation view', async({ render, page, mockRpcResponse, mockApiResponse }) => {
|
||||
await mockApiResponse('address', null as never, { pathParams: { hash: addressMock.hash }, status: 500 });
|
||||
await mockApiResponse('address_counters', addressCountersMock.forValidator, { pathParams: { hash: addressMock.hash } });
|
||||
await mockApiResponse('address_tabs_counters', null as never, { pathParams: { hash: addressMock.hash }, status: 500 });
|
||||
await mockApiResponse('address_txs', null as never, { pathParams: { hash: addressMock.hash }, status: 500 });
|
||||
await mockRpcResponse({
|
||||
Method: 'eth_getBalance',
|
||||
Parameters: [ addressMock.hash, 'latest' ],
|
||||
ReturnType: numberToHex(1234567890123456),
|
||||
});
|
||||
|
||||
const component = await render(<Address/>, { hooksConfig });
|
||||
await page.waitForResponse(config.chain.rpcUrl as string);
|
||||
|
||||
await expect(component).toHaveScreenshot({
|
||||
mask: [ page.locator(pwConfig.adsBannerSelector) ],
|
||||
maskColor: pwConfig.maskColor,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { numberToHex } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
import * as blockMock from 'mocks/blocks/block';
|
||||
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import Block from './Block';
|
||||
|
||||
const height = String(blockMock.base.height);
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { height_or_hash: height },
|
||||
},
|
||||
};
|
||||
|
||||
test.beforeEach(async({ mockTextAd }) => {
|
||||
await mockTextAd();
|
||||
});
|
||||
|
||||
test('degradation view, details tab', async({ render, mockApiResponse, mockRpcResponse, page }) => {
|
||||
await mockApiResponse('block', null as never, { pathParams: { height_or_hash: height }, status: 500 });
|
||||
await mockRpcResponse({
|
||||
Method: 'eth_getBlockByNumber',
|
||||
Parameters: [ numberToHex(Number(height)), false ],
|
||||
ReturnType: blockMock.rpcBlockBase,
|
||||
});
|
||||
|
||||
const component = await render(<Block/>, { hooksConfig });
|
||||
await page.waitForResponse(config.chain.rpcUrl as string);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('degradation view, txs tab', async({ render, mockApiResponse, mockRpcResponse, page }) => {
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { height_or_hash: height, tab: 'txs' },
|
||||
},
|
||||
};
|
||||
|
||||
await mockApiResponse('block', blockMock.base, { pathParams: { height_or_hash: height } });
|
||||
await mockApiResponse('block_txs', null as never, { pathParams: { height_or_hash: height }, status: 500 });
|
||||
await mockRpcResponse({
|
||||
Method: 'eth_getBlockByNumber',
|
||||
Parameters: [ numberToHex(Number(height)), true ],
|
||||
ReturnType: blockMock.rpcBlockWithTxsInfo,
|
||||
});
|
||||
|
||||
const component = await render(<Block/>, { hooksConfig });
|
||||
await page.waitForResponse(config.chain.rpcUrl as string);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('degradation view, withdrawals tab', async({ render, mockApiResponse, mockRpcResponse, mockEnvs, page }) => {
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { height_or_hash: height, tab: 'withdrawals' },
|
||||
},
|
||||
};
|
||||
|
||||
await mockEnvs(ENVS_MAP.beaconChain);
|
||||
await mockApiResponse('block', blockMock.withWithdrawals, { pathParams: { height_or_hash: height } });
|
||||
await mockApiResponse('block_withdrawals', null as never, { pathParams: { height_or_hash: height }, status: 500 });
|
||||
await mockRpcResponse({
|
||||
Method: 'eth_getBlockByNumber',
|
||||
Parameters: [ numberToHex(Number(height)), false ],
|
||||
ReturnType: blockMock.rpcBlockBase,
|
||||
});
|
||||
|
||||
const component = await render(<Block/>, { hooksConfig });
|
||||
await page.waitForResponse(config.chain.rpcUrl as string);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { numberToHex } from 'viem';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { apps as appsMock } from 'mocks/apps/apps';
|
||||
import { test, expect, devices } from 'playwright/lib';
|
||||
|
||||
@@ -15,13 +17,17 @@ const hooksConfig = {
|
||||
|
||||
const MARKETPLACE_CONFIG_URL = 'https://marketplace-config.json';
|
||||
|
||||
const testFn: Parameters<typeof test>[1] = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs }) => {
|
||||
const testFn: Parameters<typeof test>[1] = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs, mockRpcResponse }) => {
|
||||
await mockEnvs([
|
||||
[ 'NEXT_PUBLIC_MARKETPLACE_ENABLED', 'true' ],
|
||||
[ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ],
|
||||
]);
|
||||
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock));
|
||||
await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html');
|
||||
await mockRpcResponse({
|
||||
Method: 'eth_chainId',
|
||||
ReturnType: numberToHex(Number(config.chain.id)),
|
||||
});
|
||||
|
||||
const component = await render(
|
||||
<Flex flexDirection="column" mx={{ base: 4, lg: 6 }} h="100vh">
|
||||
@@ -33,9 +39,9 @@ const testFn: Parameters<typeof test>[1] = async({ render, mockConfigResponse, m
|
||||
await expect(component).toHaveScreenshot();
|
||||
};
|
||||
|
||||
test.fixme('base view +@dark-mode', testFn);
|
||||
test('base view +@dark-mode', testFn);
|
||||
|
||||
test.describe('mobile', () => {
|
||||
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
|
||||
test.fixme('base view', testFn);
|
||||
test('base view', testFn);
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 44 KiB |