From 80cf684aac11250e66434e96be7a3d7c49b86344 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sun, 13 Apr 2025 16:55:20 -0400 Subject: [PATCH 01/16] refactor: move test setup to its own file --- .../Snaps/SnapUIRenderer/testUtils.tsx | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 app/components/Snaps/SnapUIRenderer/testUtils.tsx diff --git a/app/components/Snaps/SnapUIRenderer/testUtils.tsx b/app/components/Snaps/SnapUIRenderer/testUtils.tsx new file mode 100644 index 000000000000..7cb6ce4af8bb --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/testUtils.tsx @@ -0,0 +1,193 @@ +import { JSXElement } from '@metamask/snaps-sdk/dist/jsx/index.cjs'; +import React from 'react'; +import renderWithProvider from '../../../util/test/renderWithProvider'; +import { RootState } from '../../../reducers'; +import { PayloadAction } from '@reduxjs/toolkit'; +import { FormState, SnapId } from '@metamask/snaps-sdk'; +import { SnapUIRenderer } from './SnapUIRenderer'; +import { act } from '@testing-library/react-native'; + + +export const MOCK_SNAP_ID = 'npm:@metamask/test-snap-bip44'; +export const MOCK_INTERFACE_ID = 'interfaceId'; + +interface RenderInterfaceOptions { + useFooter?: boolean; + onCancel?: () => void; + contentBackgroundColor?: string; + state?: Record; + stateSettings?: Record; +} + +const noOp = () => { + // no-op +}; + +export function renderInterface( + content: JSXElement | null, + { + useFooter = false, + onCancel = noOp, + state = {}, + stateSettings = {}, + }: RenderInterfaceOptions = {}, +) { + const storeState = { + settings: stateSettings, + engine: { + backgroundState: { + SubjectMetadataController: { + subjectMetadata: { + 'npm:@metamask/test-snap-bip44': { + name: '@metamask/test-snap-bip44', + version: '1.2.3', + subjectType: 'snap', + }, + }, + }, + SnapController: { + snaps: { + [MOCK_SNAP_ID]: { + id: 'npm:@metamask/test-snap-bip44', + origin: 'npm:@metamask/test-snap-bip44', + version: '5.1.2', + iconUrl: null, + initialPermissions: { + 'endowment:ethereum-provider': {}, + }, + manifest: { + description: 'An example Snap that signs messages using BLS.', + proposedName: 'BIP-44 Test Snap', + repository: { + type: 'git', + url: 'https://github.com/MetaMask/test-snaps.git', + }, + source: { + location: { + npm: { + filePath: 'dist/bundle.js', + packageName: '@metamask/test-snap-bip44', + registry: 'https://registry.npmjs.org', + }, + }, + shasum: 'L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=', + }, + version: '5.1.2', + }, + versionHistory: [ + { + date: 1680686075921, + origin: 'https://metamask.github.io', + version: '5.1.2', + }, + ], + }, + }, + }, + SnapInterfaceController: { + interfaces: { + [MOCK_INTERFACE_ID]: { + snapId: MOCK_SNAP_ID, + content, + state, + context: null, + contentType: null, + }, + }, + }, + KeyringController: { + keyrings: [] + }, + AccountsController: { + internalAccounts: { + accounts: { + 'foo': { + address: '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb', + metadata: { + name: 'My Account', + } + } + } + } + }, + AddressBookController: { + addressBook: { + '0x1': { + '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcda': { + address: '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcda', + name: 'Test Contact', + } + } + } + } + }, + }, + }; + + const result = renderWithProvider( + , + { state: storeState as unknown as RootState }, + ); + + const reducer = ( + reducerState: RootState | undefined, + action: PayloadAction<{ content: JSXElement; state: FormState }>, + ): RootState => { + // Handle initial state + const currentState = reducerState || result.store.getState(); + + if (action.type === 'updateInterface') { + return { + ...currentState, + engine: { + ...currentState.engine, + backgroundState: { + ...currentState.engine.backgroundState, + SnapInterfaceController: { + interfaces: { + [MOCK_INTERFACE_ID]: { + snapId: MOCK_SNAP_ID as SnapId, + content: action.payload.content, + state: action.payload.state ?? state, + context: null, + contentType: null, + }, + }, + }, + }, + }, + }; + } + return currentState; + }; + + const { store } = result; + + store.replaceReducer(reducer); + + const updateInterface = ( + newContent: JSXElement, + newState: FormState | null = null, + ) => { + act(() => { + store.dispatch({ + type: 'updateInterface', + payload: { + content: newContent, + state: newState, + }, + }); + }); + }; + + const getRenderCount = () => + parseInt(result.getByTestId('performance').props['data-renders'], 10); + + return { ...result, updateInterface, getRenderCount }; +} From 01df553f195c7dfdc6f896b5cbf2afe7d85a83a7 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sun, 13 Apr 2025 16:56:43 -0400 Subject: [PATCH 02/16] chore: remove dupe snap ui address test file --- .../SnapUIAddress/SnapUIAddress.test.tsx | 193 ------------------ 1 file changed, 193 deletions(-) delete mode 100644 app/components/Snaps/SnapUIAddress/SnapUIAddress.test.tsx diff --git a/app/components/Snaps/SnapUIAddress/SnapUIAddress.test.tsx b/app/components/Snaps/SnapUIAddress/SnapUIAddress.test.tsx deleted file mode 100644 index f05913facc55..000000000000 --- a/app/components/Snaps/SnapUIAddress/SnapUIAddress.test.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import React from 'react'; -import { SnapUIAddress } from './SnapUIAddress'; -import renderWithProvider from '../../../util/test/renderWithProvider'; - -const baseMockState = { - state: { - engine: { - backgroundState: { - KeyringController: { - keyrings: [] - }, - AccountsController: { - internalAccounts: { - accounts: { - 'foo': { - address: '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb', - metadata: { - name: 'My Account', - } - } - } - } - }, - AddressBookController: { - addressBook: { - '0x1': { - '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcda': { - address: '0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcda', - name: 'Test Contact', - } - } - } - } - } - }, - } -}; - -const mockStateWithoutBlockies = { - state: { - ...baseMockState.state, - settings: { - useBlockieIcon: false, - }, - }, -}; - -const mockStateWithBlockies = { - state: { - ...baseMockState.state, - settings: { - useBlockieIcon: true, - }, - }, -}; - -describe('SnapUIAddress', () => { - it('renders legacy Ethereum address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Ethereum address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders account name', () => { - const { getByText } = renderWithProvider( - , - baseMockState - ); - - expect(getByText('My Account')).toBeDefined(); - }); - - it('renders contact name', () => { - const { getByText } = renderWithProvider( - , - baseMockState - ); - - expect(getByText('Test Contact')).toBeDefined(); - }); - - - it('renders Ethereum address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Bitcoin address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Bitcoin address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Cosmos address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Cosmos address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Polkadot address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Polkadot address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Starknet address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Starknet address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Hedera address', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithoutBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders Hedera address with blockie', () => { - const { toJSON } = renderWithProvider( - , - mockStateWithBlockies, - ); - - expect(toJSON()).toMatchSnapshot(); - }); -}); From 9575bc479390049c1bec8d186fdd4694515596bb Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sun, 13 Apr 2025 16:58:15 -0400 Subject: [PATCH 03/16] refactor: move form test into its own file --- .../SnapUIRenderer/SnapUIRenderer.test.tsx | 345 +----------------- 1 file changed, 2 insertions(+), 343 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx index 5bcfd4a21ef3..c47336ae644a 100644 --- a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx +++ b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Box, Text, @@ -6,25 +5,17 @@ import { Footer, Button, Input, - JSXElement, Form, Field, - Checkbox, Section, Row, Value, Card, Image as ImageComponent, - Selector, - SelectorOption, } from '@metamask/snaps-sdk/jsx'; -import { fireEvent, act } from '@testing-library/react-native'; -import renderWithProvider from '../../../util/test/renderWithProvider'; -import { SnapUIRenderer } from './SnapUIRenderer'; +import { fireEvent } from '@testing-library/react-native'; import Engine from '../../../core/Engine/Engine'; -import { RootState } from '../../../reducers'; -import { FormState } from '@metamask/snaps-sdk'; -import { PayloadAction } from '@reduxjs/toolkit'; +import { renderInterface, MOCK_INTERFACE_ID, MOCK_SNAP_ID } from './testUtils'; jest.mock('../../../core/Engine/Engine', () => ({ controllerMessenger: { @@ -39,147 +30,6 @@ jest.mock('../../../core/Engine/Engine', () => ({ const mockEngine = jest.mocked(Engine); -const MOCK_SNAP_ID = 'npm:@metamask/test-snap-bip44'; -const MOCK_INTERFACE_ID = 'interfaceId'; - -const noOp = () => { - // no-op -}; - -function renderInterface( - content: JSXElement | null, - { useFooter = false, onCancel = noOp, state = {} } = {}, -) { - const storeState = { - engine: { - backgroundState: { - SubjectMetadataController: { - subjectMetadata: { - 'npm:@metamask/test-snap-bip44': { - name: '@metamask/test-snap-bip44', - version: '1.2.3', - subjectType: 'snap', - }, - }, - }, - SnapController: { - snaps: { - [MOCK_SNAP_ID]: { - id: 'npm:@metamask/test-snap-bip44', - origin: 'npm:@metamask/test-snap-bip44', - version: '5.1.2', - iconUrl: null, - initialPermissions: { - 'endowment:ethereum-provider': {}, - }, - manifest: { - description: 'An example Snap that signs messages using BLS.', - proposedName: 'BIP-44 Test Snap', - repository: { - type: 'git', - url: 'https://github.com/MetaMask/test-snaps.git', - }, - source: { - location: { - npm: { - filePath: 'dist/bundle.js', - packageName: '@metamask/test-snap-bip44', - registry: 'https://registry.npmjs.org', - }, - }, - shasum: 'L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=', - }, - version: '5.1.2', - }, - versionHistory: [ - { - date: 1680686075921, - origin: 'https://metamask.github.io', - version: '5.1.2', - }, - ], - }, - }, - }, - SnapInterfaceController: { - interfaces: { - [MOCK_INTERFACE_ID]: { - snapId: MOCK_SNAP_ID, - content, - state, - context: null, - contentType: null, - }, - }, - }, - }, - }, - }; - - const result = renderWithProvider( - , - { state: storeState as unknown as RootState }, - ); - - const reducer = ( - reducerState: RootState, - action: PayloadAction<{ content: JSXElement; state: FormState }>, - ) => { - if (action.type === 'updateInterface') { - return { - engine: { - backgroundState: { - ...reducerState.engine.backgroundState, - SnapInterfaceController: { - interfaces: { - [MOCK_INTERFACE_ID]: { - snapId: MOCK_SNAP_ID, - content: action.payload.content, - state: action.payload.state ?? state, - context: null, - contentType: null, - }, - }, - }, - }, - }, - }; - } - return reducerState; - }; - - const { store } = result; - - // @ts-expect-error Mock reducer doesn't fully match the type. - store.replaceReducer(reducer); - - const updateInterface = ( - newContent: JSXElement, - newState: FormState | null = null, - ) => { - act(() => { - store.dispatch({ - type: 'updateInterface', - payload: { - content: newContent, - state: newState, - }, - }); - }); - }; - - const getRenderCount = () => - parseInt(result.getByTestId('performance').props['data-renders'], 10); - - return { ...result, updateInterface, getRenderCount }; -} - describe('SnapUIRenderer', () => { beforeEach(() => { jest.resetAllMocks(); @@ -340,197 +190,6 @@ describe('SnapUIRenderer', () => { expect(toJSON()).toMatchSnapshot(); }); - it('supports forms with fields', () => { - const { toJSON, getByTestId, getByText } = renderInterface( - Box({ - children: Form({ - name: 'form', - children: [ - Field({ label: 'My Input', children: Input({ name: 'input' }) }), - Field({ - label: 'My Checkbox', - children: Checkbox({ - name: 'checkbox', - label: 'This is a checkbox', - }), - }), - Field({ - label: 'My Selector', - children: Selector({ - name: 'selector', - title: 'Select an option', - children: [ - SelectorOption({ - value: 'option1', - children: Card({ - title: 'CardTitle1', - description: 'CardDescription1', - value: 'CardValue1', - extra: 'CardExtra1', - }), - }), - SelectorOption({ - value: 'option2', - children: Card({ - title: 'CardTitle2', - description: 'CardDescription2', - value: 'CardValue2', - extra: 'CardExtra2', - }), - }), - ], - }), - }), - Button({ type: 'submit', name: 'submit', children: 'Submit' }), - ], - }), - }), - { state: { form: { selector: 'option1' } } }, - ); - - const input = getByTestId('input'); - fireEvent.changeText(input, 'abc'); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(1, MOCK_INTERFACE_ID, { - form: { input: 'abc', selector: 'option1' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 1, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: '', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - context: null, - event: { name: 'input', type: 'InputChangeEvent', value: 'abc' }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const checkbox = getByText('This is a checkbox'); - fireEvent.press(checkbox); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(2, MOCK_INTERFACE_ID, { - form: { input: 'abc', checkbox: true, selector: 'option1' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 2, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: '', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - context: null, - event: { name: 'checkbox', type: 'InputChangeEvent', value: true }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const selector = getByText('CardTitle1'); - fireEvent.press(selector); - - const selectorItem = getByText('CardTitle2'); - fireEvent.press(selectorItem); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(3, MOCK_INTERFACE_ID, { - form: { input: 'abc', checkbox: true, selector: 'option2' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 3, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: '', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - context: null, - event: { - name: 'selector', - type: 'InputChangeEvent', - value: 'option2', - }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const button = getByText('Submit'); - fireEvent.press(button); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 4, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: '', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - context: null, - event: { name: 'submit', type: 'ButtonClickEvent' }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 5, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: '', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - context: null, - event: { - name: 'form', - type: 'FormSubmitEvent', - value: { - checkbox: true, - input: 'abc', - selector: 'option2', - }, - }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - it('supports fields with multiple components', () => { const { toJSON } = renderInterface( Box({ From e661477bf811cb2d190ca10b0421002e16301bb4 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sun, 13 Apr 2025 16:59:50 -0400 Subject: [PATCH 04/16] chore: remove old snap ui address snapshot --- .../__snapshots__/SnapUIAddress.test.tsx.snap | 1399 ----------------- 1 file changed, 1399 deletions(-) delete mode 100644 app/components/Snaps/SnapUIAddress/__snapshots__/SnapUIAddress.test.tsx.snap diff --git a/app/components/Snaps/SnapUIAddress/__snapshots__/SnapUIAddress.test.tsx.snap b/app/components/Snaps/SnapUIAddress/__snapshots__/SnapUIAddress.test.tsx.snap deleted file mode 100644 index 1ac308186fb6..000000000000 --- a/app/components/Snaps/SnapUIAddress/__snapshots__/SnapUIAddress.test.tsx.snap +++ /dev/null @@ -1,1399 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SnapUIAddress renders Bitcoin address 1`] = ` - - - - - - - - - - - - 128Lkh3...Mp8p6 - - -`; - -exports[`SnapUIAddress renders Bitcoin address with blockie 1`] = ` - - - 128Lkh3...Mp8p6 - - -`; - -exports[`SnapUIAddress renders Cosmos address 1`] = ` - - - - - - - - - - - - cosmos1...6hdc0 - - -`; - -exports[`SnapUIAddress renders Cosmos address with blockie 1`] = ` - - - cosmos1...6hdc0 - - -`; - -exports[`SnapUIAddress renders Ethereum address 1`] = ` - - - - - - - - - - - - 0xab16a...Bfcdb - - -`; - -exports[`SnapUIAddress renders Ethereum address with blockie 1`] = ` - - - 0xab16a...Bfcdb - - -`; - -exports[`SnapUIAddress renders Hedera address 1`] = ` - - - - - - - - - - - - 0.0.123...zbhlt - - -`; - -exports[`SnapUIAddress renders Hedera address with blockie 1`] = ` - - - 0.0.123...zbhlt - - -`; - -exports[`SnapUIAddress renders Polkadot address 1`] = ` - - - - - - - - - - - - 5hmuyxw...egmfy - - -`; - -exports[`SnapUIAddress renders Polkadot address with blockie 1`] = ` - - - 5hmuyxw...egmfy - - -`; - -exports[`SnapUIAddress renders Starknet address 1`] = ` - - - - - - - - - - - - 0x02dd1...0ab57 - - -`; - -exports[`SnapUIAddress renders Starknet address with blockie 1`] = ` - - - 0x02dd1...0ab57 - - -`; - -exports[`SnapUIAddress renders legacy Ethereum address 1`] = ` - - - - - - - - - - - - 0xab16a...Bfcdb - - -`; From f689a328d2a6b77e1fa4193f9ca0abde766e3e40 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sun, 13 Apr 2025 17:00:21 -0400 Subject: [PATCH 05/16] chore: update snap ui renderer snapshots --- .../SnapUIRenderer.test.tsx.snap | 1054 ----------------- 1 file changed, 1054 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap b/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap index 837a85855c29..e810d07175fd 100644 --- a/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap +++ b/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap @@ -1782,1060 +1782,6 @@ exports[`SnapUIRenderer supports fields with multiple components 1`] = ` `; -exports[`SnapUIRenderer supports forms with fields 1`] = ` - - - - - - - - - My Input - - - - - - - - - - My Checkbox - - - - - - - - This is a checkbox - - - - - - - My Selector - - - - - - - CardTitle2 - - - CardDescription2 - - - - - - CardValue2 - - - CardExtra2 - - - - - - - - - - - - - - - - - - - - - - Select an option - - - - - - - - - - - - - - - CardTitle1 - - - CardDescription1 - - - - - - CardValue1 - - - CardExtra1 - - - - - - - - - - CardTitle2 - - - CardDescription2 - - - - - - CardValue2 - - - CardExtra2 - - - - - - - - - - - - - - Submit - - - - - - - - - -`; - exports[`SnapUIRenderer supports interactive inputs 1`] = ` Date: Sun, 13 Apr 2025 17:01:10 -0400 Subject: [PATCH 06/16] refactor: create test files and snapshots for snap ui address and form components --- .../__snapshots__/address.test.ts.snap | 1893 +++++++++++++++++ .../__snapshots__/form.test.ts.snap | 1214 +++++++++++ .../SnapUIRenderer/components/address.test.ts | 184 ++ .../SnapUIRenderer/components/form.test.ts | 227 ++ 4 files changed, 3518 insertions(+) create mode 100644 app/components/Snaps/SnapUIRenderer/components/__snapshots__/address.test.ts.snap create mode 100644 app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap create mode 100644 app/components/Snaps/SnapUIRenderer/components/address.test.ts create mode 100644 app/components/Snaps/SnapUIRenderer/components/form.test.ts diff --git a/app/components/Snaps/SnapUIRenderer/components/__snapshots__/address.test.ts.snap b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/address.test.ts.snap new file mode 100644 index 000000000000..0ba09dcffb9a --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/address.test.ts.snap @@ -0,0 +1,1893 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SnapUIAddress renders Bitcoin address 1`] = ` + + + + + + + + + + + + + + + + 128Lkh3...Mp8p6 + + + + + + + +`; + +exports[`SnapUIAddress renders Bitcoin address with blockie 1`] = ` + + + + + + + 128Lkh3...Mp8p6 + + + + + + + +`; + +exports[`SnapUIAddress renders Cosmos address 1`] = ` + + + + + + + + + + + + + + + + cosmos1...6hdc0 + + + + + + + +`; + +exports[`SnapUIAddress renders Cosmos address with blockie 1`] = ` + + + + + + + cosmos1...6hdc0 + + + + + + + +`; + +exports[`SnapUIAddress renders Ethereum address 1`] = ` + + + + + + + + + + + + + + + + 0xab16a...Bfcdb + + + + + + + +`; + +exports[`SnapUIAddress renders Ethereum address with blockie 1`] = ` + + + + + + + 0xab16a...Bfcdb + + + + + + + +`; + +exports[`SnapUIAddress renders Hedera address 1`] = ` + + + + + + + + + + + + + + + + 0.0.123...zbhlt + + + + + + + +`; + +exports[`SnapUIAddress renders Hedera address with blockie 1`] = ` + + + + + + + 0.0.123...zbhlt + + + + + + + +`; + +exports[`SnapUIAddress renders Polkadot address 1`] = ` + + + + + + + + + + + + + + + + 5hmuyxw...egmfy + + + + + + + +`; + +exports[`SnapUIAddress renders Polkadot address with blockie 1`] = ` + + + + + + + 5hmuyxw...egmfy + + + + + + + +`; + +exports[`SnapUIAddress renders Starknet address 1`] = ` + + + + + + + + + + + + + + + + 0x02dd1...0ab57 + + + + + + + +`; + +exports[`SnapUIAddress renders Starknet address with blockie 1`] = ` + + + + + + + 0x02dd1...0ab57 + + + + + + + +`; + +exports[`SnapUIAddress renders legacy Ethereum address 1`] = ` + + + + + + + + + + + + + + + + 0xab16a...Bfcdb + + + + + + + +`; diff --git a/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap new file mode 100644 index 000000000000..d4de995eafe4 --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap @@ -0,0 +1,1214 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SnapUIForm will render 1`] = ` + + + + + + + + + + + + + + + + Submit + + + + + + + + + +`; + +exports[`SnapUIForm will render with fields 1`] = ` + + + + + + + + + My Input + + + + + + + + + + My Checkbox + + + + + + + + This is a checkbox + + + + + + + My Selector + + + + + + + CardTitle2 + + + CardDescription2 + + + + + + CardValue2 + + + CardExtra2 + + + + + + + + + + + + + + + + + + + + + + Select an option + + + + + + + + + + + + + + + CardTitle1 + + + CardDescription1 + + + + + + CardValue1 + + + CardExtra1 + + + + + + + + + + CardTitle2 + + + CardDescription2 + + + + + + CardValue2 + + + CardExtra2 + + + + + + + + + + + + + + Submit + + + + + + + + + +`; diff --git a/app/components/Snaps/SnapUIRenderer/components/address.test.ts b/app/components/Snaps/SnapUIRenderer/components/address.test.ts new file mode 100644 index 000000000000..8295f1f150de --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/address.test.ts @@ -0,0 +1,184 @@ +import { renderInterface } from '../testUtils'; +import { Address } from '@metamask/snaps-sdk/jsx'; + +jest.mock('../../../../core/Engine/Engine'); + +const withBlockies = { + useBlockieIcon: true, +}; + +const withoutBlockies = { + useBlockieIcon: false, +}; + +describe('SnapUIAddress', () => { + it('renders legacy Ethereum address', () => { + const { toJSON } = renderInterface( + Address({ + address: '0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Ethereum address', () => { + const { toJSON } = renderInterface( + Address({ + address: 'eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders account name', () => { + const { getByText } = renderInterface( + Address({ + address: 'eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb', + displayName: true, + }), + { stateSettings: withoutBlockies }, + ); + + expect(getByText('My Account')).toBeDefined(); + }); + + it('renders contact name', () => { + const { getByText } = renderInterface( + Address({ + address: 'eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcda', + displayName: true, + }), + ); + + expect(getByText('Test Contact')).toBeDefined(); + }); + + it('renders Ethereum address with blockie', () => { + const { toJSON } = renderInterface( + Address({ + address: 'eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb', + }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Bitcoin address', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Bitcoin address with blockie', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Cosmos address', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'cosmos:cosmoshub-3:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Cosmos address with blockie', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'cosmos:cosmoshub-3:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0', + }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Polkadot address', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'polkadot:b0a8d493285c2df73290dfb7e61f870f:5hmuyxw9xdgbpptgypokw4thfyoe3ryenebr381z9iaegmfy', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Polkadot address with blockie', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'polkadot:b0a8d493285c2df73290dfb7e61f870f:5hmuyxw9xdgbpptgypokw4thfyoe3ryenebr381z9iaegmfy', + }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Starknet address', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'starknet:SN_GOERLI:0x02dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57', + }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Starknet address with blockie', () => { + const { toJSON } = renderInterface( + Address({ + address: + 'starknet:SN_GOERLI:0x02dd1b492765c064eac4039e3841aa5f382773b598097a40073bd8b48170ab57', + }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Hedera address', () => { + const { toJSON } = renderInterface( + Address({ address: 'hedera:mainnet:0.0.1234567890-zbhlt' }), + { stateSettings: withoutBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('renders Hedera address with blockie', () => { + const { toJSON } = renderInterface( + Address({ address: 'hedera:mainnet:0.0.1234567890-zbhlt' }), + { stateSettings: withBlockies }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); +}); diff --git a/app/components/Snaps/SnapUIRenderer/components/form.test.ts b/app/components/Snaps/SnapUIRenderer/components/form.test.ts new file mode 100644 index 000000000000..e10c5772c66e --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/form.test.ts @@ -0,0 +1,227 @@ +import { Box, Form, Input, Button, Field, Checkbox, Selector, SelectorOption, Card } from '@metamask/snaps-sdk/jsx'; +import Engine from '../../../../core/Engine/Engine'; +import { fireEvent } from '@testing-library/react-native'; +import { MOCK_INTERFACE_ID, MOCK_SNAP_ID, renderInterface } from '../testUtils'; + +jest.mock('../../../../core/Engine/Engine', () => ({ + controllerMessenger: { + call: jest.fn(), + }, + context: { + SnapInterfaceController: { + updateInterfaceState: jest.fn(), + }, + }, +})); + +const mockEngine = jest.mocked(Engine); + +describe('SnapUIForm', () => { + it('will render', () => { + const { toJSON, getByText } = renderInterface( + Box({ + children: Form({ + name: 'form', + children: [ + Input({ name: 'input' }), + Button({ type: 'submit', name: 'submit', children: 'Submit' }), + ], + }), + }), + ); + + expect(toJSON()).toMatchSnapshot(); + expect(getByText('Submit')).toBeTruthy(); + }); + + it('will render with fields', () => { + const { toJSON, getByTestId, getByText } = renderInterface( + Box({ + children: Form({ + name: 'form', + children: [ + Field({ label: 'My Input', children: Input({ name: 'input' }) }), + Field({ + label: 'My Checkbox', + children: Checkbox({ + name: 'checkbox', + label: 'This is a checkbox', + }), + }), + Field({ + label: 'My Selector', + children: Selector({ + name: 'selector', + title: 'Select an option', + children: [ + SelectorOption({ + value: 'option1', + children: Card({ + title: 'CardTitle1', + description: 'CardDescription1', + value: 'CardValue1', + extra: 'CardExtra1', + }), + }), + SelectorOption({ + value: 'option2', + children: Card({ + title: 'CardTitle2', + description: 'CardDescription2', + value: 'CardValue2', + extra: 'CardExtra2', + }), + }), + ], + }), + }), + Button({ type: 'submit', name: 'submit', children: 'Submit' }), + ], + }), + }), + { state: { form: { selector: 'option1' } } }, + ); + + const input = getByTestId('input'); + fireEvent.changeText(input, 'abc'); + + expect( + mockEngine.context.SnapInterfaceController.updateInterfaceState, + ).toHaveBeenNthCalledWith(1, MOCK_INTERFACE_ID, { + form: { input: 'abc', selector: 'option1' }, + }); + + expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( + 1, + 'SnapController:handleRequest', + { + handler: 'onUserInput', + origin: '', + request: { + jsonrpc: '2.0', + method: ' ', + params: { + context: null, + event: { name: 'input', type: 'InputChangeEvent', value: 'abc' }, + id: MOCK_INTERFACE_ID, + }, + }, + snapId: MOCK_SNAP_ID, + }, + ); + + const checkbox = getByText('This is a checkbox'); + fireEvent.press(checkbox); + + expect( + mockEngine.context.SnapInterfaceController.updateInterfaceState, + ).toHaveBeenNthCalledWith(2, MOCK_INTERFACE_ID, { + form: { input: 'abc', checkbox: true, selector: 'option1' }, + }); + + expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( + 2, + 'SnapController:handleRequest', + { + handler: 'onUserInput', + origin: '', + request: { + jsonrpc: '2.0', + method: ' ', + params: { + context: null, + event: { name: 'checkbox', type: 'InputChangeEvent', value: true }, + id: MOCK_INTERFACE_ID, + }, + }, + snapId: MOCK_SNAP_ID, + }, + ); + + const selector = getByText('CardTitle1'); + fireEvent.press(selector); + + const selectorItem = getByText('CardTitle2'); + fireEvent.press(selectorItem); + + expect( + mockEngine.context.SnapInterfaceController.updateInterfaceState, + ).toHaveBeenNthCalledWith(3, MOCK_INTERFACE_ID, { + form: { input: 'abc', checkbox: true, selector: 'option2' }, + }); + + expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( + 3, + 'SnapController:handleRequest', + { + handler: 'onUserInput', + origin: '', + request: { + jsonrpc: '2.0', + method: ' ', + params: { + context: null, + event: { + name: 'selector', + type: 'InputChangeEvent', + value: 'option2', + }, + id: MOCK_INTERFACE_ID, + }, + }, + snapId: MOCK_SNAP_ID, + }, + ); + + const button = getByText('Submit'); + fireEvent.press(button); + + expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( + 4, + 'SnapController:handleRequest', + { + handler: 'onUserInput', + origin: '', + request: { + jsonrpc: '2.0', + method: ' ', + params: { + context: null, + event: { name: 'submit', type: 'ButtonClickEvent' }, + id: MOCK_INTERFACE_ID, + }, + }, + snapId: MOCK_SNAP_ID, + }, + ); + + expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( + 5, + 'SnapController:handleRequest', + { + handler: 'onUserInput', + origin: '', + request: { + jsonrpc: '2.0', + method: ' ', + params: { + context: null, + event: { + name: 'form', + type: 'FormSubmitEvent', + value: { + checkbox: true, + input: 'abc', + selector: 'option2', + }, + }, + id: MOCK_INTERFACE_ID, + }, + }, + snapId: MOCK_SNAP_ID, + }, + ); + + expect(toJSON()).toMatchSnapshot(); + }); +}); From 100178436ece3457e98f2a49da0cd61fa90a7c04 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 16 Apr 2025 14:03:15 -0400 Subject: [PATCH 07/16] chore: delete old input test --- .../Snaps/SnapUIInput/SnapUIInput.test.tsx | 118 ------------------ 1 file changed, 118 deletions(-) delete mode 100644 app/components/Snaps/SnapUIInput/SnapUIInput.test.tsx diff --git a/app/components/Snaps/SnapUIInput/SnapUIInput.test.tsx b/app/components/Snaps/SnapUIInput/SnapUIInput.test.tsx deleted file mode 100644 index b93dcce459ed..000000000000 --- a/app/components/Snaps/SnapUIInput/SnapUIInput.test.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import { render, fireEvent } from '@testing-library/react-native'; -import { SnapUIInput } from './SnapUIInput'; -import { useSnapInterfaceContext } from '../SnapInterfaceContext'; -import { INPUT_TEST_ID } from '../../../component-library/components/Form/TextField/foundation/Input/Input.constants'; - -// Mock the entire module -jest.mock('../SnapInterfaceContext'); - -describe('SnapUIInput', () => { - const mockHandleInputChange = jest.fn(); - const mockSetCurrentFocusedInput = jest.fn(); - const mockGetValue = jest.fn(); - - beforeEach(() => { - // Set up the mock implementation before each test - (useSnapInterfaceContext as jest.Mock).mockReturnValue({ - handleInputChange: mockHandleInputChange, - getValue: mockGetValue, - setCurrentFocusedInput: mockSetCurrentFocusedInput, - focusedInput: null, - }); - - // Clear all mocks before each test - jest.clearAllMocks(); - }); - - it('renders with initial value', () => { - mockGetValue.mockReturnValue('initial value'); - - const { getByDisplayValue } = render(); - - expect(getByDisplayValue('initial value')).toBeTruthy(); - }); - - it('handles input changes', () => { - const { getByTestId } = render(); - - const input = getByTestId('textfield'); - fireEvent.changeText(input, 'new value'); - - expect(mockHandleInputChange).toHaveBeenCalledWith( - 'testInput', - 'new value', - undefined, - ); - }); - - it('handles form input changes', () => { - const { getByTestId } = render( - , - ); - - const input = getByTestId('textfield'); - fireEvent.changeText(input, 'new value'); - - expect(mockHandleInputChange).toHaveBeenCalledWith( - 'testInput', - 'new value', - 'testForm', - ); - }); - - it('handles focus events', () => { - const { getByTestId } = render(); - - const input = getByTestId('textfield'); - fireEvent(input, 'focus'); - - expect(mockSetCurrentFocusedInput).toHaveBeenCalledWith('testInput'); - }); - - it('handles blur events', () => { - const { getByTestId } = render(); - - const input = getByTestId('textfield'); - fireEvent(input, 'blur'); - - expect(mockSetCurrentFocusedInput).toHaveBeenCalledWith(null); - }); - - it('handles disabled input', () => { - const { getByTestId } = render(); - - const input = getByTestId(INPUT_TEST_ID); - expect(input.props.editable).toBe(false); - }); - - it('updates value when initialValue changes', () => { - mockGetValue.mockReturnValue('initial value'); - - const { getByDisplayValue, rerender } = render( - , - ); - - expect(getByDisplayValue('initial value')).toBeTruthy(); - - mockGetValue.mockReturnValue('updated value'); - rerender(); - - expect(getByDisplayValue('updated value')).toBeTruthy(); - }); - - it('maintains focus state when re-rendered', () => { - (useSnapInterfaceContext as jest.Mock).mockReturnValue({ - handleInputChange: mockHandleInputChange, - getValue: mockGetValue, - setCurrentFocusedInput: mockSetCurrentFocusedInput, - focusedInput: 'testInput', - }); - - const { getByTestId } = render(); - const input = getByTestId('textfield'); - - expect(input).toBeTruthy(); - expect(useSnapInterfaceContext().focusedInput).toBe('testInput'); - }); -}); From 7c185c57f21394b4134b395faf73434899a71287 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 16 Apr 2025 14:04:09 -0400 Subject: [PATCH 08/16] test: update test, create snapshot, move to components subfolder --- .../__snapshots__/input.test.ts.snap | 247 ++++++++++++++++++ .../SnapUIRenderer/components/input.test.ts | 176 +++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 app/components/Snaps/SnapUIRenderer/components/__snapshots__/input.test.ts.snap create mode 100644 app/components/Snaps/SnapUIRenderer/components/input.test.ts diff --git a/app/components/Snaps/SnapUIRenderer/components/__snapshots__/input.test.ts.snap b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/input.test.ts.snap new file mode 100644 index 000000000000..52e39382957e --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/input.test.ts.snap @@ -0,0 +1,247 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SnapUIInput handles disabled input 1`] = ` + + + + + + + + + + + + + + + + + + +`; + +exports[`SnapUIInput renders with initial value 1`] = ` + + + + + + + + + + + + + + + + + + +`; diff --git a/app/components/Snaps/SnapUIRenderer/components/input.test.ts b/app/components/Snaps/SnapUIRenderer/components/input.test.ts new file mode 100644 index 000000000000..47e52437daf3 --- /dev/null +++ b/app/components/Snaps/SnapUIRenderer/components/input.test.ts @@ -0,0 +1,176 @@ +import { renderInterface } from '../testUtils'; +import { Input, Box, Form } from '@metamask/snaps-sdk/jsx'; +import { fireEvent } from '@testing-library/react-native'; +import { INPUT_TEST_ID } from '../../../../component-library/components/Form/TextField/foundation/Input/Input.constants'; +import { TEXTFIELD_TEST_ID } from '../../../../component-library/components/Form/TextField/TextField.constants'; + +jest.mock('../../../../core/Engine/Engine', () => ({ + controllerMessenger: { + call: jest.fn(), + }, + context: { + SnapInterfaceController: { + updateInterfaceState: jest.fn(), + }, + }, +})); + +describe('SnapUIInput', () => { + const clearBorderColor = '#b7bbc8'; + const focusedBorderColor = '#4459ff'; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('renders with initial value', () => { + const { getByTestId, toJSON } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + }), + }), + { state: { testInput: 'initial value' } }, + ); + + const input = getByTestId(INPUT_TEST_ID); + + expect(input.props.value).toBe('initial value'); + expect(toJSON()).toMatchSnapshot(); + }); + + it('handles input changes', () => { + const { getByTestId } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + }), + }), + ); + + const input = getByTestId(INPUT_TEST_ID); + fireEvent.changeText(input, 'test'); + + expect(input.props.value).toBe('test'); + }); + + it('handles form input changes', () => { + const { getByTestId } = renderInterface( + Box({ + children: Form({ + name: 'testForm', + children: [Input({ name: 'testInput' })], + }), + }), + ); + + const input = getByTestId(INPUT_TEST_ID); + fireEvent.changeText(input, 'test'); + + expect(input.props.value).toBe('test'); + }); + + it('handles focus events', () => { + const { getByTestId } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + }), + }), + ); + + const input = getByTestId(INPUT_TEST_ID); + const textfield = getByTestId(TEXTFIELD_TEST_ID); + + const initialBorderColor = textfield.props.style.borderColor; + expect(initialBorderColor).toBe(clearBorderColor); + + fireEvent(input, 'focus'); + + const afterFocusBorderColor = textfield.props.style.borderColor; + expect(afterFocusBorderColor).toBe(focusedBorderColor); + }); + + it('handles blur events', () => { + const { getByTestId } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + }), + }), + ); + + const input = getByTestId(INPUT_TEST_ID); + const textfield = getByTestId(TEXTFIELD_TEST_ID); + + fireEvent(input, 'focus'); + + const afterFocusBorderColor = textfield.props.style.borderColor; + expect(afterFocusBorderColor).toBe(focusedBorderColor); + + fireEvent(input, 'blur'); + + const afterBlurBorderColor = textfield.props.style.borderColor; + expect(afterBlurBorderColor).toBe(clearBorderColor); + }); + + it('handles disabled input', () => { + const { getByTestId, toJSON } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + disabled: true, + }), + }), + ); + + const input = getByTestId(INPUT_TEST_ID); + expect(input.props.editable).toBe(false); + expect(toJSON()).toMatchSnapshot(); + }); + + it('updates value when initialValue changes', () => { + const { getByTestId, updateInterface, getByDisplayValue } = renderInterface( + Box({ children: Input({ name: 'testInput' }) }), + { state: { testInput: 'initial value' } } + ); + + const input = getByTestId(INPUT_TEST_ID); + expect(input.props.value).toBe('initial value'); + + updateInterface( + Box({ children: [Input({ name: 'testInput' }), Input({ name: 'testInput2' })] }), + { testInput: 'updated value' } + ); + + expect(getByDisplayValue('updated value')).toBeTruthy(); + }); + + it('maintains focus state when re-rendered', () => { + const { getAllByTestId, updateInterface } = renderInterface( + Box({ + children: Input({ + name: 'testInput', + }), + }), + ); + + const input = getAllByTestId(INPUT_TEST_ID)[0]; + const textfield = getAllByTestId(TEXTFIELD_TEST_ID)[0]; + + const initialBorderColor = textfield.props.style.borderColor; + expect(initialBorderColor).toBe(clearBorderColor); + + fireEvent(input, 'focus'); + + const afterFocusBorderColor = textfield.props.style.borderColor; + expect(afterFocusBorderColor).toBe(focusedBorderColor); + + updateInterface( + Box({ children: [Input({ name: 'testInput' }), Input({ name: 'testInput2' })] }) + ); + + const afterTextfield = getAllByTestId(TEXTFIELD_TEST_ID)[0]; + expect(afterTextfield.props.style.borderColor).toBe(focusedBorderColor); + }); +}); From 6a60735d75b53fe1782f66fa319534b8148ad76b Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 16 Apr 2025 14:43:38 -0400 Subject: [PATCH 09/16] chore: rename to ts and update snapshots --- .../{SnapUIRenderer.test.tsx => SnapUIRenderer.test.ts} | 0 .../{SnapUIRenderer.test.tsx.snap => SnapUIRenderer.test.ts.snap} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/components/Snaps/SnapUIRenderer/{SnapUIRenderer.test.tsx => SnapUIRenderer.test.ts} (100%) rename app/components/Snaps/SnapUIRenderer/__snapshots__/{SnapUIRenderer.test.tsx.snap => SnapUIRenderer.test.ts.snap} (100%) diff --git a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts similarity index 100% rename from app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx rename to app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts diff --git a/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap b/app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.ts.snap similarity index 100% rename from app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.tsx.snap rename to app/components/Snaps/SnapUIRenderer/__snapshots__/SnapUIRenderer.test.ts.snap From b9db97b8437040109ab67f99a85d7a3ff8d1c289 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 10:26:20 -0400 Subject: [PATCH 10/16] chore: remove dupe file --- .../SnapUIRenderer/SnapUIRenderer.test.tsx | 587 ------------------ 1 file changed, 587 deletions(-) delete mode 100644 app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx diff --git a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx deleted file mode 100644 index 6eed54da4786..000000000000 --- a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.tsx +++ /dev/null @@ -1,587 +0,0 @@ -import React from 'react'; -import { - Box, - Text, - Container, - Footer, - Button, - Input, - JSXElement, - Form, - Field, - Checkbox, - Section, - Row, - Value, - Card, - Image as ImageComponent, - Selector, - SelectorOption, -} from '@metamask/snaps-sdk/jsx'; -import { fireEvent, act } from '@testing-library/react-native'; -import renderWithProvider from '../../../util/test/renderWithProvider'; -import { SnapUIRenderer } from './SnapUIRenderer'; -import Engine from '../../../core/Engine/Engine'; -import { RootState } from '../../../reducers'; -import { FormState } from '@metamask/snaps-sdk'; -import { PayloadAction } from '@reduxjs/toolkit'; - -jest.mock('../../../core/Engine/Engine', () => ({ - controllerMessenger: { - call: jest.fn(), - }, - context: { - SnapInterfaceController: { - updateInterfaceState: jest.fn(), - }, - }, -})); - -const mockEngine = jest.mocked(Engine); - -const MOCK_SNAP_ID = 'npm:@metamask/test-snap-bip44'; -const MOCK_INTERFACE_ID = 'interfaceId'; - -const noOp = () => { - // no-op -}; - -function renderInterface( - content: JSXElement | null, - { useFooter = false, onCancel = noOp, state = {} } = {}, -) { - const storeState = { - engine: { - backgroundState: { - SubjectMetadataController: { - subjectMetadata: { - 'npm:@metamask/test-snap-bip44': { - name: '@metamask/test-snap-bip44', - version: '1.2.3', - subjectType: 'snap', - }, - }, - }, - SnapController: { - snaps: { - [MOCK_SNAP_ID]: { - id: 'npm:@metamask/test-snap-bip44', - origin: 'npm:@metamask/test-snap-bip44', - version: '5.1.2', - iconUrl: null, - initialPermissions: { - 'endowment:ethereum-provider': {}, - }, - manifest: { - description: 'An example Snap that signs messages using BLS.', - proposedName: 'BIP-44 Test Snap', - repository: { - type: 'git', - url: 'https://github.com/MetaMask/test-snaps.git', - }, - source: { - location: { - npm: { - filePath: 'dist/bundle.js', - packageName: '@metamask/test-snap-bip44', - registry: 'https://registry.npmjs.org', - }, - }, - shasum: 'L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU=', - }, - version: '5.1.2', - }, - versionHistory: [ - { - date: 1680686075921, - origin: 'https://metamask.github.io', - version: '5.1.2', - }, - ], - }, - }, - }, - SnapInterfaceController: { - interfaces: { - [MOCK_INTERFACE_ID]: { - snapId: MOCK_SNAP_ID, - content, - state, - context: null, - contentType: null, - }, - }, - }, - }, - }, - }; - - const result = renderWithProvider( - , - { state: storeState as unknown as RootState }, - ); - - const reducer = ( - reducerState: RootState, - action: PayloadAction<{ content: JSXElement; state: FormState }>, - ) => { - if (action.type === 'updateInterface') { - return { - engine: { - backgroundState: { - ...reducerState.engine.backgroundState, - SnapInterfaceController: { - interfaces: { - [MOCK_INTERFACE_ID]: { - snapId: MOCK_SNAP_ID, - content: action.payload.content, - state: action.payload.state ?? state, - context: null, - contentType: null, - }, - }, - }, - }, - }, - }; - } - return reducerState; - }; - - const { store } = result; - - // @ts-expect-error Mock reducer doesn't fully match the type. - store.replaceReducer(reducer); - - const updateInterface = ( - newContent: JSXElement, - newState: FormState | null = null, - ) => { - act(() => { - store.dispatch({ - type: 'updateInterface', - payload: { - content: newContent, - state: newState, - }, - }); - }); - }; - - const getRenderCount = () => - parseInt(result.getByTestId('performance').props['data-renders'], 10); - - return { ...result, updateInterface, getRenderCount }; -} - -describe('SnapUIRenderer', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('renders loading state', () => { - const { toJSON } = renderInterface(null); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders basic UI', () => { - const { toJSON, getByText, getRenderCount } = renderInterface( - Box({ children: Text({ children: 'Hello world!' }) }), - ); - - expect(getByText('Hello world!')).toBeDefined(); - expect(getRenderCount()).toBe(1); - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders footers', () => { - const { toJSON, getByText } = renderInterface( - Container({ - children: [ - Box({ children: Text({ children: 'Hello world!' }) }), - Footer({ children: Button({ children: 'Foo' }) }), - ], - }), - { useFooter: true }, - ); - - expect(getByText('Foo')).toBeDefined(); - expect(toJSON()).toMatchSnapshot(); - }); - - it('adds a footer if required', () => { - const { toJSON, getByText } = renderInterface( - Container({ - children: Box({ children: Text({ children: 'Hello world!' }) }), - }), - { useFooter: true }, - ); - - expect(getByText('Close')).toBeDefined(); - expect(toJSON()).toMatchSnapshot(); - }); - - it('supports the onCancel prop', () => { - const onCancel = jest.fn(); - const { toJSON, getByText } = renderInterface( - Container({ - children: [ - Box({ children: Text({ children: 'Hello world!' }) }), - Footer({ children: Button({ children: 'Foo' }) }), - ], - }), - { useFooter: true, onCancel }, - ); - - const button = getByText('Cancel'); - expect(button).toBeDefined(); - expect(toJSON()).toMatchSnapshot(); - - fireEvent.press(button); - expect(onCancel).toHaveBeenCalled(); - }); - - it('supports interactive inputs', () => { - const { toJSON, getByTestId } = renderInterface( - Box({ children: Input({ name: 'input' }) }), - ); - - const input = getByTestId('input'); - fireEvent.changeText(input, 'a'); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(1, MOCK_INTERFACE_ID, { input: 'a' }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 1, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { name: 'input', type: 'InputChangeEvent', value: 'a' }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('prefills interactive inputs with existing state', () => { - const { toJSON, getByTestId } = renderInterface( - Box({ children: Input({ name: 'input' }) }), - { state: { input: 'bar' } }, - ); - - const input = getByTestId('input'); - expect(input).toBeDefined(); - expect(input.props.value).toStrictEqual('bar'); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('re-renders when the interface changes', () => { - const { toJSON, getAllByTestId, updateInterface, getRenderCount } = - renderInterface( - Box({ children: Input({ name: 'input', type: 'number' }) }), - ); - - const inputs = getAllByTestId('input'); - expect(inputs).toHaveLength(1); - - updateInterface( - Box({ - children: [ - Input({ name: 'input', type: 'number' }), - Input({ name: 'input2', type: 'password' }), - ], - }), - ); - - const inputsAfterRerender = getAllByTestId('input'); - expect(inputsAfterRerender).toHaveLength(2); - - expect(getRenderCount()).toBe(2); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('re-syncs state when the interface changes', () => { - const { toJSON, getAllByTestId, getRenderCount, updateInterface } = - renderInterface(Box({ children: Input({ name: 'input' }) })); - - updateInterface( - Box({ children: [Input({ name: 'input' }), Input({ name: 'input2' })] }), - { input: 'bar', input2: 'foo' }, - ); - - const inputsAfterRerender = getAllByTestId('input'); - expect(inputsAfterRerender).toHaveLength(2); - expect(inputsAfterRerender[0].props.value).toStrictEqual('bar'); - expect(inputsAfterRerender[1].props.value).toStrictEqual('foo'); - - expect(getRenderCount()).toBe(2); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('supports forms with fields', () => { - const { toJSON, getByTestId, getByText } = renderInterface( - Box({ - children: Form({ - name: 'form', - children: [ - Field({ label: 'My Input', children: Input({ name: 'input' }) }), - Field({ - label: 'My Checkbox', - children: Checkbox({ - name: 'checkbox', - label: 'This is a checkbox', - }), - }), - Field({ - label: 'My Selector', - children: Selector({ - name: 'selector', - title: 'Select an option', - children: [ - SelectorOption({ - value: 'option1', - children: Card({ - title: 'CardTitle1', - description: 'CardDescription1', - value: 'CardValue1', - extra: 'CardExtra1', - }), - }), - SelectorOption({ - value: 'option2', - children: Card({ - title: 'CardTitle2', - description: 'CardDescription2', - value: 'CardValue2', - extra: 'CardExtra2', - }), - }), - ], - }), - }), - Button({ type: 'submit', name: 'submit', children: 'Submit' }), - ], - }), - }), - { state: { form: { selector: 'option1' } } }, - ); - - const input = getByTestId('input'); - fireEvent.changeText(input, 'abc'); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(1, MOCK_INTERFACE_ID, { - form: { input: 'abc', selector: 'option1' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 1, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { name: 'input', type: 'InputChangeEvent', value: 'abc' }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const checkbox = getByText('This is a checkbox'); - fireEvent.press(checkbox); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(2, MOCK_INTERFACE_ID, { - form: { input: 'abc', checkbox: true, selector: 'option1' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 2, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { name: 'checkbox', type: 'InputChangeEvent', value: true }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const selector = getByText('CardTitle1'); - fireEvent.press(selector); - - const selectorItem = getByText('CardTitle2'); - fireEvent.press(selectorItem); - - expect( - mockEngine.context.SnapInterfaceController.updateInterfaceState, - ).toHaveBeenNthCalledWith(3, MOCK_INTERFACE_ID, { - form: { input: 'abc', checkbox: true, selector: 'option2' }, - }); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 3, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { - name: 'selector', - type: 'InputChangeEvent', - value: 'option2', - }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - const button = getByText('Submit'); - fireEvent.press(button); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 4, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { name: 'submit', type: 'ButtonClickEvent' }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - expect(mockEngine.controllerMessenger.call).toHaveBeenNthCalledWith( - 5, - 'SnapController:handleRequest', - { - handler: 'onUserInput', - origin: 'metamask', - request: { - jsonrpc: '2.0', - method: ' ', - params: { - event: { - name: 'form', - type: 'FormSubmitEvent', - value: { - checkbox: true, - input: 'abc', - selector: 'option2', - }, - }, - id: MOCK_INTERFACE_ID, - }, - }, - snapId: MOCK_SNAP_ID, - }, - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('supports fields with multiple components', () => { - const { toJSON } = renderInterface( - Box({ - children: Form({ - name: 'form', - children: [ - Field({ - label: 'My Input', - children: [ - Box({ - children: [ - ImageComponent({ src: '' }), - ], - }), - Input({ name: 'input' }), - Button({ type: 'submit', name: 'submit', children: 'Submit' }), - ], - }), - ], - }), - }), - ); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('renders complex nested components', () => { - const { toJSON, getRenderCount } = renderInterface( - Container({ - children: [ - Box({ - children: [ - Section({ - children: [ - Row({ - label: 'Key', - children: Value({ value: 'Value', extra: 'Extra' }), - }), - Card({ - image: '', - title: 'CardTitle', - description: 'CardDescription', - value: 'CardValue', - extra: 'CardExtra', - }), - ], - }), - ], - }), - Footer({ children: Button({ children: 'Foo' }) }), - ], - }), - { useFooter: true }, - ); - - expect(getRenderCount()).toBe(1); - - expect(toJSON()).toMatchSnapshot(); - }); -}); From 8902b20e2f5e1bbfacb37e17ca9a5fd8c84274b4 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 10:44:08 -0400 Subject: [PATCH 11/16] fix: add correct origin to tests --- .../Snaps/SnapUIRenderer/SnapUIRenderer.test.ts | 2 +- .../Snaps/SnapUIRenderer/components/form.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts index c47336ae644a..826e89742fd7 100644 --- a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts +++ b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts @@ -115,7 +115,7 @@ describe('SnapUIRenderer', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', diff --git a/app/components/Snaps/SnapUIRenderer/components/form.test.ts b/app/components/Snaps/SnapUIRenderer/components/form.test.ts index e10c5772c66e..863806364d12 100644 --- a/app/components/Snaps/SnapUIRenderer/components/form.test.ts +++ b/app/components/Snaps/SnapUIRenderer/components/form.test.ts @@ -96,7 +96,7 @@ describe('SnapUIForm', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', @@ -124,7 +124,7 @@ describe('SnapUIForm', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', @@ -155,7 +155,7 @@ describe('SnapUIForm', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', @@ -181,7 +181,7 @@ describe('SnapUIForm', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', @@ -200,7 +200,7 @@ describe('SnapUIForm', () => { 'SnapController:handleRequest', { handler: 'onUserInput', - origin: '', + origin: 'metamask', request: { jsonrpc: '2.0', method: ' ', From 9835d01c94ecda6dac0b2f535fdc123e966e24a2 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 11:07:50 -0400 Subject: [PATCH 12/16] fix: remove null context from tests --- app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts | 1 - app/components/Snaps/SnapUIRenderer/components/form.test.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts index 826e89742fd7..3953ea8f8404 100644 --- a/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts +++ b/app/components/Snaps/SnapUIRenderer/SnapUIRenderer.test.ts @@ -120,7 +120,6 @@ describe('SnapUIRenderer', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'input', type: 'InputChangeEvent', value: 'a' }, id: MOCK_INTERFACE_ID, }, diff --git a/app/components/Snaps/SnapUIRenderer/components/form.test.ts b/app/components/Snaps/SnapUIRenderer/components/form.test.ts index 863806364d12..c20103f9f8c5 100644 --- a/app/components/Snaps/SnapUIRenderer/components/form.test.ts +++ b/app/components/Snaps/SnapUIRenderer/components/form.test.ts @@ -101,7 +101,6 @@ describe('SnapUIForm', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'input', type: 'InputChangeEvent', value: 'abc' }, id: MOCK_INTERFACE_ID, }, @@ -129,7 +128,6 @@ describe('SnapUIForm', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'checkbox', type: 'InputChangeEvent', value: true }, id: MOCK_INTERFACE_ID, }, @@ -160,7 +158,6 @@ describe('SnapUIForm', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'selector', type: 'InputChangeEvent', @@ -186,7 +183,6 @@ describe('SnapUIForm', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'submit', type: 'ButtonClickEvent' }, id: MOCK_INTERFACE_ID, }, @@ -205,7 +201,6 @@ describe('SnapUIForm', () => { jsonrpc: '2.0', method: ' ', params: { - context: null, event: { name: 'form', type: 'FormSubmitEvent', From 8c27550b94c6d384034d363710b18cac43cb7575 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 11:47:33 -0400 Subject: [PATCH 13/16] feat: add backgroundState option to renderInterface --- .../Snaps/SnapUIRenderer/testUtils.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/components/Snaps/SnapUIRenderer/testUtils.tsx b/app/components/Snaps/SnapUIRenderer/testUtils.tsx index 7cb6ce4af8bb..89ba2aa03150 100644 --- a/app/components/Snaps/SnapUIRenderer/testUtils.tsx +++ b/app/components/Snaps/SnapUIRenderer/testUtils.tsx @@ -17,18 +17,33 @@ interface RenderInterfaceOptions { contentBackgroundColor?: string; state?: Record; stateSettings?: Record; + backgroundState?: Record; } const noOp = () => { // no-op }; + +/** + * Renders a Snap UI interface. + * + * @param content - The content to render. + * @param options - The options for the render. + * @param options.useFooter - Whether to use the footer. + * @param options.onCancel - The function to call when the cancel button is pressed. + * @param options.state - The state to render. + * @param options.backgroundState - The background state to render. + * @param options.stateSettings - The state settings to render. + * @returns The rendered interface. + */ export function renderInterface( content: JSXElement | null, { useFooter = false, onCancel = noOp, state = {}, + backgroundState = {}, stateSettings = {}, }: RenderInterfaceOptions = {}, ) { @@ -36,6 +51,7 @@ export function renderInterface( settings: stateSettings, engine: { backgroundState: { + ...backgroundState, SubjectMetadataController: { subjectMetadata: { 'npm:@metamask/test-snap-bip44': { From 8c7a5fd9ac24e5d74c0df5fba3d0a95bea4e98ad Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 11:47:46 -0400 Subject: [PATCH 14/16] fix: update form test snapshot --- .../components/__snapshots__/form.test.ts.snap | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap index d4de995eafe4..15a8e24f6c88 100644 --- a/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap +++ b/app/components/Snaps/SnapUIRenderer/components/__snapshots__/form.test.ts.snap @@ -543,12 +543,10 @@ exports[`SnapUIForm will render with fields 1`] = ` [ { "flexDirection": "column", - "textAlign": "right", }, undefined, ] } - textAlign="right" > @@ -577,6 +576,7 @@ exports[`SnapUIForm will render with fields 1`] = ` "fontWeight": "400", "letterSpacing": 0, "lineHeight": 24, + "textAlign": "right", } } > @@ -980,12 +980,10 @@ exports[`SnapUIForm will render with fields 1`] = ` [ { "flexDirection": "column", - "textAlign": "right", }, undefined, ] } - textAlign="right" > @@ -1014,6 +1013,7 @@ exports[`SnapUIForm will render with fields 1`] = ` "fontWeight": "400", "letterSpacing": 0, "lineHeight": 24, + "textAlign": "right", } } > @@ -1130,12 +1130,10 @@ exports[`SnapUIForm will render with fields 1`] = ` [ { "flexDirection": "column", - "textAlign": "right", }, undefined, ] } - textAlign="right" > @@ -1164,6 +1163,7 @@ exports[`SnapUIForm will render with fields 1`] = ` "fontWeight": "400", "letterSpacing": 0, "lineHeight": 24, + "textAlign": "right", } } > From 6845192d25f3047163e9431c0d2b525460a53bbf Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 11:53:08 -0400 Subject: [PATCH 15/16] fix: update renderInterface JSDoc --- app/components/Snaps/SnapUIRenderer/testUtils.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/components/Snaps/SnapUIRenderer/testUtils.tsx b/app/components/Snaps/SnapUIRenderer/testUtils.tsx index 89ba2aa03150..bc26ddd68321 100644 --- a/app/components/Snaps/SnapUIRenderer/testUtils.tsx +++ b/app/components/Snaps/SnapUIRenderer/testUtils.tsx @@ -28,13 +28,13 @@ const noOp = () => { /** * Renders a Snap UI interface. * - * @param content - The content to render. - * @param options - The options for the render. - * @param options.useFooter - Whether to use the footer. - * @param options.onCancel - The function to call when the cancel button is pressed. - * @param options.state - The state to render. - * @param options.backgroundState - The background state to render. - * @param options.stateSettings - The state settings to render. + * @param content - The JSXElement to render. + * @param options - The options for rendering the interface. + * @param options.useFooter - Whether to render the footer. + * @param options.onCancel - The function to call when the interface is cancelled. + * @param options.state - The state of the interface. + * @param options.backgroundState - The initial background state. + * @param options.stateSettings - The initial state settings. * @returns The rendered interface. */ export function renderInterface( From dae0d66dec7eebdfb0e9f53fffa6fc80d5925c33 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Apr 2025 15:27:59 -0400 Subject: [PATCH 16/16] fix: use shorter import path --- app/components/Snaps/SnapUIRenderer/testUtils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Snaps/SnapUIRenderer/testUtils.tsx b/app/components/Snaps/SnapUIRenderer/testUtils.tsx index bc26ddd68321..4c1d633efb53 100644 --- a/app/components/Snaps/SnapUIRenderer/testUtils.tsx +++ b/app/components/Snaps/SnapUIRenderer/testUtils.tsx @@ -1,4 +1,4 @@ -import { JSXElement } from '@metamask/snaps-sdk/dist/jsx/index.cjs'; +import { JSXElement } from '@metamask/snaps-sdk/jsx'; import React from 'react'; import renderWithProvider from '../../../util/test/renderWithProvider'; import { RootState } from '../../../reducers';