Skip to content

Commit

Permalink
Merge branch 'main' into fix/dockerfile_missing_tsc
Browse files Browse the repository at this point in the history
  • Loading branch information
andreahaku authored Dec 19, 2023
2 parents 224177c + f373377 commit 71ce453
Show file tree
Hide file tree
Showing 40 changed files with 1,071 additions and 644 deletions.
1 change: 1 addition & 0 deletions e2e/.dapps.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
REACT_DAPP_URL=
TEST_DAPP_URL=
8 changes: 8 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# E2E SDK Automation

### Node version

`nvm use 18`

### Setup:

`yarn setup`

### Set a test SRP:

`export SRP=TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST`

### Env files needed:

.ios.env:

- BUNDLE_ID=
- DEVICE_NAME=
- PLATFORM_VERSION=
Expand All @@ -21,6 +24,7 @@
- DEVICE_UDID=

.android.env:

- BUNDLE_ID=
- DEVICE_NAME=
- PLATFORM_VERSION=
Expand All @@ -29,13 +33,17 @@
- APP_ACTIVITY=

.dapps.env:

- REACT_DAPP_URL=
- TEST_DAPP_URL=

.env:

- BROWSERSTACK_USERNAME=
- BROWSERSTACK_ACCESS_KEY=

### Run tests:

`yarn test:ios`

#### Currently setup to run locally
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"scripts": {
"test:android": "wdio run test/configs/local/wdio.android.app.local.conf.ts",
"test:ios": "wdio run test/configs/local/wdio.ios.app.local.conf.ts",
"test:ios:browserstack": "wdio run test/configs/browserstack/wdio.ios.app.browserstack.conf.ts",
"setup": "yarn install",
"build": "tsc --build",
"clean": "yarn clean:reports && rm -rf node_modules",
Expand Down
23 changes: 23 additions & 0 deletions e2e/src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ export const Browsers = {
CHROME: 'com.android.chrome',
};

export const NATIVE_OS_APPS = {
ANDROID: {
SETTINGS: 'com.android.settings',
},
};

export const Platforms = {
ANDROID: 'ANDROID',
IOS: 'IOS',
};

export const PLATFORM = driver.isIOS ? Platforms.IOS : Platforms.ANDROID;

export const SRP =
process.env.SRP ??
'test test test test test test test test test test test test';
Expand All @@ -19,3 +32,13 @@ export const BROWSER_BUNDLE_ID = driver.isIOS

// This comes from the config file, it'll never be undefined otherwise there are no tests to run
export const METAMASK_BUNDLE_ID = process.env.BUNDLE_ID as string;

export const METAMASK_APP_NAME_ANDROID =
METAMASK_BUNDLE_ID === 'io.metamask.qa' ? 'MetaMask-QA' : 'MetaMask';

export const WDIO_IOS_CLASS_CHAIN = '-ios class chain:';
export const WDIO_IOS_PREDICATE_STRING = '-ios predicate string:';
export const WDIO_ANDROID_UI_AUTOMATOR = 'android=';
export const WDIO_XPATH = '';
export const WDIO_ACCESSIBILITY_ID = '~';
export const WDIO_RESOURCE_ID = 'id:';
12 changes: 10 additions & 2 deletions e2e/src/Gestures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@ export default class Gestures {
const toPercentage = await Utils.getCoordinatesForDeviceFromPercentage(to);

await browser.touchAction([
{ action: Actions.PRESS as ActionTypes, x: fromPercentage.x, y: fromPercentage.y },
{
action: Actions.PRESS as ActionTypes,
x: fromPercentage.x,
y: fromPercentage.y,
},
{ action: Actions.WAIT as ActionTypes, ms: 2000 },
{ action: Actions.MOVE_TO as ActionTypes, x: toPercentage.x, y: toPercentage.y },
{
action: Actions.MOVE_TO as ActionTypes,
x: toPercentage.x,
y: toPercentage.y,
},
'release',
]);
}
Expand Down
82 changes: 82 additions & 0 deletions e2e/src/Selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
WDIO_ACCESSIBILITY_ID,
WDIO_ANDROID_UI_AUTOMATOR,
WDIO_IOS_PREDICATE_STRING,
WDIO_RESOURCE_ID,
WDIO_XPATH,
} from './Constants';

class WDIOSelector {
/*
* Returns the locator for the given accessibilityId
* @param accessibilityId: string
* */
accessibilityId(accessibilityId: string): string {
return `${WDIO_ACCESSIBILITY_ID}${accessibilityId}`;
}

/*
* Returns the locator for the given xpath
* @param xpath: string
* */
xpath(xpath: string): string {
// Even though webdriver.io xpath selector is an empty string, it may change at any point
return `${WDIO_XPATH}${xpath}`;
}

/*
* Returns the locator for the given resource-id
* @param resourceId: string
* */
resourceId(resourceId: string): string {
return `${WDIO_RESOURCE_ID}${resourceId}`;
}
}

export class AndroidSelector extends WDIOSelector {
private readonly uiAutomatorPredicate = 'new UiSelector()';

private uiAutomatorSelector = '';

constructor() {
super();
this.uiAutomatorSelector = `${WDIO_ANDROID_UI_AUTOMATOR}${this.uiAutomatorPredicate}`;
}

uiAutomatorAndText(text: string): string {
return this.uiAutomatorSelector.concat(`.text("${text}")`);
}

uiAutomatorAndClassName(className: string): string {
return this.uiAutomatorSelector.concat(`.className("${className}")`);
}

uiAutomatorAndDescription(description: string): string {
return this.uiAutomatorSelector.concat(`.description("${description}")`);
}

static by() {
return new AndroidSelector();
}
}

export class IOSSelector extends WDIOSelector {
private predicateStringSelector = '';

constructor() {
super();
this.predicateStringSelector = `${WDIO_IOS_PREDICATE_STRING}`;
}

predicateString(predicateString: string): string {
return this.predicateStringSelector.concat(predicateString);
}

iosClassChain(classChain: string): string {
return this.predicateStringSelector.concat(classChain);
}

static by() {
return new IOSSelector();
}
}
14 changes: 0 additions & 14 deletions e2e/src/Strategies.ts

This file was deleted.

41 changes: 14 additions & 27 deletions e2e/src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,30 @@ import FixtureServer from '../test/fixtures/FixtureServer';
import {
BrowserSize,
Coordinates,
MetaMaskElementLocator,
MetaMaskElementSelector,
ScreenPercentage,
} from './types';
import { FIXTURE_SERVER_PORT } from './Constants';
import { FIXTURE_SERVER_PORT, PLATFORM, Platforms } from './Constants';

export const Platform = driver.isIOS ? 'IOS' : 'ANDROID';

class Utils {
static getLocatorPerPlatformAndStrategy(
locator: MetaMaskElementLocator,
): string {
const platformLocator = driver.isIOS
? locator.iosLocator
: locator.androidLocator;

// In case the locator was not provided for the platform it is running on
if (platformLocator === undefined) {
throw new Error(`Locator for ${Platform} needs to be provided!`);
}

if (driver.isIOS && locator.iosLocator !== undefined) {
return `${platformLocator.strategy}${locator.iosLocator.locator}`;
} else if (driver.isAndroid && locator.androidLocator !== undefined) {
// Explicitly check for driver.isAndroid in case we want to add web tests
return `${platformLocator.strategy}${locator.androidLocator.locator}`;
}
throw new Error('Platform locator is undefined');
export const getSelectorForPlatform = (locator: MetaMaskElementSelector) => {
const platformSelector =
PLATFORM === Platforms.IOS ? locator.iosSelector : locator.androidSelector;
if (platformSelector === undefined) {
throw new Error(`Selector for ${PLATFORM} needs to be provided!`);
}

return platformSelector;
};

class Utils {
static async launchApp(bundleId: string): Promise<void> {
// Location can be either url for web test dapp or bundleId for native app
console.log(`Launching ${Platform} DAPP with bundleId: ${bundleId}`);
console.log(`Launching ${PLATFORM} DAPP with bundleId: ${bundleId}`);
await driver.activateApp(bundleId);
}

static async launchMetaMask(): Promise<void> {
console.log(`Launching MetaMask on ${Platform}`);
console.log(`Launching MetaMask on ${PLATFORM}`);
const metamaskBundleId = process.env.BUNDLE_ID as string;
await driver.activateApp(metamaskBundleId);
}
Expand Down Expand Up @@ -76,7 +63,7 @@ class Utils {
* Not the same as the dappTerminate that cleans a session
* */
static async killApp(bundleId: string): Promise<void> {
console.log(`Terminating ${Platform} DAPP with bundleId: ${bundleId}`);
console.log(`Terminating ${PLATFORM} DAPP with bundleId: ${bundleId}`);
await driver.terminateApp(bundleId);
}

Expand Down
118 changes: 118 additions & 0 deletions e2e/src/screens/Android/AndroidSettingsOpeningLinksScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { ChainablePromiseElement } from 'webdriverio';
import { METAMASK_APP_NAME_ANDROID } from '../../Constants';
import Gestures from '../../Gestures';
import { getSelectorForPlatform } from '../../Utils';
import { AndroidSelector } from '../../Selectors';

class AndroidSettingsOpeningLinksScreen {
get openingLinksMetaMaskAppOption(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
`//android.widget.TextView[@resource-id="android:id/title" and @text="${METAMASK_APP_NAME_ANDROID}"]`,
),
}),
);
}

get addLinksButton(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//*[@resource-id="android:id/title" and @text="Add link"]',
),
}),
);
}

get addSupportedLinksButton(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//android.widget.Button[@resource-id="android:id/button1"]',
),
}),
);
}

get firstSupportedLink(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//*[@resource-id="android:id/text1" and @text="metamask-alternate.app.link"]',
),
}),
);
}

get secondSupportedLink(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//*[@resource-id="android:id/text1" and @text="metamask-alternate.test-app.link"]',
),
}),
);
}

get thirdSupportedLink(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//*[@resource-id="android:id/text1" and @text="metamask.test-app.link"]',
),
}),
);
}

get forthSupportedLink(): ChainablePromiseElement<WebdriverIO.Element> {
return $(
getSelectorForPlatform({
androidSelector: AndroidSelector.by().xpath(
'//*[@resource-id="android:id/text1" and @text="metamask.app.link"]',
),
}),
);
}

async scrollToMetaMaskAppOption(): Promise<void> {
let isMetaMaskLinksButtonDisplayed = await (
await this.openingLinksMetaMaskAppOption
).isDisplayed();

while (!isMetaMaskLinksButtonDisplayed) {
await Gestures.swipeByPercentage({ x: 50, y: 90 }, { x: 50, y: 5 });
isMetaMaskLinksButtonDisplayed = await (
await this.openingLinksMetaMaskAppOption
).isDisplayed();
}
}

async tapMetaMaskAppOption(): Promise<void> {
await (await this.openingLinksMetaMaskAppOption).click();
}

async selectAllMetaMaskSupportedLinks(): Promise<void> {
await (await this.firstSupportedLink).click();
await (await this.secondSupportedLink).click();
await (await this.thirdSupportedLink).click();
await (await this.forthSupportedLink).click();
}

async tapAddMetaMaskSupportedLinks(): Promise<void> {
await (await this.addSupportedLinksButton).click();
}

async tapAddLinksButton(): Promise<void> {
await (await this.addLinksButton).click();
}

async isAddLinksButtonEnabled(): Promise<boolean> {
return await (await this.addLinksButton).isEnabled();
}
}

const androidSettingsOpeningLinksScreen =
new AndroidSettingsOpeningLinksScreen();

export default androidSettingsOpeningLinksScreen;
Loading

0 comments on commit 71ce453

Please sign in to comment.