Skip to content

Commit

Permalink
Call the API logout endpoint when the user logs out (#2469)
Browse files Browse the repository at this point in the history
* Call the API logout endpoint when the user logs out

* fix tests

* one more fix
  • Loading branch information
tom2drum authored Dec 16, 2024
1 parent e1ca14e commit 476d771
Show file tree
Hide file tree
Showing 22 changed files with 129 additions and 79 deletions.
1 change: 1 addition & 0 deletions configs/envs/.env.main
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
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_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_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com
Expand Down
3 changes: 3 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ export const RESOURCES = {
auth_link_address: {
path: '/api/account/v2/address/link',
},
auth_logout: {
path: '/api/account/auth/logout',
},

// STATS MICROSERVICE API
stats_counters: {
Expand Down
19 changes: 18 additions & 1 deletion lib/hooks/useFetch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export default function useFetch() {
};

return fetch(path, reqParams).then(response => {

const isJson = response.headers.get('content-type')?.includes('application/json');

if (!response.ok) {
const error = {
status: response.status,
Expand All @@ -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(
(jsonError) => Promise.reject({
payload: jsonError as Error,
Expand All @@ -73,7 +86,11 @@ export default function useFetch() {
);

} else {
return response.json() as Promise<Success>;
if (isJson) {
return response.json() as Promise<Success>;
}

return Promise.resolve() as Promise<Success>;
}
});
}, [ rollbar ]);
Expand Down
4 changes: 1 addition & 3 deletions mocks/config/network.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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: 'Arbitrum on xDai', url: 'https://blockscout.com/xdai/aox', group: 'Mainnets' },
{ title: 'Ethereum', url: 'https://blockscout.com/eth/mainnet', group: 'Mainnets' },
Expand All @@ -13,5 +13,3 @@ const FEATURED_NETWORKS: Array<FeaturedNetwork> = [
{ title: 'LUKSO L14', url: 'https://blockscout.com/lukso/l14', group: 'Other' },
{ title: 'Astar', url: 'https://blockscout.com/astar', group: 'Other' },
];

export const FEATURED_NETWORKS_MOCK = JSON.stringify(FEATURED_NETWORKS);
6 changes: 3 additions & 3 deletions playwright/fixtures/mockConfigResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import type { TestFixture, Page } from '@playwright/test';
import config from 'configs/app';
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) => {
await use(async(envName, envValue, content, isImage) => {
const url = config.app.baseUrl + buildExternalAssetFilePath(envName, envValue);

if (isImage) {
if (isImage && typeof content === 'string') {
await page.route(url, (route) => route.fulfill({
status: 200,
path: content,
}));
} else {
await page.route(url, (route) => route.fulfill({
status: 200,
body: content,
json: content,
}));
}

Expand Down
4 changes: 2 additions & 2 deletions playwright/fixtures/mockContractReadResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as
if (_isEqual(params, callParams) && id) {
return route.fulfill({
status: 200,
body: JSON.stringify({
json: {
id,
jsonrpc: '2.0',
result: encodeFunctionResult({
abi: [ abiItem ],
functionName: abiItem.name,
result,
}),
}),
},
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion playwright/fixtures/mockTextAd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
status: 200,
body: JSON.stringify(textAdMock.duck),
json: textAdMock.duck,
}));
await page.route(textAdMock.duck.ad.thumbnail, (route) => {
return route.fulfill({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const CHECK_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type:
test('base view', async({ render, page }) => {
await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(mocks.ADDRESS_CHECK_RESPONSE.SUCCESS),
json: mocks.ADDRESS_CHECK_RESPONSE.SUCCESS,
}));
const props = {
onContinue: () => {},
Expand All @@ -25,7 +25,7 @@ test('base view', async({ render, page }) => {
test('SOURCE_CODE_NOT_VERIFIED_ERROR view +@mobile', async({ render, page }) => {
await page.route(CHECK_ADDRESS_URL, (route) => route.fulfill({
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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const VERIFY_ADDRESS_URL = buildUrl('address_verification', { chainId: '1', type
test('base view', async({ render, page }) => {
await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(mocks.ADDRESS_VERIFY_RESPONSE.SUCCESS),
json: mocks.ADDRESS_VERIFY_RESPONSE.SUCCESS,
}));

const props = {
Expand All @@ -28,7 +28,7 @@ test('base view', async({ render, page }) => {
test('INVALID_SIGNER_ERROR view +@mobile', async({ render, page }) => {
await page.route(VERIFY_ADDRESS_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(mocks.ADDRESS_VERIFY_RESPONSE.INVALID_SIGNER_ERROR),
json: mocks.ADDRESS_VERIFY_RESPONSE.INVALID_SIGNER_ERROR,
}));

const props = {
Expand Down
6 changes: 3 additions & 3 deletions ui/pages/Marketplace.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ test.beforeEach(async({ mockConfigResponse, mockEnvs, mockAssetResponse, page })
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', '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_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock));
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
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 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,
body: JSON.stringify(ratingsMock),
json: ratingsMock,
}));
});

Expand Down
6 changes: 3 additions & 3 deletions ui/pages/MarketplaceApp.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ const testFn = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs,
[ 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY', '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_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock));
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, appsMock);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, securityReportsMock);
await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html');
await mockRpcResponse({
Method: 'eth_chainId',
ReturnType: numberToHex(Number(config.chain.id)),
});
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,
body: JSON.stringify(ratingsMock),
json: ratingsMock,
}));

const component = await render(
Expand Down
2 changes: 1 addition & 1 deletion ui/pages/SearchResults.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ test.describe('with apps', () => {
[ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ],
]);
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[1].logo, './playwright/mocks/image_s.jpg');
const component = await render(<SearchResults/>, { hooksConfig });
Expand Down
3 changes: 2 additions & 1 deletion ui/shared/Page/PageTitle.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ test.beforeEach(async({ mockTextAd, mockAssetResponse }) => {
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/>);
await page.mouse.move(1000, 1000);
await expect(component).toHaveScreenshot();
});

Expand Down
4 changes: 2 additions & 2 deletions ui/shared/nft/NftMedia.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ test.describe('no url', () => {
await page.route(ANIMATION_MEDIA_TYPE_API_URL, (route) => {
return route.fulfill({
status: 200,
body: JSON.stringify({ type: undefined }),
json: { type: undefined },
});
});
await mockAssetResponse(IMAGE_URL, './playwright/mocks/image_long.jpg');
Expand Down Expand Up @@ -121,7 +121,7 @@ test.describe('page', () => {
await mockAssetResponse(MEDIA_URL, './playwright/mocks/page.html');
await page.route(MEDIA_TYPE_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ type: 'html' }),
json: { type: 'html' },
}));
});

Expand Down
56 changes: 31 additions & 25 deletions ui/shared/pagination/useQueryWithPages.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,18 @@ beforeEach(() => {
fetch.resetMocks();
});

const responseInit = {
headers: {
'Content-Type': 'application/json',
},
};

it('returns correct data if there is only one page', async() => {
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
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 });
await waitForApiResponse();
Expand All @@ -84,7 +90,7 @@ describe('if there are multiple pages', () => {
};

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 });
await waitForApiResponse();
Expand All @@ -109,10 +115,10 @@ describe('if there are multiple pages', () => {
routerPush.mockClear();
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });

fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3), responseInit);
fetch.once(JSON.stringify(responses.page_1), responseInit);

// INITIAL LOAD
const { result: r } = renderHook(() => useQueryWithPages(params), { wrapper });
Expand Down Expand Up @@ -285,10 +291,10 @@ describe('if there are multiple pages', () => {
const routerPush = jest.fn(() => Promise.resolve());
useRouter.mockReturnValue({ ...router, pathname: '/current-route', push: routerPush });

fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3), responseInit);
fetch.once(JSON.stringify(responses.page_1), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down Expand Up @@ -343,8 +349,8 @@ describe('if there are multiple pages', () => {
pathParams: { hash: addressMock.hash },
scrollRef: scrollRef as unknown as React.RefObject<HTMLDivElement>,
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand All @@ -367,7 +373,7 @@ describe('if there is page query param in URL', () => {
resourceName: 'address_txs',
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 });
await waitForApiResponse();
Expand All @@ -391,8 +397,8 @@ describe('if there is page query param in URL', () => {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_3));
fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_3), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down Expand Up @@ -439,9 +445,9 @@ describe('queries with filters', () => {
// @ts-ignore:
sorting: { sort: 'val-desc' },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_filtered));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_filtered), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down Expand Up @@ -488,8 +494,8 @@ describe('queries with filters', () => {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down Expand Up @@ -526,9 +532,9 @@ describe('queries with sorting', () => {
pathParams: { hash: addressMock.hash },
filters: { filter: 'from' },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_sorted));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);
fetch.once(JSON.stringify(responses.page_sorted), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down Expand Up @@ -580,8 +586,8 @@ describe('queries with sorting', () => {
// @ts-ignore:
sorting: { sort: 'val-desc' },
};
fetch.once(JSON.stringify(responses.page_1));
fetch.once(JSON.stringify(responses.page_2));
fetch.once(JSON.stringify(responses.page_1), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);

const { result } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
Expand Down
Loading

0 comments on commit 476d771

Please sign in to comment.