mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
[FLASK] Add Snaps privacy warning on snap install (#18835)
* Add Snaps privacy warning on snap install Add snap install warning status to storage Add storybook Add test for snap-privacy-warning Resolve button type issue Fix popup display logic Update fixture Update popup information and read more handling Replace deprecated button Update unit test * Update buttons and add cancel flow * Refactoring (review 1) * Add more unit tests
This commit is contained in:
parent
49f8052b15
commit
f788121c3b
31
app/_locales/en/messages.json
generated
31
app/_locales/en/messages.json
generated
@ -654,6 +654,9 @@
|
|||||||
"clearActivityDescription": {
|
"clearActivityDescription": {
|
||||||
"message": "This resets the account's nonce and erases data from the activity tab in your wallet. Only the current account and network will be affected. Your balances and incoming transactions won't change."
|
"message": "This resets the account's nonce and erases data from the activity tab in your wallet. Only the current account and network will be affected. Your balances and incoming transactions won't change."
|
||||||
},
|
},
|
||||||
|
"click": {
|
||||||
|
"message": "Click"
|
||||||
|
},
|
||||||
"clickToConnectLedgerViaWebHID": {
|
"clickToConnectLedgerViaWebHID": {
|
||||||
"message": "Click here to connect your Ledger via WebHID",
|
"message": "Click here to connect your Ledger via WebHID",
|
||||||
"description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid"
|
"description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid"
|
||||||
@ -1562,6 +1565,10 @@
|
|||||||
"followUsOnTwitter": {
|
"followUsOnTwitter": {
|
||||||
"message": "Follow us on Twitter"
|
"message": "Follow us on Twitter"
|
||||||
},
|
},
|
||||||
|
"forMoreDetails": {
|
||||||
|
"message": "for more details.",
|
||||||
|
"description": "Click for more details message in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
"forbiddenIpfsGateway": {
|
"forbiddenIpfsGateway": {
|
||||||
"message": "Forbidden IPFS Gateway: Please specify a CID gateway"
|
"message": "Forbidden IPFS Gateway: Please specify a CID gateway"
|
||||||
},
|
},
|
||||||
@ -3843,9 +3850,29 @@
|
|||||||
"snapsNoInsight": {
|
"snapsNoInsight": {
|
||||||
"message": "The snap didn't return any insight"
|
"message": "The snap didn't return any insight"
|
||||||
},
|
},
|
||||||
|
"snapsPrivacyWarningFirstMessage": {
|
||||||
|
"message": "Installing a snap retrieves data from third parties. They may collect your personal information.",
|
||||||
|
"description": "First part of a message in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
|
"snapsPrivacyWarningSecondMessage": {
|
||||||
|
"message": "MetaMask has no access to this information.",
|
||||||
|
"description": "Second part of a message in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
"snapsSettingsDescription": {
|
"snapsSettingsDescription": {
|
||||||
"message": "Manage your Snaps"
|
"message": "Manage your Snaps"
|
||||||
},
|
},
|
||||||
|
"snapsThirdPartyNoticeReadMorePartOne": {
|
||||||
|
"message": "Any information you share with third-party-developed snaps will be collected directly by those snaps in accordance with their privacy policies. ",
|
||||||
|
"description": "First part of a tooltip content in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
|
"snapsThirdPartyNoticeReadMorePartThree": {
|
||||||
|
"message": "MetaMask has no access to information you share with these third parties.",
|
||||||
|
"description": "Third part of a tooltip content in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
|
"snapsThirdPartyNoticeReadMorePartTwo": {
|
||||||
|
"message": "During the installation of a snap, npmjs (npmjs.com) and AWS (aws.amazon.com) may collect your IP address. Please refer to their privacy policies for more information.",
|
||||||
|
"description": "Second part of a tooltip content in popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
"snapsToggle": {
|
"snapsToggle": {
|
||||||
"message": "A snap will only run if it is enabled"
|
"message": "A snap will only run if it is enabled"
|
||||||
},
|
},
|
||||||
@ -4466,6 +4493,10 @@
|
|||||||
"thingsToKeep": {
|
"thingsToKeep": {
|
||||||
"message": "Things to keep in mind:"
|
"message": "Things to keep in mind:"
|
||||||
},
|
},
|
||||||
|
"thirdPartySoftware": {
|
||||||
|
"message": "Third party software",
|
||||||
|
"description": "Title of a popup modal displayed when installing a snap for the first time."
|
||||||
|
},
|
||||||
"thisCollection": {
|
"thisCollection": {
|
||||||
"message": "this collection"
|
"message": "this collection"
|
||||||
},
|
},
|
||||||
|
@ -183,6 +183,20 @@ export default class AppStateController extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
/**
|
||||||
|
* Record if popover for snaps privacy warning has been shown
|
||||||
|
* on the first install of a snap.
|
||||||
|
*
|
||||||
|
* @param {boolean} shown - shown status
|
||||||
|
*/
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus(shown) {
|
||||||
|
this.store.updateState({
|
||||||
|
snapsInstallPrivacyWarningShown: shown,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record the timestamp of the last time the user has seen the outdated browser warning
|
* Record the timestamp of the last time the user has seen the outdated browser warning
|
||||||
*
|
*
|
||||||
|
@ -346,4 +346,23 @@ describe('AppStateController', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('setSnapsInstallPrivacyWarningShownStatus', () => {
|
||||||
|
it('updates the status of snaps install privacy warning', () => {
|
||||||
|
appStateController = createAppStateController();
|
||||||
|
const updateStateSpy = jest.spyOn(
|
||||||
|
appStateController.store,
|
||||||
|
'updateState',
|
||||||
|
);
|
||||||
|
|
||||||
|
appStateController.setSnapsInstallPrivacyWarningShownStatus(true);
|
||||||
|
|
||||||
|
expect(updateStateSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(updateStateSpy).toHaveBeenCalledWith({
|
||||||
|
snapsInstallPrivacyWarningShown: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateStateSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2207,6 +2207,12 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
),
|
),
|
||||||
setTermsOfUseLastAgreed:
|
setTermsOfUseLastAgreed:
|
||||||
appStateController.setTermsOfUseLastAgreed.bind(appStateController),
|
appStateController.setTermsOfUseLastAgreed.bind(appStateController),
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus:
|
||||||
|
appStateController.setSnapsInstallPrivacyWarningShownStatus.bind(
|
||||||
|
appStateController,
|
||||||
|
),
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
setOutdatedBrowserWarningLastShown:
|
setOutdatedBrowserWarningLastShown:
|
||||||
appStateController.setOutdatedBrowserWarningLastShown.bind(
|
appStateController.setOutdatedBrowserWarningLastShown.bind(
|
||||||
appStateController,
|
appStateController,
|
||||||
|
@ -158,6 +158,7 @@ function defaultFixture() {
|
|||||||
[CHAIN_IDS.GOERLI]: true,
|
[CHAIN_IDS.GOERLI]: true,
|
||||||
[CHAIN_IDS.LOCALHOST]: true,
|
[CHAIN_IDS.LOCALHOST]: true,
|
||||||
},
|
},
|
||||||
|
snapsInstallPrivacyWarningShown: true,
|
||||||
},
|
},
|
||||||
CachedBalancesController: {
|
CachedBalancesController: {
|
||||||
cachedBalances: {
|
cachedBalances: {
|
||||||
|
@ -13,6 +13,7 @@ import { PageContainerFooter } from '../../ui/page-container';
|
|||||||
import PermissionsConnectFooter from '../permissions-connect-footer';
|
import PermissionsConnectFooter from '../permissions-connect-footer';
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
import { RestrictedMethods } from '../../../../shared/constants/permissions';
|
import { RestrictedMethods } from '../../../../shared/constants/permissions';
|
||||||
|
import SnapPrivacyWarning from '../snaps/snap-privacy-warning';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
import { PermissionPageContainerContent } from '.';
|
import { PermissionPageContainerContent } from '.';
|
||||||
|
|
||||||
@ -24,6 +25,8 @@ export default class PermissionPageContainer extends Component {
|
|||||||
allIdentitiesSelected: PropTypes.bool,
|
allIdentitiesSelected: PropTypes.bool,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
currentPermissions: PropTypes.object,
|
currentPermissions: PropTypes.object,
|
||||||
|
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
request: PropTypes.object,
|
request: PropTypes.object,
|
||||||
requestMetadata: PropTypes.object,
|
requestMetadata: PropTypes.object,
|
||||||
@ -108,6 +111,12 @@ export default class PermissionPageContainer extends Component {
|
|||||||
caveats: [{ type: SnapCaveatType.SnapIds, value: dedupedCaveats }],
|
caveats: [{ type: SnapCaveatType.SnapIds, value: dedupedCaveats }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSnapsPrivacyWarning() {
|
||||||
|
this.setState({
|
||||||
|
isShowingSnapsPrivacyWarning: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
getRequestedMethodNames(props) {
|
getRequestedMethodNames(props) {
|
||||||
@ -123,6 +132,14 @@ export default class PermissionPageContainer extends Component {
|
|||||||
legacy_event: true,
|
legacy_event: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
if (this.props.request.permissions[WALLET_SNAP_PERMISSION_KEY]) {
|
||||||
|
if (this.props.snapsInstallPrivacyWarningShown === false) {
|
||||||
|
this.showSnapsPrivacyWarning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancel = () => {
|
onCancel = () => {
|
||||||
@ -167,8 +184,33 @@ export default class PermissionPageContainer extends Component {
|
|||||||
allIdentitiesSelected,
|
allIdentitiesSelected,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
const setIsShowingSnapsPrivacyWarning = (value) => {
|
||||||
|
this.setState({
|
||||||
|
isShowingSnapsPrivacyWarning: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmSnapsPrivacyWarning = () => {
|
||||||
|
setIsShowingSnapsPrivacyWarning(false);
|
||||||
|
this.props.setSnapsInstallPrivacyWarningShownStatus(true);
|
||||||
|
};
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container permission-approval-container">
|
<div className="page-container permission-approval-container">
|
||||||
|
{
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
<>
|
||||||
|
{this.state.isShowingSnapsPrivacyWarning && (
|
||||||
|
<SnapPrivacyWarning
|
||||||
|
onAccepted={() => confirmSnapsPrivacyWarning()}
|
||||||
|
onCanceled={() => this.onCancel()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
}
|
||||||
<PermissionPageContainerContent
|
<PermissionPageContainerContent
|
||||||
requestMetadata={requestMetadata}
|
requestMetadata={requestMetadata}
|
||||||
subjectMetadata={targetSubjectMetadata}
|
subjectMetadata={targetSubjectMetadata}
|
||||||
|
1
ui/components/app/snaps/snap-privacy-warning/index.js
Normal file
1
ui/components/app/snaps/snap-privacy-warning/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './snap-privacy-warning';
|
@ -0,0 +1,140 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||||
|
import Box from '../../../ui/box/box';
|
||||||
|
import Popover from '../../../ui/popover';
|
||||||
|
import {
|
||||||
|
AvatarIcon,
|
||||||
|
Button,
|
||||||
|
BUTTON_LINK_SIZES,
|
||||||
|
BUTTON_PRIMARY_SIZES,
|
||||||
|
BUTTON_VARIANT,
|
||||||
|
ButtonLink,
|
||||||
|
IconName,
|
||||||
|
IconSize,
|
||||||
|
Text,
|
||||||
|
} from '../../../component-library';
|
||||||
|
import {
|
||||||
|
AlignItems,
|
||||||
|
BackgroundColor,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
DISPLAY,
|
||||||
|
IconColor,
|
||||||
|
JustifyContent,
|
||||||
|
TextVariant,
|
||||||
|
} from '../../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
export default function SnapPrivacyWarning({ onAccepted, onCanceled }) {
|
||||||
|
const t = useI18nContext();
|
||||||
|
const [isDescriptionOpen, setIsDescriptionOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleReadMoreClick = () => {
|
||||||
|
setIsDescriptionOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover className="snap-privacy-warning">
|
||||||
|
<Box padding={4}>
|
||||||
|
<Box
|
||||||
|
className="snap-privacy-warning__info-icon"
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
>
|
||||||
|
<AvatarIcon
|
||||||
|
iconName={IconName.Info}
|
||||||
|
color={IconColor.infoDefault}
|
||||||
|
backgroundColor={BackgroundColor.primaryMuted}
|
||||||
|
size={IconSize.Md}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
className="snap-privacy-warning__title"
|
||||||
|
marginTop={4}
|
||||||
|
marginBottom={6}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
justifyContent={JustifyContent.center}
|
||||||
|
alignItems={AlignItems.center}
|
||||||
|
>
|
||||||
|
<Text variant={TextVariant.headingMd}>{t('thirdPartySoftware')}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box className="snap-privacy-warning__message">
|
||||||
|
<Text variant={TextVariant.bodyMd}>
|
||||||
|
{t('snapsPrivacyWarningFirstMessage')}
|
||||||
|
</Text>
|
||||||
|
{!isDescriptionOpen && (
|
||||||
|
<>
|
||||||
|
<Text variant={TextVariant.bodyMd} paddingTop={6}>
|
||||||
|
{t('snapsPrivacyWarningSecondMessage')}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
variant={TextVariant.bodyMd}
|
||||||
|
className="snap-privacy-warning__more-details"
|
||||||
|
>
|
||||||
|
{t('click')}
|
||||||
|
<ButtonLink
|
||||||
|
size={BUTTON_LINK_SIZES.INHERIT}
|
||||||
|
onClick={handleReadMoreClick}
|
||||||
|
data-testid="snapsPrivacyPopup_readMoreButton"
|
||||||
|
>
|
||||||
|
{t('here')}
|
||||||
|
</ButtonLink>
|
||||||
|
{t('forMoreDetails')}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isDescriptionOpen && (
|
||||||
|
<>
|
||||||
|
<Text variant={TextVariant.bodyMd} paddingTop={6}>
|
||||||
|
{t('snapsThirdPartyNoticeReadMorePartOne')}
|
||||||
|
</Text>
|
||||||
|
<Text variant={TextVariant.bodyMd} paddingTop={6}>
|
||||||
|
{t('snapsThirdPartyNoticeReadMorePartTwo')}
|
||||||
|
</Text>
|
||||||
|
<Text variant={TextVariant.bodyMd} paddingTop={6}>
|
||||||
|
{t('snapsThirdPartyNoticeReadMorePartThree')}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
className="snap-privacy-warning__ok-button"
|
||||||
|
marginTop={6}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant={BUTTON_VARIANT.SECONDARY}
|
||||||
|
size={BUTTON_PRIMARY_SIZES.LG}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
className="snap-privacy-warning__cancel-button"
|
||||||
|
onClick={onCanceled}
|
||||||
|
marginRight={2}
|
||||||
|
>
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={BUTTON_VARIANT.PRIMARY}
|
||||||
|
size={BUTTON_PRIMARY_SIZES.LG}
|
||||||
|
width={BLOCK_SIZES.FULL}
|
||||||
|
className="snap-privacy-warning__ok-button"
|
||||||
|
onClick={onAccepted}
|
||||||
|
marginLeft={2}
|
||||||
|
>
|
||||||
|
{t('accept')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapPrivacyWarning.propTypes = {
|
||||||
|
/**
|
||||||
|
* onAccepted handler
|
||||||
|
*/
|
||||||
|
onAccepted: PropTypes.func.isRequired,
|
||||||
|
/**
|
||||||
|
* onCanceled handler
|
||||||
|
*/
|
||||||
|
onCanceled: PropTypes.func.isRequired,
|
||||||
|
};
|
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import SnapPrivacyWarning from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/snaps/SnapPrivacyWarning',
|
||||||
|
component: SnapPrivacyWarning,
|
||||||
|
argTypes: {
|
||||||
|
onAccepted: {
|
||||||
|
action: 'onAccepted',
|
||||||
|
},
|
||||||
|
onCanceled: {
|
||||||
|
action: 'onCanceled',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SnapPrivacyWarning {...args} />;
|
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
DefaultStory.args = {};
|
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { renderWithProvider } from '../../../../../test/jest';
|
||||||
|
import SnapPrivacyWarning from './snap-privacy-warning';
|
||||||
|
|
||||||
|
describe('Snap Privacy Warning Popover', () => {
|
||||||
|
it('renders snaps privacy warning popover and works with accept flow', () => {
|
||||||
|
const mockOnAcceptCallback = jest.fn();
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<SnapPrivacyWarning
|
||||||
|
onAccepted={mockOnAcceptCallback}
|
||||||
|
onCanceled={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Third party software')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Installing a snap retrieves data from third parties. They may collect your personal information.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText('MetaMask has no access to this information.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
const clickHereToReadMoreButton = getByTestId(
|
||||||
|
'snapsPrivacyPopup_readMoreButton',
|
||||||
|
);
|
||||||
|
expect(clickHereToReadMoreButton).toBeDefined();
|
||||||
|
clickHereToReadMoreButton.click();
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'Any information you share with third-party-developed snaps will be collected directly by those snaps in accordance with their privacy policies.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
'During the installation of a snap, npmjs (npmjs.com) and AWS (aws.amazon.com) may collect your IP address. Please refer to their privacy policies for more information.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: /Accept/iu,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
screen
|
||||||
|
.getByRole('button', {
|
||||||
|
name: /Accept/iu,
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
expect(mockOnAcceptCallback).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders snaps privacy warning popover and works with cancel flow', () => {
|
||||||
|
const mockOnAcceptCallback = jest.fn();
|
||||||
|
const mockOnCanceledCallback = jest.fn();
|
||||||
|
renderWithProvider(
|
||||||
|
<SnapPrivacyWarning
|
||||||
|
onAccepted={mockOnAcceptCallback}
|
||||||
|
onCanceled={mockOnCanceledCallback}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Third party software')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: /Cancel/iu,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
screen
|
||||||
|
.getByRole('button', {
|
||||||
|
name: /Cancel/iu,
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
expect(mockOnCanceledCallback).toHaveBeenCalled();
|
||||||
|
expect(mockOnAcceptCallback).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -67,6 +67,9 @@ interface AppState {
|
|||||||
customTokenAmount: string;
|
customTokenAmount: string;
|
||||||
txId: number | null;
|
txId: number | null;
|
||||||
accountDetailsAddress: string;
|
accountDetailsAddress: string;
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
snapsInstallPrivacyWarningShown: boolean;
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AppSliceState {
|
interface AppSliceState {
|
||||||
@ -132,6 +135,9 @@ const initialState: AppState = {
|
|||||||
scrollToBottom: true,
|
scrollToBottom: true,
|
||||||
txId: null,
|
txId: null,
|
||||||
accountDetailsAddress: '',
|
accountDetailsAddress: '',
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
snapsInstallPrivacyWarningShown: false,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function reduceApp(
|
export default function reduceApp(
|
||||||
|
@ -46,6 +46,8 @@ export default class PermissionConnect extends Component {
|
|||||||
requestState: PropTypes.object.isRequired,
|
requestState: PropTypes.object.isRequired,
|
||||||
approvePendingApproval: PropTypes.func.isRequired,
|
approvePendingApproval: PropTypes.func.isRequired,
|
||||||
rejectPendingApproval: PropTypes.func.isRequired,
|
rejectPendingApproval: PropTypes.func.isRequired,
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func.isRequired,
|
||||||
|
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
totalPages: PropTypes.string.isRequired,
|
totalPages: PropTypes.string.isRequired,
|
||||||
page: PropTypes.string.isRequired,
|
page: PropTypes.string.isRequired,
|
||||||
@ -76,6 +78,9 @@ export default class PermissionConnect extends Component {
|
|||||||
permissionsApproved: null,
|
permissionsApproved: null,
|
||||||
origin: this.props.origin,
|
origin: this.props.origin,
|
||||||
targetSubjectMetadata: this.props.targetSubjectMetadata || {},
|
targetSubjectMetadata: this.props.targetSubjectMetadata || {},
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
snapsInstallPrivacyWarningShown: this.props.snapsInstallPrivacyWarningShown,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeUnload = () => {
|
beforeUnload = () => {
|
||||||
@ -298,6 +303,7 @@ export default class PermissionConnect extends Component {
|
|||||||
requestState,
|
requestState,
|
||||||
approvePendingApproval,
|
approvePendingApproval,
|
||||||
rejectPendingApproval,
|
rejectPendingApproval,
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
@ -305,6 +311,9 @@ export default class PermissionConnect extends Component {
|
|||||||
permissionsApproved,
|
permissionsApproved,
|
||||||
redirecting,
|
redirecting,
|
||||||
targetSubjectMetadata,
|
targetSubjectMetadata,
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
snapsInstallPrivacyWarningShown,
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -356,6 +365,14 @@ export default class PermissionConnect extends Component {
|
|||||||
selectedAccountAddresses.has(account.address),
|
selectedAccountAddresses.has(account.address),
|
||||||
)}
|
)}
|
||||||
targetSubjectMetadata={targetSubjectMetadata}
|
targetSubjectMetadata={targetSubjectMetadata}
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
snapsInstallPrivacyWarningShown={
|
||||||
|
snapsInstallPrivacyWarningShown
|
||||||
|
}
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus={
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
getSnapInstallOrUpdateRequests,
|
getSnapInstallOrUpdateRequests,
|
||||||
getRequestState,
|
getRequestState,
|
||||||
|
getSnapsInstallPrivacyWarningShown,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
getRequestType,
|
getRequestType,
|
||||||
getTargetSubjectMetadata,
|
getTargetSubjectMetadata,
|
||||||
@ -24,6 +25,7 @@ import {
|
|||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
resolvePendingApproval,
|
resolvePendingApproval,
|
||||||
rejectPendingApproval,
|
rejectPendingApproval,
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
} from '../../store/actions';
|
} from '../../store/actions';
|
||||||
import {
|
import {
|
||||||
@ -133,6 +135,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
snapResultPath,
|
snapResultPath,
|
||||||
requestState,
|
requestState,
|
||||||
isSnap,
|
isSnap,
|
||||||
|
snapsInstallPrivacyWarningShown: getSnapsInstallPrivacyWarningShown(state),
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
permissionsRequest,
|
permissionsRequest,
|
||||||
permissionsRequestId,
|
permissionsRequestId,
|
||||||
@ -162,6 +165,9 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
dispatch(resolvePendingApproval(id, value)),
|
dispatch(resolvePendingApproval(id, value)),
|
||||||
rejectPendingApproval: (id, error) =>
|
rejectPendingApproval: (id, error) =>
|
||||||
dispatch(rejectPendingApproval(id, error)),
|
dispatch(rejectPendingApproval(id, error)),
|
||||||
|
setSnapsInstallPrivacyWarningShownStatus: (shown) => {
|
||||||
|
dispatch(setSnapsInstallPrivacyWarningShownStatus(shown));
|
||||||
|
},
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
showNewAccountModal: ({ onCreateNewAccount, newAccountNumber }) => {
|
showNewAccountModal: ({ onCreateNewAccount, newAccountNumber }) => {
|
||||||
return dispatch(
|
return dispatch(
|
||||||
|
@ -1492,4 +1492,23 @@ export function getSnapsList(state) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the state of snaps privacy warning popover.
|
||||||
|
*
|
||||||
|
* @param state - Redux state object.
|
||||||
|
* @returns True if popover has been shown, false otherwise.
|
||||||
|
*/
|
||||||
|
export function getSnapsInstallPrivacyWarningShown(state) {
|
||||||
|
const { snapsInstallPrivacyWarningShown } = state.metamask;
|
||||||
|
|
||||||
|
if (
|
||||||
|
snapsInstallPrivacyWarningShown === undefined ||
|
||||||
|
snapsInstallPrivacyWarningShown === null
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapsInstallPrivacyWarningShown;
|
||||||
|
}
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
@ -469,4 +469,18 @@ describe('Selectors', () => {
|
|||||||
)(mockState);
|
)(mockState);
|
||||||
expect(isFantomTokenSupported).toBeFalsy();
|
expect(isFantomTokenSupported).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns proper values for snaps privacy warning shown status', () => {
|
||||||
|
mockState.metamask.snapsInstallPrivacyWarningShown = false;
|
||||||
|
expect(selectors.getSnapsInstallPrivacyWarningShown(mockState)).toBe(false);
|
||||||
|
|
||||||
|
mockState.metamask.snapsInstallPrivacyWarningShown = true;
|
||||||
|
expect(selectors.getSnapsInstallPrivacyWarningShown(mockState)).toBe(true);
|
||||||
|
|
||||||
|
mockState.metamask.snapsInstallPrivacyWarningShown = undefined;
|
||||||
|
expect(selectors.getSnapsInstallPrivacyWarningShown(mockState)).toBe(false);
|
||||||
|
|
||||||
|
mockState.metamask.snapsInstallPrivacyWarningShown = null;
|
||||||
|
expect(selectors.getSnapsInstallPrivacyWarningShown(mockState)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4678,3 +4678,20 @@ export async function getCurrentNetworkEIP1559Compatibility(): Promise<
|
|||||||
}
|
}
|
||||||
return networkEIP1559Compatibility;
|
return networkEIP1559Compatibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
|
/**
|
||||||
|
* Set status of popover warning for the first snap installation.
|
||||||
|
*
|
||||||
|
* @param shown - True if popover has been shown.
|
||||||
|
* @returns Promise Resolved on successfully submitted background request.
|
||||||
|
*/
|
||||||
|
export function setSnapsInstallPrivacyWarningShownStatus(shown: boolean) {
|
||||||
|
return async () => {
|
||||||
|
await submitRequestToBackground(
|
||||||
|
'setSnapsInstallPrivacyWarningShownStatus',
|
||||||
|
[shown],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
Loading…
Reference in New Issue
Block a user