Commit 476d7715 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Call the API logout endpoint when the user logs out (#2469)

* Call the API logout endpoint when the user logs out

* fix tests

* one more fix
parent e1ca14e5
...@@ -60,6 +60,7 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation ...@@ -60,6 +60,7 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/sepolia-testnet.png
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
......
...@@ -263,6 +263,9 @@ export const RESOURCES = { ...@@ -263,6 +263,9 @@ export const RESOURCES = {
auth_link_address: { auth_link_address: {
path: '/api/account/v2/address/link', path: '/api/account/v2/address/link',
}, },
auth_logout: {
path: '/api/account/auth/logout',
},
// STATS MICROSERVICE API // STATS MICROSERVICE API
stats_counters: { stats_counters: {
......
...@@ -47,6 +47,9 @@ export default function useFetch() { ...@@ -47,6 +47,9 @@ export default function useFetch() {
}; };
return fetch(path, reqParams).then(response => { return fetch(path, reqParams).then(response => {
const isJson = response.headers.get('content-type')?.includes('application/json');
if (!response.ok) { if (!response.ok) {
const error = { const error = {
status: response.status, status: response.status,
...@@ -61,6 +64,16 @@ export default function useFetch() { ...@@ -61,6 +64,16 @@ export default function useFetch() {
}); });
} }
if (!isJson) {
return response.text().then(
(textError) => Promise.reject({
payload: textError,
status: response.status,
statusText: response.statusText,
}),
);
}
return response.json().then( return response.json().then(
(jsonError) => Promise.reject({ (jsonError) => Promise.reject({
payload: jsonError as Error, payload: jsonError as Error,
...@@ -73,7 +86,11 @@ export default function useFetch() { ...@@ -73,7 +86,11 @@ export default function useFetch() {
); );
} else { } else {
return response.json() as Promise<Success>; if (isJson) {
return response.json() as Promise<Success>;
}
return Promise.resolve() as Promise<Success>;
} }
}); });
}, [ rollbar ]); }, [ rollbar ]);
......
import type { FeaturedNetwork } from 'types/networks'; import type { FeaturedNetwork } from 'types/networks';
const FEATURED_NETWORKS: Array<FeaturedNetwork> = [ export const FEATURED_NETWORKS: Array<FeaturedNetwork> = [
{ title: 'Gnosis Chain', url: 'https://blockscout.com/xdai/mainnet', group: 'Mainnets', isActive: true }, { title: 'Gnosis Chain', url: 'https://blockscout.com/xdai/mainnet', group: 'Mainnets', isActive: true },
{ title: 'Arbitrum on xDai', url: 'https://blockscout.com/xdai/aox', group: 'Mainnets' }, { title: 'Arbitrum on xDai', url: 'https://blockscout.com/xdai/aox', group: 'Mainnets' },
{ title: 'Ethereum', url: 'https://blockscout.com/eth/mainnet', group: 'Mainnets' }, { title: 'Ethereum', url: 'https://blockscout.com/eth/mainnet', group: 'Mainnets' },
...@@ -13,5 +13,3 @@ const FEATURED_NETWORKS: Array<FeaturedNetwork> = [ ...@@ -13,5 +13,3 @@ const FEATURED_NETWORKS: Array<FeaturedNetwork> = [
{ title: 'LUKSO L14', url: 'https://blockscout.com/lukso/l14', group: 'Other' }, { title: 'LUKSO L14', url: 'https://blockscout.com/lukso/l14', group: 'Other' },
{ title: 'Astar', url: 'https://blockscout.com/astar', group: 'Other' }, { title: 'Astar', url: 'https://blockscout.com/astar', group: 'Other' },
]; ];
export const FEATURED_NETWORKS_MOCK = JSON.stringify(FEATURED_NETWORKS);
...@@ -3,13 +3,13 @@ import type { TestFixture, Page } from '@playwright/test'; ...@@ -3,13 +3,13 @@ import type { TestFixture, Page } from '@playwright/test';
import config from 'configs/app'; import config from 'configs/app';
import { buildExternalAssetFilePath } from 'configs/app/utils'; import { buildExternalAssetFilePath } from 'configs/app/utils';
export type MockConfigResponseFixture = (envName: string, envValue: string, content: string, isImage?: boolean) => Promise<string>; export type MockConfigResponseFixture = (envName: string, envValue: string, content: unknown, isImage?: boolean) => Promise<string>;
const fixture: TestFixture<MockConfigResponseFixture, { page: Page }> = async({ page }, use) => { const fixture: TestFixture<MockConfigResponseFixture, { page: Page }> = async({ page }, use) => {
await use(async(envName, envValue, content, isImage) => { await use(async(envName, envValue, content, isImage) => {
const url = config.app.baseUrl + buildExternalAssetFilePath(envName, envValue); const url = config.app.baseUrl + buildExternalAssetFilePath(envName, envValue);
if (isImage) { if (isImage && typeof content === 'string') {
await page.route(url, (route) => route.fulfill({ await page.route(url, (route) => route.fulfill({
status: 200, status: 200,
path: content, path: content,
...@@ -17,7 +17,7 @@ const fixture: TestFixture<MockConfigResponseFixture, { page: Page }> = async({ ...@@ -17,7 +17,7 @@ const fixture: TestFixture<MockConfigResponseFixture, { page: Page }> = async({
} else { } else {
await page.route(url, (route) => route.fulfill({ await page.route(url, (route) => route.fulfill({
status: 200, status: 200,
body: content, json: content,
})); }));
} }
......
...@@ -46,7 +46,7 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as ...@@ -46,7 +46,7 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as
if (_isEqual(params, callParams) && id) { if (_isEqual(params, callParams) && id) {
return route.fulfill({ return route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ json: {
id, id,
jsonrpc: '2.0', jsonrpc: '2.0',
result: encodeFunctionResult({ result: encodeFunctionResult({
...@@ -54,7 +54,7 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as ...@@ -54,7 +54,7 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as
functionName: abiItem.name, functionName: abiItem.name,
result, result,
}), }),
}), },
}); });
} }
}); });
......
...@@ -9,7 +9,7 @@ const fixture: TestFixture<MockTextAdFixture, { page: Page }> = async({ page }, ...@@ -9,7 +9,7 @@ const fixture: TestFixture<MockTextAdFixture, { page: Page }> = async({ page },
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({ await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(textAdMock.duck), json: textAdMock.duck,
})); }));
await page.route(textAdMock.duck.ad.thumbnail, (route) => { await page.route(textAdMock.duck.ad.thumbnail, (route) => {
return route.fulfill({ return route.fulfill({
......
...@@ -11,7 +11,7 @@ const CHECK_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type: ...@@ -11,7 +11,7 @@ const CHECK_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type:
test('base view', async({ render, page }) => { test('base view', async({ render, page }) => {
await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({ await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(mocks.ADDRESS_CHECK_RESPONSE.SUCCESS), json: mocks.ADDRESS_CHECK_RESPONSE.SUCCESS,
})); }));
const props = { const props = {
onContinue: () => {}, onContinue: () => {},
...@@ -25,7 +25,7 @@ test('base view', async({ render, page }) => { ...@@ -25,7 +25,7 @@ test('base view', async({ render, page }) => {
test('SOURCE_CODE_NOT_VERIFIED_ERROR view +@mobile', async({ render, page }) => { test('SOURCE_CODE_NOT_VERIFIED_ERROR view +@mobile', async({ render, page }) => {
await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({ await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(mocks.ADDRESS_CHECK_RESPONSE.SOURCE_CODE_NOT_VERIFIED_ERROR), json: mocks.ADDRESS_CHECK_RESPONSE.SOURCE_CODE_NOT_VERIFIED_ERROR,
})); }));
const props = { const props = {
......
...@@ -11,7 +11,7 @@ const VERIFY_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type ...@@ -11,7 +11,7 @@ const VERIFY_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type
test('base view', async({ render, page }) => { test('base view', async({ render, page }) => {
await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({ await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(mocks.ADDRESS_VERIFY_RESPONSE.SUCCESS), json: mocks.ADDRESS_VERIFY_RESPONSE.SUCCESS,
})); }));
const props = { const props = {
...@@ -28,7 +28,7 @@ test('base view', async({ render, page }) => { ...@@ -28,7 +28,7 @@ test('base view', async({ render, page }) => {
test('INVALID_SIGNER_ERROR view +@mobile', async({ render, page }) => { test('INVALID_SIGNER_ERROR view +@mobile', async({ render, page }) => {
await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({ await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(mocks.ADDRESS_VERIFY_RESPONSE.INVALID_SIGNER_ERROR), json: mocks.ADDRESS_VERIFY_RESPONSE.INVALID_SIGNER_ERROR,
})); }));
const props = { const props = {
......
...@@ -19,12 +19,12 @@ test.beforeEach(async({ mockConfigResponse, mockEnvs, mockAssetResponse, page }) ...@@ -19,12 +19,12 @@ test.beforeEach(async({ mockConfigResponse, mockEnvs, mockAssetResponse, page })
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', 'test' ], [ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', 'test' ],
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID', 'test' ], [ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID', 'test' ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, securityReportsMock);
await Promise.all(appsMock.map(app => mockAssetResponse(app.logo, './playwright/mocks/image_s.jpg'))); await Promise.all(appsMock.map(app => mockAssetResponse(app.logo, './playwright/mocks/image_s.jpg')));
await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({ await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(ratingsMock), json: ratingsMock,
})); }));
}); });
......
...@@ -29,8 +29,8 @@ const testFn = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs, ...@@ -29,8 +29,8 @@ const testFn = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs,
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', 'test' ], [ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', 'test' ],
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID', 'test' ], [ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID', 'test' ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, securityReportsMock);
await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html'); await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html');
await mockRpcResponse({ await mockRpcResponse({
Method: 'eth_chainId', Method: 'eth_chainId',
...@@ -38,7 +38,7 @@ const testFn = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs, ...@@ -38,7 +38,7 @@ const testFn = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs,
}); });
await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({ await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify(ratingsMock), json: ratingsMock,
})); }));
const component = await render( const component = await render(
......
...@@ -177,7 +177,7 @@ test.describe('with apps', () => { ...@@ -177,7 +177,7 @@ test.describe('with apps', () => {
[ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ], [ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ],
]); ]);
await mockApiResponse('search', data, { queryParams: { q: 'o' } }); await mockApiResponse('search', data, { queryParams: { q: 'o' } });
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
await mockAssetResponse(appsMock[0].logo, './playwright/mocks/image_s.jpg'); await mockAssetResponse(appsMock[0].logo, './playwright/mocks/image_s.jpg');
await mockAssetResponse(appsMock[1].logo, './playwright/mocks/image_s.jpg'); await mockAssetResponse(appsMock[1].logo, './playwright/mocks/image_s.jpg');
const component = await render(<SearchResults/>, { hooksConfig }); const component = await render(<SearchResults/>, { hooksConfig });
......
...@@ -11,8 +11,9 @@ test.beforeEach(async({ mockTextAd, mockAssetResponse }) => { ...@@ -11,8 +11,9 @@ test.beforeEach(async({ mockTextAd, mockAssetResponse }) => {
await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/image_s.jpg'); await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/image_s.jpg');
}); });
test('default view +@mobile', async({ render }) => { test('default view +@mobile', async({ render, page }) => {
const component = await render(<DefaultView/>); const component = await render(<DefaultView/>);
await page.mouse.move(1000, 1000);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
......
...@@ -42,7 +42,7 @@ test.describe('no url', () => { ...@@ -42,7 +42,7 @@ test.describe('no url', () => {
await page.route(ANIMATION_MEDIA_TYPE_API_URL, (route) => { await page.route(ANIMATION_MEDIA_TYPE_API_URL, (route) => {
return route.fulfill({ return route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ type: undefined }), json: { type: undefined },
}); });
}); });
await mockAssetResponse(IMAGE_URL, './playwright/mocks/image_long.jpg'); await mockAssetResponse(IMAGE_URL, './playwright/mocks/image_long.jpg');
...@@ -121,7 +121,7 @@ test.describe('page', () => { ...@@ -121,7 +121,7 @@ test.describe('page', () => {
await mockAssetResponse(MEDIA_URL, './playwright/mocks/page.html'); await mockAssetResponse(MEDIA_URL, './playwright/mocks/page.html');
await page.route(MEDIA_TYPE_API_URL, (route) => route.fulfill({ await page.route(MEDIA_TYPE_API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ type: 'html' }), json: { type: 'html' },
})); }));
}); });
......
...@@ -56,12 +56,18 @@ beforeEach(() => { ...@@ -56,12 +56,18 @@ beforeEach(() => {
fetch.resetMocks(); fetch.resetMocks();
}); });
const responseInit = {
headers: {
'Content-Type': 'application/json',
},
};
it('returns correct data if there is only one page', async() => { it('returns correct data if there is only one page', async() => {
const params: Params<'address_txs'> = { const params: Params<'address_txs'> = {
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
}; };
fetch.mockResponse(JSON.stringify(responses.page_empty)); fetch.mockResponse(JSON.stringify(responses.page_empty), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -84,7 +90,7 @@ describe('if there are multiple pages', () => { ...@@ -84,7 +90,7 @@ describe('if there are multiple pages', () => {
}; };
it('return correct data for the first page', async() => { it('return correct data for the first page', async() => {
fetch.mockResponse(JSON.stringify(responses.page_1)); fetch.mockResponse(JSON.stringify(responses.page_1), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -109,10 +115,10 @@ describe('if there are multiple pages', () => { ...@@ -109,10 +115,10 @@ describe('if there are multiple pages', () => {
routerPush.mockClear(); routerPush.mockClear();
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush }); useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3)); fetch.once(JSON.stringify(responses.page_3), responseInit);
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
// INITIAL LOAD // INITIAL LOAD
const { result: r } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result: r } = renderHook(() => useQueryWithPages(params), { wrapper });
...@@ -285,10 +291,10 @@ describe('if there are multiple pages', () => { ...@@ -285,10 +291,10 @@ describe('if there are multiple pages', () => {
const routerPush = jest.fn(() => Promise.resolve()); const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush }); useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3)); fetch.once(JSON.stringify(responses.page_3), responseInit);
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -343,8 +349,8 @@ describe('if there are multiple pages', () => { ...@@ -343,8 +349,8 @@ describe('if there are multiple pages', () => {
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
scrollRef: scrollRef as unknown as React.RefObject<HTMLDivElement>, scrollRef: scrollRef as unknown as React.RefObject<HTMLDivElement>,
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -367,7 +373,7 @@ describe('if there is page query param in URL', () => { ...@@ -367,7 +373,7 @@ describe('if there is page query param in URL', () => {
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
}; };
fetch.mockResponse(JSON.stringify(responses.page_empty)); fetch.mockResponse(JSON.stringify(responses.page_empty), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -391,8 +397,8 @@ describe('if there is page query param in URL', () => { ...@@ -391,8 +397,8 @@ describe('if there is page query param in URL', () => {
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
}; };
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3)); fetch.once(JSON.stringify(responses.page_3), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -439,9 +445,9 @@ describe('queries with filters', () => { ...@@ -439,9 +445,9 @@ describe('queries with filters', () => {
// @ts-ignore: // @ts-ignore:
sorting: { sort: 'val-desc' }, sorting: { sort: 'val-desc' },
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_filtered)); fetch.once(JSON.stringify(responses.page_filtered), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -488,8 +494,8 @@ describe('queries with filters', () => { ...@@ -488,8 +494,8 @@ describe('queries with filters', () => {
resourceName: 'address_txs', resourceName: 'address_txs',
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -526,9 +532,9 @@ describe('queries with sorting', () => { ...@@ -526,9 +532,9 @@ describe('queries with sorting', () => {
pathParams: { hash: addressMock.hash }, pathParams: { hash: addressMock.hash },
filters: { filter: 'from' }, filters: { filter: 'from' },
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_sorted)); fetch.once(JSON.stringify(responses.page_sorted), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
...@@ -580,8 +586,8 @@ describe('queries with sorting', () => { ...@@ -580,8 +586,8 @@ describe('queries with sorting', () => {
// @ts-ignore: // @ts-ignore:
sorting: { sort: 'val-desc' }, sorting: { sort: 'val-desc' },
}; };
fetch.once(JSON.stringify(responses.page_1)); fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2)); fetch.once(JSON.stringify(responses.page_2), responseInit);
const { result } = renderHook(() => useQueryWithPages(params), { wrapper }); const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse(); await waitForApiResponse();
......
...@@ -4,13 +4,17 @@ import React from 'react'; ...@@ -4,13 +4,17 @@ import React from 'react';
import type { Route } from 'nextjs-routes'; import type { Route } from 'nextjs-routes';
import config from 'configs/app';
import useApiFetch from 'lib/api/useApiFetch';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useToast from 'lib/hooks/useToast';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
const PROTECTED_ROUTES: Array<Route['pathname']> = [ const PROTECTED_ROUTES: Array<Route['pathname']> = [
'/account/api-key', '/account/api-key',
'/account/custom-abi', '/account/custom-abi',
'/account/rewards',
'/account/tag-address', '/account/tag-address',
'/account/verified-addresses', '/account/verified-addresses',
'/account/watchlist', '/account/watchlist',
...@@ -20,26 +24,46 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [ ...@@ -20,26 +24,46 @@ const PROTECTED_ROUTES: Array<Route['pathname']> = [
export default function useLogout() { export default function useLogout() {
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const toast = useToast();
const apiFetch = useApiFetch();
return React.useCallback(async() => { return React.useCallback(async() => {
cookies.remove(cookies.NAMES.API_TOKEN); try {
await apiFetch('auth_logout');
queryClient.resetQueries({ cookies.remove(cookies.NAMES.API_TOKEN);
queryKey: getResourceKey('user_info'),
exact: true, if (config.features.rewards.isEnabled) {
}); const rewardsToken = cookies.get(cookies.NAMES.REWARDS_API_TOKEN);
queryClient.resetQueries({ await apiFetch('rewards_logout', { fetchParams: {
queryKey: getResourceKey('custom_abi'), method: 'POST',
exact: true, headers: { Authorization: `Bearer ${ rewardsToken }` },
}); } });
cookies.remove(cookies.NAMES.REWARDS_API_TOKEN);
mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_ACCESS, { Action: 'Logged out' }, { send_immediately: true }); }
if ( queryClient.resetQueries({
PROTECTED_ROUTES.includes(router.pathname) || queryKey: getResourceKey('user_info'),
(router.pathname === '/txs' && router.query.tab === 'watchlist') exact: true,
) { });
router.push({ pathname: '/' }, undefined, { shallow: true }); queryClient.resetQueries({
queryKey: getResourceKey('custom_abi'),
exact: true,
});
mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_ACCESS, { Action: 'Logged out' }, { send_immediately: true });
if (
PROTECTED_ROUTES.includes(router.pathname) ||
(router.pathname === '/txs' && router.query.tab === 'watchlist')
) {
router.push({ pathname: '/' }, undefined, { shallow: true });
}
} catch (error) {
toast({
status: 'error',
title: 'Logout failed',
description: 'Please try again later',
});
} }
}, [ queryClient, router ]); }, [ apiFetch, queryClient, router, toast ]);
} }
...@@ -13,7 +13,7 @@ test.describe('with custom links, max cols', () => { ...@@ -13,7 +13,7 @@ test.describe('with custom links, max cols', () => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ], [ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, JSON.stringify(FOOTER_LINKS)); await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, FOOTER_LINKS);
await injectMetaMaskProvider(); await injectMetaMaskProvider();
await mockApiResponse('homepage_indexing_status', { await mockApiResponse('homepage_indexing_status', {
finished_indexing: false, finished_indexing: false,
...@@ -43,7 +43,7 @@ test.describe('with custom links, min cols', () => { ...@@ -43,7 +43,7 @@ test.describe('with custom links, min cols', () => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ], [ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, JSON.stringify([ FOOTER_LINKS[0] ])); await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, [ FOOTER_LINKS[0] ]);
await render(<Footer/>); await render(<Footer/>);
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
......
import type { BrowserContext } from '@playwright/test'; import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS } from 'mocks/config/network';
import { contextWithAuth } from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import { test, expect, devices } from 'playwright/lib'; import { test, expect, devices } from 'playwright/lib';
...@@ -24,7 +24,7 @@ test.beforeEach(async({ mockEnvs, mockConfigResponse, mockAssetResponse }) => { ...@@ -24,7 +24,7 @@ test.beforeEach(async({ mockEnvs, mockConfigResponse, mockAssetResponse }) => {
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ], [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK); await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS);
await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg'); await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg');
}); });
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS } from 'mocks/config/network';
import { contextWithAuth } from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
...@@ -27,7 +27,7 @@ test.beforeEach(async({ mockEnvs, mockConfigResponse }) => { ...@@ -27,7 +27,7 @@ test.beforeEach(async({ mockEnvs, mockConfigResponse }) => {
...ENVS_MAP.rewardsService, ...ENVS_MAP.rewardsService,
[ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ], [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK); await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS);
}); });
test.describe('no auth', () => { test.describe('no auth', () => {
......
import React from 'react'; import React from 'react';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS } from 'mocks/config/network';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
import NetworkMenu from './NetworkMenu'; import NetworkMenu from './NetworkMenu';
...@@ -14,7 +14,7 @@ test('base view +@dark-mode', async({ render, page, mockConfigResponse, mockAsse ...@@ -14,7 +14,7 @@ test('base view +@dark-mode', async({ render, page, mockConfigResponse, mockAsse
await mockEnvs([ await mockEnvs([
[ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ], [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK); await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS);
await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg'); await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg');
const component = await render(<NetworkMenu/>); const component = await render(<NetworkMenu/>);
......
...@@ -206,7 +206,7 @@ test.describe('with apps', () => { ...@@ -206,7 +206,7 @@ test.describe('with apps', () => {
const apiUrl = await mockApiResponse('quick_search', [ const apiUrl = await mockApiResponse('quick_search', [
searchMock.token1, searchMock.token1,
], { queryParams: { q: 'o' } }); ], { queryParams: { q: 'o' } });
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
await mockAssetResponse(appsMock[0].logo, './playwright/mocks/image_s.jpg'); await mockAssetResponse(appsMock[0].logo, './playwright/mocks/image_s.jpg');
await mockAssetResponse(appsMock[1].logo, './playwright/mocks/image_s.jpg'); await mockAssetResponse(appsMock[1].logo, './playwright/mocks/image_s.jpg');
......
import React from 'react'; import React from 'react';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS } from 'mocks/config/network';
import * as statsMock from 'mocks/stats/index'; import * as statsMock from 'mocks/stats/index';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
...@@ -39,7 +39,7 @@ test('with horizontal nav bar layout', async({ render, mockApiResponse, mockEnvs ...@@ -39,7 +39,7 @@ test('with horizontal nav bar layout', async({ render, mockApiResponse, mockEnvs
[ 'NEXT_PUBLIC_NAVIGATION_LAYOUT', 'horizontal' ], [ 'NEXT_PUBLIC_NAVIGATION_LAYOUT', 'horizontal' ],
[ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ], [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
]); ]);
await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK); await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS);
await mockAssetResponse('https://localhost:3000/my-logo.png', './playwright/mocks/image_s.jpg'); await mockAssetResponse('https://localhost:3000/my-logo.png', './playwright/mocks/image_s.jpg');
const component = await render(<TopBar/>); const component = await render(<TopBar/>);
......
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