Skip to content

Commit

Permalink
Extract simulation part of snaps-jest to separate package (MetaMask…
Browse files Browse the repository at this point in the history
…#2727)

This extracts all simulation logic from `snaps-jest` to a new package
`snaps-simulation` (not to be confused with `snaps-simulator`). This
makes it possible to use the simulation framework outside of Jest, for
example to run a Snap in any Node.js application. It will also enable
removing the simulation framework used by `snaps-simulator` to reduce
code duplication, and improve maintainability.
  • Loading branch information
Mrtenz authored Sep 17, 2024
1 parent bea39ff commit a123418
Show file tree
Hide file tree
Showing 88 changed files with 1,357 additions and 252 deletions.
19 changes: 3 additions & 16 deletions packages/snaps-jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,15 @@
"@jest/environment": "^29.5.0",
"@jest/expect": "^29.5.0",
"@jest/globals": "^29.5.0",
"@metamask/base-controller": "^6.0.2",
"@metamask/eth-json-rpc-middleware": "^14.0.0",
"@metamask/json-rpc-engine": "^9.0.2",
"@metamask/json-rpc-middleware-stream": "^8.0.2",
"@metamask/key-tree": "^9.1.2",
"@metamask/permission-controller": "^11.0.0",
"@metamask/phishing-controller": "^12.0.2",
"@metamask/snaps-controllers": "workspace:^",
"@metamask/snaps-execution-environments": "workspace:^",
"@metamask/snaps-rpc-methods": "workspace:^",
"@metamask/snaps-sdk": "workspace:^",
"@metamask/snaps-utils": "workspace:^",
"@metamask/snaps-simulation": "workspace:^",
"@metamask/superstruct": "^3.1.0",
"@metamask/utils": "^9.2.1",
"@reduxjs/toolkit": "^1.9.5",
"express": "^4.18.2",
"jest-environment-node": "^29.5.0",
"jest-matcher-utils": "^29.5.0",
"mime": "^3.0.0",
"readable-stream": "^3.6.2",
"redux": "^4.2.1",
"redux-saga": "^1.2.3"
"redux": "^4.2.1"
},
"devDependencies": {
"@jest/types": "^29.6.3",
Expand All @@ -73,11 +60,11 @@
"@metamask/eslint-config-jest": "^12.1.0",
"@metamask/eslint-config-nodejs": "^12.1.0",
"@metamask/eslint-config-typescript": "^12.1.0",
"@metamask/snaps-utils": "workspace:^",
"@swc/core": "1.3.78",
"@swc/jest": "^0.2.26",
"@ts-bridge/cli": "^0.5.1",
"@types/jest": "^27.5.1",
"@types/mime": "^3.0.0",
"@types/semver": "^7.5.0",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^6.21.0",
Expand Down
10 changes: 7 additions & 3 deletions packages/snaps-jest/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import type {
} from '@jest/environment';
import type { AbstractExecutionService } from '@metamask/snaps-controllers';
import type { SnapId } from '@metamask/snaps-sdk';
import type {
InstalledSnap,
InstallSnapOptions,
} from '@metamask/snaps-simulation';
import { installSnap } from '@metamask/snaps-simulation';
import { assert, createModuleLogger } from '@metamask/utils';
import type { Server } from 'http';
import NodeEnvironment from 'jest-environment-node';
import type { AddressInfo } from 'net';

import type { InstalledSnap, InstallSnapOptions } from './internals';
import { handleInstallSnap, rootLogger, startServer } from './internals';
import { rootLogger, startServer } from './internals';
import type { SnapsEnvironmentOptions } from './options';
import { getOptions } from './options';

Expand Down Expand Up @@ -88,7 +92,7 @@ export class SnapsEnvironment extends NodeEnvironment {
options: Partial<InstallSnapOptions<Service>> = {},
) {
await this.#instance?.executionService.terminateAllSnaps();
this.#instance = await handleInstallSnap(snapId as SnapId, options);
this.#instance = await installSnap(snapId as SnapId, options);
return this.#instance;
}

Expand Down
18 changes: 9 additions & 9 deletions packages/snaps-jest/src/helpers.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { NodeProcessExecutionService } from '@metamask/snaps-controllers/node';
import { DialogType } from '@metamask/snaps-sdk';
import { Text } from '@metamask/snaps-sdk/jsx';
import { getSnapManifest } from '@metamask/snaps-utils/test-utils';

import type { InstallSnapOptions } from '@metamask/snaps-simulation';
import {
assertIsAlertDialog,
assertIsConfirmationDialog,
assertIsPromptDialog,
installSnap,
} from './helpers';
import type { InstallSnapOptions } from './internals';
import { handleInstallSnap } from './internals';
installSnap as simulateSnap,
} from '@metamask/snaps-simulation';
import { getSnapManifest } from '@metamask/snaps-utils/test-utils';

