@metamask/snaps-jest
Version:
A Jest preset for end-to-end testing MetaMask Snaps, including a Jest environment, and a set of Jest matchers
1 lines • 14 kB
Source Map (JSON)
{"version":3,"file":"helpers.cjs","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AbstractExecutionService } from '@metamask/snaps-controllers';\nimport type {\n AccountSelectorState,\n AssetSelectorState,\n SnapId,\n} from '@metamask/snaps-sdk';\nimport type {\n InstallSnapOptions,\n SimulationAccount,\n SimulationAsset,\n Snap,\n} from '@metamask/snaps-simulation';\nimport { logInfo } from '@metamask/snaps-utils';\nimport { assert, createModuleLogger } from '@metamask/utils';\nimport type {\n CaipAccountId,\n CaipAssetType,\n CaipChainId,\n} from '@metamask/utils';\n\nimport {\n rootLogger,\n getEnvironment,\n getPseudoRandomUuidGenerator,\n getScopesFromAssets,\n} from './internals';\n\nconst log = createModuleLogger(rootLogger, 'helpers');\n\n/**\n * Get the options for {@link installSnap}.\n *\n * @param snapId - The ID of the Snap, or the options.\n * @param options - The options, if any.\n * @returns The options.\n */\nfunction getOptions<\n Service extends new (\n ...args: any[]\n ) => InstanceType<typeof AbstractExecutionService>,\n>(\n snapId: SnapId | Partial<InstallSnapOptions<Service>> | undefined,\n options: Partial<InstallSnapOptions<Service>>,\n): [SnapId | undefined, Partial<InstallSnapOptions<Service>>] {\n if (typeof snapId === 'object') {\n return [undefined, snapId];\n }\n\n return [snapId, options];\n}\n\n/**\n * Load a snap into the environment. This is the main entry point for testing\n * snaps: It returns a {@link Snap} object that can be used to interact with the\n * snap.\n *\n * @example\n * import { installSnap } from '@metamask/snaps-jest';\n *\n * describe('My Snap', () => {\n * it('should do something', async () => {\n * const { request } = await installSnap('local:my-snap');\n * const response = await request({\n * method: 'foo',\n * params: ['bar'],\n * });\n * expect(response).toRespondWith('bar');\n * });\n * });\n * @returns The snap.\n * @throws If the built-in server is not running, and no snap ID is provided.\n */\nexport async function installSnap(): Promise<Snap>;\n\n/**\n * Load a snap into the environment. This is the main entry point for testing\n * snaps: It returns a {@link Snap} object that can be used to interact with the\n * snap.\n *\n * @example\n * import { installSnap } from '@metamask/snaps-jest';\n *\n * describe('My Snap', () => {\n * it('should do something', async () => {\n * const { request } = await installSnap('local:my-snap');\n * const response = await request({\n * method: 'foo',\n * params: ['bar'],\n * });\n * expect(response).toRespondWith('bar');\n * });\n * });\n * @param options - The options to use.\n * @param options.executionService - The execution service to use. Defaults to\n * {@link NodeThreadExecutionService}. You do not need to provide this unless\n * you are testing a custom execution service.\n * @param options.executionServiceOptions - The options to use when creating the\n * execution service, if any. This should only include options specific to the\n * provided execution service.\n * @param options.options - The simulation options.\n * @returns The snap.\n * @throws If the built-in server is not running, and no snap ID is provided.\n */\nexport async function installSnap<\n Service extends new (\n ...args: any[]\n ) => InstanceType<typeof AbstractExecutionService>,\n>(options: Partial<InstallSnapOptions<Service>>): Promise<Snap>;\n\n/**\n * Load a snap into the environment. This is the main entry point for testing\n * snaps: It returns a {@link Snap} object that can be used to interact with the\n * snap.\n *\n * @example\n * import { installSnap } from '@metamask/snaps-jest';\n *\n * describe('My Snap', () => {\n * it('should do something', async () => {\n * const { request } = await installSnap('local:my-snap');\n * const response = await request({\n * method: 'foo',\n * params: ['bar'],\n * });\n * expect(response).toRespondWith('bar');\n * });\n * });\n * @param snapId - The ID of the snap, including the prefix (`local:`). Defaults\n * to the URL of the built-in server, if it is running. This supports both\n * local snap IDs and NPM snap IDs.\n * @param options - The options to use.\n * @param options.executionService - The execution service to use. Defaults to\n * {@link NodeThreadExecutionService}. You do not need to provide this unless\n * you are testing a custom execution service.\n * @param options.executionServiceOptions - The options to use when creating the\n * execution service, if any. This should only include options specific to the\n * provided execution service.\n * @param options.options - The simulation options.\n * @returns The snap.\n * @throws If the built-in server is not running, and no snap ID is provided.\n */\nexport async function installSnap<\n Service extends new (\n ...args: any[]\n ) => InstanceType<typeof AbstractExecutionService>,\n>(\n snapId: SnapId,\n options?: Partial<InstallSnapOptions<Service>>,\n): Promise<Snap>;\n\n/**\n * Load a snap into the environment. This is the main entry point for testing\n * snaps: It returns a {@link Snap} object that can be used to interact with the\n * snap.\n *\n * @example\n * import { installSnap } from '@metamask/snaps-jest';\n *\n * describe('My Snap', () => {\n * it('should do something', async () => {\n * const { request } = await installSnap('local:my-snap');\n * const response = await request({\n * method: 'foo',\n * params: ['bar'],\n * });\n * expect(response).toRespondWith('bar');\n * });\n * });\n * @param snapId - The ID of the snap, including the prefix (`local:`). Defaults\n * to the URL of the built-in server, if it is running. This supports both\n * local snap IDs and NPM snap IDs.\n * @param options - The options to use.\n * @param options.executionService - The execution service to use. Defaults to\n * {@link NodeThreadExecutionService}. You do not need to provide this unless\n * you are testing a custom execution service.\n * @param options.executionServiceOptions - The options to use when creating the\n * execution service, if any. This should only include options specific to the\n * provided execution service.\n * @param options.options - The simulation options.\n * @returns The snap.\n * @throws If the built-in server is not running, and no snap ID is provided.\n */\nexport async function installSnap<\n Service extends new (\n ...args: any[]\n ) => InstanceType<typeof AbstractExecutionService>,\n>(\n snapId?: SnapId | Partial<InstallSnapOptions<Service>>,\n options: Partial<InstallSnapOptions<Service>> = {},\n): Promise<Snap> {\n const resolvedOptions = getOptions(snapId, options);\n\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n /* eslint-disable @typescript-eslint/unbound-method */\n const {\n request,\n onTransaction,\n sendTransaction,\n onSignature,\n onCronjob,\n runCronjob,\n onBackgroundEvent,\n onHomePage,\n onSettingsPage,\n onKeyringRequest,\n onInstall,\n onUpdate,\n onStart,\n onNameLookup,\n onProtocolRequest,\n onClientRequest,\n mockJsonRpc,\n close,\n } = await getEnvironment().installSnap(...resolvedOptions);\n /* eslint-enable @typescript-eslint/unbound-method */\n\n return {\n request,\n onTransaction,\n sendTransaction,\n onSignature,\n onCronjob,\n runCronjob,\n onBackgroundEvent,\n onHomePage,\n onSettingsPage,\n onKeyringRequest,\n onInstall,\n onUpdate,\n onStart,\n onNameLookup,\n onProtocolRequest,\n onClientRequest,\n mockJsonRpc,\n close: async () => {\n log('Closing execution service.');\n logInfo(\n 'Calling `snap.close()` is deprecated, and will be removed in a future release. Snaps are now automatically closed when the test ends.',\n );\n\n await close();\n },\n };\n}\n\n/**\n * Get the state of an AccountSelector based on a {@link SimulationAccount}.\n *\n * @param account - The {@link SimulationAccount} to get the state from.\n * @returns The state of the AccountSelector.\n */\nexport function getStateFromAccount(\n account: SimulationAccount,\n): AccountSelectorState {\n const { address, scopes } = account;\n return {\n addresses: scopes.map((scope) => `${scope}:${address}`) as CaipAccountId[],\n accountId: account.id,\n };\n}\n\n/**\n * Get the state of an AssetSelector based on a {@link SimulationAsset}.\n *\n * @param id - The Asset id as a CAIP-19 asset type.\n * @param assets - The {@link SimulationAsset} to get the state from.\n * @returns The state of the AssetSelector.\n */\nexport function getStateFromAsset(\n id: CaipAssetType,\n assets: Record<CaipAssetType, SimulationAsset>,\n): AssetSelectorState {\n const asset = assets[id];\n\n assert(asset, `Asset with ID \"${id}\" not found in simulation assets.`);\n\n const { symbol, name } = asset;\n\n return {\n asset: id,\n symbol,\n name,\n };\n}\n\n/**\n * Generate a pseudo-random UUID.\n *\n * @returns A pseudo-random UUID string.\n */\nconst getPseudoRandomUuid = getPseudoRandomUuidGenerator();\n\n/**\n * The base options for the {@link getMockAccount} function.\n */\nexport type BaseMockAccountOptions = {\n /**\n * The address of the account.\n */\n address: string;\n\n /**\n * The ID of the account. If not provided, a pseudo-random UUID will be\n * generated.\n */\n id?: string;\n\n /**\n * Whether the account is selected by default.\n */\n selected?: boolean;\n\n /**\n * Whether the account is owned by the snap.\n */\n owned?: boolean;\n};\n\n/**\n * Options for creating a mock account with assets or scopes. If `scopes` are\n * not provided, they will be derived from the `assets`.\n *\n * @see BaseMockAccountOptions\n */\nexport type MockAccountOptionsWithAssets = BaseMockAccountOptions & {\n /**\n * The assets associated with the account. These should be in CAIP format.\n */\n assets: CaipAssetType[];\n\n /**\n * The scopes associated with the account. If not provided, they will be\n * derived from the `assets`.\n */\n scopes?: CaipChainId[];\n};\n\n/**\n * Options for creating a mock account with scopes, and optionally assets.\n *\n * @see BaseMockAccountOptions\n */\nexport type MockAccountOptionsWithScopes = BaseMockAccountOptions & {\n /**\n * The scopes associated with the account. These should be in CAIP format.\n */\n scopes: CaipChainId[];\n\n /**\n * The assets associated with the account. If not provided, it will default\n * to an empty array.\n */\n assets?: CaipAssetType[];\n};\n\nexport type GetMockAccountOptions =\n | MockAccountOptionsWithAssets\n | MockAccountOptionsWithScopes;\n\n/**\n * Get a mock account object for testing purposes.\n *\n * @param options - The options for creating the mock account.\n * @param options.address - The address of the account.\n * @param options.scopes - The scopes associated with the account, in CAIP\n * format. If not provided, they will be derived from the `assets`.\n * @param options.assets - The assets associated with the account, in CAIP\n * format. If not provided, it will default to an empty array.\n * @param options.selected - Whether the account is selected by default.\n * @param options.owned - Whether the account is owned by the snap.\n * @param options.id - The ID of the account. If not provided, a pseudo-random\n * UUID will be generated.\n * @returns A mock account object with the specified properties.\n */\nexport function getMockAccount({\n address,\n assets = [],\n selected = false,\n owned = false,\n id = getPseudoRandomUuid(),\n scopes = getScopesFromAssets(assets),\n}: GetMockAccountOptions): SimulationAccount {\n return {\n address,\n id,\n scopes,\n selected,\n owned,\n assets,\n };\n}\n"]}