diff --git a/packages/snaps-jest/README.md b/packages/snaps-jest/README.md index 8392da80cb..9365a38da1 100644 --- a/packages/snaps-jest/README.md +++ b/packages/snaps-jest/README.md @@ -329,6 +329,39 @@ describe('MySnap', () => { }); ``` +### `snap.onKeyringRequest` + +The `onKeyringRequest` function can be used to process keyring request. It takes +few arguments, which are similar to a JSON-RPC request object. It returns +a promise that resolves to the response from the keyring request handler. + +```js +import { installSnap } from '@metamask/snaps-jest'; + +describe('onKeyringRequest', () => { + it('sends keyring request', async () => { + const { onKeyringRequest } = await installSnap(); + + const response = await onKeyringRequest({ + origin: 'https://metamask.github.io', + params: { + options: { + privateKey: 'foo-bar', + }, + }, + method: 'keyring_createAccount', + }); + + expect(response).toBe({ + /* Add expected result here */ + }); + }); +}); +``` + +It returns an object with a response, and some additional metadata, which can be +checked using the [Jest matchers](#jest-matchers): + ### Jest matchers `@metamask/snaps-jest` includes a set of Jest matchers that can be used to diff --git a/packages/snaps-jest/src/helpers.test.tsx b/packages/snaps-jest/src/helpers.test.tsx index 87b9400715..6be9793850 100644 --- a/packages/snaps-jest/src/helpers.test.tsx +++ b/packages/snaps-jest/src/helpers.test.tsx @@ -731,6 +731,46 @@ describe('installSnap', () => { }); }); + describe('onKeyringRequest', () => { + it('sends a keyring request and returns the result', async () => { + jest.spyOn(console, 'log').mockImplementation(); + + const { snapId, close: closeServer } = await getMockServer({ + sourceCode: ` + module.exports.onKeyringRequest = async ({ origin, request }) => { + return request; + } + `, + }); + + const { onKeyringRequest, close } = await installSnap(snapId); + const response = await onKeyringRequest({ + origin: 'metamask.io', + params: {}, + method: 'keyring_listAccounts', + }); + + expect(response).toStrictEqual( + expect.objectContaining({ + response: { + result: { + params: {}, + id: 1, + method: 'keyring_listAccounts', + jsonrpc: '2.0', + }, + }, + }), + ); + + // `close` is deprecated because the Jest environment will automatically + // close the Snap when the test finishes. However, we still need to close + // the Snap in this test because it's run outside the Jest environment. + await close(); + await closeServer(); + }); + }); + describe('mockJsonRpc', () => { it('mocks a JSON-RPC method', async () => { jest.spyOn(console, 'log').mockImplementation(); diff --git a/packages/snaps-jest/src/helpers.ts b/packages/snaps-jest/src/helpers.ts index 09002ae37a..061e9cc2b3 100644 --- a/packages/snaps-jest/src/helpers.ts +++ b/packages/snaps-jest/src/helpers.ts @@ -178,6 +178,7 @@ export async function installSnap< onCronjob, runCronjob, onHomePage, + onKeyringRequest, mockJsonRpc, close, } = await getEnvironment().installSnap(...resolvedOptions); @@ -190,6 +191,7 @@ export async function installSnap< onCronjob, runCronjob, onHomePage, + onKeyringRequest, mockJsonRpc, close: async () => { log('Closing execution service.'); diff --git a/packages/snaps-simulation/src/helpers.test.tsx b/packages/snaps-simulation/src/helpers.test.tsx index 1c04e36b19..9983b479cc 100644 --- a/packages/snaps-simulation/src/helpers.test.tsx +++ b/packages/snaps-simulation/src/helpers.test.tsx @@ -465,6 +465,45 @@ describe('helpers', () => { }); }); + describe('onKeyringRequest', () => { + it('sends a keyring request and returns the result', async () => { + jest.spyOn(console, 'log').mockImplementation(); + + const { snapId, close: closeServer } = await getMockServer({ + sourceCode: ` + module.exports.onKeyringRequest = async ({ origin, request }) => { + return { success: true }; + } + `, + }); + + const { onKeyringRequest, close } = await installSnap(snapId); + const response = await onKeyringRequest({ + origin: 'metamask.io', + params: { + foo: 'bar', + }, + method: 'keyring_createAccount', + }); + + expect(response).toStrictEqual( + expect.objectContaining({ + response: { + result: { + success: true, + }, + }, + }), + ); + + // `close` is deprecated because the Jest environment will automatically + // close the Snap when the test finishes. However, we still need to close + // the Snap in this test because it's run outside the Jest environment. + await close(); + await closeServer(); + }); + }); + describe('mockJsonRpc', () => { it('mocks a JSON-RPC method', async () => { jest.spyOn(console, 'log').mockImplementation(); diff --git a/packages/snaps-simulation/src/helpers.ts b/packages/snaps-simulation/src/helpers.ts index 66c653bd29..ec71034377 100644 --- a/packages/snaps-simulation/src/helpers.ts +++ b/packages/snaps-simulation/src/helpers.ts @@ -16,10 +16,12 @@ import { import type { CronjobOptions, JsonRpcMockOptions, + KeyringOptions, RequestOptions, SignatureOptions, SnapRequest, SnapResponseWithInterface, + SnapResponseWithoutInterface, TransactionOptions, } from './types'; @@ -114,6 +116,16 @@ export type SnapHelpers = { */ onHomePage(): Promise; + /** + * Send a keyring request to the Snap. + * + * @param keyringRequest - Keyring request. + * @returns The response. + */ + onKeyringRequest( + keyringRequest: KeyringOptions, + ): Promise; + /** * Mock a JSON-RPC request. This will cause the snap to respond with the * specified response when a request with the specified method is sent. @@ -216,6 +228,24 @@ export function getHelpers({ }); }; + const onKeyringRequest = async ( + request: KeyringOptions, + ): Promise => { + log('Sending keyring request %o.', request); + + const response = await handleRequest({ + snapId, + store, + executionService, + runSaga, + controllerMessenger, + handler: HandlerType.OnKeyringRequest, + request, + }); + + return response; + }; + return { request: (request) => { log('Sending request %o.', request); @@ -234,6 +264,8 @@ export function getHelpers({ onTransaction, sendTransaction: onTransaction, + onKeyringRequest, + onSignature: async ( request: unknown, ): Promise => { diff --git a/packages/snaps-simulation/src/types.ts b/packages/snaps-simulation/src/types.ts index 8678308634..639fe5e626 100644 --- a/packages/snaps-simulation/src/types.ts +++ b/packages/snaps-simulation/src/types.ts @@ -62,6 +62,11 @@ export type CronjobOptions = Omit; */ export type TransactionOptions = Infer; +/** + * The options to use for keyring requests. + */ +export type KeyringOptions = RequestOptions; + /** * The options to use for signature requests. * @@ -405,6 +410,16 @@ export type Snap = { */ onHomePage(): Promise; + /** + * Send a keyring to the Snap. + * + * @param keyringRequest - Keyring request options. + * @returns The response. + */ + onKeyringRequest( + keyringRequest: KeyringOptions, + ): Promise; + /** * Mock a JSON-RPC request. This will cause the snap to respond with the * specified response when a request with the specified method is sent.