Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion tests/framework/PlaywrightMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,19 @@ export default class PlaywrightMatchers {
/**
* Get element by name on iOS
* @param name - The name to search for
* @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 })).
* @returns The wrapped element
*/
static async getElementByNameiOS(name: string): Promise<PlaywrightElement> {
static async getElementByNameiOS(
name: string,
lazy = false,
): Promise<PlaywrightElement> {
const isIOS = await PlatformDetector.isIOS();
if (!isIOS) throw new Error('This function is only valid for iOS');
const xpath = `//*[contains(@name,'${name}')]`;
if (lazy) {
return await this.getLazyElementByXPath(xpath);
}
return await this.getElementByXPath(xpath);
}

Expand Down
19 changes: 13 additions & 6 deletions tests/page-objects/MMConnect/DappConnectionModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import PlaywrightMatchers from '../../framework/PlaywrightMatchers';
import UnifiedGestures from '../../framework/UnifiedGestures';
import { getDriver } from '../../framework/PlaywrightUtilities';
import { ConnectAccountBottomSheetSelectorsIDs } from '../../../app/components/Views/AccountConnect/ConnectAccountBottomSheet.testIds';
import { ConnectedAccountsSelectorsIDs } from '../../../app/components/Views/AccountConnect/ConnectedAccountModal.testIds';
import { AccountCellIds } from '../../../app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.testIds';
import { CellComponentSelectorsIDs } from '../../../app/component-library/components/Cells/Cell/CellComponent.testIds';
import { sleep } from '../../framework';
Expand All @@ -33,10 +34,16 @@ class DappConnectionModal {

get editAccountsButton(): EncapsulatedElementType {
return encapsulated({
appium: () =>
PlaywrightMatchers.getElementByXPath(
'//android.view.ViewGroup[@content-desc="Edit accounts"]',
),
appium: {
android: () =>
PlaywrightMatchers.getElementByXPath(
'//android.view.ViewGroup[@content-desc="Edit accounts"]',
),
ios: () =>
PlaywrightMatchers.getElementById(
ConnectedAccountsSelectorsIDs.ACCOUNT_LIST_BOTTOM_SHEET,
),
},
});
}

Expand Down Expand Up @@ -130,10 +137,10 @@ class DappConnectionModal {
direction: 'down',
percent: 1.0,
});
const element = await asPlaywrightElement(
const networkButton = await asPlaywrightElement(
this.getNetworkButton(networkName),
);
await element.click();
await networkButton.click();
},
});
}
Expand Down
248 changes: 248 additions & 0 deletions tests/page-objects/MMConnect/UniswapDapp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import {
asPlaywrightElement,
encapsulated,
encapsulatedAction,
EncapsulatedElementType,
PlatformDetector,
PlaywrightAssertions,
PlaywrightGestures,
PlaywrightMatchers,
sleep,
UnifiedGestures,
} from '../../framework';

