Skip to content

Commit

Permalink
Merge branch 'develop' into jl/added-generic-polling-hook-replace-use…
Browse files Browse the repository at this point in the history
…Safe
  • Loading branch information
adonesky1 authored Mar 8, 2024
2 parents 56fedb4 + 6c810aa commit d7b1e5e
Show file tree
Hide file tree
Showing 79 changed files with 3,003 additions and 100 deletions.
44 changes: 37 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,32 @@ For Jest debugging guide using Node.js, see [docs/tests/jest.md](docs/tests/jest

### Running E2E Tests

Our e2e test suite can be run on either Firefox or Chrome.
Our e2e test suite can be run on either Firefox or Chrome. Here's how to get started with e2e testing:

1. **required** `yarn build:test` to create a test build.
2. run tests, targeting the browser:
#### Preparing a Test Build

- Firefox e2e tests can be run with `yarn test:e2e:firefox`.
- Chrome e2e tests can be run with `yarn test:e2e:chrome`. The `chromedriver` package major version must match the major version of your local Chrome installation. If they don't match, update whichever is behind before running Chrome e2e tests.
Before running e2e tests, ensure you've run `yarn install` to download dependencies. Next, you'll need a test build. You have 3 options:

These test scripts all support additional options, which might be helpful for debugging. Run the script with the flag `--help` to see all options.
1. Use `yarn download-builds:test` to quickly download and unzip test builds for Chrome and Firefox into the `./dist/` folder. This method is fast and convenient for standard testing.
2. Create a custom test build: for testing against different build types, use `yarn build:test`. This command allows you to generate test builds for various types, including:
- `yarn build:test` for main build
- `yarn build:test:flask` for flask build
- `yarn build:test:mmi` for mmi build
- `yarn build:test:mv3` for mv3 build
3. Start a test build with live changes: `yarn start:test` is particularly useful for development. It starts a test build that automatically recompiles application code upon changes.This option is ideal for iterative testing and development.
This command also allows you to generate test builds for various types, including:
- `yarn start:test` for main build
- `yarn start:test:flask` for flask build
- `yarn start:test:mv3` for mv3 build

Note: The `yarn start:test` command (which initiates the testDev build type) has LavaMoat disabled for both the build system and the application, offering a streamlined testing experience during development. On the other hand, `yarn build:test` enables LavaMoat for enhanced security in both the build system and application, mirroring production environments more closely.

#### Running Tests
Once you have your test build ready, choose the browser for your e2e tests:
- For Firefox, run `yarn test:e2e:firefox`.
- For Chrome, run `yarn test:e2e:chrome`.

These scripts support additional options for debugging. Use `--help`to see all available options.

#### Running a single e2e test

Expand All @@ -138,7 +155,20 @@ Single e2e tests can be run with `yarn test:e2e:single test/e2e/tests/TEST_NAME.
```

For example, to run the `account-details` tests using Chrome, with debug logging and with the browser set to remain open upon failure, you would use:
`yarn test:e2e:single test/e2e/tests/account-details.spec.js --browser=chrome --debug --leave-running`
`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome --debug --leave-running`


#### Running e2e tests against specific feature flag
While developing new features, we often use feature flags. As we prepare to make these features generally available (GA), we remove the feature flags. Existing feature flags are listed in the `.metamaskrc.dist` file. To execute e2e tests with a particular feature flag enabled, it's necessary to first generate a test build with that feature flag activated. There are two ways to achieve this:

- To enable a feature flag in your local configuration, you should first ensure you have a `.metamaskrc` file copied from `.metamaskrc.dist`. Then, within your local `.metamaskrc` file, you can set the desired feature flag to true. Following this, a test build with the feature flag enabled can be created by executing `yarn build:test`.

- Alternatively, for enabling a feature flag directly during the test build creation, you can pass the parameter as true via the command line. For instance, activating the MULTICHAIN feature flag can be done by running `MULTICHAIN=1 yarn build:test` or `MULTICHAIN=1 yarn start:test` . This method allows for quick adjustments to feature flags without altering the `.metamaskrc` file.

Once you've created a test build with the desired feature flag enabled, proceed to run your tests as usual. Your tests will now run against the version of the extension with the specific feature flag activated. For example:
`yarn test:e2e:single test/e2e/tests/account-menu/account-details.spec.js --browser=chrome --debug --leave-running`

This approach ensures that your e2e tests accurately reflect the user experience for the upcoming GA features.

#### Running specific builds types e2e test

Expand Down
19 changes: 19 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion shared/constants/permissions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ describe('EndowmentPermissions', () => {
expect(Object.keys(EndowmentPermissions).sort()).toStrictEqual(
[
'endowment:name-lookup',
'endowment:page-home',
'endowment:signature-insight',
...Object.keys(endowmentPermissionBuilders).filter(
(targetName) =>
Expand Down
4 changes: 1 addition & 3 deletions shared/constants/snaps/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export const EndowmentPermissions = Object.freeze({
'endowment:rpc': 'endowment:rpc',
'endowment:webassembly': 'endowment:webassembly',
'endowment:lifecycle-hooks': 'endowment:lifecycle-hooks',
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
'endowment:page-home': 'endowment:page-home',
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
'endowment:signature-insight': 'endowment:signature-insight',
'endowment:name-lookup': 'endowment:name-lookup',
///: END:ONLY_INCLUDE_IF
Expand All @@ -24,8 +24,6 @@ export const ExcludedSnapPermissions = Object.freeze({

export const ExcludedSnapEndowments = Object.freeze({
///: BEGIN:ONLY_INCLUDE_IF(build-main)
'endowment:page-home':
'This endowment is experimental and therefore not available.',
'endowment:name-lookup':
'This endowment is experimental and therefore not available.',
'endowment:signature-insight':
Expand Down
2 changes: 2 additions & 0 deletions shared/constants/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ export const LISTED_CONTRACT_ADDRESSES = Object.keys(contractMap).map(
* @property {number} [decimals] - The number of decimals of the selected
* 'ERC20' asset.
* @property {number} [tokenId] - The id of the selected 'NFT' asset.
* @property {string} [image] - A URL to the image of the NFT or ERC20 icon.
* @property {TokenStandardStrings} [standard] - The standard of the selected
* asset.
* @property {boolean} [isERC721] - True when the asset is a ERC721 token.
*/

export const STATIC_MAINNET_TOKEN_LIST = Object.keys(contractMap).reduce(
(acc, base) => {
const { logo, ...tokenMetadata } = contractMap[base];
Expand Down
1 change: 0 additions & 1 deletion test/e2e/run-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const { loadBuildTypesConfig } = require('../../development/lib/build-type');
const FLASK_ONLY_TESTS = [
'test-snap-txinsights-v2.spec.js',
'test-snap-namelookup.spec.js',
'test-snap-homepage.spec.js',
'test-snap-siginsights.spec.js',
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,14 @@ const renderCurrencyInput = (asset: Asset, amount: Amount) => {
) {
return (
<>
<Box marginLeft={'auto'}>
<Box
marginLeft={'auto'}
textAlign={TextAlign.End}
paddingTop={2}
paddingBottom={2}
>
<Text variant={TextVariant.bodySm}>{t('tokenId')}</Text>
<Text
variant={TextVariant.bodySm}
fontWeight={FontWeight.Bold}
marginLeft={10}
>
<Text variant={TextVariant.bodySm} fontWeight={FontWeight.Bold}>
{asset?.details?.tokenId}
</Text>
</Box>
Expand All @@ -100,13 +101,7 @@ const renderCurrencyInput = (asset: Asset, amount: Amount) => {
asset.details?.standard === TokenStandard.ERC1155
) {
return (
<Box
marginLeft={'auto'}
textAlign={TextAlign.End}
paddingTop={2}
paddingBottom={2}
width={BlockSize.Max}
>
<Box marginLeft={'auto'} textAlign={TextAlign.End} width={BlockSize.Max}>
<Text variant={TextVariant.bodyMd} ellipsis>
{t('amount')}
</Text>
Expand Down Expand Up @@ -163,7 +158,10 @@ export const AssetPickerAmount = () => {
/>
);
}
if (asset.details?.standard === TokenStandard.ERC20) {
if (
asset.details?.standard === TokenStandard.ERC20 ||
asset.details?.standard === TokenStandard.ERC721
) {
return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Details should be defined for token assets
Expand Down Expand Up @@ -214,13 +212,13 @@ export const AssetPickerAmount = () => {
paddingRight={4}
borderRadius={BorderRadius.LG}
borderColor={
amount.error ? BorderColor.errorDefault : BorderColor.primaryDefault
error ? BorderColor.errorDefault : BorderColor.primaryDefault
}
borderStyle={BorderStyle.solid}
borderWidth={2}
marginTop={2}
paddingTop={3}
paddingBottom={3}
paddingTop={1}
paddingBottom={1}
>
<AssetPicker asset={asset} />
{renderCurrencyInput(asset, amount)}
Expand Down Expand Up @@ -260,8 +258,10 @@ export const AssetPickerAmount = () => {
alignItems={AlignItems.flexEnd}
>
<Text
variant={TextVariant.bodyMd}
variant={TextVariant.bodySm}
color={TextColor.textAlternative}
width={BlockSize.ThreeFourths}
textAlign={TextAlign.End}
ellipsis
>
ID: {`#${asset.details?.tokenId}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,35 @@ import {
TextVariant,
} from '../../../../helpers/constants/design-system';
import { AssetType } from '../../../../../shared/constants/transaction';
import { getNativeCurrencyImage, getTokenList } from '../../../../selectors';
import {
getIpfsGateway,
getNativeCurrencyImage,
getTokenList,
} from '../../../../selectors';
import { getNativeCurrency } from '../../../../ducks/metamask/metamask';
import { AssetPickerModal } from '../asset-picker-modal/asset-picker-modal';
import { getAssetImageURL } from '../../../../helpers/utils/util';

// A component that lets the user pick from a list of assets.
export default function AssetPicker({ asset }: { asset: Asset }) {
const nativeCurrency = useSelector(getNativeCurrency);
const nativeCurrencyImage = useSelector(getNativeCurrencyImage);
const tokenList = useSelector(getTokenList);
const ipfsGateway = useSelector(getIpfsGateway);
const [showAssetPickerModal, setShowAssetPickerModal] = useState(false);

const image =
asset.type === AssetType.native
? nativeCurrencyImage
: // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: type 'string' can't be used to index type '{}'
tokenList?.[asset.details?.address?.toLowerCase()]?.iconUrl;
let image: string | undefined;
if (asset.type === AssetType.native) {
image = nativeCurrencyImage;
} else if (asset.type === AssetType.token) {
image =
asset.details?.image ??
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: type 'string' can't be used to index type '{}'
tokenList?.[asset.details?.address?.toLowerCase()]?.iconUrl;
} else if (asset.type === AssetType.NFT) {
image = getAssetImageURL(asset.details?.image, ipfsGateway);
}

// TODO: Handle long symbols in the UI
const symbol =
Expand Down
24 changes: 21 additions & 3 deletions ui/components/multichain/import-account/private-key.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { TextFieldSize, TextFieldType } from '../../component-library';
import { FormTextField } from '../../component-library/form-text-field/deprecated';
import {
FormTextField,
TextFieldSize,
TextFieldType,
} from '../../component-library';

import { useI18nContext } from '../../../hooks/useI18nContext';
import ShowHideToggle from '../../ui/show-hide-toggle';
import BottomButtons from './bottom-buttons';

export default function PrivateKeyImportView({
Expand All @@ -12,6 +17,7 @@ export default function PrivateKeyImportView({
}) {
const t = useI18nContext();
const [privateKey, setPrivateKey] = useState('');
const [showPrivateKey, setShowPrivateKey] = useState(false);

const warning = useSelector((state) => state.appState.warning);

Expand All @@ -32,7 +38,6 @@ export default function PrivateKeyImportView({
id="private-key-box"
size={TextFieldSize.Lg}
autoFocus
type={TextFieldType.Password}
helpText={warning}
error
label={t('pastePrivateKey')}
Expand All @@ -42,6 +47,19 @@ export default function PrivateKeyImportView({
onKeyPress: handleKeyPress,
}}
marginBottom={4}
type={showPrivateKey ? TextFieldType.Text : TextFieldType.Password}
textFieldProps={{
endAccessory: (
<ShowHideToggle
shown={showPrivateKey}
id="show-hide-private-key"
title={t('privateKeyShow')}
ariaLabelShown={t('privateKeyShown')}
ariaLabelHidden={t('privateKeyHidden')}
onChange={() => setShowPrivateKey(!showPrivateKey)}
/>
),
}}
/>

<BottomButtons
Expand Down
13 changes: 13 additions & 0 deletions ui/components/multichain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,17 @@ export { AvatarGroup } from './avatar-group';
export { AssetPickerAmount } from './asset-picker-amount';
export { AddressListItem } from './address-list-item';
export { ConnectedStatus } from './connected-status';
export { NotificationListItem } from './notification-list-item';
export { NotificationListItemIcon } from './notification-list-item-icon';
export { NotificationListItemText } from './notification-list-item-text';
export { NotificationListSnapButton } from './notification-list-snap-button';
export { NotificationDetail } from './notification-detail';
export { NotificationDetailAddress } from './notification-detail-address';
export { NotificationDetailAsset } from './notification-detail-asset';
export { NotificationDetailCollection } from './notification-detail-collection';
export { NotificationDetailCopyButton } from './notification-detail-copy-button';
export { NotificationDetailNetworkFee } from './notification-detail-network-fee';
export { NotificationDetailInfo } from './notification-detail-info';
export { NotificationDetailNft } from './notification-detail-nft';
export { NotificationDetailTitle } from './notification-detail-title';
export { Toast, ToastContainer } from './toast';
5 changes: 5 additions & 0 deletions ui/components/multichain/multichain-components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@
@import 'asset-picker-amount';
@import 'asset-picker-amount/asset-picker';
@import 'asset-picker-amount/asset-picker-modal';
@import 'notification-detail';
@import 'notification-detail-collection';
@import 'notification-detail-nft';
@import 'notification-list-item-icon';
@import 'notification-list-item';
@import 'toast';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { NotificationDetailAddress } from './notification-detail-address';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { Meta } from '@storybook/react';
import {
NotificationDetailAddress,
NotificationDetailAddressProps,
} from './notification-detail-address';

export default {
title:
'Components/Multichain/Notification/NotificationDetail/NotificationDetailAddress',
component: NotificationDetailAddress,
} as Meta;

const Template = (args: NotificationDetailAddressProps) => (
<NotificationDetailAddress {...args} />
);

export const DefaultStory = Template.bind({});
DefaultStory.args = {
side: 'From (You)',
address: '0x7830c87C02e56AFf27FA8Ab1241711331FA86F43',
};

export const ToStory = Template.bind({});
ToStory.args = {
side: 'To',
address: '0x55FE002aefF02F77364de339a1292923A15844B8',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { NotificationDetailAddress } from './notification-detail-address';

describe('NotificationDetailAddress', () => {
it('renders without crashing', () => {
render(
<NotificationDetailAddress
side="From"
address="0x7830c87C02e56AFf27FA8Ab1241711331FA86F43"
/>,
);
expect(screen.getByText('From')).toBeInTheDocument();
});
});
Loading

0 comments on commit d7b1e5e

Please sign in to comment.