Commit 83723cb1 authored by isstuev's avatar isstuev

fixes&tests

parent 76eb57f2
...@@ -251,3 +251,26 @@ export const l2tx: Transaction = { ...@@ -251,3 +251,26 @@ export const l2tx: Transaction = {
l1_gas_used: '17060', l1_gas_used: '17060',
l1_fee: '1584574188135760', l1_fee: '1584574188135760',
}; };
export const base2 = {
...base,
hash: '0x02d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
from: {
...base.from,
hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859',
},
};
export const base3 = {
...base,
hash: '0x12d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
from: {
...base.from,
hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859',
},
};
export const base4 = {
...base,
hash: '0x22d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
};
...@@ -5,6 +5,8 @@ import { WebSocketServer } from 'ws'; ...@@ -5,6 +5,8 @@ import { WebSocketServer } from 'ws';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import type { NewBlockSocketResponse } from 'types/api/block'; import type { NewBlockSocketResponse } from 'types/api/block';
import type { SmartContractVerificationResponse } from 'types/api/contract'; import type { SmartContractVerificationResponse } from 'types/api/contract';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import type { Transaction } from 'types/api/transaction';
type ReturnType = () => Promise<WebSocket>; type ReturnType = () => Promise<WebSocket>;
...@@ -58,11 +60,14 @@ export const joinChannel = async(socket: WebSocket, channelName: string) => { ...@@ -58,11 +60,14 @@ export const joinChannel = async(socket: WebSocket, channelName: string) => {
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'coin_balance', payload: { coin_balance: AddressCoinBalanceHistoryItem }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'coin_balance', payload: { coin_balance: AddressCoinBalanceHistoryItem }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_balance', payload: { block_number: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_balance', payload: { block_number: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transaction: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transaction: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'transaction', payload: { transactions: Array<Transaction> }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transaction: number }): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transaction: number }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'pending_transaction', payload: { pending_transactions: Array<Transaction> }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'new_block', payload: NewBlockSocketResponse): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'new_block', payload: NewBlockSocketResponse): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'verification_result', payload: SmartContractVerificationResponse): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'verification_result', payload: SmartContractVerificationResponse): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'total_supply', payload: { total_supply: number}): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'total_supply', payload: { total_supply: number}): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'changed_bytecode', payload: Record<string, never>): void; export function sendMessage(socket: WebSocket, channel: Channel, msg: 'changed_bytecode', payload: Record<string, never>): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: 'token_transfer', payload: { token_transfers: Array<TokenTransfer> }): void;
export function sendMessage(socket: WebSocket, channel: Channel, msg: string, payload: unknown): void { export function sendMessage(socket: WebSocket, channel: Channel, msg: string, payload: unknown): void {
socket.send(JSON.stringify([ socket.send(JSON.stringify([
...channel, ...channel,
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { test, expect, devices } from '@playwright/experimental-ct-react'; import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { erc1155A } from 'mocks/tokens/tokenTransfer'; import * as tokenTransferMock from 'mocks/tokens/tokenTransfer';
import * as socketServer from 'playwright/fixtures/socketServer';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import AddressTokenTransfers from './AddressTokenTransfers'; import AddressTokenTransfers from './AddressTokenTransfers';
const API_URL = buildApiUrl('address_token_transfers', { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' }) + const CURRENT_ADDRESS = '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859';
const API_URL = buildApiUrl('address_token_transfers', { hash: CURRENT_ADDRESS }) +
'?token=0x1189a607CEac2f0E14867de4EB15b15C9FFB5859'; '?token=0x1189a607CEac2f0E14867de4EB15b15C9FFB5859';
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859', token: '0x1189a607CEac2f0E14867de4EB15b15C9FFB5859' }, query: { hash: CURRENT_ADDRESS, token: '0x1189a607CEac2f0E14867de4EB15b15C9FFB5859' },
}, },
}; };
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('with token filter and pagination', async({ mount, page }) => { test('with token filter and pagination', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ items: [ erc1155A ], next_page_params: { block_number: 1 } }), body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
})); }));
const component = await mount( const component = await mount(
...@@ -37,7 +48,7 @@ test('with token filter and pagination', async({ mount, page }) => { ...@@ -37,7 +48,7 @@ test('with token filter and pagination', async({ mount, page }) => {
test('with token filter and no pagination', async({ mount, page }) => { test('with token filter and no pagination', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ items: [ erc1155A ] }), body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ] }),
})); }));
const component = await mount( const component = await mount(
...@@ -57,7 +68,7 @@ test.describe('mobile', () => { ...@@ -57,7 +68,7 @@ test.describe('mobile', () => {
test('with token filter and pagination', async({ mount, page }) => { test('with token filter and pagination', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ items: [ erc1155A ], next_page_params: { block_number: 1 } }), body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
})); }));
const component = await mount( const component = await mount(
...@@ -74,7 +85,7 @@ test.describe('mobile', () => { ...@@ -74,7 +85,7 @@ test.describe('mobile', () => {
test('with token filter and no pagination', async({ mount, page }) => { test('with token filter and no pagination', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ items: [ erc1155A ] }), body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ] }),
})); }));
const component = await mount( const component = await mount(
...@@ -88,3 +99,160 @@ test.describe('mobile', () => { ...@@ -88,3 +99,160 @@ test.describe('mobile', () => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
}); });
test.describe('socket', () => {
test('without overload', async({ mount, page, createSocket }) => {
const hooksConfigNoToken = {
router: {
query: { hash: CURRENT_ADDRESS },
},
};
const API_URL_NO_TOKEN = buildApiUrl('address_token_transfers', { hash: CURRENT_ADDRESS }) + '?type=';
await page.route(API_URL_NO_TOKEN, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTokenTransfers/>
</TestApp>,
{ hooksConfig: hooksConfigNoToken },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'token_transfer', { token_transfers: [ tokenTransferMock.erc1155B, tokenTransferMock.erc1155C ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(4);
});
test('with overload', async({ mount, page, createSocket }) => {
const hooksConfigNoToken = {
router: {
query: { hash: CURRENT_ADDRESS },
},
};
const API_URL_NO_TOKEN = buildApiUrl('address_token_transfers', { hash: CURRENT_ADDRESS }) + '?type=';
await page.route(API_URL_NO_TOKEN, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTokenTransfers overloadCount={ 2 }/>
</TestApp>,
{ hooksConfig: hooksConfigNoToken },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'token_transfer', { token_transfers: [ tokenTransferMock.erc1155B, tokenTransferMock.erc1155C ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
const counter = await page.locator('tbody tr:nth-child(1)').textContent();
expect(counter?.startsWith('1 ')).toBe(true);
});
test('without overload, with filters', async({ mount, page, createSocket }) => {
const hooksConfigWithFilter = {
router: {
query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' },
},
};
const API_URL_WITH_FILTER = buildApiUrl('address_token_transfers', { hash: CURRENT_ADDRESS }) + '?type=ERC-1155';
await page.route(API_URL_WITH_FILTER, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTokenTransfers/>
</TestApp>,
{ hooksConfig: hooksConfigWithFilter },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'token_transfer', { token_transfers: [ tokenTransferMock.erc1155B, tokenTransferMock.erc20 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
});
test('with overload, with filters', async({ mount, page, createSocket }) => {
const hooksConfigWithFilter = {
router: {
query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' },
},
};
const API_URL_WITH_FILTER = buildApiUrl('address_token_transfers', { hash: CURRENT_ADDRESS }) + '?type=ERC-1155';
await page.route(API_URL_WITH_FILTER, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ tokenTransferMock.erc1155A ], next_page_params: { block_number: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTokenTransfers overloadCount={ 2 }/>
</TestApp>,
{ hooksConfig: hooksConfigWithFilter },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(
socket,
channel,
'token_transfer',
{ token_transfers: [ tokenTransferMock.erc1155B, tokenTransferMock.erc20, tokenTransferMock.erc1155C, tokenTransferMock.erc721 ] },
);
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
const counter = await page.locator('tbody tr:nth-child(1)').textContent();
expect(counter?.startsWith('1 ')).toBe(true);
});
});
...@@ -63,7 +63,13 @@ const matchFilters = (filters: Filters, tokenTransfer: TokenTransfer, address?: ...@@ -63,7 +63,13 @@ const matchFilters = (filters: Filters, tokenTransfer: TokenTransfer, address?:
return true; return true;
}; };
const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}) => { type Props = {
scrollRef?: React.RefObject<HTMLDivElement>;
// for tests only
overloadCount?: number;
}
const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -118,11 +124,12 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -118,11 +124,12 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
setSocketAlert(''); setSocketAlert('');
const newItems: Array<TokenTransfer> = []; const newItems: Array<TokenTransfer> = [];
let newCount = 0;
payload.token_transfers.forEach(transfer => { payload.token_transfers.forEach(transfer => {
if (data?.items && data.items.length + newItems.length >= OVERLOAD_COUNT) { if (data?.items && data.items.length + newItems.length >= overloadCount) {
if (matchFilters(filters, transfer, currentAddress)) { if (matchFilters(filters, transfer, currentAddress)) {
setNewItemsCount(prev => prev + 1); newCount++;
} }
} else { } else {
if (matchFilters(filters, transfer, currentAddress)) { if (matchFilters(filters, transfer, currentAddress)) {
...@@ -131,6 +138,10 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD ...@@ -131,6 +138,10 @@ const AddressTokenTransfers = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLD
} }
}); });
if (newCount > 0) {
setNewItemsCount(prev => prev + newCount);
}
if (newItems.length > 0) { if (newItems.length > 0) {
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('address_token_transfers', { pathParams: { hash: currentAddress }, queryParams: { ...filters } }), getResourceKey('address_token_transfers', { pathParams: { hash: currentAddress }, queryParams: { ...filters } }),
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { base as txMock } from 'mocks/txs/tx'; import * as txMock from 'mocks/txs/tx';
import * as socketServer from 'playwright/fixtures/socketServer';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import AddressTxs from './AddressTxs'; import AddressTxs from './AddressTxs';
const API_URL = buildApiUrl('address_txs', { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' }); const CURRENT_ADDRESS = '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859';
const API_URL = buildApiUrl('address_txs', { hash: CURRENT_ADDRESS });
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' }, query: { hash: CURRENT_ADDRESS },
}, },
}; };
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('address txs +@mobile +@desktop-xl', async({ mount, page }) => { test('address txs +@mobile +@desktop-xl', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ items: [ txMock, txMock ], next_page_params: { block: 1 } }), body: JSON.stringify({ items: [ txMock.base, txMock.base ], next_page_params: { block: 1 } }),
})); }));
const component = await mount( const component = await mount(
...@@ -32,3 +43,167 @@ test('address txs +@mobile +@desktop-xl', async({ mount, page }) => { ...@@ -32,3 +43,167 @@ test('address txs +@mobile +@desktop-xl', async({ mount, page }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test.describe('socket', () => {
test('without overload', async({ mount, page, createSocket }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base ], next_page_params: { block: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base4 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(4);
});
test('with update', async({ mount, page, createSocket }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.pending ], next_page_params: { block: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base, txMock.base2 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
});
test('with overload', async({ mount, page, createSocket }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base ], next_page_params: { block: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs overloadCount={ 2 }/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base3, txMock.base4 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
const counter = await page.locator('tbody tr:nth-child(1)').textContent();
expect(counter?.startsWith('2 ')).toBe(true);
});
test('without overload, with filters', async({ mount, page, createSocket }) => {
const hooksConfigWithFilter = {
router: {
query: { hash: CURRENT_ADDRESS, filter: 'from' },
},
};
const API_URL_WITH_FILTER = buildApiUrl('address_txs', { hash: CURRENT_ADDRESS }) + '?filter=from';
await page.route(API_URL_WITH_FILTER, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base ], next_page_params: { block: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs/>
</TestApp>,
{ hooksConfig: hooksConfigWithFilter },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base4 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
});
test('with overload, with filters', async({ mount, page, createSocket }) => {
const hooksConfigWithFilter = {
router: {
query: { hash: CURRENT_ADDRESS, filter: 'from' },
},
};
const API_URL_WITH_FILTER = buildApiUrl('address_txs', { hash: CURRENT_ADDRESS }) + '?filter=from';
await page.route(API_URL_WITH_FILTER, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ items: [ txMock.base ], next_page_params: { block: 1 } }),
}));
await mount(
<TestApp withSocket>
<Box h={{ base: '134px', lg: 6 }}/>
<AddressTxs overloadCount={ 2 }/>
</TestApp>,
{ hooksConfig: hooksConfigWithFilter },
);
const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, `addresses:${ CURRENT_ADDRESS.toLowerCase() }`);
const itemsCount = await page.locator('tbody tr').count();
expect(itemsCount).toBe(2);
socketServer.sendMessage(socket, channel, 'transaction', { transactions: [ txMock.base2, txMock.base3, txMock.base4 ] });
await page.waitForSelector('tbody tr:nth-child(3)');
const itemsCountNew = await page.locator('tbody tr').count();
expect(itemsCountNew).toBe(3);
const counter = await page.locator('tbody tr:nth-child(1)').textContent();
expect(counter?.startsWith('1 ')).toBe(true);
});
});
...@@ -5,6 +5,7 @@ import React from 'react'; ...@@ -5,6 +5,7 @@ import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address'; import type { AddressFromToFilter, AddressTransactionsResponse } from 'types/api/address';
import { AddressFromToFilterValues } from 'types/api/address'; import { AddressFromToFilterValues } from 'types/api/address';
import type { Transaction } from 'types/api/transaction';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import getFilterValueFromQuery from 'lib/getFilterValueFromQuery'; import getFilterValueFromQuery from 'lib/getFilterValueFromQuery';
...@@ -26,7 +27,27 @@ const OVERLOAD_COUNT = 75; ...@@ -26,7 +27,27 @@ const OVERLOAD_COUNT = 75;
const getFilterValue = (getFilterValueFromQuery<AddressFromToFilter>).bind(null, AddressFromToFilterValues); const getFilterValue = (getFilterValueFromQuery<AddressFromToFilter>).bind(null, AddressFromToFilterValues);
const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}) => { const matchFilter = (filterValue: AddressFromToFilter, transaction: Transaction, address?: string) => {
if (!filterValue) {
return true;
}
if (filterValue === 'from') {
return transaction.from.hash === address;
}
if (filterValue === 'to') {
return transaction.to?.hash === address;
}
};
type Props = {
scrollRef?: React.RefObject<HTMLDivElement>;
// for tests only
overloadCount?: number;
}
const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Props) => {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
...@@ -69,30 +90,35 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>} ...@@ -69,30 +90,35 @@ const AddressTxs = ({ scrollRef }: {scrollRef?: React.RefObject<HTMLDivElement>}
return; return;
} }
const newItems = [ ...prevData.items ]; const newItems: Array<Transaction> = [];
let newCount = 0;
payload.transactions.forEach(tx => { payload.transactions.forEach(tx => {
const currIndex = newItems.findIndex((item) => item.hash === tx.hash); const currIndex = prevData.items.findIndex((item) => item.hash === tx.hash);
if (currIndex > -1) { if (currIndex > -1) {
newItems[currIndex] = tx; prevData.items[currIndex] = tx;
} else { } else {
if (!filterValue || if (matchFilter(filterValue, tx, currentAddress)) {
(filterValue === 'from' && tx.from.hash === currentAddress) || if (newItems.length + prevData.items.length >= overloadCount) {
(filterValue === 'to' && tx.to?.hash === currentAddress) newCount++;
) {
if (newItems.length >= OVERLOAD_COUNT) {
setNewItemsCount(prev => prev + 1);
} else { } else {
newItems.unshift(tx); newItems.push(tx);
} }
} }
} }
}); });
if (newCount > 0) {
setNewItemsCount(prev => prev + newCount);
}
return { return {
...prevData, ...prevData,
items: newItems, items: [
...newItems,
...prevData.items,
],
}; };
}); });
}; };
......
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