import { installSnap } from './helpers';
import { getMockServer } from './test-utils';

describe('installSnap', () => {
beforeEach(() => {
Object.defineProperty(global, 'snapsEnvironment', {
writable: true,
value: {
installSnap: handleInstallSnap,
installSnap: simulateSnap,
},
});
});
Expand Down Expand Up @@ -215,7 +215,7 @@ describe('installSnap', () => {
writable: true,
value: {
installSnap: async (_: string, options: InstallSnapOptions<any>) => {
return handleInstallSnap(snapId, options);
return simulateSnap(snapId, options);
},
},
});
Expand Down Expand Up @@ -260,7 +260,7 @@ describe('installSnap', () => {
writable: true,
value: {
installSnap: async (_: string, options: InstallSnapOptions<any>) => {
return handleInstallSnap(snapId, options);
return simulateSnap(snapId, options);
},
},
});
Expand Down
123 changes: 10 additions & 113 deletions packages/snaps-jest/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
import type { AbstractExecutionService } from '@metamask/snaps-controllers';
import type { SnapId } from '@metamask/snaps-sdk';
import { DialogType } from '@metamask/snaps-sdk';
import type { FooterElement } from '@metamask/snaps-sdk/jsx';
import { HandlerType, getJsxChildren, logInfo } from '@metamask/snaps-utils';
import { create } from '@metamask/superstruct';
import {
assert,
assertStruct,
createModuleLogger,
hasProperty,
} from '@metamask/utils';

import type { InstallSnapOptions } from '@metamask/snaps-simulation';
import {
rootLogger,
handleRequest,
TransactionOptionsStruct,
getEnvironment,
JsonRpcMockOptionsStruct,
SignatureOptionsStruct,
SnapResponseWithInterfaceStruct,
getElementByType,
} from './internals';
import type { InstallSnapOptions } from './internals';
import {
handleRequest,
TransactionOptionsStruct,
addJsonRpcMock,
removeJsonRpcMock,
} from './internals/simulation/store/mocks';
SnapResponseWithInterfaceStruct,
} from '@metamask/snaps-simulation';
import { HandlerType, logInfo } from '@metamask/snaps-utils';
import { create } from '@metamask/superstruct';
import { assertStruct, createModuleLogger } from '@metamask/utils';

import { rootLogger, getEnvironment } from './internals';
import type {
SnapResponseWithInterface,
CronjobOptions,
JsonRpcMockOptions,
Snap,
SnapResponse,
TransactionOptions,
SnapInterface,
SnapAlertInterface,
SnapInterfaceActions,
SnapConfirmationInterface,
SnapPromptInterface,
DefaultSnapInterface,
DefaultSnapInterfaceWithFooter,
DefaultSnapInterfaceWithPartialFooter,
DefaultSnapInterfaceWithoutFooter,
} from './types';

const log = createModuleLogger(rootLogger, 'helpers');
Expand Down Expand Up @@ -79,89 +59,6 @@ function assertIsResponseWithInterface(
assertStruct(response, SnapResponseWithInterfaceStruct);
}

/**
* Ensure that the actual interface is an alert dialog.
*
* @param ui - The interface to verify.
*/
export function assertIsAlertDialog(
ui: SnapInterface,
): asserts ui is SnapAlertInterface & SnapInterfaceActions {
assert(hasProperty(ui, 'type') && ui.type === DialogType.Alert);
}

/**
* Ensure that the actual interface is a confirmation dialog.
*
* @param ui - The interface to verify.
*/
export function assertIsConfirmationDialog(
ui: SnapInterface,
): asserts ui is SnapConfirmationInterface & SnapInterfaceActions {
assert(hasProperty(ui, 'type') && ui.type === DialogType.Confirmation);
}

/**
* Ensure that the actual interface is a Prompt dialog.
*
* @param ui - The interface to verify.
*/
export function assertIsPromptDialog(
ui: SnapInterface,
): asserts ui is SnapPromptInterface & SnapInterfaceActions {
assert(hasProperty(ui, 'type') && ui.type === DialogType.Prompt);
}

/**
* Ensure that the actual interface is a custom dialog.
*
* @param ui - The interface to verify.
*/
export function assertIsCustomDialog(
ui: SnapInterface,
): asserts ui is DefaultSnapInterface & SnapInterfaceActions {
assert(!hasProperty(ui, 'type'));
}

/**
* Ensure that the actual interface is a custom dialog with a complete footer.
*
* @param ui - The interface to verify.
*/
export function assertCustomDialogHasFooter(
ui: DefaultSnapInterface & SnapInterfaceActions,
): asserts ui is DefaultSnapInterfaceWithFooter & SnapInterfaceActions {
const footer = getElementByType<FooterElement>(ui.content, 'Footer');

assert(footer && getJsxChildren(footer).length === 2);
}

/**
* Ensure that the actual interface is a custom dialog with a partial footer.
*
* @param ui - The interface to verify.
*/
export function assertCustomDialogHasPartialFooter(
ui: DefaultSnapInterface & SnapInterfaceActions,
): asserts ui is DefaultSnapInterfaceWithPartialFooter & SnapInterfaceActions {
const footer = getElementByType<FooterElement>(ui.content, 'Footer');

assert(footer && getJsxChildren(footer).length === 1);
}

/**
* Ensure that the actual interface is a custom dialog without a footer.
*
* @param ui - The interface to verify.
*/
export function assertCustomDialogHasNoFooter(
ui: DefaultSnapInterface & SnapInterfaceActions,
): asserts ui is DefaultSnapInterfaceWithoutFooter & SnapInterfaceActions {
const footer = getElementByType<FooterElement>(ui.content, 'Footer');

assert(!footer);
}

/**
* Load a snap into the environment. This is the main entry point for testing
* snaps: It returns a {@link Snap} object that can be used to interact with the
Expand Down
9 changes: 9 additions & 0 deletions packages/snaps-jest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ export { default, default as TestEnvironment } from './environment';
export * from './helpers';
export * from './options';
export * from './types';

export {
assertCustomDialogHasNoFooter,
assertCustomDialogHasPartialFooter,
assertIsAlertDialog,
assertIsConfirmationDialog,
assertIsCustomDialog,
assertIsPromptDialog,
} from '@metamask/snaps-simulation';
3 changes: 0 additions & 3 deletions packages/snaps-jest/src/internals/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
export * from './environment';
export * from './logger';
export * from './request';
export * from './server';
export * from './simulation';
export * from './structs';
5 changes: 4 additions & 1 deletion packages/snaps-jest/src/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import type {
} from '@metamask/snaps-sdk';
import type { JSXElement } from '@metamask/snaps-sdk/jsx';
import { isJSXElementUnsafe } from '@metamask/snaps-sdk/jsx';
import {
InterfaceStruct,
SnapResponseStruct,
} from '@metamask/snaps-simulation';
import {
getJsxElementFromComponent,
serialiseJsx,
Expand All @@ -31,7 +35,6 @@ import {
RECEIVED_COLOR,
} from 'jest-matcher-utils';

import { InterfaceStruct, SnapResponseStruct } from './internals';
import type { SnapResponse } from './types';

/**
Expand Down
1 change: 0 additions & 1 deletion packages/snaps-jest/src/test-utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './jest';
export * from './options';
export * from './response';
export * from './server';
export * from './controller';
3 changes: 3 additions & 0 deletions packages/snaps-jest/src/test-utils/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ export function getMockInterfaceResponse(
content,
clickElement: jest.fn(),
typeInField: jest.fn(),
selectInDropdown: jest.fn(),
selectFromRadioGroup: jest.fn(),
uploadFile: jest.fn(),
};
}
9 changes: 4 additions & 5 deletions packages/snaps-jest/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { NotificationType, EnumToUnion } from '@metamask/snaps-sdk';
import type { JSXElement } from '@metamask/snaps-sdk/jsx';
import type { InferMatching } from '@metamask/snaps-utils';
import type { Infer } from '@metamask/superstruct';
import type { Json, JsonRpcId, JsonRpcParams } from '@metamask/utils';

import type {
SignatureOptionsStruct,
SnapOptionsStruct,
SnapResponseStruct,
TransactionOptionsStruct,
} from '../internals';
} from '@metamask/snaps-simulation';
import type { InferMatching } from '@metamask/snaps-utils';
import type { Infer } from '@metamask/superstruct';
import type { Json, JsonRpcId, JsonRpcParams } from '@metamask/utils';

export type RequestOptions = {
/**
Expand Down
11 changes: 1 addition & 10 deletions packages/snaps-jest/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,11 @@
"./src/**/__snapshots__"
],
"references": [
{
"path": "../snaps-controllers/tsconfig.build.json"
},
{
"path": "../snaps-execution-environments/tsconfig.build.json"
},
{
"path": "../snaps-rpc-methods/tsconfig.build.json"
},
{
"path": "../snaps-sdk/tsconfig.build.json"
},
{
"path": "../snaps-utils/tsconfig.build.json"
"path": "../snaps-simulation/tsconfig.build.json"
}
]
}
11 changes: 1 addition & 10 deletions packages/snaps-jest/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,11 @@
},
"include": ["./src", "package.json"],
"references": [
{
"path": "../snaps-controllers"
},
{
"path": "../snaps-execution-environments"
},
{
"path": "../snaps-rpc-methods"
},
{
"path": "../snaps-sdk"
},
{
"path": "../snaps-utils"
"path": "../snaps-simulation"
}
]
}
Loading

0 comments on commit a123418

Please sign in to comment.