Skip to content

Commit 40baecc

Browse files
committed
test: add e2e coverage
1 parent ffee6e5 commit 40baecc

2 files changed

Lines changed: 385 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { Assertions, Gestures, Matchers } from '../../framework';
2+
3+
const SwapTrendingTokensSelectorIDs = {
4+
SECTION: 'bridge-trending-tokens-section',
5+
PRICE_FILTER: 'bridge-trending-price-filter',
6+
NETWORK_FILTER: 'bridge-trending-network-filter',
7+
TIME_FILTER: 'bridge-trending-time-filter',
8+
PRICE_BOTTOM_SHEET: 'trending-token-price-change-bottom-sheet',
9+
NETWORK_BOTTOM_SHEET: 'trending-token-network-bottom-sheet',
10+
TIME_BOTTOM_SHEET: 'trending-token-time-bottom-sheet',
11+
INNER_LIST: 'trending-tokens-list',
12+
CLOSE_BUTTON: 'close-button',
13+
TIME_SELECT_6H: 'time-select-6h',
14+
BRIDGE_VIEW_SCROLL: 'bridge-view-scroll',
15+
} as const;
16+
17+
class SwapTrendingTokensView {
18+
private el(id: string): DetoxElement {
19+
return Matchers.getElementByID(id);
20+
}
21+
22+
async expectSectionVisible(timeout = 10000): Promise<void> {
23+
await Assertions.expectElementToBeVisible(
24+
this.el(SwapTrendingTokensSelectorIDs.SECTION),
25+
{
26+
timeout,
27+
description: 'Trending section should be visible',
28+
},
29+
);
30+
}
31+
32+
async expectSectionNotVisible(timeout = 10000): Promise<void> {
33+
await Assertions.expectElementToNotBeVisible(
34+
this.el(SwapTrendingTokensSelectorIDs.SECTION),
35+
{
36+
timeout,
37+
description: 'Trending section should not be visible',
38+
},
39+
);
40+
}
41+
42+
async expectNoInnerList(): Promise<void> {
43+
await Assertions.expectElementToNotBeVisible(
44+
this.el(SwapTrendingTokensSelectorIDs.INNER_LIST),
45+
{
46+
description:
47+
'Bridge trending should not render an inner list scroll container',
48+
},
49+
);
50+
}
51+
52+
async expectPriceBottomSheetVisible(): Promise<void> {
53+
await Assertions.expectElementToBeVisible(
54+
this.el(SwapTrendingTokensSelectorIDs.PRICE_BOTTOM_SHEET),
55+
{
56+
description: 'Price sort bottom sheet should be visible',
57+
},
58+
);
59+
}
60+
61+
async expectTimeBottomSheetVisible(): Promise<void> {
62+
await Assertions.expectElementToBeVisible(
63+
this.el(SwapTrendingTokensSelectorIDs.TIME_BOTTOM_SHEET),
64+
{
65+
description: 'Time bottom sheet should be visible',
66+
},
67+
);
68+
}
69+
70+
async expectNetworkBottomSheetVisible(): Promise<void> {
71+
await Assertions.expectElementToBeVisible(
72+
this.el(SwapTrendingTokensSelectorIDs.NETWORK_BOTTOM_SHEET),
73+
{
74+
description: 'Network bottom sheet should be visible',
75+
},
76+
);
77+
}
78+
79+
tokenRow(assetId: string): DetoxElement {
80+
return Matchers.getElementByID(`trending-token-row-item-${assetId}`);
81+
}
82+
83+
async scrollToFilters(): Promise<void> {
84+
await Gestures.scrollToElement(
85+
this.el(SwapTrendingTokensSelectorIDs.PRICE_FILTER),
86+
Matchers.getIdentifier(SwapTrendingTokensSelectorIDs.BRIDGE_VIEW_SCROLL),
87+
{
88+
direction: 'down',
89+
scrollAmount: 250,
90+
elemDescription: 'Scroll bridge view to trending filters',
91+
},
92+
);
93+
}
94+
95+
async openPriceFilter(): Promise<void> {
96+
await Gestures.waitAndTap(
97+
this.el(SwapTrendingTokensSelectorIDs.PRICE_FILTER),
98+
{
99+
elemDescription: 'Tap trending price filter',
100+
},
101+
);
102+
}
103+
104+
async openTimeFilter(): Promise<void> {
105+
await Gestures.waitAndTap(
106+
this.el(SwapTrendingTokensSelectorIDs.TIME_FILTER),
107+
{
108+
elemDescription: 'Tap trending time filter',
109+
},
110+
);
111+
}
112+
113+
async openNetworkFilter(): Promise<void> {
114+
await Gestures.waitAndTap(
115+
this.el(SwapTrendingTokensSelectorIDs.NETWORK_FILTER),
116+
{
117+
elemDescription: 'Tap trending network filter',
118+
},
119+
);
120+
}
121+
122+
async closeBottomSheet(): Promise<void> {
123+
await Gestures.waitAndTap(
124+
this.el(SwapTrendingTokensSelectorIDs.CLOSE_BUTTON),
125+
{
126+
elemDescription: 'Close trending bottom sheet',
127+
},
128+
);
129+
}
130+
131+
async selectTimeSixHours(): Promise<void> {
132+
await Gestures.waitAndTap(
133+
this.el(SwapTrendingTokensSelectorIDs.TIME_SELECT_6H),
134+
{
135+
elemDescription: 'Select 6h time filter',
136+
},
137+
);
138+
}
139+
140+
async selectNetworkByName(networkName: string): Promise<void> {
141+
await Gestures.waitAndTap(Matchers.getElementByText(networkName), {
142+
elemDescription: `Select trending network ${networkName}`,
143+
});
144+
}
145+
146+
async tapTokenRow(assetId: string): Promise<void> {
147+
await Gestures.waitAndTap(this.tokenRow(assetId), {
148+
elemDescription: `Tap trending token row ${assetId}`,
149+
});
150+
}
151+
152+
async expectTokenRowVisible(assetId: string): Promise<void> {
153+
await Assertions.expectElementToBeVisible(this.tokenRow(assetId), {
154+
timeout: 10000,
155+
description: `Trending token row ${assetId} should be visible`,
156+
});
157+
}
158+
159+
async expectTokenRowNotVisible(assetId: string): Promise<void> {
160+
await Assertions.expectElementToNotBeVisible(this.tokenRow(assetId), {
161+
timeout: 10000,
162+
description: `Trending token row ${assetId} should not be visible`,
163+
});
164+
}
165+
}
166+
167+
export default new SwapTrendingTokensView();
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import { Mockttp } from 'mockttp';
2+
import { withFixtures } from '../../framework/fixtures/FixtureHelper';
3+
import { LocalNode, LocalNodeType } from '../../framework/types';
4+
import { loginToApp } from '../../flows/wallet.flow';
5+
import WalletView from '../../page-objects/wallet/WalletView';
6+
import QuoteView from '../../page-objects/swaps/QuoteView';
7+
import SwapTrendingTokensView from '../../page-objects/swaps/SwapTrendingTokensView';
8+
import { Assertions } from '../../framework';
9+
import CommonView from '../../page-objects/CommonView';
10+
import TokenOverview from '../../page-objects/wallet/TokenOverview';
11+
import FixtureBuilder from '../../framework/fixtures/FixtureBuilder';
12+
import { prepareSwapsTestEnvironment } from '../../helpers/swap/prepareSwapsTestEnvironment';
13+
import { testSpecificMock } from '../../helpers/swap/bridge-mocks';
14+
import { GET_QUOTE_ETH_USDC_RESPONSE } from '../../helpers/swap/constants';
15+
import { getDecodedProxiedURL } from '../notifications/utils/helpers';
16+
import { SmokeTrade } from '../../tags';
17+
import { AnvilPort } from '../../framework/fixtures/FixtureUtils';
18+
import { AnvilManager } from '../../seeder/anvil-manager';
19+
import enContent from '../../../locales/languages/en.json';
20+
import { createRemoteFeatureFlagsMock } from '../../api-mocking/helpers/remoteFeatureFlagsHelper';
21+
import { setupMockRequest } from '../../api-mocking/helpers/mockHelpers';
22+
23+
const BASE_CHAIN_ID_DECIMAL = '8453';
24+
25+
const ETHEREUM_TRENDING_ASSET_ID =
26+
'eip155:1/erc20:0x1111111111111111111111111111111111111111';
27+
const BASE_TRENDING_ASSET_ID =
28+
'eip155:8453/erc20:0x2222222222222222222222222222222222222222';
29+
30+
const TRENDING_ALL_NETWORKS_RESPONSE = [
31+
{
32+
assetId: ETHEREUM_TRENDING_ASSET_ID,
33+
symbol: 'ETHX',
34+
name: 'Ethereum Trending',
35+
decimals: 18,
36+
price: '10',
37+
aggregatedUsdVolume: 2000000,
38+
marketCap: 100000000,
39+
priceChangePct: {
40+
h24: '0.2',
41+
h6: '0.1',
42+
h1: '0.01',
43+
m5: '0.001',
44+
},
45+
},
46+
{
47+
assetId: BASE_TRENDING_ASSET_ID,
48+
symbol: 'BASEX',
49+
name: 'Base Trending',
50+
decimals: 18,
51+
price: '8',
52+
aggregatedUsdVolume: 3000000,
53+
marketCap: 200000000,
54+
priceChangePct: {
55+
h24: '0.3',
56+
h6: '0.15',
57+
h1: '0.02',
58+
m5: '0.002',
59+
},
60+
},
61+
];
62+
63+
const TRENDING_BASE_ONLY_RESPONSE = [TRENDING_ALL_NETWORKS_RESPONSE[1]];
64+
65+
const setupSwapsTrendingTokensMock = async (mockServer: Mockttp) => {
66+
const { response } = createRemoteFeatureFlagsMock({
67+
swapsTrendingTokens: true,
68+
});
69+
70+
await setupMockRequest(
71+
mockServer,
72+
{
73+
requestMethod: 'GET',
74+
url: /client-config\.api\.cx\.metamask\.io\/v1\/flags/i,
75+
response,
76+
responseCode: 200,
77+
},
78+
1001,
79+
);
80+
};
81+
82+
const setupTrendingTokensMock = async (mockServer: Mockttp) => {
83+
await mockServer
84+
.forGet('/proxy')
85+
.matching((request) => {
86+
const decodedUrl = getDecodedProxiedURL(request.url);
87+
return /\/v3\/tokens\/trending/.test(decodedUrl);
88+
})
89+
.asPriority(1001)
90+
.thenCallback((request) => {
91+
const decodedUrl = getDecodedProxiedURL(request.url);
92+
const isBaseOnlyRequest = decodedUrl.includes(
93+
`chainIds=eip155:${BASE_CHAIN_ID_DECIMAL}`,
94+
);
95+
96+
return {
97+
statusCode: 200,
98+
json: isBaseOnlyRequest
99+
? TRENDING_BASE_ONLY_RESPONSE
100+
: TRENDING_ALL_NETWORKS_RESPONSE,
101+
};
102+
});
103+
};
104+
105+
const setupQuoteFallbackMock = async (mockServer: Mockttp) => {
106+
await setupMockRequest(
107+
mockServer,
108+
{
109+
requestMethod: 'GET',
110+
url: /getQuote/i,
111+
response: GET_QUOTE_ETH_USDC_RESPONSE,
112+
responseCode: 200,
113+
},
114+
1000,
115+
);
116+
};
117+
118+
const openSwapFromWalletActions = async () => {
119+
await loginToApp();
120+
await prepareSwapsTestEnvironment();
121+
await WalletView.tapWalletSwapButton();
122+
};
123+
124+
const withBridgeFixtures = async (run: () => Promise<void>) => {
125+
await withFixtures(
126+
{
127+
fixture: ({ localNodes }: { localNodes?: LocalNode[] }) => {
128+
const node = localNodes?.[0] as unknown as AnvilManager;
129+
const rpcPort =
130+
node instanceof AnvilManager
131+
? (node.getPort() ?? AnvilPort())
132+
: undefined;
133+
134+
return new FixtureBuilder()
135+
.withNetworkController({
136+
providerConfig: {
137+
chainId: '0x1',
138+
rpcUrl: `http://localhost:${rpcPort ?? AnvilPort()}`,
139+
type: 'custom',
140+
nickname: 'Localhost',
141+
ticker: 'ETH',
142+
},
143+
})
144+
.withDisabledSmartTransactions()
145+
.build();
146+
},
147+
localNodeOptions: [
148+
{
149+
type: LocalNodeType.anvil,
150+
options: {
151+
chainId: 1,
152+
},
153+
},
154+
],
155+
restartDevice: true,
156+
testSpecificMock: async (mockServer: Mockttp) => {
157+
await testSpecificMock(mockServer);
158+
await setupSwapsTrendingTokensMock(mockServer);
159+
await setupTrendingTokensMock(mockServer);
160+
await setupQuoteFallbackMock(mockServer);
161+
},
162+
},
163+
run,
164+
);
165+
};
166+
167+
describe(SmokeTrade('Swap Trending Tokens (Bridge zero-state)'), () => {
168+
beforeEach(() => {
169+
jest.setTimeout(180000);
170+
});
171+
172+
it('zero-state trending supports filters then row navigation', async () => {
173+
await withBridgeFixtures(async () => {
174+
await openSwapFromWalletActions();
175+
176+
await SwapTrendingTokensView.expectSectionVisible();
177+
await SwapTrendingTokensView.expectNoInnerList();
178+
179+
await SwapTrendingTokensView.scrollToFilters();
180+
181+
await SwapTrendingTokensView.openPriceFilter();
182+
await SwapTrendingTokensView.expectPriceBottomSheetVisible();
183+
await Assertions.expectTextDisplayed(enContent.trending.high_to_low, {
184+
description: 'Default price change sort should be high to low',
185+
});
186+
await SwapTrendingTokensView.closeBottomSheet();
187+
188+
await SwapTrendingTokensView.openTimeFilter();
189+
await SwapTrendingTokensView.expectTimeBottomSheetVisible();
190+
await SwapTrendingTokensView.selectTimeSixHours();
191+
192+
await SwapTrendingTokensView.openNetworkFilter();
193+
await SwapTrendingTokensView.expectNetworkBottomSheetVisible();
194+
await SwapTrendingTokensView.selectNetworkByName('Base');
195+
196+
await SwapTrendingTokensView.expectTokenRowVisible(
197+
BASE_TRENDING_ASSET_ID,
198+
);
199+
await SwapTrendingTokensView.expectTokenRowNotVisible(
200+
ETHEREUM_TRENDING_ASSET_ID,
201+
);
202+
203+
await SwapTrendingTokensView.tapTokenRow(BASE_TRENDING_ASSET_ID);
204+
await Assertions.expectElementToBeVisible(TokenOverview.tokenPrice, {
205+
timeout: 10000,
206+
description: 'Token details should open from trending token row tap',
207+
});
208+
209+
await CommonView.tapBackButton();
210+
await SwapTrendingTokensView.expectSectionVisible();
211+
212+
await QuoteView.tapSourceAmountInput();
213+
await QuoteView.enterAmount('1');
214+
215+
await SwapTrendingTokensView.expectSectionNotVisible();
216+
});
217+
});
218+
});

0 commit comments

Comments
 (0)