From ae17944ed8790611b69dfdb55439946d30639013 Mon Sep 17 00:00:00 2001 From: Anton Lykhoyda Date: Thu, 10 Nov 2022 11:05:36 +0100 Subject: [PATCH] feat: added notification snap to methods-snap #137 (#166) * pair improvements * fix lint * feat: add notify Snap method * Added getAllNotifications method * go back after getting all notifications * remove timeout * fix post merge errors Co-authored-by: Bernard Co-authored-by: Marin Petrunic --- src/metamask/index.ts | 3 ++- src/page.ts | 10 +++++++--- src/playwright/page.ts | 18 +++++++++++------- src/puppeteer/page.ts | 18 +++++++++++------- src/snap/getAllNotifications.ts | 22 ++++++++++++++++++++++ src/snap/index.ts | 1 + src/snap/types.ts | 2 ++ src/types.ts | 3 ++- test/flask/methods-snap/snap.manifest.json | 2 +- test/flask/methods-snap/src/index.ts | 18 +++++++++++++++++- test/flask/snaps.spec.ts | 12 ++++++++++++ test/global.ts | 1 + test/global_flask.ts | 1 + 13 files changed, 90 insertions(+), 21 deletions(-) create mode 100644 src/snap/getAllNotifications.ts diff --git a/src/metamask/index.ts b/src/metamask/index.ts index 4eef1dcc..84d15843 100644 --- a/src/metamask/index.ts +++ b/src/metamask/index.ts @@ -4,7 +4,7 @@ import { DappeteerPage } from "../page"; import { acceptDialog } from "../snap/acceptDialog"; import { rejectDialog } from "../snap/rejectDialog"; -import { installSnap, invokeSnap } from "../snap"; +import { getAllNotifications, installSnap, invokeSnap } from "../snap"; import { addNetwork } from "./addNetwork"; import { addToken } from "./addToken"; import { approve } from "./approve"; @@ -55,6 +55,7 @@ export const getMetaMask = (page: DappeteerPage): Promise => { deleteNetwork: deleteNetwork(page), }, snaps: { + getAllNotifications: getAllNotifications(page), acceptDialog: acceptDialog(page), rejectDialog: rejectDialog(page), invokeSnap, diff --git a/src/page.ts b/src/page.ts index a4a003e1..525fe123 100644 --- a/src/page.ts +++ b/src/page.ts @@ -3,10 +3,14 @@ import { DappeteerElementHandle } from "./element"; export interface DappeteerPage

