Jest API and options
You can use the @metamask/snaps-jest
package for end-to-end Snaps testing.
This reference describes the available API methods, Jest matchers,
and options.
API methods
installSnap
Installs a Snap in the browser. We recommend using this method in each test to ensure that it starts with a clean slate.
Parameters
By default, if the built-in server is enabled, installSnap
installs the Snap from the built-in server.
Otherwise, you must specify a Snap ID to install.
Returns
An object with functions that can be used to interact with the Snap.
Example
import { installSnap } from '@metamask/snaps-jest';
describe('MySnap', () => {
it('should do something', async () => {
await installSnap(/* optional Snap ID */);
// ...
});
});
request
Sends a JSON-RPC request to the Snap.
Parameters
A JSON-RPC request object with an addition optional origin
property.
Returns
A promise that resolves to the response from the onRpcRequest
function,
which can be checked using Jest matchers.
Example
import { installSnap } from '@metamask/snaps-jest';
describe('MySnap', () => {
it('should do something', async () => {
const { request } = await installSnap(/* optional snap ID */);
const response = await request({
origin: 'http://localhost:8080',
method: 'foo',
params: [],
});
/* Check the response using Jest matchers. Since the response
* is a standard JSON-RPC response, you can use any standard
* Jest matchers to check it, including snapshot matchers. */
expect(response).toRespondWith('bar');
expect(response).not.toRespondWithError('baz');
expect(response).toMatchSnapshot();
});
});
sendTransaction
Sends a transaction to the Snap.
Parameters
A transaction object with the following properties:
origin
chainId
from
to
value
data
gasLimit
maxFeePerGas
maxPriorityFeePerGas
nonce
All properties are optional. The addresses are randomly generated by default. Most values can be specified as a hex string or a decimal number.
Returns
An object with the user interface that was shown by the Snap, in the
onTransaction
function.
Example
import { installSnap } from '@metamask/snaps-jest';
import { panel, text } from '@metamask/snaps-sdk';
describe('MySnap', () => {
it('should do something', async () => {
const { sendTransaction } = await installSnap(/* optional Snap ID */);
const response = await sendTransaction({
value: '0x0',
data: '0x',
gasLimit: '0x5208',
maxFeePerGas: '0x5208',
maxPriorityFeePerGas: '0x5208',
nonce: '0x0',
});
expect(response).toRender(panel([text('Hello, world!')]));
});
});
runCronJob
Runs a cronjob in the Snap.
The request is normally specified in the Snap manifest file under the
endowment:cronjob
permission, but this method alows you to
run cronjobs that are not specified in the manifest as well.
Parameters
A JSON-RPC request object.
Returns
A promise that resolves to the response from the onCronjob
function,
which can be checked using Jest matchers.
Example
import { installSnap } from '@metamask/snaps-jest';
describe('MySnap', () => {
it('should do something', async () => {
const { runCronjob } = await installSnap(/* optional snap ID */);
const response = await runCronjob({
method: 'foo',
params: [],
});
// Check the response using Jest matchers
expect(response).toRespondWith('bar');
expect(response).not.toRespondWithError('baz');
});
});
close
Closes the page the test is running on. This is useful for cleaning up after a test, and is not required. It can potentially speed up your tests, since it prevents too many pages from being open at the same time.
Example
import { installSnap } from '@metamask/snaps-jest';
describe('MySnap', () => {
it('should do something', async () => {
const { close } = await installSnap(/* optional Snap ID */);
// ...
await close();
});
});
getInterface
If your Snap uses snap_dialog
to show user interfaces, you can use the
request.getInterface
method to interact with the user interfaces.
This method is present on the return value of the request
method.
Returns
This method waits for the user interface to be shown, and returns an object with functions that can be used to interact with the user interface.
Example
import { installSnap } from '@metamask/snaps-jest';
import { text } from '@metamask/snaps-sdk';
import { assert } from '@metamask/utils';
describe('MySnap', () => {
it('should do something', async () => {
const { request } = await installSnap(/* optional Snap ID */);
// Note: You cannot resolve the promise yet!
const response = request({
method: 'foo',
});
const ui = await response.getInterface();
// This is useful if you're using TypeScript, since it infers the type
// of the user interface.
assert(ui.type === 'alert');
expect(ui).toRender(text('Hello, world!'));
// "Click" the OK button.
await ui.ok();
// Now you can resolve the promise.
const result = await response;
expect(result).toRespondWith('bar');
});
});
mock
Mocks the response of any network request made by the Snap through the
endowment:network-access
permission.
Parameters
An object with the following properties:
url
- The URL of the request. This can be a string or a regular expression.partial
- If set totrue
, the request is mocked if the URL starts with the given URL. This option is ignored if a regular expression is provided to theurl
option. The default isfalse
.response
- An object with the response.status
- The status code of the response. The default is200
.body
- The body of the response. The default is an empty string.headers
- An object with the headers of the response. By default, this uses headers that enable CORS.contentType
- The content type of the response. The default istext/plain
.
All properties are optional except url
.
Returns
An object with an unmock
function that can be used to remove the mock.
If the mock is not removed, it remains active only for the rest of the Snap installation, so it does
not affect other tests with a new Snap installation.
Example
import { installSnap } from '@metamask/snaps-jest';
describe('MySnap', () => {
it('should do something', async () => {
const { mock, request } = await installSnap(/* optional Snap ID */);
const { unmock } = mock({
url: 'https://example.com',
status: 200,
body: 'Hello, world!',
});
// ...
unmock();
});
});
Jest matchers
@metamask/snaps-jest
includes the following Jest matchers that you can use to assert that a
response from a Snap matches an expected value:
toRespondWith(expectedResponse)
- Checks if a response matches an expected response. This matcher checks theresult
property of the response. If the response is an error, this matcher fails.toRespondWithError(expectedError)
- Checks if a response matches an expected error. This matcher checks theerror
property of the response. If the response is not an error, this matcher fails.toSendNotification(notificationText)
- Checks if a Snap sent a notification.toRender(expectedInterface)
- Checks if a Snap rendered an interface. This is useful for testing the user interface of a Snap, either for asnap_dialog
or a user interface rendered by the transaction insights API.
Options
You can pass the following options when configuring @metamask/snaps-jest
.
All options are optional.
browser
Options for the browser used to run the tests.
The headless
browser option enables or disables running the browser in headless mode.
Set this option to false
to see the browser window while the tests are running.
The default is true
.
Disabling browser.headless
requires you to have a graphical environment available, so we do not
recommend this for CI environments.
It can be useful for debugging in conjunction with the keepAlive
option.
Example
module.exports = {
preset: '@metamask/snaps-jest',
testEnvironmentOptions: {
browser: {
headless: false,
},
},
};
executionEnvironmentUrl
The URL of the execution environment to use for testing. This is the URL to be loaded by the Snaps simulator in the tests. The default is the URL of the built-in HTTP server included in this package.
This option is intended for advanced use cases. In most cases, you do not need to configure this option.
Example
module.exports = {
preset: '@metamask/snaps-jest',
testEnvironmentOptions: {
executionEnvironmentUrl: 'http://localhost:8080',
},
};
keepAlive
Enables or disables keeping the Jest environment running after the tests have finished.
When set to true
, the Jest process does not exit on its own, and must be manually terminated.
The default is false
.
This is useful for debugging, but should not be used in CI environments.
Example
module.exports = {
preset: '@metamask/snaps-jest',
testEnvironmentOptions: {
keepAlive: true,
},
};
server
Options for the built-in HTTP server included with this package. This server serves the execution environment, simulator, and the Snap bundle during tests.
The server options are:
enabled
- Enables or disables the built-in HTTP server. Set tofalse
to use your own HTTP server, which you can specify using theexecutionEnvironmentUrl
andsimulatorUrl
options. The default istrue
.port
- The port to use for the built-in HTTP server. The default is a random available (unprivileged) port.root
- The path to the root directory to serve the Snap files from. This is useful if you want to serve the Snap files from a different directory than the one Jest is running from. The default is the current working directory.
Example
module.exports = {
preset: '@metamask/snaps-jest',
testEnvironmentOptions: {
server: {
enabled: false,
port: 8080,
root: '/path/to/snap/files',
},
},
};
simulatorUrl
The URL of the simulator to use for testing. This is the URL to be loaded in the browser when running tests. The default is the URL of the built-in HTTP server included with this package.
This option is intended for advanced use cases. In most cases, you do not need to configure this option.
Example
module.exports = {
preset: '@metamask/snaps-jest',
testEnvironmentOptions: {
simulatorUrl: 'http://localhost:8081',
},
};