Skip to content

Commit

Permalink
Improve correctness of clickElement (#2334)
Browse files Browse the repository at this point in the history
Fixes two issues with the latest version of `snaps-jest`:
- Errors thrown when handling `onUserInput` were not unwrapped properly.
- `ButtonClickEvent` was not triggered when submitting a form. This is a
divergence from the client implementation.
  • Loading branch information
FrederikBolding authored Apr 16, 2024
1 parent da03d4a commit 7a28461
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 46 deletions.
43 changes: 42 additions & 1 deletion packages/snaps-jest/src/internals/simulation/interface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
panel,
text,
} from '@metamask/snaps-sdk';
import { HandlerType } from '@metamask/snaps-utils';
import { HandlerType, WrappedSnapError } from '@metamask/snaps-utils';
import { MOCK_SNAP_ID } from '@metamask/snaps-utils/test-utils';
import { assert } from '@metamask/utils';
import type { SagaIterator } from 'redux-saga';
Expand Down Expand Up @@ -299,6 +299,22 @@ describe('clickElement', () => {
'baz',
);

expect(handleRpcRequestMock).toHaveBeenCalledWith(MOCK_SNAP_ID, {
origin: '',
handler: HandlerType.OnUserInput,
request: {
jsonrpc: '2.0',
method: ' ',
params: {
event: {
type: UserInputEventType.ButtonClickEvent,
name: 'baz',
},
id: interfaceId,
},
},
});

expect(handleRpcRequestMock).toHaveBeenCalledWith(MOCK_SNAP_ID, {
origin: '',
handler: HandlerType.OnUserInput,
Expand Down Expand Up @@ -339,6 +355,31 @@ describe('clickElement', () => {

expect(handleRpcRequestMock).not.toHaveBeenCalled();
});

it('unwraps errors', async () => {
const content = button({ value: 'foo', name: 'foo' });

const interfaceId = await interfaceController.createInterface(
MOCK_SNAP_ID,
content,
);

handleRpcRequestMock.mockRejectedValue(
new WrappedSnapError(new Error('bar')),
);

await expect(
clickElement(
rootControllerMessenger,
interfaceId,
content,
MOCK_SNAP_ID,
'foo',
),
).rejects.toThrow('bar');

expect(handleRpcRequestMock).toHaveBeenCalled();
});
});

describe('mergeValue', () => {
Expand Down
94 changes: 49 additions & 45 deletions packages/snaps-jest/src/internals/simulation/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
Input,
InterfaceState,
SnapId,
UserInputEvent,
} from '@metamask/snaps-sdk';
import {
ButtonType,
Expand All @@ -13,7 +14,7 @@ import {
UserInputEventType,
assert,
} from '@metamask/snaps-sdk';
import { HandlerType, hasChildren } from '@metamask/snaps-utils';
import { HandlerType, hasChildren, unwrapError } from '@metamask/snaps-utils';
import type { PayloadAction } from '@reduxjs/toolkit';
import { type SagaIterator } from 'redux-saga';
import { call, put, select, take } from 'redux-saga/effects';
Expand Down Expand Up @@ -191,6 +192,42 @@ export function getElement(

return undefined;
}
/**
* Handle submitting event requests to OnUserInput including unwrapping potential errors.
*
* @param controllerMessenger - The controller messenger used to call actions.
* @param snapId - The Snap ID.
* @param id - The interface ID.
* @param event - The event to submit.
*/
async function handleEvent(
controllerMessenger: RootControllerMessenger,
snapId: SnapId,
id: string,
event: UserInputEvent,
) {
try {
await controllerMessenger.call(
'ExecutionService:handleRpcRequest',
snapId,
{
origin: '',
handler: HandlerType.OnUserInput,
request: {
jsonrpc: '2.0',
method: ' ',
params: {
event,
id,
},
},
},
);
} catch (error) {
const [unwrapped] = unwrapError(error);
throw unwrapped;
}
}

/**
* Click on an element of the Snap interface.
Expand All @@ -214,57 +251,24 @@ export async function clickElement(
'No button found in the interface.',
);

// Button click events are always triggered.
await handleEvent(controllerMessenger, snapId, id, {
type: UserInputEventType.ButtonClickEvent,
name: result.element.name,
});

if (result.form && result.element.buttonType === ButtonType.Submit) {
const { state } = controllerMessenger.call(
'SnapInterfaceController:getInterface',
snapId,
id,
);

await controllerMessenger.call(
'ExecutionService:handleRpcRequest',
snapId,
{
origin: '',
handler: HandlerType.OnUserInput,
request: {
jsonrpc: '2.0',
method: ' ',
params: {
event: {
type: UserInputEventType.FormSubmitEvent,
name: result.form,
value: state[result.form],
},
id,
},
},
},
);

return;
}

if (result.element.buttonType !== ButtonType.Submit) {
await controllerMessenger.call(
'ExecutionService:handleRpcRequest',
snapId,
{
origin: '',
handler: HandlerType.OnUserInput,
request: {
jsonrpc: '2.0',
method: ' ',
params: {
event: {
type: UserInputEventType.ButtonClickEvent,
name: result.element.name,
},
id,
},
},
},
);
await handleEvent(controllerMessenger, snapId, id, {
type: UserInputEventType.FormSubmitEvent,
name: result.form,
value: state[result.form] as Record<string, string | null>,
});
}
}

Expand Down

0 comments on commit 7a28461

Please sign in to comment.