{ $(selector: string): Promise; - $eval( + $eval( selector: string, - evalFn: (e: HTMLElement) => Promise | void - ): Promise; + evalFn: (e: HTMLElement) => Promise | T + ): Promise; + $$eval( + selector: string, + evalFn: (e: HTMLElement[]) => Promise | T[] + ): Promise; $$(selector: string): Promise; getSource(): P; url(): string; diff --git a/src/playwright/page.ts b/src/playwright/page.ts index c0494ee5..bed3d5ee 100644 --- a/src/playwright/page.ts +++ b/src/playwright/page.ts @@ -23,14 +23,18 @@ export class DPlaywrightPage implements DappeteerPage { ); } - $eval( + $eval( selector: string, - evalFn: (e: HTMLElement) => void | Promise - ): Promise { - return this.page.$eval( - selector, - async (e) => await evalFn(e as HTMLElement) - ); + evalFn: (e: HTMLElement) => T | Promise + ): Promise { + return this.page.$eval(selector, evalFn); + } + + $$eval( + selector: string, + evalFn: (e: HTMLElement[]) => T[] | Promise + ): Promise { + return this.page.$$eval(selector, evalFn); } async $$(selector: string): Promise[]> { diff --git a/src/puppeteer/page.ts b/src/puppeteer/page.ts index e073d052..3d937253 100644 --- a/src/puppeteer/page.ts +++ b/src/puppeteer/page.ts @@ -21,14 +21,18 @@ export class DPupeteerPage implements DappeteerPage { return new DPuppeteerElementHandle(await this.page.$(selector)); } - $eval( + $eval( selector: string, - evalFn: (e: HTMLElement) => void | Promise - ): Promise { - return this.page.$eval( - selector, - async (e) => await evalFn(e as HTMLElement) - ); + evalFn: (e: HTMLElement) => T | Promise + ): Promise { + return this.page.$eval(selector, evalFn) as Promise; + } + + $$eval( + selector: string, + evalFn: (e: HTMLElement[]) => T[] | Promise + ): Promise { + return this.page.$$eval(selector, evalFn); } async $$(selector: string): Promise[]> { diff --git a/src/snap/getAllNotifications.ts b/src/snap/getAllNotifications.ts new file mode 100644 index 00000000..1606eb51 --- /dev/null +++ b/src/snap/getAllNotifications.ts @@ -0,0 +1,22 @@ +import { clickOnElement, openProfileDropdown } from "../helpers"; +import { DappeteerPage } from "../page"; +import { NotificationList } from "./types"; + +export const getAllNotifications = + (page: DappeteerPage) => async (): Promise => { + await page.bringToFront(); + await openProfileDropdown(page); + await clickOnElement(page, "Notifications"); + await page.waitForSelector(".notifications__item__details__message"); + const notificationList: NotificationList = await page.$$eval( + ".notifications__item__details__message", + (elements) => + elements.map((element) => ({ message: element.textContent })) + ); + const backButton = await page.waitForSelector( + ".notifications__header__title-container__back-button" + ); + + await backButton.click(); + return notificationList; + }; diff --git a/src/snap/index.ts b/src/snap/index.ts index 1bb795ca..a3529048 100644 --- a/src/snap/index.ts +++ b/src/snap/index.ts @@ -1,3 +1,4 @@ export { flaskOnly } from "./utils"; export { installSnap } from "./install"; export { invokeSnap } from "./invokeSnap"; +export { getAllNotifications } from "./getAllNotifications"; diff --git a/src/snap/types.ts b/src/snap/types.ts index 3e4de4c3..9001d4de 100644 --- a/src/snap/types.ts +++ b/src/snap/types.ts @@ -18,3 +18,5 @@ export interface InstallSnapResult { }; }; } + +export type NotificationList = { message: string }[]; diff --git a/src/types.ts b/src/types.ts index 7be524db..0d1df362 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,7 +4,7 @@ import { MetaMaskInpageProvider } from "@metamask/providers"; import { DappeteerPage, Serializable } from "./page"; import { Path } from "./setup/utils/metaMaskDownloader"; import { InstallStep } from "./snap/install"; -import { InstallSnapResult } from "./snap/types"; +import { InstallSnapResult, NotificationList } from "./snap/types"; import { RECOMMENDED_METAMASK_VERSION } from "./index"; export type DappeteerLaunchOptions = { @@ -73,6 +73,7 @@ export type Dappeteer = { }; page: DappeteerPage; snaps: { + getAllNotifications: () => Promise; invokeSnap: ( page: DappeteerPage, snapId: string, diff --git a/test/flask/methods-snap/snap.manifest.json b/test/flask/methods-snap/snap.manifest.json index c9b3bbc1..d30b5edb 100644 --- a/test/flask/methods-snap/snap.manifest.json +++ b/test/flask/methods-snap/snap.manifest.json @@ -3,7 +3,7 @@ "description": "An example Snap written in TypeScript.", "proposedName": "Methods Snap\n", "source": { - "shasum": "BCwqoDyQltaL0dh0IrzN1A66D1jPMLAseJOHmqI1faU=", + "shasum": "xxu7kMfZ4zKgviSZ5K4gZKSDDGEYi2VqOK5bKAqoybo=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/test/flask/methods-snap/src/index.ts b/test/flask/methods-snap/src/index.ts index f8f0bf37..772789de 100644 --- a/test/flask/methods-snap/src/index.ts +++ b/test/flask/methods-snap/src/index.ts @@ -1,6 +1,9 @@ import { OnRpcRequestHandler } from "@metamask/snap-types"; -export const onRpcRequest: OnRpcRequestHandler = ({ origin, request }) => { +export const onRpcRequest: OnRpcRequestHandler = async ({ + origin, + request, +}) => { switch (request.method) { case "confirm": return wallet.request({ @@ -15,6 +18,19 @@ export const onRpcRequest: OnRpcRequestHandler = ({ origin, request }) => { }, ], }); + case "notify_inApp": + { + await wallet.request({ + method: "snap_notify", + params: [ + { + type: "inApp", + message: `Hello, in App notification`, + }, + ], + }); + } + break; default: throw new Error("Method not found."); } diff --git a/test/flask/snaps.spec.ts b/test/flask/snaps.spec.ts index 6db6a2fa..017a8b5a 100644 --- a/test/flask/snaps.spec.ts +++ b/test/flask/snaps.spec.ts @@ -108,5 +108,17 @@ describe("snaps", function () { expect(await invokeAction).to.equal(false); }); + + it("should invoke IN APP NOTIFICATIONS and check for a text", async function (this: TestContext) { + await metamask.snaps.invokeSnap( + testPage, + getSnapIdByName(this, Snaps.METHODS_SNAP), + "notify_inApp" + ); + + const notifications = await metamask.snaps.getAllNotifications(); + + expect(notifications[0].message).to.equal("Hello, in App notification"); + }); }); }); diff --git a/test/global.ts b/test/global.ts index 75e69b70..f0e13394 100644 --- a/test/global.ts +++ b/test/global.ts @@ -12,6 +12,7 @@ import { deployContract, startLocalEthereum, startTestServer } from "./deploy"; export const mochaHooks = { async beforeAll(this: Mocha.Context): Promise { + this.timeout(100000); const ethereum = await startLocalEthereum({ wallet: { mnemonic: LOCAL_PREFUNDED_MNEMONIC, diff --git a/test/global_flask.ts b/test/global_flask.ts index b9a6ce98..fe859f39 100644 --- a/test/global_flask.ts +++ b/test/global_flask.ts @@ -15,6 +15,7 @@ import { export const mochaHooks = { async beforeAll(this: Mocha.Context): Promise { + this.timeout(100000); const ethereum = await startLocalEthereum({ wallet: { mnemonic: LOCAL_PREFUNDED_MNEMONIC,