Commit 6bfc4955 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Playwright tests refactoring, pt.2 (#1787)

* create storageState fixture

* remove auth fixture

* renaming

* migrate bridge tokens and shibarium rollup tests

* migrate to common app config

* update screenshots

* re-implement fixtures for mocking envs, features and auth state

* update screenshot
parent d1f7e165
...@@ -6,6 +6,7 @@ import { WagmiProvider } from 'wagmi'; ...@@ -6,6 +6,7 @@ import { WagmiProvider } from 'wagmi';
import type { Props as PageProps } from 'nextjs/getServerSideProps'; import type { Props as PageProps } from 'nextjs/getServerSideProps';
import config from 'configs/app';
import { AppContextProvider } from 'lib/contexts/app'; import { AppContextProvider } from 'lib/contexts/app';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import wagmiConfig from 'lib/web3/wagmiConfig'; import wagmiConfig from 'lib/web3/wagmiConfig';
...@@ -43,7 +44,7 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props ...@@ -43,7 +44,7 @@ const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props
return ( return (
<ChakraProvider theme={ theme }> <ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://${ app.domain }:${ app.socketPort }` : undefined }> <SocketProvider url={ withSocket ? `ws://${ config.app.host }:${ app.socketPort }` : undefined }>
<AppContextProvider { ...appContext }> <AppContextProvider { ...appContext }>
<GrowthBookProvider> <GrowthBookProvider>
<WagmiProvider config={ wagmiConfig! }> <WagmiProvider config={ wagmiConfig! }>
......
import type { BrowserContext } from '@playwright/test'; import type { BrowserContext, TestFixture } from '@playwright/test';
import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import { domain } from 'playwright/utils/app';
export default function authFixture(context: BrowserContext) { export function authenticateUser(context: BrowserContext) {
context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain, path: '/' } ]); context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain: config.app.host, path: '/' } ]);
} }
export const contextWithAuth: TestFixture<BrowserContext, { context: BrowserContext }> = async({ context }, use) => {
authenticateUser(context);
use(context);
};
...@@ -7,7 +7,13 @@ interface Env { ...@@ -7,7 +7,13 @@ interface Env {
value: string; value: string;
} }
// keep in mind that all passed variables here should be present in env config files (.env.pw or .env.poa) /**
* @deprecated please use mockEnvs fixture
*
* @export
* @param {Array<Env>} envs
* @return {*} {Parameters<typeof test.extend>[0]['context']}
*/
export default function contextWithEnvsFixture(envs: Array<Env>): Parameters<typeof test.extend>[0]['context'] { export default function contextWithEnvsFixture(envs: Array<Env>): Parameters<typeof test.extend>[0]['context'] {
return async({ browser }, use) => { return async({ browser }, use) => {
const context = await createContextWithStorage(browser, envs); const context = await createContextWithStorage(browser, envs);
......
...@@ -7,6 +7,13 @@ interface Feature { ...@@ -7,6 +7,13 @@ interface Feature {
value: unknown; value: unknown;
} }
/**
* @deprecated please use mockFeatures fixture
*
* @export
* @param {Array<Feature>} envs
* @return {*} {Parameters<typeof test.extend>[0]['context']}
*/
export default function contextWithFeaturesFixture(envs: Array<Feature>): Parameters<typeof test.extend>[0]['context'] { export default function contextWithFeaturesFixture(envs: Array<Feature>): Parameters<typeof test.extend>[0]['context'] {
return async({ browser }, use) => { return async({ browser }, use) => {
const storageItems = envs.map(({ id, value }) => ({ name: `pw_feature:${ id }`, value: JSON.stringify(value) })); const storageItems = envs.map(({ id, value }) => ({ name: `pw_feature:${ id }`, value: JSON.stringify(value) }));
......
import type { Browser } from '@playwright/test'; import type { Browser } from '@playwright/test';
import * as app from 'playwright/utils/app'; import config from 'configs/app';
/**
* @deprecated please use mockEnvs or mockFeatures fixture
*
* @export
* @param {Browser} browser
* @param {Array<{ name: string; value: string }>} localStorage
* @return {*}
*/
export default async function createContextWithEnvs(browser: Browser, localStorage: Array<{ name: string; value: string }>) { export default async function createContextWithEnvs(browser: Browser, localStorage: Array<{ name: string; value: string }>) {
return browser.newContext({ return browser.newContext({
storageState: { storageState: {
origins: [ origins: [
{ origin: app.url, localStorage }, { origin: config.app.baseUrl, localStorage },
], ],
cookies: [], cookies: [],
}, },
......
import type { TestFixture, Page } from '@playwright/test';
import type { WalletProvider } from 'types/web3';
export type InjectMetaMaskProvider = () => Promise<void>;
const fixture: TestFixture<InjectMetaMaskProvider, { page: Page }> = async({ page }, use) => {
await use(async() => {
await page.evaluate(() => {
window.ethereum = {
isMetaMask: true,
_events: {},
} as WalletProvider;
});
});
};
export default fixture;
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<void>;
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) {
await page.route(url, (route) => route.fulfill({
status: 200,
path: content,
}));
} else {
await page.route(url, (route) => route.fulfill({
status: 200,
body: content,
}));
}
});
};
export default fixture;
/* eslint-disable max-len */
import type { TestFixture, Page } from '@playwright/test';
export type MockEnvsFixture = (envs: Array<[string, string]>) => Promise<void>;
const fixture: TestFixture<MockEnvsFixture, { page: Page }> = async({ page }, use) => {
await use(async(envs) => {
for (const [ name, value ] of envs) {
await page.evaluate(({ name, value }) => {
window.localStorage.setItem(name, value);
}, { name, value });
}
});
};
export default fixture;
export const ENVS_MAP: Record<string, Array<[string, string]>> = {
shibariumRollup: [
[ 'NEXT_PUBLIC_ROLLUP_TYPE', 'shibarium' ],
[ 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', 'https://localhost:3101' ],
],
bridgedTokens: [
[ 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS', '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]' ],
[ 'NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES', '[{"type":"omni","title":"OmniBridge","short_title":"OMNI"},{"type":"amb","title":"Arbitrary Message Bridge","short_title":"AMB"}]' ],
],
userOps: [
[ 'NEXT_PUBLIC_HAS_USER_OPS', 'true' ],
],
};
/* eslint-disable max-len */
import type { TestFixture, Page } from '@playwright/test';
export type MockFeaturesFixture = (features: Array<[string, unknown]>) => Promise<void>;
const fixture: TestFixture<MockFeaturesFixture, { page: Page }> = async({ page }, use) => {
await use(async(features) => {
for (const [ name, value ] of features) {
await page.evaluate(({ name, value }) => {
window.localStorage.setItem(`pw_feature:${ name }`, JSON.stringify(value));
}, { name, value });
}
});
};
export default fixture;
...@@ -3,27 +3,35 @@ import { test as base } from '@playwright/experimental-ct-react'; ...@@ -3,27 +3,35 @@ import { test as base } from '@playwright/experimental-ct-react';
import * as textAdMock from 'mocks/ad/textAd'; import * as textAdMock from 'mocks/ad/textAd';
import type { MockApiResponseFixture } from './fixtures/mockApiResponse'; import * as injectMetaMaskProvider from './fixtures/injectMetaMaskProvider';
import mockApiResponseFixture from './fixtures/mockApiResponse'; import * as mockApiResponse from './fixtures/mockApiResponse';
import type { MockAssetResponseFixture } from './fixtures/mockAssetResponse'; import * as mockAssetResponse from './fixtures/mockAssetResponse';
import mockAssetResponseFixture from './fixtures/mockAssetResponse'; import * as mockConfigResponse from './fixtures/mockConfigResponse';
import type { RenderFixture } from './fixtures/render'; import * as mockEnvs from './fixtures/mockEnvs';
import renderFixture from './fixtures/render'; import * as mockFeatures from './fixtures/mockFeatures';
import type { CreateSocketFixture } from './fixtures/socketServer'; import * as render from './fixtures/render';
import { createSocket as createSocketFixture } from './fixtures/socketServer'; import * as socketServer from './fixtures/socketServer';
interface Fixtures { interface Fixtures {
render: RenderFixture; render: render.RenderFixture;
mockApiResponse: MockApiResponseFixture; mockApiResponse: mockApiResponse.MockApiResponseFixture;
mockAssetResponse: MockAssetResponseFixture; mockAssetResponse: mockAssetResponse.MockAssetResponseFixture;
createSocket: CreateSocketFixture; mockConfigResponse: mockConfigResponse.MockConfigResponseFixture;
mockEnvs: mockEnvs.MockEnvsFixture;
mockFeatures: mockFeatures.MockFeaturesFixture;
createSocket: socketServer.CreateSocketFixture;
injectMetaMaskProvider: injectMetaMaskProvider.InjectMetaMaskProvider;
} }
const test = base.extend<Fixtures>({ const test = base.extend<Fixtures>({
render: renderFixture, render: render.default,
mockApiResponse: mockApiResponseFixture, mockApiResponse: mockApiResponse.default,
mockAssetResponse: mockAssetResponseFixture, mockAssetResponse: mockAssetResponse.default,
createSocket: createSocketFixture, mockConfigResponse: mockConfigResponse.default,
mockEnvs: mockEnvs.default,
mockFeatures: mockFeatures.default,
createSocket: socketServer.createSocket,
injectMetaMaskProvider: injectMetaMaskProvider.default,
}); });
test.beforeEach(async({ page }) => { test.beforeEach(async({ page }) => {
......
This diff is collapsed.
export const url = `${ process.env.NEXT_PUBLIC_APP_PROTOCOL }://${ process.env.NEXT_PUBLIC_APP_HOST }:${ process.env.NEXT_PUBLIC_APP_PORT }`;
export const domain = process.env.NEXT_PUBLIC_APP_HOST;
export const socketPort = 3200; export const socketPort = 3200;
...@@ -4,8 +4,6 @@ import config from 'configs/app'; ...@@ -4,8 +4,6 @@ import config from 'configs/app';
import type { ResourceName, ResourcePathParams } from 'lib/api/resources'; import type { ResourceName, ResourcePathParams } from 'lib/api/resources';
import { RESOURCES } from 'lib/api/resources'; import { RESOURCES } from 'lib/api/resources';
// DEPRECATED
/** /**
* @deprecated please use fixture mockApiResponse from playwright/lib.tsx for rendering test suite * @deprecated please use fixture mockApiResponse from playwright/lib.tsx for rendering test suite
* *
......
...@@ -20,20 +20,6 @@ export const featureEnvs = { ...@@ -20,20 +20,6 @@ export const featureEnvs = {
{ name: 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', value: 'https://localhost:3101' }, { name: 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', value: 'https://localhost:3101' },
{ name: 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' }, { name: 'NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' },
], ],
shibariumRollup: [
{ name: 'NEXT_PUBLIC_ROLLUP_TYPE', value: 'shibarium' },
{ name: 'NEXT_PUBLIC_ROLLUP_L1_BASE_URL', value: 'https://localhost:3101' },
],
bridgedTokens: [
{
name: 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS',
value: '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]',
},
{
name: 'NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES',
value: '[{"type":"omni","title":"OmniBridge","short_title":"OMNI"},{"type":"amb","title":"Arbitrary Message Bridge","short_title":"AMB"}]',
},
],
txInterpretation: [ txInterpretation: [
{ name: 'NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER', value: 'blockscout' }, { name: 'NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER', value: 'blockscout' },
], ],
......
...@@ -8,7 +8,7 @@ export type TokensResponse = { ...@@ -8,7 +8,7 @@ export type TokensResponse = {
items_count: number; items_count: number;
name: string; name: string;
market_cap: string | null; market_cap: string | null;
}; } | null;
} }
export type TokensFilters = { q: string; type: Array<TokenType> | undefined }; export type TokensFilters = { q: string; type: Array<TokenType> | undefined };
......
import { test as base, devices, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import { apps as appsMock } from 'mocks/apps/apps'; import { apps as appsMock } from 'mocks/apps/apps';
import { securityReports as securityReportsMock } from 'mocks/apps/securityReports'; import { securityReports as securityReportsMock } from 'mocks/apps/securityReports';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect, devices } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import Marketplace from './Marketplace'; import Marketplace from './Marketplace';
const MARKETPLACE_CONFIG_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || ''; const MARKETPLACE_CONFIG_URL = 'http://localhost/marketplace-config.json';
const MARKETPLACE_SECURITY_REPORTS_URL =
app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', 'https://marketplace-security-reports.json') || ''; test.beforeEach(async({ mockConfigResponse, mockEnvs, mockAssetResponse }) => {
const MARKETPLACE_BANNER_CONTENT_URL = await mockEnvs([
app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL', 'https://marketplace-banner.html') || ''; [ 'NEXT_PUBLIC_MARKETPLACE_ENABLED', 'true' ],
const MARKETPLACE_BANNER_LINK_URL = 'https://example.com'; [ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ],
]);
const test = base.extend({ await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock));
context: contextWithEnvs([ await Promise.all(appsMock.map(app => mockAssetResponse(app.logo, './playwright/mocks/image_s.jpg')));
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
}); });
const testFn: Parameters<typeof test>[1] = async({ mount, page }) => { test('base view +@dark-mode', async({ render }) => {
await page.route(MARKETPLACE_CONFIG_URL, (route) => route.fulfill({ const component = await render(<Marketplace/>);
status: 200,
body: JSON.stringify(appsMock),
}));
await Promise.all(appsMock.map(app =>
page.route(app.logo, (route) =>
route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
}),
),
));
await page.route(MARKETPLACE_BANNER_CONTENT_URL, (route) => route.fulfill({
status: 200,
path: './playwright/mocks/banner.html',
}));
const component = await mount(
<TestApp>
<Marketplace/>
</TestApp>,
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
};
test('base view +@dark-mode', testFn);
const testWithFeaturedApp = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
{ name: 'NEXT_PUBLIC_MARKETPLACE_FEATURED_APP', value: 'hop-exchange' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
}); });
testWithFeaturedApp('with featured app +@dark-mode', testFn); test('with featured app +@dark-mode', async({ render, mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_MARKETPLACE_FEATURED_APP', 'hop-exchange' ],
]);
const component = await render(<Marketplace/>);
const testWithBanner = test.extend({ await expect(component).toHaveScreenshot();
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
{ name: 'NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL', value: MARKETPLACE_BANNER_CONTENT_URL },
{ name: 'NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL', value: MARKETPLACE_BANNER_LINK_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
}); });
testWithBanner('with banner +@dark-mode', testFn); test('with banner +@dark-mode', async({ render, mockEnvs, mockConfigResponse }) => {
const MARKETPLACE_BANNER_CONTENT_URL = 'https://localhost/marketplace-banner.html';
const MARKETPLACE_BANNER_LINK_URL = 'https://example.com';
const testWithScoreFeature = test.extend({ await mockEnvs([
context: contextWithEnvs([ [ 'NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL', MARKETPLACE_BANNER_CONTENT_URL ],
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL }, [ 'NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL', MARKETPLACE_BANNER_LINK_URL ],
{ name: 'NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', value: MARKETPLACE_SECURITY_REPORTS_URL }, ]);
{ name: 'pw_feature:security_score_exp', value: 'true' }, await mockConfigResponse('MARKETPLACE_BANNER_CONTENT_URL', MARKETPLACE_BANNER_CONTENT_URL, './playwright/mocks/page.html', true);
// eslint-disable-next-line @typescript-eslint/no-explicit-any const component = await render(<Marketplace/>);
]) as any,
});
testWithScoreFeature('with scores +@dark-mode', async({ mount, page }) => { await expect(component).toHaveScreenshot();
await page.route(MARKETPLACE_CONFIG_URL, (route) => route.fulfill({ });
status: 200,
body: JSON.stringify(appsMock),
}));
await page.route(MARKETPLACE_SECURITY_REPORTS_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(securityReportsMock),
}));
await Promise.all(appsMock.map(app =>
page.route(app.logo, (route) =>
route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
}),
),
));
const component = await mount(
<TestApp>
<Marketplace/>
</TestApp>,
);
test('with scores +@dark-mode', async({ render, mockConfigResponse, mockEnvs, mockFeatures }) => {
const MARKETPLACE_SECURITY_REPORTS_URL = 'https://marketplace-security-reports.json';
await mockEnvs([
[ 'NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL ],
]);
await mockFeatures([
[ 'security_score_exp', true ],
]);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock));
const component = await render(<Marketplace/>);
await component.getByText('Apps scores').click(); await component.getByText('Apps scores').click();
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
...@@ -121,36 +66,45 @@ testWithScoreFeature('with scores +@dark-mode', async({ mount, page }) => { ...@@ -121,36 +66,45 @@ testWithScoreFeature('with scores +@dark-mode', async({ mount, page }) => {
test.describe('mobile', () => { test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', testFn); test('base view', async({ render }) => {
testWithFeaturedApp('with featured app', testFn); const component = await render(<Marketplace/>);
testWithBanner('with banner', testFn);
await expect(component).toHaveScreenshot();
testWithScoreFeature('with scores', async({ mount, page }) => { });
await page.route(MARKETPLACE_CONFIG_URL, (route) => route.fulfill({
status: 200, test('with featured app', async({ render, mockEnvs }) => {
body: JSON.stringify(appsMock), await mockEnvs([
})); [ 'NEXT_PUBLIC_MARKETPLACE_FEATURED_APP', 'hop-exchange' ],
]);
await page.route(MARKETPLACE_SECURITY_REPORTS_URL, (route) => route.fulfill({ const component = await render(<Marketplace/>);
status: 200,
body: JSON.stringify(securityReportsMock), await expect(component).toHaveScreenshot();
})); });
await Promise.all(appsMock.map(app => test('with banner', async({ render, mockEnvs, mockConfigResponse }) => {
page.route(app.logo, (route) => const MARKETPLACE_BANNER_CONTENT_URL = 'https://localhost/marketplace-banner.html';
route.fulfill({ const MARKETPLACE_BANNER_LINK_URL = 'https://example.com';
status: 200,
path: './playwright/mocks/image_s.jpg', await mockEnvs([
}), [ 'NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL', MARKETPLACE_BANNER_CONTENT_URL ],
), [ 'NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL', MARKETPLACE_BANNER_LINK_URL ],
)); ]);
await mockConfigResponse('MARKETPLACE_BANNER_CONTENT_URL', MARKETPLACE_BANNER_CONTENT_URL, './playwright/mocks/page.html', true);
const component = await mount( const component = await render(<Marketplace/>);
<TestApp>
<Marketplace/> await expect(component).toHaveScreenshot();
</TestApp>, });
);
test('with scores', async({ render, mockConfigResponse, mockEnvs, mockFeatures }) => {
const MARKETPLACE_SECURITY_REPORTS_URL = 'https://marketplace-security-reports.json';
await mockEnvs([
[ 'NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL ],
]);
await mockFeatures([
[ 'security_score_exp', true ],
]);
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock));
const component = await render(<Marketplace/>);
await component.getByText('Apps scores').click(); await component.getByText('Apps scores').click();
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
......
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import { apps as appsMock } from 'mocks/apps/apps'; import { apps as appsMock } from 'mocks/apps/apps';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect, devices } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import MarketplaceApp from './MarketplaceApp'; import MarketplaceApp from './MarketplaceApp';
const MARKETPLACE_CONFIG_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || '';
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { id: appsMock[0].id }, query: { id: appsMock[0].id },
...@@ -19,39 +13,26 @@ const hooksConfig = { ...@@ -19,39 +13,26 @@ const hooksConfig = {
}, },
}; };
const testFn: Parameters<typeof test>[1] = async({ mount, page }) => { const MARKETPLACE_CONFIG_URL = 'https://marketplace-config.json';
await page.route(MARKETPLACE_CONFIG_URL, (route) => route.fulfill({
status: 200, const testFn: Parameters<typeof test>[1] = async({ render, mockConfigResponse, mockAssetResponse, mockEnvs }) => {
body: JSON.stringify(appsMock), await mockEnvs([
})); [ 'NEXT_PUBLIC_MARKETPLACE_ENABLED', 'true' ],
[ 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL ],
await page.route(appsMock[0].url, (route) => ]);
route.fulfill({ await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock));
status: 200, await mockAssetResponse(appsMock[0].url, './mocks/apps/app.html');
path: './mocks/apps/app.html',
}),
);
const component = await mount( const component = await render(
<TestApp> <Flex flexDirection="column" mx={{ base: 4, lg: 6 }} h="100vh">
{ /* added Flex as a Layout because the iframe has negative margins */ } <MarketplaceApp/>
<Flex flexDirection="column" mx={{ base: 4, lg: 6 }} h="100vh"> </Flex>,
<MarketplaceApp/>
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}; };
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
test('base view +@dark-mode', testFn); test('base view +@dark-mode', testFn);
test.describe('mobile', () => { test.describe('mobile', () => {
......
This diff is collapsed.
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { data as depositsData } from 'mocks/shibarium/deposits'; import { data as depositsData } from 'mocks/shibarium/deposits';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import TestApp from 'playwright/TestApp'; import { test, expect } from 'playwright/lib';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import ShibariumDeposits from './ShibariumDeposits'; import ShibariumDeposits from './ShibariumDeposits';
const DEPOSITS_API_URL = buildApiUrl('shibarium_deposits'); test('base view +@mobile', async({ render, mockApiResponse, mockEnvs }) => {
const DEPOSITS_COUNT_API_URL = buildApiUrl('shibarium_deposits_count'); await mockEnvs(ENVS_MAP.shibariumRollup);
await mockApiResponse('shibarium_deposits', depositsData);
await mockApiResponse('shibarium_deposits_count', 3971111);
const test = base.extend({ const component = await render(<ShibariumDeposits/>);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.shibariumRollup) as any,
});
test('base view +@mobile', async({ mount, page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: '',
}));
await page.route(DEPOSITS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(depositsData),
}));
await page.route(DEPOSITS_COUNT_API_URL, (route) => route.fulfill({
status: 200,
body: '3971111',
}));
const component = await mount(
<TestApp>
<ShibariumDeposits/>
</TestApp>,
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { data as withdrawalsData } from 'mocks/shibarium/withdrawals'; import { data as withdrawalsData } from 'mocks/shibarium/withdrawals';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import TestApp from 'playwright/TestApp'; import { test, expect } from 'playwright/lib';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import ShibariuWithdrawals from './ShibariumWithdrawals'; import ShibariumWithdrawals from './ShibariumWithdrawals';
const test = base.extend({ test('base view +@mobile', async({ render, mockApiResponse, mockEnvs }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.shibariumRollup) as any,
});
const WITHDRAWALS_API_URL = buildApiUrl('shibarium_withdrawals');
const WITHDRAWALS_COUNT_API_URL = buildApiUrl('shibarium_withdrawals_count');
test('base view +@mobile', async({ mount, page }) => {
// test on mobile is flaky // test on mobile is flaky
// my assumption is there is not enough time to calculate hashes truncation so component is unstable // my assumption is there is not enough time to calculate hashes truncation so component is unstable
// so I raised the test timeout to check if it helps // so I raised the test timeout to check if it helps
test.slow(); test.slow();
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: '',
}));
await page.route(WITHDRAWALS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(withdrawalsData),
}));
await page.route(WITHDRAWALS_COUNT_API_URL, (route) => route.fulfill({ await mockEnvs(ENVS_MAP.shibariumRollup);
status: 200, await mockApiResponse('shibarium_withdrawals', withdrawalsData);
body: '397', await mockApiResponse('shibarium_withdrawals_count', 397);
}));
const component = await mount( const component = await render(<ShibariumWithdrawals/>);
<TestApp>
<ShibariuWithdrawals/>
</TestApp>,
);
await expect(component).toHaveScreenshot({ timeout: 10_000 }); await expect(component).toHaveScreenshot({ timeout: 10_000 });
}); });
import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import * as verifiedAddressesMocks from 'mocks/account/verifiedAddresses'; import * as verifiedAddressesMocks from 'mocks/account/verifiedAddresses';
import { token as contract } from 'mocks/address/address'; import { token as contract } from 'mocks/address/address';
import { tokenInfo, tokenCounters, bridgedTokenA } from 'mocks/tokens/tokenInfo'; import { tokenInfo, tokenCounters, bridgedTokenA } from 'mocks/tokens/tokenInfo';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import * as socketServer from 'playwright/fixtures/socketServer'; import * as socketServer from 'playwright/fixtures/socketServer';
import TestApp from 'playwright/TestApp'; import { test, expect, devices } from 'playwright/lib';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import Token from './Token'; import Token from './Token';
const TOKEN_API_URL = buildApiUrl('token', { hash: '1' });
const TOKEN_COUNTERS_API_URL = buildApiUrl('token_counters', { hash: '1' });
const TOKEN_TRANSFERS_API_URL = buildApiUrl('token_transfers', { hash: '1' });
const ADDRESS_API_URL = buildApiUrl('address', { hash: '1' });
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: '1', tab: 'token_transfers' }, query: { hash: '1', tab: 'token_transfers' },
...@@ -23,45 +17,19 @@ const hooksConfig = { ...@@ -23,45 +17,19 @@ const hooksConfig = {
}, },
}; };
const test = base.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket,
});
// FIXME // FIXME
// test cases which use socket cannot run in parallel since the socket server always run on the same port // 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.describe.configure({ mode: 'serial' });
test.beforeEach(async({ page }) => { test.beforeEach(async({ mockApiResponse }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({ await mockApiResponse('token', tokenInfo, { pathParams: { hash: '1' } });
status: 200, await mockApiResponse('address', contract, { pathParams: { hash: '1' } });
body: '', await mockApiResponse('token_counters', tokenCounters, { pathParams: { hash: '1' } });
})); await mockApiResponse('token_transfers', { items: [], next_page_params: null }, { pathParams: { hash: '1' } });
await page.route(TOKEN_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(tokenInfo),
}));
await page.route(ADDRESS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contract),
}));
await page.route(TOKEN_COUNTERS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(tokenCounters),
}));
await page.route(TOKEN_TRANSFERS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({}),
}));
}); });
test('base view', async({ mount, page, createSocket }) => { test('base view', async({ render, page, createSocket }) => {
const component = await mount( const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
<TestApp withSocket>
<Token/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'tokens:1'); const channel = await socketServer.joinChannel(socket, 'tokens:1');
...@@ -73,24 +41,11 @@ test('base view', async({ mount, page, createSocket }) => { ...@@ -73,24 +41,11 @@ test('base view', async({ mount, page, createSocket }) => {
}); });
}); });
test('with verified info', async({ mount, page, createSocket }) => { test('with verified info', async({ render, page, createSocket, mockApiResponse, mockAssetResponse }) => {
const VERIFIED_INFO_URL = buildApiUrl('token_verified_info', { chainId: '1', hash: '1' }); await mockApiResponse('token_verified_info', verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED, { pathParams: { chainId: '1', hash: '1' } });
await page.route(VERIFIED_INFO_URL, (route) => route.fulfill({ await mockAssetResponse(tokenInfo.icon_url as string, './playwright/mocks/image_s.jpg');
body: JSON.stringify(verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED),
}));
await page.route(tokenInfo.icon_url as string, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount( const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
<TestApp withSocket>
<Token/>
</TestApp>,
{ hooksConfig },
);
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'tokens:1'); const channel = await socketServer.joinChannel(socket, 'tokens:1');
...@@ -104,55 +59,16 @@ test('with verified info', async({ mount, page, createSocket }) => { ...@@ -104,55 +59,16 @@ test('with verified info', async({ mount, page, createSocket }) => {
}); });
}); });
const bridgedTokenTest = base.extend<socketServer.SocketServerFixture>({ test('bridged token', async({ render, page, createSocket, mockApiResponse, mockAssetResponse, mockEnvs }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockEnvs(ENVS_MAP.bridgedTokens);
context: contextWithEnvs(configs.featureEnvs.bridgedTokens) as any, await mockApiResponse('token', bridgedTokenA, { pathParams: { hash: '1' } });
createSocket: socketServer.createSocket, await mockApiResponse('address', contract, { pathParams: { hash: '1' } });
}); await mockApiResponse('token_counters', tokenCounters, { pathParams: { hash: '1' } });
await mockApiResponse('token_transfers', { items: [], next_page_params: null }, { pathParams: { hash: '1' } });
bridgedTokenTest('bridged token', async({ mount, page, createSocket }) => { await mockApiResponse('token_verified_info', verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED, { pathParams: { chainId: '1', hash: '1' } });
await mockAssetResponse(tokenInfo.icon_url as string, './playwright/mocks/image_s.jpg');
const VERIFIED_INFO_URL = buildApiUrl('token_verified_info', { chainId: '1', hash: '1' });
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: '',
}));
await page.route(TOKEN_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(bridgedTokenA),
}));
await page.route(ADDRESS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(contract),
}));
await page.route(TOKEN_COUNTERS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(tokenCounters),
}));
await page.route(TOKEN_TRANSFERS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({}),
}));
await page.route(VERIFIED_INFO_URL, (route) => route.fulfill({
body: JSON.stringify(verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED),
}));
await page.route(tokenInfo.icon_url as string, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp withSocket>
<Token/>
</TestApp>,
{ hooksConfig },
);
const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'tokens:1'); const channel = await socketServer.joinChannel(socket, 'tokens:1');
socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 }); socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 });
...@@ -165,14 +81,9 @@ bridgedTokenTest('bridged token', async({ mount, page, createSocket }) => { ...@@ -165,14 +81,9 @@ bridgedTokenTest('bridged token', async({ mount, page, createSocket }) => {
test.describe('mobile', () => { test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', async({ mount, page, createSocket }) => {
const component = await mount(
<TestApp withSocket>
<Token/>
</TestApp>,
{ hooksConfig },
);
test('base view', async({ render, page, createSocket }) => {
const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'tokens:1'); const channel = await socketServer.joinChannel(socket, 'tokens:1');
socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 }); socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 });
...@@ -183,25 +94,11 @@ test.describe('mobile', () => { ...@@ -183,25 +94,11 @@ test.describe('mobile', () => {
}); });
}); });
test('with verified info', async({ mount, page, createSocket }) => { test('with verified info', async({ render, page, createSocket, mockApiResponse, mockAssetResponse }) => {
const VERIFIED_INFO_URL = buildApiUrl('token_verified_info', { chainId: '1', hash: '1' }); await mockApiResponse('token_verified_info', verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED, { pathParams: { chainId: '1', hash: '1' } });
await page.route(VERIFIED_INFO_URL, (route) => route.fulfill({ await mockAssetResponse(tokenInfo.icon_url as string, './playwright/mocks/image_s.jpg');
body: JSON.stringify(verifiedAddressesMocks.TOKEN_INFO_APPLICATION.APPROVED),
}));
await page.route(tokenInfo.icon_url as string, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp withSocket>
<Token/>
</TestApp>,
{ hooksConfig },
);
const component = await render(<Token/>, { hooksConfig }, { withSocket: true });
const socket = await createSocket(); const socket = await createSocket();
const channel = await socketServer.joinChannel(socket, 'tokens:1'); const channel = await socketServer.joinChannel(socket, 'tokens:1');
socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 }); socketServer.sendMessage(socket, channel, 'total_supply', { total_supply: 10 ** 20 });
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import * as textAdMock from 'mocks/ad/textAd';
import * as tokens from 'mocks/tokens/tokenInfo'; import * as tokens from 'mocks/tokens/tokenInfo';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import TestApp from 'playwright/TestApp'; import { test, expect } from 'playwright/lib';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import Tokens from './Tokens'; import Tokens from './Tokens';
base.beforeEach(async({ page }) => { test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: JSON.stringify(textAdMock.duck),
}));
await page.route(textAdMock.duck.ad.thumbnail, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
});
base('base view +@mobile +@dark-mode', async({ mount, page }) => {
const allTokens = { const allTokens = {
items: [ items: [
tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, tokens.tokenInfoERC20d, tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, tokens.tokenInfoERC20d,
...@@ -35,6 +18,7 @@ base('base view +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -35,6 +18,7 @@ base('base view +@mobile +@dark-mode', async({ mount, page }) => {
holder_count: 1, holder_count: 1,
items_count: 1, items_count: 1,
name: 'a', name: 'a',
market_cap: '0',
}, },
}; };
const filteredTokens = { const filteredTokens = {
...@@ -44,40 +28,25 @@ base('base view +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -44,40 +28,25 @@ base('base view +@mobile +@dark-mode', async({ mount, page }) => {
next_page_params: null, next_page_params: null,
}; };
const ALL_TOKENS_API_URL = buildApiUrl('tokens'); await mockApiResponse('tokens', allTokens);
const FILTERED_TOKENS_API_URL = buildApiUrl('tokens') + '?q=foo'; await mockApiResponse('tokens', filteredTokens, { queryParams: { q: 'foo' } });
await page.route(ALL_TOKENS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(allTokens),
}));
await page.route(FILTERED_TOKENS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(filteredTokens),
}));
const component = await mount( const component = await render(
<TestApp> <div>
<Box h={{ base: '134px', lg: 6 }}/> <Box h={{ base: '134px', lg: 6 }}/>
<Tokens/> <Tokens/>
</TestApp>, </div>,
); );
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
await component.getByRole('textbox', { name: 'Token name or symbol' }).focus(); await component.getByRole('textbox', { name: 'Token name or symbol' }).focus();
await component.getByRole('textbox', { name: 'Token name or symbol' }).type('foo'); await component.getByRole('textbox', { name: 'Token name or symbol' }).fill('foo');
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
base.describe('bridged tokens', async() => { test.describe('bridged tokens', async() => {
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.bridgedTokens) as any,
});
const bridgedTokens = { const bridgedTokens = {
items: [ items: [
tokens.bridgedTokenA, tokens.bridgedTokenA,
...@@ -88,6 +57,7 @@ base.describe('bridged tokens', async() => { ...@@ -88,6 +57,7 @@ base.describe('bridged tokens', async() => {
holder_count: 1, holder_count: 1,
items_count: 1, items_count: 1,
name: 'a', name: 'a',
market_cap: null,
}, },
}; };
const bridgedFilteredTokens = { const bridgedFilteredTokens = {
...@@ -101,26 +71,17 @@ base.describe('bridged tokens', async() => { ...@@ -101,26 +71,17 @@ base.describe('bridged tokens', async() => {
query: { tab: 'bridged' }, query: { tab: 'bridged' },
}, },
}; };
const BRIDGED_TOKENS_API_URL = buildApiUrl('tokens_bridged');
test.beforeEach(async({ page }) => {
await page.route(BRIDGED_TOKENS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(bridgedTokens),
}));
});
test('base view', async({ mount, page }) => { test('base view', async({ render, page, mockApiResponse, mockEnvs }) => {
await page.route(BRIDGED_TOKENS_API_URL + '?chain_ids=99', (route) => route.fulfill({ await mockEnvs(ENVS_MAP.bridgedTokens);
status: 200, await mockApiResponse('tokens_bridged', bridgedTokens);
body: JSON.stringify(bridgedFilteredTokens), await mockApiResponse('tokens_bridged', bridgedFilteredTokens, { queryParams: { chain_ids: '99' } });
}));
const component = await mount( const component = await render(
<TestApp> <div>
<Box h={{ base: '134px', lg: 6 }}/> <Box h={{ base: '134px', lg: 6 }}/>
<Tokens/> <Tokens/>
</TestApp>, </div>,
{ hooksConfig }, { hooksConfig },
); );
......
import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import * as validatorsMock from 'mocks/validators/index'; import * as validatorsMock from 'mocks/validators/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect } from 'playwright/lib';
import { test as base, expect } from 'playwright/lib';
import * as configs from 'playwright/utils/configs';
import Validators from './Validators'; import Validators from './Validators';
const test = base.extend<{ context: BrowserContext }>({ const chainType = 'stability';
context: contextWithEnvs(configs.featureEnvs.validators),
});
test('base view +@mobile', async({ render, mockApiResponse }) => { test('base view +@mobile', async({ render, mockApiResponse, mockEnvs }) => {
await mockApiResponse('validators', validatorsMock.validatorsResponse, { pathParams: { chainType: 'stability' } }); await mockEnvs([
await mockApiResponse('validators_counters', validatorsMock.validatorsCountersResponse, { pathParams: { chainType: 'stability' } }); [ 'NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE', chainType ],
]);
await mockApiResponse('validators', validatorsMock.validatorsResponse, { pathParams: { chainType } });
await mockApiResponse('validators_counters', validatorsMock.validatorsCountersResponse, { pathParams: { chainType } });
const component = await render(<Validators/>); const component = await render(<Validators/>);
......
import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import * as mocks from 'mocks/account/verifiedAddresses'; import * as mocks from 'mocks/account/verifiedAddresses';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import { test as base, expect } from 'playwright/lib'; import { test as base, expect } from 'playwright/lib';
import VerifiedAddresses from './VerifiedAddresses'; import VerifiedAddresses from './VerifiedAddresses';
const test = base.extend({ const test = base.extend<{ context: BrowserContext }>({
context: ({ context }, use) => { context: contextWithAuth,
authFixture(context);
use(context);
},
}); });
test.beforeEach(async({ mockAssetResponse }) => { test.beforeEach(async({ mockAssetResponse }) => {
......
import { test as base, expect } from '@playwright/experimental-ct-react'; import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp'; import { test as base, expect } from 'playwright/lib';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import AccountActionsMenu from './AccountActionsMenu'; import AccountActionsMenu from './AccountActionsMenu';
const USER_INFO_URL = buildApiUrl('user_info'); const test = base.extend<{ context: BrowserContext }>({
context: contextWithAuth,
const test = base.extend({
context: ({ context }, use) => {
authFixture(context);
use(context);
},
}); });
test.use({ viewport: { width: 200, height: 200 } }); test.use({ viewport: { width: 200, height: 200 } });
...@@ -28,56 +22,32 @@ test.describe('with multiple items', async() => { ...@@ -28,56 +22,32 @@ test.describe('with multiple items', async() => {
}, },
}; };
test.beforeEach(async({ page }) => { test.beforeEach(async({ mockApiResponse }) => {
await page.route(USER_INFO_URL, (route) => route.fulfill({ mockApiResponse('user_info', profileMock.base);
body: JSON.stringify(profileMock.base),
}));
}); });
test('base view', async({ mount, page }) => { test('base view', async({ render, page }) => {
const component = await mount( const component = await render(<AccountActionsMenu/>, { hooksConfig });
<TestApp>
<AccountActionsMenu/>
</TestApp>,
{ hooksConfig },
);
await component.getByRole('button').click(); await component.getByRole('button').click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
test('base view with styles', async({ mount, page }) => { test('base view with styles', async({ render, page }) => {
const component = await mount( const component = await render(<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>, { hooksConfig });
<TestApp>
<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);
await component.getByRole('button').click(); await component.getByRole('button').click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
test('loading', async({ mount }) => { test('loading', async({ render }) => {
const component = await mount( const component = await render(<AccountActionsMenu isLoading/>, { hooksConfig });
<TestApp>
<AccountActionsMenu isLoading/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('loading with styles', async({ mount }) => { test('loading with styles', async({ render }) => {
const component = await mount( const component = await render(<AccountActionsMenu isLoading m={ 2 } outline="1px solid lightpink"/>, { hooksConfig });
<TestApp>
<AccountActionsMenu isLoading m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
...@@ -92,39 +62,22 @@ test.describe('with one item', async() => { ...@@ -92,39 +62,22 @@ test.describe('with one item', async() => {
}, },
}; };
test('base view', async({ mount, page }) => { test('base view', async({ render, page }) => {
const component = await mount( const component = await render(<AccountActionsMenu/>, { hooksConfig });
<TestApp>
<AccountActionsMenu/>
</TestApp>,
{ hooksConfig },
);
await component.getByRole('button').hover(); await component.getByRole('button').hover();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
test('base view with styles', async({ mount, page }) => { test('base view with styles', async({ render, page }) => {
const component = await mount( const component = await render(<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>, { hooksConfig });
<TestApp>
<AccountActionsMenu m={ 2 } outline="1px solid lightpink"/>
</TestApp>,
{ hooksConfig },
);
await component.getByRole('button').hover(); await component.getByRole('button').hover();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
test('loading', async({ mount }) => { test('loading', async({ render }) => {
const component = await mount( const component = await render(<AccountActionsMenu isLoading/>, { hooksConfig });
<TestApp>
<AccountActionsMenu isLoading/>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
......
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import type { WalletProvider } from 'types/web3';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import { FOOTER_LINKS } from 'mocks/config/footerLinks'; import { FOOTER_LINKS } from 'mocks/config/footerLinks';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import Footer from './Footer'; import Footer from './Footer';
const FOOTER_LINKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', 'https://localhost:3000/footer-links.json') || ''; const FOOTER_LINKS_URL = 'https://localhost:3000/footer-links.json';
const BACKEND_VERSION_API_URL = buildApiUrl('config_backend_version'); test.describe('with custom links, max cols', () => {
const INDEXING_ALERT_API_URL = buildApiUrl('homepage_indexing_status'); test.beforeEach(async({ render, mockApiResponse, mockConfigResponse, injectMetaMaskProvider, mockEnvs }) => {
await mockEnvs([
base.describe('with custom links, max cols', () => { [ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ],
const test = base.extend({ ]);
context: contextWithEnvs([ await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, JSON.stringify(FOOTER_LINKS));
{ name: 'NEXT_PUBLIC_FOOTER_LINKS', value: FOOTER_LINKS_URL }, await injectMetaMaskProvider();
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockApiResponse('homepage_indexing_status', {
]) as any, finished_indexing: false,
}); finished_indexing_blocks: false,
indexed_internal_transactions_ratio: '0.1',
test.beforeEach(async({ page, mount }) => { indexed_blocks_ratio: '0.1',
await page.route(FOOTER_LINKS_URL, (route) => {
return route.fulfill({
body: JSON.stringify(FOOTER_LINKS),
});
}); });
await page.evaluate(() => { await render(<Footer/>);
window.ethereum = {
isMetaMask: true,
_events: {},
} as WalletProvider;
});
await page.route(INDEXING_ALERT_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ finished_indexing: false, indexed_internal_transactions_ratio: 0.1 }),
}));
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
}); });
test('+@mobile +@dark-mode', async({ page }) => { test('+@mobile +@dark-mode', async({ page }) => {
...@@ -65,74 +38,35 @@ base.describe('with custom links, max cols', () => { ...@@ -65,74 +38,35 @@ base.describe('with custom links, max cols', () => {
}); });
}); });
base.describe('with custom links, min cols', () => { test.describe('with custom links, min cols', () => {
const test = base.extend({ test('base view +@dark-mode +@mobile', async({ render, page, mockConfigResponse, mockEnvs }) => {
context: contextWithEnvs([ await mockEnvs([
{ name: 'NEXT_PUBLIC_FOOTER_LINKS', value: FOOTER_LINKS_URL }, [ 'NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL ],
// eslint-disable-next-line @typescript-eslint/no-explicit-any ]);
]) as any, await mockConfigResponse('NEXT_PUBLIC_FOOTER_LINKS', FOOTER_LINKS_URL, JSON.stringify([ FOOTER_LINKS[0] ]));
}); await render(<Footer/>);
test('base view +@dark-mode +@mobile', async({ mount, page }) => {
await page.route(FOOTER_LINKS_URL, (route) => {
return route.fulfill({
body: JSON.stringify([ FOOTER_LINKS[0] ]),
});
});
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
}); });
base.describe('without custom links', () => { test.describe('without custom links', () => {
base('base view +@dark-mode +@mobile', async({ mount, page }) => { test('base view +@dark-mode +@mobile', async({ render, page, injectMetaMaskProvider, mockApiResponse }) => {
await page.evaluate(() => { await injectMetaMaskProvider();
window.ethereum = { await mockApiResponse('config_backend_version', { backend_version: 'v5.2.0-beta.+commit.1ce1a355' });
isMetaMask: true, await render(<Footer/>);
_events: {},
} as WalletProvider;
});
await page.route(BACKEND_VERSION_API_URL, (route) => {
return route.fulfill({
body: JSON.stringify({
backend_version: 'v5.2.0-beta.+commit.1ce1a355',
}),
});
});
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
base('with indexing alert +@dark-mode +@mobile', async({ mount, page }) => { test('with indexing alert +@dark-mode +@mobile', async({ render, injectMetaMaskProvider, mockApiResponse }) => {
await page.evaluate(() => { await injectMetaMaskProvider();
window.ethereum = { await mockApiResponse('homepage_indexing_status', {
providers: [ { isMetaMask: true, _events: {} } ], finished_indexing: false,
} as WalletProvider; finished_indexing_blocks: false,
indexed_internal_transactions_ratio: '0.1',
indexed_blocks_ratio: '0.1',
}); });
await page.route(INDEXING_ALERT_API_URL, (route) => route.fulfill({ const component = await render(<Footer/>);
status: 200,
body: JSON.stringify({ finished_indexing: false, indexed_internal_transactions_ratio: 0.1 }),
}));
const component = await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
}); });
import { test as base, expect, devices } from '@playwright/experimental-ct-react'; import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import authFixture from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect, devices } from 'playwright/lib';
import createContextWithStorage from 'playwright/fixtures/createContextWithStorage';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import Burger from './Burger'; import Burger from './Burger';
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; const FEATURED_NETWORKS_URL = 'https://localhost:3000/featured-networks.json';
const LOGO_URL = 'https://localhost:3000/my-logo.png'; const LOGO_URL = 'https://localhost:3000/my-logo.png';
base.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
const hooksConfig = { const hooksConfig = {
router: { router: {
...@@ -24,32 +20,16 @@ const hooksConfig = { ...@@ -24,32 +20,16 @@ const hooksConfig = {
}, },
}; };
const test = base.extend({ test.beforeEach(async({ mockEnvs, mockConfigResponse, mockAssetResponse }) => {
context: contextWithEnvs([ await mockEnvs([
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
// eslint-disable-next-line @typescript-eslint/no-explicit-any ]);
]) as any, await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK);
await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg');
}); });
test('base view', async({ mount, page }) => { test('base view', async({ render, page }) => {
await page.route(FEATURED_NETWORKS_URL, (route) => { const component = await render(<Burger/>, { hooksConfig });
return route.fulfill({
body: FEATURED_NETWORKS_MOCK,
});
});
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<Burger/>
</TestApp>,
{ hooksConfig },
);
await component.locator('div[aria-label="Menu button"]').click(); await component.locator('div[aria-label="Menu button"]').click();
await expect(page.locator('.chakra-modal__content-container')).toHaveScreenshot(); await expect(page.locator('.chakra-modal__content-container')).toHaveScreenshot();
...@@ -61,25 +41,8 @@ test('base view', async({ mount, page }) => { ...@@ -61,25 +41,8 @@ test('base view', async({ mount, page }) => {
test.describe('dark mode', () => { test.describe('dark mode', () => {
test.use({ colorScheme: 'dark' }); test.use({ colorScheme: 'dark' });
test('base view', async({ mount, page }) => { test('base view', async({ render, page }) => {
await page.route(FEATURED_NETWORKS_URL, (route) => { const component = await render(<Burger/>, { hooksConfig });
return route.fulfill({
body: FEATURED_NETWORKS_MOCK,
});
});
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<Burger/>
</TestApp>,
{ hooksConfig },
);
await component.locator('div[aria-label="Menu button"]').click(); await component.locator('div[aria-label="Menu button"]').click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
...@@ -89,39 +52,23 @@ test.describe('dark mode', () => { ...@@ -89,39 +52,23 @@ test.describe('dark mode', () => {
}); });
}); });
test('submenu', async({ mount, page }) => { test('submenu', async({ render, page }) => {
const component = await mount( const component = await render(<Burger/>, { hooksConfig });
<TestApp>
<Burger/>
</TestApp>,
{ hooksConfig },
);
await component.locator('div[aria-label="Menu button"]').click(); await component.locator('div[aria-label="Menu button"]').click();
await page.locator('div[aria-label="Blockchain link group"]').click(); await page.locator('div[aria-label="Blockchain link group"]').click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
test.describe('auth', () => { const authTest = test.extend<{ context: BrowserContext }>({
const extendedTest = base.extend({ context: contextWithAuth,
context: async({ browser }, use) => { });
const context = await createContextWithStorage(browser, [
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL },
]);
authFixture(context);
use(context);
},
});
extendedTest.use({ viewport: { width: devices['iPhone 13 Pro'].viewport.width, height: 800 } }); authTest.describe('auth', () => {
authTest.use({ viewport: { width: devices['iPhone 13 Pro'].viewport.width, height: 800 } });
extendedTest('base view', async({ mount, page }) => { authTest('base view', async({ render, page }) => {
const component = await mount( const component = await render(<Burger/>, { hooksConfig });
<TestApp>
<Burger/>
</TestApp>,
{ hooksConfig },
);
await component.locator('div[aria-label="Menu button"]').click(); await component.locator('div[aria-label="Menu button"]').click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
......
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react'; import type { BrowserContext, Locator } from '@playwright/test';
import type { Locator } from '@playwright/test';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils'; import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import authFixture from 'playwright/fixtures/auth'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { contextWithAuth } from 'playwright/fixtures/auth';
import createContextWithStorage from 'playwright/fixtures/createContextWithStorage'; import { test, expect } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import NavigationDesktop from './NavigationDesktop'; import NavigationDesktop from './NavigationDesktop';
...@@ -22,26 +19,24 @@ const hooksConfig = { ...@@ -22,26 +19,24 @@ const hooksConfig = {
}, },
}; };
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/config.json') || ''; const FEATURED_NETWORKS_URL = 'https://localhost:3000/featured-networks.json';
const test = base.extend({ test.beforeEach(async({ mockEnvs, mockConfigResponse }) => {
context: contextWithEnvs([ await mockEnvs([
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
// eslint-disable-next-line @typescript-eslint/no-explicit-any ]);
]) as any, await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK);
}); });
test.describe('no auth', () => { test.describe('no auth', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ mount }) => { test.beforeEach(async({ render }) => {
component = await mount( component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
}); });
...@@ -59,39 +54,31 @@ test.describe('no auth', () => { ...@@ -59,39 +54,31 @@ test.describe('no auth', () => {
}); });
}); });
base.describe('auth', () => { const authTest = test.extend<{ context: BrowserContext }>({
const test = base.extend({ context: contextWithAuth,
context: async({ browser }, use) => { });
const context = await createContextWithStorage(browser, [
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL },
]);
authFixture(context);
use(context);
},
});
authTest.describe('auth', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ mount }) => { authTest.beforeEach(async({ render }) => {
component = await mount( component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
}); });
test('+@dark-mode', async() => { authTest('+@dark-mode', async() => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test.describe('xl screen', () => { authTest.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl }); authTest.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async() => { authTest('+@dark-mode', async() => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
}); });
...@@ -100,14 +87,12 @@ base.describe('auth', () => { ...@@ -100,14 +87,12 @@ base.describe('auth', () => {
test.describe('with tooltips', () => { test.describe('with tooltips', () => {
test.use({ viewport: configs.viewport.xl }); test.use({ viewport: configs.viewport.xl });
test('', async({ mount, page }) => { test('', async({ render, page }) => {
const component = await mount( const component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
...@@ -122,14 +107,12 @@ test.describe('with tooltips', () => { ...@@ -122,14 +107,12 @@ test.describe('with tooltips', () => {
test.describe('with submenu', () => { test.describe('with submenu', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ mount, page }) => { test.beforeEach(async({ render, page }) => {
component = await mount( component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await page.locator('a[aria-label="Blockchain link group"]').hover(); await page.locator('a[aria-label="Blockchain link group"]').hover();
...@@ -148,65 +131,55 @@ test.describe('with submenu', () => { ...@@ -148,65 +131,55 @@ test.describe('with submenu', () => {
}); });
}); });
base.describe('cookie set to false', () => { const noSideBarCookieTest = test.extend({
const test = base.extend({ context: ({ context }, use) => {
context: async({ browser }, use) => { context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: config.app.host, path: '/' } ]);
const context = await createContextWithStorage(browser, [ use(context);
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, },
]); });
context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: app.domain, path: '/' } ]);
use(context);
},
});
noSideBarCookieTest.describe('cookie set to false', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ mount }) => { noSideBarCookieTest.beforeEach(async({ render }) => {
component = await mount( component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
}); });
test('', async() => { noSideBarCookieTest('', async() => {
const networkMenu = component.locator('button[aria-label="Network menu"]'); const networkMenu = component.locator('button[aria-label="Network menu"]');
await expect(networkMenu).toBeVisible(); await expect(networkMenu).toBeVisible();
}); });
test.describe('xl screen', () => { noSideBarCookieTest.describe('xl screen', () => {
test.use({ viewport: configs.viewport.xl }); noSideBarCookieTest.use({ viewport: configs.viewport.xl });
test('', async() => { noSideBarCookieTest('', async() => {
const networkMenu = component.locator('button[aria-label="Network menu"]'); const networkMenu = component.locator('button[aria-label="Network menu"]');
await expect(networkMenu).toBeVisible(); await expect(networkMenu).toBeVisible();
}); });
}); });
}); });
base.describe('cookie set to true', () => { const sideBarCookieTest = test.extend({
const test = base.extend({ context: ({ context }, use) => {
context: async({ browser }, use) => { context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'true', domain: config.app.host, path: '/' } ]);
const context = await createContextWithStorage(browser, [ use(context);
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, },
]); });
context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'true', domain: 'localhost', path: '/' } ]);
use(context);
},
});
test('navbar is collapsed', async({ mount }) => { sideBarCookieTest.describe('cookie set to true', () => {
const component = await mount( sideBarCookieTest('navbar is collapsed', async({ render }) => {
<TestApp> const component = await render(
<Flex w="100%" minH="100vh" alignItems="stretch"> <Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/> <NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/> <Box bgColor="lightpink" w="100%"/>
</Flex> </Flex>,
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
...@@ -215,14 +188,12 @@ base.describe('cookie set to true', () => { ...@@ -215,14 +188,12 @@ base.describe('cookie set to true', () => {
}); });
}); });
test('hover +@dark-mode', async({ mount }) => { test('hover +@dark-mode', async({ render }) => {
const component = await mount( const component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
...@@ -233,14 +204,12 @@ test('hover +@dark-mode', async({ mount }) => { ...@@ -233,14 +204,12 @@ test('hover +@dark-mode', async({ mount }) => {
test.describe('hover xl screen', () => { test.describe('hover xl screen', () => {
test.use({ viewport: configs.viewport.xl }); test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async({ mount }) => { test('+@dark-mode', async({ render }) => {
const component = await mount( const component = await render(
<TestApp> <Flex w="100%" minH="100vh" alignItems="stretch">
<Flex w="100%" minH="100vh" alignItems="stretch"> <NavigationDesktop/>
<NavigationDesktop/> <Box bgColor="lightpink" w="100%"/>
<Box bgColor="lightpink" w="100%"/> </Flex>,
</Flex>
</TestApp>,
{ hooksConfig }, { hooksConfig },
); );
......
import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test'; import type { Locator } from '@playwright/test';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils'; import { test, expect } from 'playwright/lib';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import NetworkLogo from './NetworkLogo'; import NetworkLogo from './NetworkLogo';
base.describe('placeholder logo', () => { const LOGO_URL = 'https://localhost:3000/my-logo.png';
const test = base.extend({ const ICON_URL = 'https://localhost:3000/my-icon.png';
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: '' }, test.describe('placeholder logo', () => {
{ name: 'NEXT_PUBLIC_NETWORK_ICON', value: '' }, test.beforeEach(async({ mockEnvs }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockEnvs([
]) as any, [ 'NEXT_PUBLIC_NETWORK_LOGO', '' ],
[ 'NEXT_PUBLIC_NETWORK_ICON', '' ],
]);
}); });
test('+@dark-mode', async({ mount }) => { test('+@dark-mode', async({ render }) => {
const component = await mount( const component = await render(<NetworkLogo/>);
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot(); await expect(component.locator('a')).toHaveScreenshot();
}); });
...@@ -32,50 +26,25 @@ base.describe('placeholder logo', () => { ...@@ -32,50 +26,25 @@ base.describe('placeholder logo', () => {
test.describe('screen xl', () => { test.describe('screen xl', () => {
test.use({ viewport: configs.viewport.xl }); test.use({ viewport: configs.viewport.xl });
test('+@dark-mode', async({ mount }) => { test('+@dark-mode', async({ render }) => {
const component = await mount( const component = await render(<NetworkLogo/>);
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot(); await expect(component.locator('a')).toHaveScreenshot();
}); });
}); });
}); });
base.describe('custom logo', () => { test.describe('custom logo', () => {
const LOGO_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
{ name: 'NEXT_PUBLIC_NETWORK_ICON', value: ICON_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
let component: Locator; let component: Locator;
test.beforeEach(async({ page, mount }) => { test.beforeEach(async({ render, mockConfigResponse, mockEnvs }) => {
await page.route(LOGO_URL, (route) => { await mockEnvs([
return route.fulfill({ [ 'NEXT_PUBLIC_NETWORK_LOGO', LOGO_URL ],
status: 200, [ 'NEXT_PUBLIC_NETWORK_ICON', ICON_URL ],
path: './playwright/mocks/network-logo.svg', ]);
}); await mockConfigResponse('NEXT_PUBLIC_NETWORK_LOGO', LOGO_URL, './playwright/mocks/network-logo.svg', true);
}); await mockConfigResponse('NEXT_PUBLIC_NETWORK_ICON', ICON_URL, './playwright/mocks/image_svg.svg', true);
await page.route(ICON_URL, (route) => { component = await render(<NetworkLogo/>);
return route.fulfill({
status: 200,
path: './playwright/mocks/image_svg.svg',
});
});
component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
}); });
test('+@dark-mode', async() => { test('+@dark-mode', async() => {
...@@ -91,54 +60,22 @@ base.describe('custom logo', () => { ...@@ -91,54 +60,22 @@ base.describe('custom logo', () => {
}); });
}); });
base.describe('custom logo with dark option -@default +@dark-mode', () => { test.describe('custom logo with dark option -@default +@dark-mode', () => {
const LOGO_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const LOGO_URL_DARK = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const ICON_URL_DARK = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
{ name: 'NEXT_PUBLIC_NETWORK_LOGO_DARK', value: LOGO_URL_DARK },
{ name: 'NEXT_PUBLIC_NETWORK_ICON', value: ICON_URL },
{ name: 'NEXT_PUBLIC_NETWORK_ICON_DARK', value: ICON_URL_DARK },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
let component: Locator; let component: Locator;
test.beforeEach(async({ page, mount }) => { test.beforeEach(async({ render, mockConfigResponse, mockEnvs }) => {
await page.route(LOGO_URL, (route) => { await mockEnvs([
return route.fulfill({ [ 'NEXT_PUBLIC_NETWORK_LOGO', LOGO_URL ],
status: 200, [ 'NEXT_PUBLIC_NETWORK_LOGO_DARK', LOGO_URL ],
path: './playwright/mocks/image_long.jpg', [ 'NEXT_PUBLIC_NETWORK_ICON', ICON_URL ],
}); [ 'NEXT_PUBLIC_NETWORK_ICON_DARK', ICON_URL ],
}); ]);
await page.route(LOGO_URL_DARK, (route) => { await mockConfigResponse('NEXT_PUBLIC_NETWORK_LOGO', LOGO_URL, './playwright/mocks/image_long.jpg', true);
return route.fulfill({ await mockConfigResponse('NEXT_PUBLIC_NETWORK_LOGO_DARK', LOGO_URL, './playwright/mocks/image_long.jpg', true);
status: 200, await mockConfigResponse('NEXT_PUBLIC_NETWORK_ICON', ICON_URL, './playwright/mocks/image_s.jpg', true);
path: './playwright/mocks/image_long.jpg', await mockConfigResponse('NEXT_PUBLIC_NETWORK_ICON_DARK', ICON_URL, './playwright/mocks/image_s.jpg', true);
});
}); component = await render(<NetworkLogo/>);
await page.route(ICON_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
await page.route(ICON_URL_DARK, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
}); });
test('', async() => { test('', async() => {
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import NetworkMenu from './NetworkMenu'; import NetworkMenu from './NetworkMenu';
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; const FEATURED_NETWORKS_URL = 'https://localhost:3000/featured-networks.json';
const LOGO_URL = 'https://localhost:3000/my-logo.png';
const extendedTest = test.extend({ test.use({ viewport: { width: 1600, height: 1000 } });
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, test('base view +@dark-mode', async({ render, page, mockConfigResponse, mockAssetResponse, mockEnvs }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockEnvs([
]) as any, [ 'NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL ],
}); ]);
await mockConfigResponse('NEXT_PUBLIC_FEATURED_NETWORKS', FEATURED_NETWORKS_URL, FEATURED_NETWORKS_MOCK);
await mockAssetResponse(LOGO_URL, './playwright/mocks/image_s.jpg');
extendedTest.use({ viewport: { width: 1600, height: 1000 } }); const component = await render(<NetworkMenu/>);
extendedTest('base view +@dark-mode', async({ mount, page }) => {
const LOGO_URL = 'https://localhost:3000/my-logo.png';
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
await page.route(FEATURED_NETWORKS_URL, (route) => {
return route.fulfill({
status: 200,
body: FEATURED_NETWORKS_MOCK,
});
});
const component = await mount(
<TestApp>
<NetworkMenu/>
</TestApp>,
);
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 36, height: 36 } }); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 36, height: 36 } });
......
import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import config from 'configs/app';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
import * as app from 'playwright/utils/app';
import ProfileMenuDesktop from './ProfileMenuDesktop'; import ProfileMenuDesktop from './ProfileMenuDesktop';
...@@ -17,14 +18,11 @@ test('no auth', async({ render, page }) => { ...@@ -17,14 +18,11 @@ test('no auth', async({ render, page }) => {
const component = await render(<ProfileMenuDesktop/>, { hooksConfig }); const component = await render(<ProfileMenuDesktop/>, { hooksConfig });
await component.locator('a').click(); await component.locator('a').click();
expect(page.url()).toBe(`${ app.url }/auth/auth0?path=%2F`); expect(page.url()).toBe(`${ config.app.baseUrl }/auth/auth0?path=%2F`);
}); });
const authTest = test.extend({ const authTest = test.extend<{ context: BrowserContext }>({
context: ({ context }, use) => { context: contextWithAuth,
authFixture(context);
use(context);
},
}); });
authTest('auth +@dark-mode', async({ render, page, mockApiResponse, mockAssetResponse }) => { authTest('auth +@dark-mode', async({ render, page, mockApiResponse, mockAssetResponse }) => {
await mockApiResponse('user_info', profileMock.base); await mockApiResponse('user_info', profileMock.base);
......
import { test, expect, devices } from '@playwright/experimental-ct-react'; import type { BrowserContext } from '@playwright/test';
import React from 'react'; import React from 'react';
import config from 'configs/app';
import * as profileMock from 'mocks/user/profile'; import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth'; import { contextWithAuth } from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp'; import { test, expect, devices } from 'playwright/lib';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import ProfileMenuMobile from './ProfileMenuMobile'; import ProfileMenuMobile from './ProfileMenuMobile';
test('no auth', async({ mount, page }) => { test('no auth', async({ render, page }) => {
const hooksConfig = { const hooksConfig = {
router: { router: {
asPath: '/', asPath: '/',
pathname: '/', pathname: '/',
}, },
}; };
const component = await mount( const component = await render(<ProfileMenuMobile/>, { hooksConfig });
<TestApp>
<ProfileMenuMobile/>
</TestApp>,
{ hooksConfig },
);
await component.locator('a').click(); await component.locator('a').click();
expect(page.url()).toBe(`${ app.url }/auth/auth0?path=%2F`);
expect(page.url()).toBe(`${ config.app.baseUrl }/auth/auth0?path=%2F`);
}); });
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test.describe('auth', () => { const authTest = test.extend<{ context: BrowserContext }>({
const extendedTest = test.extend({ context: contextWithAuth,
context: ({ context }, use) => { });
authFixture(context);
use(context);
},
});
extendedTest('base view', async({ mount, page }) => { authTest.describe('auth', () => {
await page.route(buildApiUrl('user_info'), (route) => route.fulfill({ authTest('base view', async({ render, page, mockApiResponse, mockAssetResponse }) => {
status: 200, await mockApiResponse('user_info', profileMock.base);
body: JSON.stringify(profileMock.base), await mockAssetResponse(profileMock.base.avatar, './playwright/mocks/image_s.jpg');
}));
await page.route(profileMock.base.avatar, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<ProfileMenuMobile/>
</TestApp>,
);
const component = await render(<ProfileMenuMobile/>);
await component.getByAltText(/Profile picture/i).click(); await component.getByAltText(/Profile picture/i).click();
await expect(page).toHaveScreenshot(); await expect(page).toHaveScreenshot();
}); });
}); });
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment