Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix: fix prompt clicking flakiness, fix multiple snap key permissions (
Browse files Browse the repository at this point in the history
…#194)

* add retries when confirming prompts with lower timeout

* better snap error handling, ability to accept multiple key permissions

* bail on first fail

* add context to screenshots

* remove various random timeouts, add wait for overlay, more retrys

* fix lint

* increase timeout for loading network and token details

* address PR comments

* increase timeout

* add retry to profile dropdown
  • Loading branch information
mpetrunic committed Dec 15, 2022
1 parent 086ecbd commit fe03f89
Show file tree
Hide file tree
Showing 23 changed files with 199 additions and 77 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"build": "tsc -p tsconfig.build.json",
"lint": "eslint --color --ext .ts src/ test/",
"lint:fix": "yarn run lint --fix",
"test:mm": "mocha --require ts-node/register --require test/global.ts",
"test:flask": "mocha --require ts-node/register --require test/global_flask.ts",
"test:mm": "mocha --bail --require ts-node/register --require test/global.ts",
"test:flask": "mocha --bail --require ts-node/register --require test/global_flask.ts",
"test:puppeteer:mm": "AUTOMATION=puppeteer yarn run test:mm",
"test:puppeteer:flask": "AUTOMATION=puppeteer yarn run test:flask",
"test:playwright:mm": "AUTOMATION=playwright yarn run test:mm",
Expand Down
45 changes: 24 additions & 21 deletions src/helpers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getInputByLabel,
getSettingsSwitch,
} from "./selectors";
import { retry } from "./utils";

export const clickOnSettingsSwitch = async (
page: DappeteerPage,
Expand All @@ -18,37 +19,33 @@ export const clickOnSettingsSwitch = async (
export const openNetworkDropdown = async (
page: DappeteerPage
): Promise<void> => {
const networkSwitcher = await page.waitForSelector(".network-display", {
visible: true,
});
try {
await networkSwitcher.click();
await page.waitForSelector(".network-dropdown-list", {
await retry(async () => {
const networkSwitcher = await page.waitForSelector(".network-display", {
visible: true,
timeout: 1000,
});
} catch (e) {
//retry on fail
await networkSwitcher.click();
await page.waitForSelector(".network-dropdown-list", {
visible: true,
timeout: 4000,
timeout: 1000,
});
}
}, 3);
};

export const profileDropdownClick = async (
page: DappeteerPage,
expectToClose = false
): Promise<void> => {
const accountSwitcher = await page.waitForSelector(".account-menu__icon", {
visible: true,
});
await page.waitForTimeout(500);
await accountSwitcher.click();
await page.waitForSelector(".account-menu__accounts", {
hidden: expectToClose,
});
await retry(async () => {
const accountSwitcher = await page.waitForSelector(".account-menu__icon", {
visible: true,
timeout: 2000,
});
await accountSwitcher.click();
await page.waitForSelector(".account-menu__accounts", {
hidden: expectToClose,
timeout: 2000,
});
}, 3);
};

export const openAccountDropdown = async (
Expand All @@ -72,9 +69,10 @@ export const clickOnElement = async (

export const clickOnButton = async (
page: DappeteerPage,
text: string
text: string,
options?: { timeout?: number; visible?: boolean }
): Promise<void> => {
const button = await getElementByContent(page, text, "button");
const button = await getElementByContent(page, text, "button", options);
await button.click();
};

Expand Down Expand Up @@ -118,6 +116,11 @@ export const typeOnInputField = async (
return true;
};

export async function waitForOverlay(page: DappeteerPage): Promise<void> {
await page.waitForSelectorIsGone(".loading-overlay", { timeout: 10000 });
await page.waitForSelectorIsGone(".app-loading-spinner", { timeout: 10000 });
}

/**
*
* @param page
Expand Down
1 change: 1 addition & 0 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./actions";
export * from "./selectors";
export { retry } from "./utils";
4 changes: 3 additions & 1 deletion src/helpers/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { DappeteerPage, Serializable } from "../page";
export const getElementByContent = (
page: DappeteerPage,
text: string,
type = "*"
type = "*",
options?: { timeout?: number; visible?: boolean }
): Promise<DappeteerElementHandle | null> =>
page.waitForXPath(`//${type}[contains(text(), '${text}')]`, {
timeout: 20000,
visible: true,
...options,
});

export const getInputByLabel = (
Expand Down
14 changes: 14 additions & 0 deletions src/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export async function retry<R>(
fn: () => Promise<R>,
count: number
): Promise<R> {
let error;
for (let i = 0; i < count; i++) {
try {
return await fn();
} catch (e: unknown) {
error = e;
}
}
throw error;
}
27 changes: 17 additions & 10 deletions src/metamask/addNetwork.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { clickOnButton } from "../helpers";
import { clickOnButton, retry, waitForOverlay } from "../helpers";
import { DappeteerPage } from "../page";

export const acceptAddNetwork =
(page: DappeteerPage) =>
async (shouldSwitch = false): Promise<void> => {
await page.bringToFront();
await page.reload();
await page.waitForSelector(".confirmation-footer", {
visible: true,
});
await clickOnButton(page, "Approve");
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);
await page.waitForSelector(".confirmation-page", {
timeout: 1000,
});
await clickOnButton(page, "Approve", { timeout: 500 });
}, 5);
if (shouldSwitch) {
await clickOnButton(page, "Switch network");
await page.waitForSelector(".new-network-info__wrapper", {
Expand All @@ -23,7 +26,11 @@ export const acceptAddNetwork =

export const rejectAddNetwork =
(page: DappeteerPage) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Cancel");
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);

await clickOnButton(page, "Cancel", { timeout: 500 });
}, 5);
};
22 changes: 15 additions & 7 deletions src/metamask/addToken.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { clickOnButton } from "../helpers";
import { clickOnButton, retry, waitForOverlay } from "../helpers";
import { DappeteerPage } from "../page";

export const acceptAddToken =
(page: DappeteerPage) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Add token");
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);

await clickOnButton(page, "Add token", { timeout: 500 });
}, 5);
};

export const rejectAddToken =
(page: DappeteerPage) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Cancel");
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);

await clickOnButton(page, "Cancel", { timeout: 500 });
}, 5);
};
15 changes: 9 additions & 6 deletions src/metamask/approve.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { clickOnButton } from "../helpers";
import { clickOnButton, retry, waitForOverlay } from "../helpers";
import { DappeteerPage } from "../page";

// TODO: thing about renaming this method?
export const approve = (page: DappeteerPage) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);

// TODO: step 1 of connect chose account to connect?
await clickOnButton(page, "Next");
await clickOnButton(page, "Connect");
// TODO: step 1 of connect chose account to connect?
await clickOnButton(page, "Next", { timeout: 100 });
await clickOnButton(page, "Connect", { timeout: 2000 });
}, 5);
};
21 changes: 18 additions & 3 deletions src/metamask/confirmTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { TransactionOptions } from "..";
import { clickOnButton, typeOnInputField } from "../helpers";
import {
clickOnButton,
getElementByContent,
retry,
typeOnInputField,
waitForOverlay,
} from "../helpers";
import { DappeteerPage } from "../page";

import { GetSingedIn } from "./index";
Expand All @@ -13,8 +19,17 @@ export const confirmTransaction =
if (!(await getSingedIn())) {
throw new Error("You haven't signed in yet");
}
await page.waitForTimeout(500);
await page.reload();

//retry till we get prompt
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);
await getElementByContent(page, "Edit", "button", {
timeout: 500,
visible: false,
});
}, 15);

if (options) {
await clickOnButton(page, "Edit");
Expand Down
16 changes: 13 additions & 3 deletions src/metamask/sign.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { clickOnButton } from "../helpers";
import {
clickOnButton,
getElementByContent,
retry,
waitForOverlay,
} from "../helpers";

import { DappeteerPage } from "../page";
import { GetSingedIn } from ".";
Expand All @@ -11,8 +16,13 @@ export const sign =
throw new Error("You haven't signed in yet");
}

await page.waitForTimeout(500);
await page.reload();
//retry till we get prompt
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);
await getElementByContent(page, "Sign", "button", { timeout: 200 });
}, 5);

await clickOnButton(page, "Sign");

Expand Down
12 changes: 11 additions & 1 deletion src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,18 @@ export interface DappeteerPage<P = unknown> {
): Promise<Response>;
waitForSelector(
selector: string,
opts?: Partial<{ visible: boolean; timeout: number; hidden: boolean }>
opts?: Partial<{
visible: boolean;
detached: boolean;
hidden: boolean;
timeout: number;
}>
): Promise<DappeteerElementHandle>;

waitForSelectorIsGone(
selector: string,
opts?: Partial<{ timeout: number }>
): Promise<void>;
waitForXPath(
xpath: string,
opts?: Partial<{ visible: boolean; timeout: number }>
Expand Down
11 changes: 10 additions & 1 deletion src/playwright/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class DPlaywrightPage implements DappeteerPage<Page> {
}

async reload(): Promise<void> {
await this.page.reload();
await this.page.reload({ waitUntil: "networkidle" });
}

setViewport(opts: { height: number; width: number }): Promise<void> {
Expand All @@ -97,6 +97,15 @@ export class DPlaywrightPage implements DappeteerPage<Page> {
})) as ElementHandle<HTMLElement>
);
}
async waitForSelectorIsGone(
selector: string,
opts?: Partial<{ timeout: number }>
): Promise<void> {
await this.page.waitForSelector(selector, {
timeout: opts?.timeout,
state: "hidden",
});
}

waitForXPath(
xpath: string,
Expand Down
12 changes: 11 additions & 1 deletion src/puppeteer/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class DPupeteerPage implements DappeteerPage<Page> {
}

async reload(): Promise<void> {
await this.page.reload();
await this.page.reload({ waitUntil: "networkidle0" });
}

setViewport(opts: { height: number; width: number }): Promise<void> {
Expand All @@ -111,6 +111,16 @@ export class DPupeteerPage implements DappeteerPage<Page> {
);
}

async waitForSelectorIsGone(
selector: string,
opts?: Partial<{ timeout: number }>
): Promise<void> {
await this.page.waitForSelector(selector, {
hidden: true,
...opts,
});
}

async waitForXPath(
xpath: string,
opts?: Partial<{ visible: boolean; timeout: number }>
Expand Down
11 changes: 7 additions & 4 deletions src/snap/acceptDialog.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { clickOnButton } from "../helpers";
import { clickOnButton, retry, waitForOverlay } from "../helpers";
import { DappeteerPage } from "../page";

export const acceptDialog =
(page: DappeteerPage) => async (): Promise<void> => {
await page.bringToFront();
await page.reload();
await clickOnButton(page, "Approve");
await retry(async () => {
await page.bringToFront();
await page.reload();
await waitForOverlay(page);
await clickOnButton(page, "Approve", { timeout: 100 });
}, 5);
};
6 changes: 4 additions & 2 deletions src/snap/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ export const installSnap =
if (opts.hasPermissions) {
await clickOnButton(page, "Approve & install");
if (opts.hasKeyPermissions) {
const checkbox = await page.waitForSelector(".checkbox-label", {
await page.waitForSelector(".checkbox-label", {
visible: true,
});
await checkbox.click();
for await (const checkbox of await page.$$(".checkbox-label")) {
await checkbox.click();
}
await clickOnButton(page, "Confirm");
}
} else {
Expand Down
Loading

0 comments on commit fe03f89

Please sign in to comment.