Skip to content

Commit

Permalink
[Flask] Use permissions controller as source of truth for Snaps Permi…
Browse files Browse the repository at this point in the history
…ssions UI (#6533)

* create custom component for snaps cell

* rendering permissions from permissions controller

* bip32

* testing

* use a switch statement

* dont check for the types

* handler functions

* fix snapsettings test

* cleanup

* function to check the types

* readme update
  • Loading branch information
owencraston committed Jun 14, 2023
1 parent ec12c11 commit ff739b5
Show file tree
Hide file tree
Showing 14 changed files with 1,023 additions and 397 deletions.
5 changes: 1 addition & 4 deletions app/components/Views/Snaps/SnapSettings/SnapSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ const SnapSettings = () => {
/>
</View>
<View style={styles.itemPaddedContainer}>
<SnapPermissions
permissions={snap.initialPermissions}
installedAt={snap.versionHistory[0].date}
/>
<SnapPermissions snapId={snap.id} />
</View>
<View style={styles.removeSection}>
<Text variant={TextVariant.HeadingMD}>
Expand Down
95 changes: 91 additions & 4 deletions app/components/Views/Snaps/SnapSettings/test/SnapSettings.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import { fireEvent, waitFor } from '@testing-library/react-native';
import { SemVerVersion, Status } from '@metamask/snaps-utils';
import SnapSettings from '../SnapSettings';
import {
Expand All @@ -9,6 +9,11 @@ import {
SNAP_SETTINGS_REMOVE_BUTTON,
} from '../../../../../constants/test-ids';
import Engine from '../../../../../core/Engine';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import {
PermissionConstraint,
SubjectPermissions,
} from '@metamask/permission-controller';

jest.mock('../../../../../core/Engine', () => ({
context: {
Expand Down Expand Up @@ -93,6 +98,67 @@ jest.mock('../../../../../util/navigation/navUtils', () => ({
createNavigationDetails: jest.fn(),
}));

const mockDate = 1684964145490;
const mockDate2 = 1686081721987;

const mockPermissions: SubjectPermissions<PermissionConstraint> = {
'endowment:network-access': {
id: 'Bjj3InYtb6U4ak-uja0f_',
parentCapability: 'endowment:network-access',
invoker: 'npm:@chainsafe/filsnap',
caveats: null,
date: mockDate,
},
'endowment:rpc': {
id: 'Zma-vejrSvLtHmLrbSBAX',
parentCapability: 'endowment:rpc',
invoker: 'npm:@chainsafe/filsnap',
caveats: [
{
type: 'rpcOrigin',
value: {
dapps: true,
snaps: true,
},
},
],
date: mockDate2,
},
snap_confirm: {
id: 'tVtSEUjc48Ab-gF6UI7X3',
parentCapability: 'snap_confirm',
invoker: 'npm:@chainsafe/filsnap',
caveats: null,
date: mockDate2,
},
snap_manageState: {
id: 'BKbg3uDSHHu0D1fCUTOmS',
parentCapability: 'snap_manageState',
invoker: 'npm:@chainsafe/filsnap',
caveats: null,
date: mockDate2,
},
snap_getBip44Entropy: {
id: 'MuqnOW-7BRg94sRDmVnDK',
parentCapability: 'snap_getBip44Entropy',
invoker: 'npm:@chainsafe/filsnap',
caveats: [
{
type: 'permittedCoinTypes',
value: [
{
coinType: 1,
},
{
coinType: 461,
},
],
},
],
date: mockDate2,
},
};

const mockGoBack = jest.fn();
jest.mock('@react-navigation/native', () => {
const actualReactNavigation = jest.requireActual('@react-navigation/native');
Expand All @@ -105,9 +171,28 @@ jest.mock('@react-navigation/native', () => {
};
});

const engineState = {
engine: {
backgroundState: {
PermissionController: {
subjects: {
'npm:@chainsafe/filsnap': {
permissions: mockPermissions,
},
},
},
},
},
};

describe('SnapSettings', () => {
it('renders correctly', () => {
const { getByTestId, getAllByTestId } = render(<SnapSettings />);
const { getAllByTestId, getByTestId } = renderWithProvider(
<SnapSettings />,
{
state: engineState,
},
);

const removeButton = getByTestId(SNAP_SETTINGS_REMOVE_BUTTON);
const description = getByTestId(SNAP_DETAILS_CELL);
Expand All @@ -116,14 +201,16 @@ describe('SnapSettings', () => {
expect(removeButton).toBeTruthy();
expect(description).toBeTruthy();
expect(permissionContainer).toBeTruthy();
expect(permissions.length).toBe(5);
expect(permissions.length).toBe(7);
expect(removeButton.props.children[1].props.children).toBe(
'Remove Filsnap',
);
});

it('remove snap and goes back when Remove button is pressed', async () => {
const { getByTestId } = render(<SnapSettings />);
const { getByTestId } = renderWithProvider(<SnapSettings />, {
state: engineState,
});

const removeButton = getByTestId(SNAP_SETTINGS_REMOVE_BUTTON);
fireEvent(removeButton, 'onPress');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { SNAP_ElEMENT } from '../../../../../constants/test-ids';
import { createSnapSettingsNavDetails } from '../../SnapSettings/SnapSettings';

jest.mock('react-redux', () => ({
useSelector: jest.fn(),
}));

const mockNavigate = jest.fn();
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { StyleSheet } from 'react-native';
import { Theme } from '../../../../../util/theme/models';

/**
*
* @param params Style sheet params.
* @param params.theme App theme from ThemeContext.
* @param params.vars Inputs that the style sheet depends on.
* @returns StyleSheet object.
*/
const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;
const { colors } = theme;
return StyleSheet.create({
permissionCell: {
borderRadius: 10,
borderWidth: 0,
},
cellBase: {
flexDirection: 'row',
},
cellBaseInfo: {
flex: 1,
alignItems: 'flex-start',
},
secondaryText: {
color: colors.text.alternative,
},
iconWrapper: {
marginTop: 16,
marginRight: 16,
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: colors.background.alternative,
justifyContent: 'center',
alignItems: 'center',
},
});
};
export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useMemo } from 'react';
import { View } from 'react-native';
import { useStyles } from '../../../../hooks/useStyles';
import stylesheet from './SnapPermissionCell.styles';
import {
SNAP_PERMISSIONS_DATE,
SNAP_PERMISSIONS_TITLE,
SNAP_PERMISSION_CELL,
} from '../../../../../constants/test-ids';
import Icon, {
IconColor,
IconName,
IconSize,
} from '../../../../../component-library/components/Icons/Icon';
import Card from '../../../../../component-library/components/Cards/Card';
import Text, {
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import { strings } from '../../../../../../locales/i18n';
import { toDateFormat } from '../../../../../util/date';

interface SnapPermissionCellProps {
title: string;
date: number;
}

const SnapPermissionCell = ({ title, date }: SnapPermissionCellProps) => {
const snapInstalledDate: string = useMemo(
() =>
strings('app_settings.snaps.snap_permissions.approved_date', {
date: toDateFormat(date),
}),
[date],
);

const { styles } = useStyles(stylesheet, {});
return (
<Card style={styles.permissionCell}>
<View testID={SNAP_PERMISSION_CELL} style={styles.cellBase}>
<View style={styles.iconWrapper}>
<Icon
name={IconName.Key}
size={IconSize.Md}
color={IconColor.Muted}
/>
</View>
<View style={styles.cellBaseInfo}>
<Text
testID={SNAP_PERMISSIONS_TITLE}
numberOfLines={2}
variant={TextVariant.HeadingSMRegular}
>
{title}
</Text>
<Text
testID={SNAP_PERMISSIONS_DATE}
numberOfLines={1}
variant={TextVariant.BodyMD}
style={styles.secondaryText}
>
{snapInstalledDate}
</Text>
</View>
</View>
</Card>
);
};

export default React.memo(SnapPermissionCell);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* eslint-disable import/prefer-default-export */
import SnapPermissionCell from './SnapPermissionCell';

export { SnapPermissionCell };
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { render } from '@testing-library/react-native';
import SnapPermissionCell from '../SnapPermissionCell';
import {
SNAP_PERMISSIONS_DATE,
SNAP_PERMISSIONS_TITLE,
SNAP_PERMISSION_CELL,
} from '../../../../../../constants/test-ids';

describe('SnapPermissionCell', () => {
const defaultProps = {
title: 'Permission Title',
date: 1686005090788,
};

const setup = (props = defaultProps) => {
const utils = render(<SnapPermissionCell {...props} />);
const permissionCell = utils.getByTestId(SNAP_PERMISSION_CELL);
const permissionTitle = utils.getByTestId(SNAP_PERMISSIONS_TITLE);
const permissionDate = utils.getByTestId(SNAP_PERMISSIONS_DATE);

return {
...utils,
permissionCell,
permissionTitle,
permissionDate,
};
};

test('renders correctly', () => {
const { permissionCell, permissionTitle, permissionDate } = setup();

const expectedDate = 'Approved on Jun 5 at 6:44 pm';

expect(permissionCell).toBeDefined();
expect(permissionTitle.props.children).toEqual(defaultProps.title);
expect(permissionDate.props.children).toEqual(expectedDate);
});

test('displays custom title and secondary text', () => {
const customProps = {
title: 'Custom Title',
date: 1686005090788,
};
const expectedDate = 'Approved on Jun 5 at 6:44 pm';
const { permissionTitle, permissionDate } = setup(customProps);

expect(permissionTitle.props.children).toEqual(customProps.title);
expect(permissionDate.props.children).toEqual(expectedDate);
});
});
Loading

0 comments on commit ff739b5

Please sign in to comment.