Skip to content

Commit 0ad59ee

Browse files
authored
Merge branch 'main' into rn-upgrade/0.81.5-no-unit-tests
2 parents a223a2a + 38817db commit 0ad59ee

5 files changed

Lines changed: 363 additions & 113 deletions

File tree

tests/framework/PlaywrightMatchers.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,19 @@ export default class PlaywrightMatchers {
229229
/**
230230
* Get element by name on iOS
231231
* @param name - The name to search for
232+
* @param lazy - Whether to get a lazy element. Lazy elements are not required to be present in the DOM. This is useful for negative assertions where the element may never have been rendered (e.g. waitForDisplayed({ reverse: true })).
232233
* @returns The wrapped element
233234
*/
234-
static async getElementByNameiOS(name: string): Promise<PlaywrightElement> {
235+
static async getElementByNameiOS(
236+
name: string,
237+
lazy = false,
238+
): Promise<PlaywrightElement> {
235239
const isIOS = await PlatformDetector.isIOS();
236240
if (!isIOS) throw new Error('This function is only valid for iOS');
237241
const xpath = `//*[contains(@name,'${name}')]`;
242+
if (lazy) {
243+
return await this.getLazyElementByXPath(xpath);
244+
}
238245
return await this.getElementByXPath(xpath);
239246
}
240247

tests/page-objects/MMConnect/DappConnectionModal.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import PlaywrightMatchers from '../../framework/PlaywrightMatchers';
88
import UnifiedGestures from '../../framework/UnifiedGestures';
99
import { getDriver } from '../../framework/PlaywrightUtilities';
1010
import { ConnectAccountBottomSheetSelectorsIDs } from '../../../app/components/Views/AccountConnect/ConnectAccountBottomSheet.testIds';
11+
import { ConnectedAccountsSelectorsIDs } from '../../../app/components/Views/AccountConnect/ConnectedAccountModal.testIds';
1112
import { AccountCellIds } from '../../../app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.testIds';
1213
import { CellComponentSelectorsIDs } from '../../../app/component-library/components/Cells/Cell/CellComponent.testIds';
1314
import { sleep } from '../../framework';
@@ -33,10 +34,16 @@ class DappConnectionModal {
3334

3435
get editAccountsButton(): EncapsulatedElementType {
3536
return encapsulated({
36-
appium: () =>
37-
PlaywrightMatchers.getElementByXPath(
38-
'//android.view.ViewGroup[@content-desc="Edit accounts"]',
39-
),
37+
appium: {
38+
android: () =>
39+
PlaywrightMatchers.getElementByXPath(
40+
'//android.view.ViewGroup[@content-desc="Edit accounts"]',
41+
),
42+
ios: () =>
43+
PlaywrightMatchers.getElementById(
44+
ConnectedAccountsSelectorsIDs.ACCOUNT_LIST_BOTTOM_SHEET,
45+
),
46+
},
4047
});
4148
}
4249

@@ -130,10 +137,10 @@ class DappConnectionModal {
130137
direction: 'down',
131138
percent: 1.0,
132139
});
133-
const element = await asPlaywrightElement(
140+
const networkButton = await asPlaywrightElement(
134141
this.getNetworkButton(networkName),
135142
);
136-
await element.click();
143+
await networkButton.click();
137144
},
138145
});
139146
}
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import {
2+
asPlaywrightElement,
3+
encapsulated,
4+
encapsulatedAction,
5+
EncapsulatedElementType,
6+
PlatformDetector,
7+
PlaywrightAssertions,
8+
PlaywrightGestures,
9+
PlaywrightMatchers,
10+
sleep,
11+
UnifiedGestures,
12+
} from '../../framework';
13+
14+
class UniswapDapp {
15+
private getByXPath(xpath: string): EncapsulatedElementType {
16+
return encapsulated({
17+
appium: () => PlaywrightMatchers.getLazyElementByXPath(xpath),
18+
});
19+
}
20+
21+
get connectButton(): EncapsulatedElementType {
22+
return encapsulated({
23+
appium: {
24+
android: () =>
25+
PlaywrightMatchers.getLazyElementByXPath(
26+
'//*[@data-testid="navbar-connect-wallet"]',
27+
),
28+
ios: () =>
29+
PlaywrightMatchers.getElementById('Connect', { exact: true }),
30+
},
31+
});
32+
}
33+
34+
get walletConnect(): EncapsulatedElementType {
35+
return encapsulated({
36+
appium: {
37+
android: () =>
38+
PlaywrightMatchers.getElementByXPath(
39+
'//*[contains(normalize-space(.), "WalletConnect")]',
40+
),
41+
ios: () =>
42+
PlaywrightMatchers.getElementByXPath(
43+
'//XCUIElementTypeStaticText[@name="WalletConnect"]',
44+
),
45+
},
46+
});
47+
}
48+
49+
get metaMaskWalletOption(): EncapsulatedElementType {
50+
return encapsulated({
51+
appium: {
52+
android: () =>
53+
PlaywrightMatchers.getLazyElementByXPath(
54+
'//android.widget.Button[@text="MetaMask MetaMask"]',
55+
),
56+
ios: () =>
57+
PlaywrightMatchers.getElementById('MetaMask MetaMask', {
58+
exact: true,
59+
}),
60+
},
61+
});
62+
}
63+
64+
get metaMaskDeeplinkButton(): EncapsulatedElementType {
65+
return encapsulated({
66+
appium: {
67+
android: () =>
68+
PlaywrightMatchers.getLazyElementByXPath(
69+
'//android.widget.TextView[@text="MetaMask"]',
70+
),
71+
ios: () =>
72+
PlaywrightMatchers.getLazyElementByXPath(
73+
'//XCUIElementTypeOther[@name="textfield"]',
74+
),
75+
},
76+
});
77+
}
78+
79+
get uniswapDialog(): EncapsulatedElementType {
80+
return this.getByXPath('//android.app.AlertDialog');
81+
}
82+
83+
get uniswapIcon(): EncapsulatedElementType {
84+
return encapsulated({
85+
appium: () => PlaywrightMatchers.getElementById('account-icon'),
86+
});
87+
}
88+
89+
get solanaPopup(): EncapsulatedElementType {
90+
return encapsulated({
91+
appium: () =>
92+
PlaywrightMatchers.getElementByText('Use Solana on Uniswap'),
93+
});
94+
}
95+
96+
get SolanaPopup(): EncapsulatedElementType {
97+
return this.solanaPopup;
98+
}
99+
100+
async waitForConnectButtonVisible(timeoutMs = 20000): Promise<void> {
101+
await this.waitForElementVisible(
102+
this.connectButton,
103+
timeoutMs,
104+
'UniswapDapp: connect button not visible',
105+
);
106+
}
107+
108+
async waitForWalletConnectVisible(timeoutMs = 15000): Promise<void> {
109+
await this.waitForElementVisible(
110+
this.walletConnect,
111+
timeoutMs,
112+
'UniswapDapp: WalletConnect option not visible',
113+
);
114+
}
115+
116+
async tapConnect(): Promise<void> {
117+
await PlaywrightGestures.waitAndTap(
118+
await asPlaywrightElement(this.connectButton),
119+
{
120+
delay: 3000, // 3 seconds - DOM might not be ready yet
121+
},
122+
);
123+
}
124+
125+
async tapOnWalletConnect(): Promise<void> {
126+
await PlaywrightGestures.waitAndTap(
127+
await asPlaywrightElement(this.walletConnect),
128+
{
129+
delay: 3000, // 3 seconds - DOM might not be ready yet
130+
},
131+
);
132+
}
133+
134+
async connectWithMetaMask(): Promise<void> {
135+
await this.waitForConnectButtonVisible();
136+
await this.tapConnect();
137+
await this.waitForWalletConnectVisible();
138+
await this.tapOnWalletConnect();
139+
}
140+
141+
async connectIOS(timeoutMs = 20000): Promise<void> {
142+
await this.waitForConnectButtonVisible(timeoutMs);
143+
await this.tapConnect();
144+
}
145+
146+
async selectWalletConnectOption(): Promise<void> {
147+
await this.tapOnWalletConnect();
148+
}
149+
150+
async tapOnMetaMaskWalletOption(): Promise<void> {
151+
await UnifiedGestures.waitAndTap(this.metaMaskWalletOption, {
152+
description: 'tap MetaMask wallet option',
153+
});
154+
}
155+
156+
async tapOnMetaMaskDeeplinkButton(): Promise<void> {
157+
await encapsulatedAction({
158+
appium: async () => {
159+
await sleep(2000);
160+
await PlaywrightGestures.waitAndTap(
161+
await asPlaywrightElement(this.metaMaskDeeplinkButton),
162+
);
163+
},
164+
});
165+
}
166+
167+
async tapOnMetaMaskWalletOptionAndOpenDeeplink(): Promise<void> {
168+
await this.tapOnMetaMaskWalletOption();
169+
if (PlatformDetector.isAndroid()) {
170+
await this.tapOnMetaMaskDeeplinkButton();
171+
}
172+
}
173+
174+
async isUniswapDisplayed(timeoutMs = 30000): Promise<void> {
175+
await encapsulatedAction({
176+
appium: async () => {
177+
if (PlatformDetector.isAndroid()) {
178+
const dialogVisible = await this.isElementVisible(
179+
this.uniswapDialog,
180+
timeoutMs,
181+
);
182+
183+
if (dialogVisible) {
184+
return;
185+
}
186+
187+
const iconVisible = await this.isElementVisible(
188+
this.uniswapIcon,
189+
timeoutMs,
190+
);
191+
192+
if (!iconVisible) {
193+
throw new Error(
194+
'Neither Uniswap dialog nor account icon is visible in Android context',
195+
);
196+
}
197+
198+
return;
199+
}
200+
201+
await this.waitForElementVisible(
202+
this.solanaPopup,
203+
timeoutMs,
204+
'UniswapDapp: Solana popup not visible',
205+
);
206+
},
207+
});
208+
}
209+
210+
private async waitForElementVisible(
211+
targetElement: EncapsulatedElementType,
212+
timeoutMs: number,
213+
timeoutMsg: string,
214+
): Promise<void> {
215+
await encapsulatedAction({
216+
appium: async () => {
217+
await PlaywrightAssertions.expectConditionWithRetry(
218+
async () => {
219+
const resolvedElement = await asPlaywrightElement(targetElement);
220+
await resolvedElement.waitForDisplayed({
221+
timeout: timeoutMs,
222+
timeoutMsg,
223+
});
224+
},
225+
{
226+
maxRetries: 5,
227+
description: timeoutMsg,
228+
},
229+
);
230+
},
231+
});
232+
}
233+
234+
private async isElementVisible(
235+
targetElement: EncapsulatedElementType,
236+
timeoutMs: number,
237+
): Promise<boolean> {
238+
try {
239+
const resolvedElement = await asPlaywrightElement(targetElement);
240+
await resolvedElement.waitForDisplayed({ timeout: timeoutMs });
241+
return true;
242+
} catch {
243+
return false;
244+
}
245+
}
246+
}
247+
248+
export default new UniswapDapp();

0 commit comments

Comments
 (0)