From 17f284903d216121dedd98b0168b9514b0e49b74 Mon Sep 17 00:00:00 2001 From: Anton Lykhoyda Date: Wed, 26 Oct 2022 14:44:44 +0200 Subject: [PATCH] feat: Add invokeSnap method; update installSnap method parameter; (#159) * Add invokeSnap method; update installSnap method parameter; * adjust invokeSnap method after review * Fix params type in invokeSnap method * update invokeSnap Return type * update invokeSnap with generic params * update test expectation * fix lint --- src/snap/index.ts | 1 + src/snap/install.ts | 5 ++-- src/snap/invokeSnap.ts | 29 +++++++++++++++++++++ src/types.ts | 7 ++++++ test/flask/snaps.spec.ts | 54 ++++++++++++++++++++++++++++++---------- 5 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 src/snap/invokeSnap.ts diff --git a/src/snap/index.ts b/src/snap/index.ts index 0ce34cd3..1bb795ca 100644 --- a/src/snap/index.ts +++ b/src/snap/index.ts @@ -1,2 +1,3 @@ export { flaskOnly } from "./utils"; export { installSnap } from "./install"; +export { invokeSnap } from "./invokeSnap"; diff --git a/src/snap/install.ts b/src/snap/install.ts index e2d298aa..be1ba7d5 100644 --- a/src/snap/install.ts +++ b/src/snap/install.ts @@ -20,12 +20,13 @@ export async function installSnap( hasKeyPermissions: boolean; customSteps?: InstallStep[]; version?: string; - } + }, + installationSnapUrl: string = "https://google.com" ): Promise { flaskOnly(page); //need to open page to access window.ethereum const installPage = await page.browser().newPage(); - await installPage.goto("https://google.com"); + await installPage.goto(installationSnapUrl); const installAction = installPage.evaluate( (opts: { snapId: string; version?: string }) => window.ethereum.request<{ snaps: { [snapId: string]: {} } }>({ diff --git a/src/snap/invokeSnap.ts b/src/snap/invokeSnap.ts new file mode 100644 index 00000000..4dfb5ab6 --- /dev/null +++ b/src/snap/invokeSnap.ts @@ -0,0 +1,29 @@ +import { Page, Serializable } from "puppeteer"; +import { flaskOnly } from "./utils"; + +export async function invokeSnap< + R = unknown, + P extends Serializable = Serializable +>( + page: Page, + snapId: string, + method: string, + params: P +): ReturnType> { + flaskOnly(page); + return page.evaluate( + async (opts: { snapId: string; method: string; params: P }) => { + return window.ethereum.request({ + method: "wallet_invokeSnap", + params: [ + `${opts.snapId}`, + { + method: opts.method, + params: opts.params, + }, + ], + }); + }, + { snapId, method, params } + ); +} diff --git a/src/types.ts b/src/types.ts index eb50f756..06a275ae 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,16 @@ import * as puppeteer from "puppeteer"; +import { MetaMaskInpageProvider } from "@metamask/providers"; import { Path } from "./setup/metaMaskDownloader"; import { RECOMMENDED_METAMASK_VERSION } from "./index"; +declare global { + interface Window { + ethereum: MetaMaskInpageProvider; + } +} + export type LaunchOptions = (OfficialOptions | CustomOptions) & { //install flask (canary) version of metamask. metaMaskFlask?: boolean; diff --git a/test/flask/snaps.spec.ts b/test/flask/snaps.spec.ts index 597d0f92..602ed9be 100644 --- a/test/flask/snaps.spec.ts +++ b/test/flask/snaps.spec.ts @@ -1,8 +1,14 @@ +import { expect } from "chai"; import * as dappeteer from "../../src"; -import { installSnap } from "../../src/snap"; +import { installSnap, invokeSnap } from "../../src/snap"; import { TestContext } from "../constant"; import { Snaps } from "../deploy"; import { toUrl } from "../utils/utils"; +import { clickOnButton } from "../../src/helpers"; + +function getSnapIdByName(testContext: TestContext, snapName: Snaps): string { + return `local:${toUrl(testContext.snapServers[snapName].address())}`; +} describe("snaps", function () { let metamask: dappeteer.Dappeteer; @@ -19,20 +25,16 @@ describe("snaps", function () { }); it("should install base snap from npm", async function (this: TestContext) { - await installSnap( - metamask.page, - "local:" + toUrl(this.snapServers[Snaps.BASE_SNAP].address()), - { - hasPermissions: false, - hasKeyPermissions: false, - } - ); + await installSnap(metamask.page, getSnapIdByName(this, Snaps.BASE_SNAP), { + hasPermissions: false, + hasKeyPermissions: false, + }); }); it("should install permissions snap from npm", async function (this: TestContext) { await installSnap( metamask.page, - "local:" + toUrl(this.snapServers[Snaps.PERMISSIONS_SNAP].address()), + getSnapIdByName(this, Snaps.PERMISSIONS_SNAP), { hasPermissions: true, hasKeyPermissions: false, @@ -41,13 +43,39 @@ describe("snaps", function () { }); it("should install keys snap from npm", async function (this: TestContext) { + await installSnap(metamask.page, getSnapIdByName(this, Snaps.KEYS_SNAP), { + hasPermissions: true, + hasKeyPermissions: true, + }); + }); + + it("should invoke provided snap method", async function (this: TestContext) { + const snapAddress = "http://localhost:8545"; await installSnap( metamask.page, - "local:" + toUrl(this.snapServers[Snaps.KEYS_SNAP].address()), + getSnapIdByName(this, Snaps.PERMISSIONS_SNAP), { hasPermissions: true, - hasKeyPermissions: true, - } + hasKeyPermissions: false, + }, + snapAddress + ); + + const testPage = await metamask.page.browser().newPage(); + await testPage.goto(snapAddress); + + const invokeAction = invokeSnap( + testPage, + getSnapIdByName(this, Snaps.PERMISSIONS_SNAP), + "hello", + { version: "latest" } ); + + await metamask.page.bringToFront(); + await metamask.page.reload(); + + await clickOnButton(metamask.page, "Approve"); + + expect(await invokeAction).to.equal(true); }); });