class UniswapDapp {
private getByXPath(xpath: string): EncapsulatedElementType {
return encapsulated({
appium: () => PlaywrightMatchers.getLazyElementByXPath(xpath),
});
}

get connectButton(): EncapsulatedElementType {
return encapsulated({
appium: {
android: () =>
PlaywrightMatchers.getLazyElementByXPath(
'//*[@data-testid="navbar-connect-wallet"]',
),
ios: () =>
PlaywrightMatchers.getElementById('Connect', { exact: true }),
},
});
}

get walletConnect(): EncapsulatedElementType {
return encapsulated({
appium: {
android: () =>
PlaywrightMatchers.getElementByXPath(
'//*[contains(normalize-space(.), "WalletConnect")]',
),
ios: () =>
PlaywrightMatchers.getElementByXPath(
'//XCUIElementTypeStaticText[@name="WalletConnect"]',
),
},
});
}

get metaMaskWalletOption(): EncapsulatedElementType {
return encapsulated({
appium: {
android: () =>
PlaywrightMatchers.getLazyElementByXPath(
'//android.widget.Button[@text="MetaMask MetaMask"]',
),
ios: () =>
PlaywrightMatchers.getElementById('MetaMask MetaMask', {
exact: true,
}),
},
});
}

get metaMaskDeeplinkButton(): EncapsulatedElementType {
return encapsulated({
appium: {
android: () =>
PlaywrightMatchers.getLazyElementByXPath(
'//android.widget.TextView[@text="MetaMask"]',
),
ios: () =>
PlaywrightMatchers.getLazyElementByXPath(
'//XCUIElementTypeOther[@name="textfield"]',
),
},
});
}

get uniswapDialog(): EncapsulatedElementType {
return this.getByXPath('//android.app.AlertDialog');
}

get uniswapIcon(): EncapsulatedElementType {
return encapsulated({
appium: () => PlaywrightMatchers.getElementById('account-icon'),
});
}

get solanaPopup(): EncapsulatedElementType {
return encapsulated({
appium: () =>
PlaywrightMatchers.getElementByText('Use Solana on Uniswap'),
});
}

get SolanaPopup(): EncapsulatedElementType {
return this.solanaPopup;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused SolanaPopup getter is dead code

Low Severity

The PascalCase SolanaPopup getter is an alias for solanaPopup that is never referenced anywhere in the codebase. It was carried over from the old wdio UniswapDapp.js (which used this.SolanaPopup), but the new code exclusively uses the camelCase solanaPopup (in isUniswapDisplayed). This is dead code that adds confusion, especially since PascalCase getters are unconventional in TypeScript.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a514014. Configure here.


async waitForConnectButtonVisible(timeoutMs = 20000): Promise<void> {
await this.waitForElementVisible(
this.connectButton,
timeoutMs,
'UniswapDapp: connect button not visible',
);
}

async waitForWalletConnectVisible(timeoutMs = 15000): Promise<void> {
await this.waitForElementVisible(
this.walletConnect,
timeoutMs,
'UniswapDapp: WalletConnect option not visible',
);
}

async tapConnect(): Promise<void> {
await PlaywrightGestures.waitAndTap(
await asPlaywrightElement(this.connectButton),
{
delay: 3000, // 3 seconds - DOM might not be ready yet
},
);
}

async tapOnWalletConnect(): Promise<void> {
await PlaywrightGestures.waitAndTap(
await asPlaywrightElement(this.walletConnect),
{
delay: 3000, // 3 seconds - DOM might not be ready yet
},
);
}

async connectWithMetaMask(): Promise<void> {
await this.waitForConnectButtonVisible();
await this.tapConnect();
await this.waitForWalletConnectVisible();
await this.tapOnWalletConnect();
}

async connectIOS(timeoutMs = 20000): Promise<void> {
await this.waitForConnectButtonVisible(timeoutMs);
await this.tapConnect();
}

async selectWalletConnectOption(): Promise<void> {
await this.tapOnWalletConnect();
}

async tapOnMetaMaskWalletOption(): Promise<void> {
await UnifiedGestures.waitAndTap(this.metaMaskWalletOption, {
description: 'tap MetaMask wallet option',
});
}

async tapOnMetaMaskDeeplinkButton(): Promise<void> {
await encapsulatedAction({
appium: async () => {
await sleep(2000);
await PlaywrightGestures.waitAndTap(
await asPlaywrightElement(this.metaMaskDeeplinkButton),
);
},
});
}

async tapOnMetaMaskWalletOptionAndOpenDeeplink(): Promise<void> {
await this.tapOnMetaMaskWalletOption();
if (PlatformDetector.isAndroid()) {
await this.tapOnMetaMaskDeeplinkButton();
}
}

async isUniswapDisplayed(timeoutMs = 30000): Promise<void> {
await encapsulatedAction({
appium: async () => {
if (PlatformDetector.isAndroid()) {
const dialogVisible = await this.isElementVisible(
this.uniswapDialog,
timeoutMs,
);

if (dialogVisible) {
return;
}

const iconVisible = await this.isElementVisible(
this.uniswapIcon,
timeoutMs,
);

if (!iconVisible) {
throw new Error(
'Neither Uniswap dialog nor account icon is visible in Android context',
);
}

return;
}

await this.waitForElementVisible(
this.solanaPopup,
timeoutMs,
'UniswapDapp: Solana popup not visible',
);
},
});
}

private async waitForElementVisible(
targetElement: EncapsulatedElementType,
timeoutMs: number,
timeoutMsg: string,
): Promise<void> {
await encapsulatedAction({
appium: async () => {
await PlaywrightAssertions.expectConditionWithRetry(
async () => {
const resolvedElement = await asPlaywrightElement(targetElement);
await resolvedElement.waitForDisplayed({
timeout: timeoutMs,
timeoutMsg,
});
},
{
maxRetries: 5,
description: timeoutMsg,
},
);
},
});
}

private async isElementVisible(
targetElement: EncapsulatedElementType,
timeoutMs: number,
): Promise<boolean> {
try {
const resolvedElement = await asPlaywrightElement(targetElement);
await resolvedElement.waitForDisplayed({ timeout: timeoutMs });
return true;
} catch {
return false;
}
}
}

export default new UniswapDapp();
Loading
Loading