From 7b60d828b4de870e05f7678bf412d7929dc0d670 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 18 Dec 2024 09:59:46 +0100 Subject: [PATCH] Add tests to example --- .../packages/manage-state/src/index.test.ts | 238 ++++++++++++++++++ .../src/methods/specifications.ts | 2 +- packages/snaps-simulation/src/simulation.ts | 21 +- 3 files changed, 259 insertions(+), 2 deletions(-) diff --git a/packages/examples/packages/manage-state/src/index.test.ts b/packages/examples/packages/manage-state/src/index.test.ts index f2a61df6f6..09182c23c7 100644 --- a/packages/examples/packages/manage-state/src/index.test.ts +++ b/packages/examples/packages/manage-state/src/index.test.ts @@ -20,6 +20,244 @@ describe('onRpcRequest', () => { }); }); + describe('setState', () => { + it('sets the state to the params', async () => { + const { request } = await installSnap(); + + expect( + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + }, + }), + ).toRespondWith(null); + + expect( + await request({ + method: 'getState', + }), + ).toRespondWith({ + items: ['foo'], + }); + }); + + it('sets the state at a specific key', async () => { + const { request } = await installSnap(); + + expect( + await request({ + method: 'setState', + params: { + value: 'foo', + key: 'nested.key', + }, + }), + ).toRespondWith(null); + + expect( + await request({ + method: 'getState', + }), + ).toRespondWith({ + nested: { + key: 'foo', + }, + }); + }); + + it('sets the unencrypted state to the params', async () => { + const { request } = await installSnap(); + + expect( + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + encrypted: false, + }, + }), + ).toRespondWith(null); + + expect( + await request({ + method: 'getState', + }), + ).toRespondWith(null); + + expect( + await request({ + method: 'getState', + params: { + encrypted: false, + }, + }), + ).toRespondWith({ + items: ['foo'], + }); + }); + + it('throws if the state is not an object and no key is specified', async () => { + const { request } = await installSnap(); + + const response = await request({ + method: 'setState', + params: { + value: 'foo', + }, + }); + + expect(response).toRespondWithError( + expect.objectContaining({ + code: -32602, + message: + 'Invalid params: Value must be an object if key is not provided.', + }), + ); + }); + }); + + describe('getState', () => { + it('returns `null` if no state has been set', async () => { + const { request } = await installSnap(); + + const response = await request({ + method: 'getState', + }); + + expect(response).toRespondWith(null); + }); + + it('returns the state', async () => { + const { request } = await installSnap(); + + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + }, + }); + + const response = await request({ + method: 'getState', + }); + + expect(response).toRespondWith({ + items: ['foo'], + }); + }); + + it('returns the state at a specific key', async () => { + const { request } = await installSnap(); + + await request({ + method: 'setState', + params: { + value: { + nested: { + key: 'foo', + }, + }, + }, + }); + + const response = await request({ + method: 'getState', + params: { + key: 'nested.key', + }, + }); + + expect(response).toRespondWith('foo'); + }); + + it('returns the unencrypted state', async () => { + const { request } = await installSnap(); + + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + encrypted: false, + }, + }); + + const response = await request({ + method: 'getState', + params: { + encrypted: false, + }, + }); + + expect(response).toRespondWith({ + items: ['foo'], + }); + }); + }); + + describe('clearState', () => { + it('clears the state', async () => { + const { request } = await installSnap(); + + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + }, + }); + + await request({ + method: 'clearState', + }); + + const response = await request({ + method: 'getState', + }); + + expect(response).toRespondWith(null); + }); + + it('clears the unencrypted state', async () => { + const { request } = await installSnap(); + + await request({ + method: 'setState', + params: { + value: { + items: ['foo'], + }, + encrypted: false, + }, + }); + + await request({ + method: 'clearState', + params: { + encrypted: false, + }, + }); + + const response = await request({ + method: 'getState', + params: { + encrypted: false, + }, + }); + + expect(response).toRespondWith(null); + }); + }); + describe('legacy_setState', () => { it('sets the state to the params', async () => { const { request } = await installSnap(); diff --git a/packages/snaps-simulation/src/methods/specifications.ts b/packages/snaps-simulation/src/methods/specifications.ts index 45c5ab4cf5..ed08afeada 100644 --- a/packages/snaps-simulation/src/methods/specifications.ts +++ b/packages/snaps-simulation/src/methods/specifications.ts @@ -59,7 +59,7 @@ export function resolve(result: unknown) { * resolve with `undefined`. * @returns The function implementation. */ -export function asyncResolve(result?: unknown) { +export function asyncResolve(result?: Type) { return async () => result; } diff --git a/packages/snaps-simulation/src/simulation.ts b/packages/snaps-simulation/src/simulation.ts index 94737b0cd9..cf84f7f7a2 100644 --- a/packages/snaps-simulation/src/simulation.ts +++ b/packages/snaps-simulation/src/simulation.ts @@ -38,7 +38,7 @@ import { getSnapFile } from './files'; import type { SnapHelpers } from './helpers'; import { getHelpers } from './helpers'; import { resolveWithSaga } from './interface'; -import { getEndowments } from './methods'; +import { asyncResolve, getEndowments } from './methods'; import { getPermittedClearSnapStateMethodImplementation, getPermittedGetSnapStateMethodImplementation, @@ -123,6 +123,22 @@ export type RestrictedMiddlewareHooks = { }; export type PermittedMiddlewareHooks = { + /** + * A hook that gets whether the requesting origin has a given permission. + * + * @param permissionName - The name of the permission to check. + * @returns Whether the origin has the permission. + */ + hasPermission: (permissionName: string) => boolean; + + /** + * A hook that returns a promise that resolves once the extension is unlocked. + * + * @param shouldShowUnlockRequest - Whether to show the unlock request. + * @returns A promise that resolves once the extension is unlocked. + */ + getUnlockPromise: (shouldShowUnlockRequest: boolean) => Promise; + /** * A hook that returns the Snap's auxiliary file for the given path. This hook * is bound to the Snap ID. @@ -372,6 +388,9 @@ export function getPermittedHooks( runSaga: RunSagaFunction, ): PermittedMiddlewareHooks { return { + hasPermission: () => true, + getUnlockPromise: asyncResolve(), + getSnapFile: async (path: string, encoding: AuxiliaryFileEncoding) => await getSnapFile(snapFiles.auxiliaryFiles, path, encoding),