Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add onInstall and onUpdate to snaps-jest #2849

Merged
merged 7 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,6 @@ This will make it easier for developers to understand the examples, and to use
them as a starting point for their own snaps.

Each snap (if applicable) should have end-to-end tests that test the snap's
functionality. The tests should be located in the `src/index.test.ts` file, and
functionality. The tests should be located in the `src/index.test.tsx` file, and
david0xd marked this conversation as resolved.
Show resolved Hide resolved
make use of `@metamask/snaps-jest` to test the snap. You can have a look at the
existing E2E tests for inspiration.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { SnapConfig } from '@metamask/snaps-cli';

const config: SnapConfig = {
input: './src/index.ts',
input: './src/index.tsx',
server: {
port: 8022,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "TSu0FIqVXvJG6WzqtKPx5kN2fjveQ8EypKCk/jAShmM=",
"shasum": "U+ry3tQOBH2gb+WQYNBYTqKst27YLCCNPNrL3mzwxY8=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
13 changes: 0 additions & 13 deletions packages/examples/packages/lifecycle-hooks/src/index.test.ts

This file was deleted.

41 changes: 41 additions & 0 deletions packages/examples/packages/lifecycle-hooks/src/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, it } from '@jest/globals';
import { installSnap } from '@metamask/snaps-jest';
import { Box, Text } from '@metamask/snaps-sdk/jsx';

describe('onInstall', () => {
it('shows dialog when the snap is installed', async () => {
const { onInstall } = await installSnap();

const response = await onInstall();

const screen = response.getInterface();

expect(screen).toRender(
david0xd marked this conversation as resolved.
Show resolved Hide resolved
<Box>
<Text>
The Snap was installed successfully, and the "onInstall" handler was
called.
</Text>
</Box>,
);
});
});

describe('onUpdate', () => {
it('shows dialog when the snap is updated', async () => {
const { onUpdate } = await installSnap();

const response = await onUpdate();

const screen = response.getInterface();

expect(screen).toRender(
<Box>
<Text>
The Snap was updated successfully, and the "onUpdate" handler was
called.
</Text>
</Box>,
);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { OnInstallHandler, OnUpdateHandler } from '@metamask/snaps-sdk';
import { heading, panel, text } from '@metamask/snaps-sdk';
import { Box, Text } from '@metamask/snaps-sdk/jsx';

/**
* Handle installation of the snap. This handler is called when the snap is
Expand All @@ -9,18 +9,21 @@ import { heading, panel, text } from '@metamask/snaps-sdk';
* as usual.
*
* @see https://docs.metamask.io/snaps/reference/exports/#oninstall
* @returns The JSON-RPC response.
*/
export const onInstall: OnInstallHandler = async () => {
await snap.request({
return await snap.request({
method: 'snap_dialog',
params: {
type: 'alert',
content: panel([
heading('Installation successful'),
text(
'The snap was installed successfully, and the "onInstall" handler was called.',
),
]),
content: (
<Box>
<Text>
The Snap was installed successfully, and the "onInstall" handler was
called.
</Text>
</Box>
),
},
});
};
Expand All @@ -33,18 +36,21 @@ export const onInstall: OnInstallHandler = async () => {
* as usual.
*
* @see https://docs.metamask.io/snaps/reference/exports/#onupdate
* @returns The JSON-RPC response.
*/
export const onUpdate: OnUpdateHandler = async () => {
await snap.request({
return await snap.request({
method: 'snap_dialog',
params: {
type: 'alert',
content: panel([
heading('Update successful'),
text(
'The snap was updated successfully, and the "onUpdate" handler was called.',
),
]),
content: (
<Box>
<Text>
The Snap was updated successfully, and the "onUpdate" handler was
called.
</Text>
</Box>
),
},
});
};
52 changes: 52 additions & 0 deletions packages/snaps-jest/src/helpers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,58 @@ describe('installSnap', () => {
});
});

describe('onInstall', () => {
it('sends onInstall request and returns the result', async () => {
jest.spyOn(console, 'log').mockImplementation();

const { snapId, close: closeServer } = await getMockServer({
sourceCode: `
module.exports.onInstall = async () => {
return { content: { type: 'text', value: 'Hello, world!' } };
};
`,
});

const { onInstall, close } = await installSnap(snapId);
const response = await onInstall();

expect(response).toStrictEqual(
expect.objectContaining({
getInterface: expect.any(Function),
}),
);

await close();
await closeServer();
});
});

describe('onUpdate', () => {
it('sends onUpdate request and returns the result', async () => {
jest.spyOn(console, 'log').mockImplementation();

const { snapId, close: closeServer } = await getMockServer({
sourceCode: `
module.exports.onUpdate = async () => {
return { content: { type: 'text', value: 'Hello, world!' } };
};
`,
});

const { onUpdate, close } = await installSnap(snapId);
const response = await onUpdate();

expect(response).toStrictEqual(
expect.objectContaining({
getInterface: expect.any(Function),
}),
);

await close();
await closeServer();
});
});

describe('mockJsonRpc', () => {
it('mocks a JSON-RPC method', async () => {
jest.spyOn(console, 'log').mockImplementation();
Expand Down
4 changes: 4 additions & 0 deletions packages/snaps-jest/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ export async function installSnap<
runCronjob,
onHomePage,
onKeyringRequest,
onInstall,
onUpdate,
mockJsonRpc,
close,
} = await getEnvironment().installSnap(...resolvedOptions);
Expand All @@ -192,6 +194,8 @@ export async function installSnap<
runCronjob,
onHomePage,
onKeyringRequest,
onInstall,
onUpdate,
mockJsonRpc,
close: async () => {
log('Closing execution service.');
Expand Down
52 changes: 52 additions & 0 deletions packages/snaps-simulation/src/helpers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,58 @@ describe('helpers', () => {
});
});

describe('onInstall', () => {
it('sends a OnInstall request and returns the result', async () => {
jest.spyOn(console, 'log').mockImplementation();

const { snapId, close: closeServer } = await getMockServer({
sourceCode: `
module.exports.onInstall = async () => {
return { content: { type: 'text', value: 'Hello, world!' } };
};
`,
});

const { onInstall, close } = await installSnap(snapId);
const response = await onInstall();

expect(response).toStrictEqual(
expect.objectContaining({
getInterface: expect.any(Function),
}),
);

await close();
await closeServer();
});
});

describe('onUpdate', () => {
it('sends a OnUpdate request and returns the result', async () => {
jest.spyOn(console, 'log').mockImplementation();

const { snapId, close: closeServer } = await getMockServer({
sourceCode: `
module.exports.onUpdate = async () => {
return { content: { type: 'text', value: 'Hello, world!' } };
};
`,
});

const { onUpdate, close } = await installSnap(snapId);
const response = await onUpdate();

expect(response).toStrictEqual(
expect.objectContaining({
getInterface: expect.any(Function),
}),
);

await close();
await closeServer();
});
});

describe('mockJsonRpc', () => {
it('mocks a JSON-RPC method', async () => {
jest.spyOn(console, 'log').mockImplementation();
Expand Down
54 changes: 54 additions & 0 deletions packages/snaps-simulation/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ export type SnapHelpers = {
keyringRequest: KeyringOptions,
): Promise<SnapResponseWithoutInterface>;

/**
* Get the response from the Snap's `onInstall` handler.
*
* @returns The response.
*/
onInstall(): Promise<SnapResponseWithInterface>;
david0xd marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get the response from the Snap's `onUpdate` handler.
*
* @returns The response.
*/
onUpdate(): Promise<SnapResponseWithInterface>;

/**
* 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.
Expand Down Expand Up @@ -266,6 +280,46 @@ export function getHelpers({

onKeyringRequest,

onInstall: async (): Promise<SnapResponseWithInterface> => {
log('Running onInstall handler.');

const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
runSaga,
handler: HandlerType.OnInstall,
request: {
method: '',
},
});

assertIsResponseWithInterface(response);

return response;
},

onUpdate: async (): Promise<SnapResponseWithInterface> => {
log('Running onUpdate handler.');

const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
runSaga,
handler: HandlerType.OnUpdate,
request: {
method: '',
},
});

assertIsResponseWithInterface(response);

return response;
},

onSignature: async (
request: unknown,
): Promise<SnapResponseWithInterface> => {
Expand Down
14 changes: 14 additions & 0 deletions packages/snaps-simulation/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,20 @@ export type Snap = {
keyringRequest: KeyringOptions,
): Promise<SnapResponseWithoutInterface>;

/**
* Get the response from the Snap's `onInstall` handler.
*
* @returns The response.
*/
onInstall(): Promise<SnapResponseWithInterface>;

/**
* Get the response from the Snap's `onUpdate` handler.
*
* @returns The response.
*/
onUpdate(): Promise<SnapResponseWithInterface>;

/**
* 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.
Expand Down
Loading