mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Collectible Unit Tests (#17632)
* Add ipfs gateway and collectible state to mock-state.json * Add collectible-default-image test with snapshot and testids * Add Collectible Details test, snapshot, and test-ids * Add Collectible Options tests and test-ids * Add Collectible Items test and test-ids. * Add more tests to Add Collectible component * Update Security Tab snapshot with ipfs gateway state value * Add data-testid to Menu component for menu background * Lint * Bump coverage targets * Remove commented import --------- Co-authored-by: David Walsh <davidwalsh83@gmail.com>
This commit is contained in:
parent
fd53de6af5
commit
14efbf1eea
@ -6,10 +6,10 @@
|
|||||||
// subset of files to check against these targets.
|
// subset of files to check against these targets.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
global: {
|
global: {
|
||||||
branches: 50,
|
|
||||||
functions: 55,
|
|
||||||
lines: 62.25,
|
lines: 62.25,
|
||||||
|
branches: 50.5,
|
||||||
statements: 61.5,
|
statements: 61.5,
|
||||||
|
functions: 55,
|
||||||
},
|
},
|
||||||
transforms: {
|
transforms: {
|
||||||
branches: 100,
|
branches: 100,
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"mostRecentOverviewPage": "/mostRecentOverviewPage"
|
"mostRecentOverviewPage": "/mostRecentOverviewPage"
|
||||||
},
|
},
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"ipfsGateway": "",
|
"ipfsGateway": "dweb.link",
|
||||||
"dismissSeedBackUpReminder": false,
|
"dismissSeedBackUpReminder": false,
|
||||||
"usePhishDetect": true,
|
"usePhishDetect": true,
|
||||||
"useMultiAccountBalanceChecker": false,
|
"useMultiAccountBalanceChecker": false,
|
||||||
@ -259,6 +259,161 @@
|
|||||||
"maxBaseFee": "75",
|
"maxBaseFee": "75",
|
||||||
"priorityFee": "2"
|
"priorityFee": "2"
|
||||||
},
|
},
|
||||||
|
"collectiblesDropdownState": {
|
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||||
|
"0x5": {
|
||||||
|
"0x495f947276749Ce646f68AC8c248420045cb7b5e": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allNftContracts": {
|
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "1",
|
||||||
|
"name": "MUNK #1",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"137": [
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "1",
|
||||||
|
"name": "MUNK #1",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"11155111": [
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "1",
|
||||||
|
"name": "MUNK #1",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"5": [
|
||||||
|
{
|
||||||
|
"address": "0x495f947276749Ce646f68AC8c248420045cb7b5e",
|
||||||
|
"tokenId": "58076532811975507823669075598676816378162417803895263482849101575514658701313",
|
||||||
|
"name": "Punk #4",
|
||||||
|
"creator": {
|
||||||
|
"user": {
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"profile_img_url": null,
|
||||||
|
"address": "0x806627172af48bd5b0765d3449a7def80d6576ff",
|
||||||
|
"config": ""
|
||||||
|
},
|
||||||
|
"description": "Red Mohawk bam!",
|
||||||
|
"image": "https://lh3.googleusercontent.com/BdxvLseXcfl57BiuQcQYdJ64v-aI8din7WPk0Pgo3qQFhAUH-B6i-dCqqc_mCkRIzULmwzwecnohLhrcH8A9mpWIZqA7ygc52Sr81hE",
|
||||||
|
"standard": "ERC1155"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x495f947276749Ce646f68AC8c248420045cb7b5e",
|
||||||
|
"tokenId": "58076532811975507823669075598676816378162417803895263482849101574415147073537",
|
||||||
|
"name": "Punk #3",
|
||||||
|
"creator": {
|
||||||
|
"user": {
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"profile_img_url": null,
|
||||||
|
"address": "0x806627172af48bd5b0765d3449a7def80d6576ff",
|
||||||
|
"config": ""
|
||||||
|
},
|
||||||
|
"description": "Clown PUNK!!!",
|
||||||
|
"image": "https://lh3.googleusercontent.com/H7VrxaalZv4PF1B8U7ADuc8AfuqTVyzmMEDQ5OXKlx0Tqu5XiwsKYj4j_pAF6wUJjLMQbSN_0n3fuj84lNyRhFW9hyrxqDfY1IiQEQ",
|
||||||
|
"standard": "ERC1155"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x495f947276749Ce646f68AC8c248420045cb7b5e",
|
||||||
|
"tokenId": "58076532811975507823669075598676816378162417803895263482849101573315635445761",
|
||||||
|
"name": "Punk #2",
|
||||||
|
"creator": {
|
||||||
|
"user": {
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"profile_img_url": null,
|
||||||
|
"address": "0x806627172af48bd5b0765d3449a7def80d6576ff",
|
||||||
|
"config": ""
|
||||||
|
},
|
||||||
|
"description": "Got glasses and black hair!",
|
||||||
|
"image": "https://lh3.googleusercontent.com/CHNTSlKB_Gob-iwTq8jcag6XwBkTqBMLt_vEKeBv18Q4AoPFAEPceqK6mRzkad2s5djx6CT5zbGQwDy81WwtNzViK5dQbG60uAWv",
|
||||||
|
"standard": "ERC1155"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x495f947276749Ce646f68AC8c248420045cb7b5e",
|
||||||
|
"tokenId": "58076532811975507823669075598676816378162417803895263482849101572216123817985",
|
||||||
|
"name": "Punk #1",
|
||||||
|
"creator": {
|
||||||
|
"user": {
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"profile_img_url": null,
|
||||||
|
"address": "0x806627172af48bd5b0765d3449a7def80d6576ff",
|
||||||
|
"config": ""
|
||||||
|
},
|
||||||
|
"image": "https://lh3.googleusercontent.com/4jfPi-nQNWCUXD5qVNVWX7LX2UufU_elEJcvICFlsTdcBXv70asnDEOlI8oKECZxlXq1wseeIXMwmP5tLyOUxMKk",
|
||||||
|
"standard": "ERC1155"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x495f947276749Ce646f68AC8c248420045cb7b5e",
|
||||||
|
"tokenId": "58076532811975507823669075598676816378162417803895263482849101571116612190209",
|
||||||
|
"name": "Punk #4651",
|
||||||
|
"creator": {
|
||||||
|
"user": {
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"profile_img_url": null,
|
||||||
|
"address": "0x806627172af48bd5b0765d3449a7def80d6576ff",
|
||||||
|
"config": ""
|
||||||
|
},
|
||||||
|
"image": "https://lh3.googleusercontent.com/BdxvLseXcfl57BiuQcQYdJ64v-aI8din7WPk0Pgo3qQFhAUH-B6i-dCqqc_mCkRIzULmwzwecnohLhrcH8A9mpWIZqA7ygc52Sr81hE",
|
||||||
|
"standard": "ERC1155"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "1",
|
||||||
|
"name": "MUNK #1",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "2",
|
||||||
|
"name": "MUNK #2",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "3",
|
||||||
|
"name": "MUNK #3",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"153": [
|
||||||
|
{
|
||||||
|
"address": "0xDc7382Eb0Bc9C352A4CbA23c909bDA01e0206414",
|
||||||
|
"tokenId": "1",
|
||||||
|
"name": "MUNK #1",
|
||||||
|
"description": null,
|
||||||
|
"image": "ipfs://QmTSZUNt8AKyDabkyXXXP4oHWDnaVXgNdXoJGEyaYzLbeL",
|
||||||
|
"standard": "ERC721"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"tokenList": {
|
"tokenList": {
|
||||||
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": {
|
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": {
|
||||||
"address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
"address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Collectible Default Image should match snapshot with all provided props 1`] = `
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="collectible-default collectible-default--clickable"
|
||||||
|
data-testid="collectible-default-image"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography collectible-default__text typography--h6 typography--weight-normal typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Collectible Name
|
||||||
|
|
||||||
|
<br />
|
||||||
|
#
|
||||||
|
123
|
||||||
|
</h6>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Collectible Default Image should match snapshot with missing image click handler 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="collectible-default"
|
||||||
|
data-testid="collectible-default-image"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography collectible-default__text typography--h6 typography--weight-normal typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Collectible Name
|
||||||
|
|
||||||
|
<br />
|
||||||
|
#
|
||||||
|
123
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Collectible Default Image should render with no props 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="collectible-default"
|
||||||
|
data-testid="collectible-default-image"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography collectible-default__text typography--h6 typography--weight-normal typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
[unknownCollection]
|
||||||
|
|
||||||
|
<br />
|
||||||
|
#
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -15,6 +15,7 @@ export default function CollectibleDefaultImage({
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
data-testid="collectible-default-image"
|
||||||
className={classnames('collectible-default', {
|
className={classnames('collectible-default', {
|
||||||
'collectible-default--clickable': handleImageClick,
|
'collectible-default--clickable': handleImageClick,
|
||||||
})}
|
})}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent } from '@testing-library/react';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import CollectibleDefaultImage from '.';
|
||||||
|
|
||||||
|
describe('Collectible Default Image', () => {
|
||||||
|
it('should render with no props', () => {
|
||||||
|
const { container } = renderWithProvider(<CollectibleDefaultImage />);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match snapshot with all provided props', () => {
|
||||||
|
const props = {
|
||||||
|
name: 'Collectible Name',
|
||||||
|
tokenId: '123',
|
||||||
|
handleImageClick: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<CollectibleDefaultImage {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match snapshot with missing image click handler', () => {
|
||||||
|
const props = {
|
||||||
|
name: 'Collectible Name',
|
||||||
|
tokenId: '123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<CollectibleDefaultImage {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render collectible name', () => {
|
||||||
|
const props = {
|
||||||
|
name: 'Collectible Name',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { queryByText } = renderWithProvider(
|
||||||
|
<CollectibleDefaultImage {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleElement = queryByText(`${props.name} #`);
|
||||||
|
|
||||||
|
expect(collectibleElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render collectible name and tokenId', () => {
|
||||||
|
const props = {
|
||||||
|
name: 'Collectible Name',
|
||||||
|
tokenId: '123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { queryByText } = renderWithProvider(
|
||||||
|
<CollectibleDefaultImage {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleElement = queryByText(`${props.name} #${props.tokenId}`);
|
||||||
|
|
||||||
|
expect(collectibleElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle image click', () => {
|
||||||
|
const props = {
|
||||||
|
handleImageClick: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDefaultImage {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleImageElement = queryByTestId('collectible-default-image');
|
||||||
|
fireEvent.click(collectibleImageElement);
|
||||||
|
|
||||||
|
expect(props.handleImageClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,167 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Collectible Details should match minimal props and state snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="asset-navigation"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="asset-breadcrumb"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fas fa-chevron-left asset-breadcrumb__chevron"
|
||||||
|
data-testid="asset__back"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
Test Account
|
||||||
|
</span>
|
||||||
|
/
|
||||||
|
<span
|
||||||
|
class="asset-breadcrumb__asset"
|
||||||
|
>
|
||||||
|
MUNK #1
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="fas fa-ellipsis-v collectible-options__button"
|
||||||
|
data-testid="collectible-options__button"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box collectible-details box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="collectible-details__top-section"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box collectible-details__card box--flex-direction-row box--justify-content-center box--background-color-background-default box--rounded-md box--border-style-solid box--border-color-border-muted box--border-width-1 box--display-flex"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="MUNK #1 1"
|
||||||
|
class="collectible-details__image"
|
||||||
|
src="https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box collectible-details__info box--flex-direction-column box--justify-content-space-between box--display-flex"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h4
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-2 box--flex-direction-row typography typography--h4 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
MUNK #1
|
||||||
|
</h4>
|
||||||
|
<h5
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-4 box--flex-direction-row typography typography--h5 typography--weight-normal typography--style-normal typography--color-text-muted typography--overflowwrap-break-word"
|
||||||
|
>
|
||||||
|
#
|
||||||
|
1
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row box--width-1/2"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button btn--rounded btn-primary collectible-details__send-button"
|
||||||
|
data-testid="collectible-send-button"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--margin-bottom-2 box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-right-2 box--margin-bottom-4 box--flex-direction-row typography collectible-details__link-title typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Source
|
||||||
|
</h6>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-4 box--flex-direction-row typography collectible-details__image-source typography--h6 typography--weight-normal typography--style-normal typography--color-primary-default"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link"
|
||||||
|
>
|
||||||
|
https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link
|
||||||
|
</a>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-right-2 box--margin-bottom-4 box--flex-direction-row typography collectible-details__link-title typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Link
|
||||||
|
</h6>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-4 box--flex-direction-row typography collectible-details__image-source typography--h6 typography--weight-normal typography--style-normal typography--color-primary-default"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="https://bafybeiclzx7zfjvuiuwobn5ip3ogc236bjqfjzoblumf4pau4ep6dqramu.ipfs.dweb.link"
|
||||||
|
/>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-right-2 box--margin-bottom-4 box--flex-direction-row typography collectible-details__link-title typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||||
|
>
|
||||||
|
Contract address
|
||||||
|
</h6>
|
||||||
|
<div
|
||||||
|
class="box collectible-details__contract-wrapper box--display-flex box--flex-direction-row"
|
||||||
|
>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-4 box--flex-direction-row typography typography--h6 typography--weight-normal typography--style-normal typography--color-text-alternative typography--overflowwrap-break-word"
|
||||||
|
>
|
||||||
|
0xDc7...6414
|
||||||
|
</h6>
|
||||||
|
<div
|
||||||
|
class="collectible-details__tooltip-wrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class=""
|
||||||
|
data-original-title="Copy to clipboard"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="copy"
|
||||||
|
class="box mm-button-icon mm-button-icon--size-lg collectible-details__contract-copy-button box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-icon-alternative box--background-color-transparent box--rounded-lg"
|
||||||
|
data-testid="collectible-address-copy"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box mm-icon mm-icon--size-lg box--flex-direction-row box--color-inherit"
|
||||||
|
style="mask-image: url('./images/icons/copy.svg');"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h6
|
||||||
|
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
|
||||||
|
>
|
||||||
|
Disclaimer: MetaMask pulls the media file from the source url. This url sometimes is changed by the marketplace the NFT was minted on.
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -150,6 +150,7 @@ export default function CollectibleDetails({ collectible }) {
|
|||||||
onClick={onSend}
|
onClick={onSend}
|
||||||
disabled={sendDisabled}
|
disabled={sendDisabled}
|
||||||
className="collectible-details__send-button"
|
className="collectible-details__send-button"
|
||||||
|
data-testid="collectible-send-button"
|
||||||
>
|
>
|
||||||
{t('send')}
|
{t('send')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -416,6 +417,7 @@ export default function CollectibleDetails({ collectible }) {
|
|||||||
ariaLabel="copy"
|
ariaLabel="copy"
|
||||||
color={IconColor.iconAlternative}
|
color={IconColor.iconAlternative}
|
||||||
className="collectible-details__contract-copy-button"
|
className="collectible-details__contract-copy-button"
|
||||||
|
data-testid="collectible-address-copy"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleAddressCopy(address);
|
handleAddressCopy(address);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,286 @@
|
|||||||
|
import { fireEvent, waitFor } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import copyToClipboard from 'copy-to-clipboard';
|
||||||
|
import { startNewDraftTransaction } from '../../../ducks/send';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import { DEFAULT_ROUTE, SEND_ROUTE } from '../../../helpers/constants/routes';
|
||||||
|
import { AssetType } from '../../../../shared/constants/transaction';
|
||||||
|
import {
|
||||||
|
removeAndIgnoreNft,
|
||||||
|
setRemoveCollectibleMessage,
|
||||||
|
} from '../../../store/actions';
|
||||||
|
import CollectibleDetails from './collectible-details';
|
||||||
|
|
||||||
|
jest.mock('copy-to-clipboard');
|
||||||
|
|
||||||
|
const mockHistoryPush = jest.fn();
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useLocation: jest.fn(() => ({ search: '' })),
|
||||||
|
useHistory: () => ({
|
||||||
|
push: mockHistoryPush,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../ducks/send/index.js', () => ({
|
||||||
|
...jest.requireActual('../../../ducks/send/index.js'),
|
||||||
|
startNewDraftTransaction: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue(jest.fn().mockResolvedValue()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../store/actions.ts', () => ({
|
||||||
|
...jest.requireActual('../../../store/actions.ts'),
|
||||||
|
checkAndUpdateSingleNftOwnershipStatus: jest.fn().mockReturnValue(jest.fn()),
|
||||||
|
removeAndIgnoreNft: jest.fn().mockReturnValue(jest.fn()),
|
||||||
|
setRemoveCollectibleMessage: jest.fn().mockReturnValue(jest.fn()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Collectible Details', () => {
|
||||||
|
const mockStore = configureMockStore([thunk])(mockState);
|
||||||
|
|
||||||
|
const collectibles =
|
||||||
|
mockState.metamask.allNftContracts[mockState.metamask.selectedAddress][5];
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
collectible: collectibles[5],
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match minimal props and state snapshot', () => {
|
||||||
|
const { container } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should route to '/' route when the back button is clicked`, () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const backButton = queryByTestId('asset__back');
|
||||||
|
|
||||||
|
fireEvent.click(backButton);
|
||||||
|
|
||||||
|
expect(mockHistoryPush).toHaveBeenCalledWith(DEFAULT_ROUTE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should call removeAndIgnoreNft with proper collectible details and route to '/' when removing collectible`, () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const removeCollectibleButton = queryByTestId('collectible-item-remove');
|
||||||
|
fireEvent.click(removeCollectibleButton);
|
||||||
|
|
||||||
|
expect(removeAndIgnoreNft).toHaveBeenCalledWith(
|
||||||
|
collectibles[5].address,
|
||||||
|
collectibles[5].tokenId,
|
||||||
|
);
|
||||||
|
expect(setRemoveCollectibleMessage).toHaveBeenCalledWith('success');
|
||||||
|
expect(mockHistoryPush).toHaveBeenCalledWith(DEFAULT_ROUTE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should copy collectible address', async () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const copyAddressButton = queryByTestId('collectible-address-copy');
|
||||||
|
fireEvent.click(copyAddressButton);
|
||||||
|
|
||||||
|
expect(copyToClipboard).toHaveBeenCalledWith(collectibles[5].address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to draft transaction send route with ERC721 data', async () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleSendButton = queryByTestId('collectible-send-button');
|
||||||
|
fireEvent.click(collectibleSendButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(startNewDraftTransaction).toHaveBeenCalledWith({
|
||||||
|
type: AssetType.NFT,
|
||||||
|
details: collectibles[5],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockHistoryPush).toHaveBeenCalledWith(SEND_ROUTE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render send button if isCurrentlyOwned is false', () => {
|
||||||
|
const sixthCollectibleProps = {
|
||||||
|
collectible: collectibles[6],
|
||||||
|
};
|
||||||
|
collectibles[6].isCurrentlyOwned = false;
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...sixthCollectibleProps} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleSendButton = queryByTestId('collectible-send-button');
|
||||||
|
expect(collectibleSendButton).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`Alternative Networks' OpenSea Links`, () => {
|
||||||
|
it('should open opeasea link with goeli testnet chainId', async () => {
|
||||||
|
global.platform = { openTab: jest.fn() };
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
fireEvent.click(openOpenSea);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: `https://testnets.opensea.io/assets/${collectibles[5].address}/${collectibles[5].tokenId}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open tab to mainnet opensea url with collectible info', async () => {
|
||||||
|
global.platform = { openTab: jest.fn() };
|
||||||
|
|
||||||
|
const mainnetState = {
|
||||||
|
...mockState,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
provider: {
|
||||||
|
chainId: '0x1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mainnetMockStore = configureMockStore([thunk])(mainnetState);
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
mainnetMockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
fireEvent.click(openOpenSea);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: `https://opensea.io/assets/${collectibles[5].address}/${collectibles[5].tokenId}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open tab to polygon opensea url with collectible info', async () => {
|
||||||
|
const polygonState = {
|
||||||
|
...mockState,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
provider: {
|
||||||
|
chainId: '0x89',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const polygonMockStore = configureMockStore([thunk])(polygonState);
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
polygonMockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
fireEvent.click(openOpenSea);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: `https://opensea.io/assets/matic/${collectibles[5].address}/${collectibles[5].tokenId}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open tab to sepolia opensea url with collectible info', async () => {
|
||||||
|
const sepoliaState = {
|
||||||
|
...mockState,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
provider: {
|
||||||
|
chainId: '0xaa36a7',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const sepoliaMockStore = configureMockStore([thunk])(sepoliaState);
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
sepoliaMockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
fireEvent.click(openOpenSea);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: `https://testnets.opensea.io/assets/${collectibles[5].address}/${collectibles[5].tokenId}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render opensea redirect button', async () => {
|
||||||
|
const randomNetworkState = {
|
||||||
|
...mockState,
|
||||||
|
metamask: {
|
||||||
|
...mockState.metamask,
|
||||||
|
provider: {
|
||||||
|
chainId: '0x99',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const randomNetworkMockStore = configureMockStore([thunk])(
|
||||||
|
randomNetworkState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleDetails {...props} />,
|
||||||
|
randomNetworkMockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(openOpenSea).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -22,6 +22,7 @@ const CollectibleOptions = ({ onRemove, onViewOnOpensea }) => {
|
|||||||
{collectibleOptionsOpen ? (
|
{collectibleOptionsOpen ? (
|
||||||
<Menu
|
<Menu
|
||||||
anchorElement={collectibleOptionsButtonElement}
|
anchorElement={collectibleOptionsButtonElement}
|
||||||
|
data-testid="close-collectible-options-menu"
|
||||||
onHide={() => setCollectibleOptionsOpen(false)}
|
onHide={() => setCollectibleOptionsOpen(false)}
|
||||||
>
|
>
|
||||||
{onViewOnOpensea ? (
|
{onViewOnOpensea ? (
|
||||||
@ -38,7 +39,7 @@ const CollectibleOptions = ({ onRemove, onViewOnOpensea }) => {
|
|||||||
) : null}
|
) : null}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
iconName={ICON_NAMES.TRASH}
|
iconName={ICON_NAMES.TRASH}
|
||||||
data-testid="collectible-options__hide"
|
data-testid="collectible-item-remove"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCollectibleOptionsOpen(false);
|
setCollectibleOptionsOpen(false);
|
||||||
onRemove();
|
onRemove();
|
||||||
@ -54,7 +55,7 @@ const CollectibleOptions = ({ onRemove, onViewOnOpensea }) => {
|
|||||||
|
|
||||||
CollectibleOptions.propTypes = {
|
CollectibleOptions.propTypes = {
|
||||||
onRemove: PropTypes.func.isRequired,
|
onRemove: PropTypes.func.isRequired,
|
||||||
onViewOnOpensea: PropTypes.func.isRequired,
|
onViewOnOpensea: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CollectibleOptions;
|
export default CollectibleOptions;
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
import { fireEvent, waitFor } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import CollectibleOptions from './collectible-options';
|
||||||
|
|
||||||
|
describe('Collectible Options Component', () => {
|
||||||
|
const props = {
|
||||||
|
onRemove: jest.fn(),
|
||||||
|
onViewOnOpensea: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should expand collectible options menu`', async () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleOptions {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
|
||||||
|
expect(queryByTestId('collectible-item-remove')).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(queryByTestId('collectible-item-remove')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expand and close menu options when clicked`', async () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleOptions {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const closeOptionMenuButton = queryByTestId(
|
||||||
|
'close-collectible-options-menu',
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(closeOptionMenuButton);
|
||||||
|
|
||||||
|
expect(closeOptionMenuButton).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should click onRemove handler and close option menu', () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleOptions {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const removeCollectibleButton = queryByTestId('collectible-item-remove');
|
||||||
|
|
||||||
|
fireEvent.click(removeCollectibleButton);
|
||||||
|
|
||||||
|
expect(props.onRemove).toHaveBeenCalled();
|
||||||
|
expect(removeCollectibleButton).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should click onViewOnOpensea handler and close option menu', () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(
|
||||||
|
<CollectibleOptions {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const openOptionMenuButton = queryByTestId('collectible-options__button');
|
||||||
|
const removeCollectibleButton = queryByTestId('collectible-item-remove');
|
||||||
|
|
||||||
|
fireEvent.click(openOptionMenuButton);
|
||||||
|
|
||||||
|
const openOpenSea = queryByTestId('collectible-options__view-on-opensea');
|
||||||
|
|
||||||
|
fireEvent.click(openOpenSea);
|
||||||
|
|
||||||
|
expect(props.onViewOnOpensea).toHaveBeenCalled();
|
||||||
|
expect(removeCollectibleButton).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -134,10 +134,11 @@ export default function CollectiblesItems({
|
|||||||
return (
|
return (
|
||||||
<div className="collectibles-items__collection" key={`collection-${key}`}>
|
<div className="collectibles-items__collection" key={`collection-${key}`}>
|
||||||
<button
|
<button
|
||||||
|
className="collectibles-items__collection-wrapper"
|
||||||
|
data-testid="collection-expander-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateCollectibleDropDownStateKey(key, isExpanded);
|
updateCollectibleDropDownStateKey(key, isExpanded);
|
||||||
}}
|
}}
|
||||||
className="collectibles-items__collection-wrapper"
|
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
marginBottom={2}
|
marginBottom={2}
|
||||||
@ -183,6 +184,7 @@ export default function CollectiblesItems({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
data-testid="collectible-wrapper"
|
||||||
width={width}
|
width={width}
|
||||||
key={`collectible-${i}`}
|
key={`collectible-${i}`}
|
||||||
className="collectibles-items__item-wrapper"
|
className="collectibles-items__item-wrapper"
|
||||||
@ -202,6 +204,7 @@ export default function CollectiblesItems({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="collectibles-items__item-image"
|
className="collectibles-items__item-image"
|
||||||
|
data-testid="collectible-image"
|
||||||
src={collectibleImage}
|
src={collectibleImage}
|
||||||
alt={collectibleImageAlt}
|
alt={collectibleImageAlt}
|
||||||
/>
|
/>
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent } from '@testing-library/react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import mockState from '../../../../test/data/mock-state.json';
|
||||||
|
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||||
|
import { updateCollectibleDropDownState } from '../../../store/actions';
|
||||||
|
import CollectiblesItems from '.';
|
||||||
|
|
||||||
|
const mockHistoryPush = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useLocation: jest.fn(() => ({ search: '' })),
|
||||||
|
useHistory: () => ({
|
||||||
|
push: mockHistoryPush,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../store/actions.ts', () => ({
|
||||||
|
...jest.requireActual('../../../store/actions.ts'),
|
||||||
|
updateCollectibleDropDownState: jest.fn().mockReturnValue(jest.fn()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Collectibles Item Component', () => {
|
||||||
|
const collectibles =
|
||||||
|
mockState.metamask.allNftContracts[mockState.metamask.selectedAddress][5];
|
||||||
|
const props = {
|
||||||
|
collections: {
|
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
|
||||||
|
collectibles,
|
||||||
|
collectionImage: '',
|
||||||
|
collectionName: 'Collectible Collection',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previouslyOwnedCollection: {
|
||||||
|
collectibles: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockStore = configureMockStore([thunk])(mockState);
|
||||||
|
|
||||||
|
it('should expand collectible collection showing individual collectibles', async () => {
|
||||||
|
const { queryByTestId, queryAllByTestId, rerender } = renderWithProvider(
|
||||||
|
<CollectiblesItems {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectionExpanderButton = queryByTestId(
|
||||||
|
'collection-expander-button',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(queryAllByTestId('collectible-wrapper')).toHaveLength(0);
|
||||||
|
|
||||||
|
fireEvent.click(collectionExpanderButton);
|
||||||
|
|
||||||
|
expect(updateCollectibleDropDownState).toHaveBeenCalledWith({
|
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
|
||||||
|
'0x5': {
|
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': true,
|
||||||
|
'0x495f947276749Ce646f68AC8c248420045cb7b5e': false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
rerender(<CollectiblesItems {...props} />, mockStore);
|
||||||
|
|
||||||
|
expect(queryAllByTestId('collectible-wrapper')).toHaveLength(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should collectible click image', () => {
|
||||||
|
const { queryAllByTestId } = renderWithProvider(
|
||||||
|
<CollectiblesItems {...props} />,
|
||||||
|
mockStore,
|
||||||
|
);
|
||||||
|
|
||||||
|
const collectibleImages = queryAllByTestId('collectible-image');
|
||||||
|
|
||||||
|
fireEvent.click(collectibleImages[0]);
|
||||||
|
|
||||||
|
const firstCollectible = collectibles[0];
|
||||||
|
const collectibleRoute = `/asset/${firstCollectible.address}/${firstCollectible.tokenId}`;
|
||||||
|
|
||||||
|
expect(mockHistoryPush).toHaveBeenCalledWith(collectibleRoute);
|
||||||
|
});
|
||||||
|
});
|
@ -8,6 +8,7 @@ const Menu = ({
|
|||||||
anchorElement,
|
anchorElement,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
'data-testid': dataTestId,
|
||||||
onHide,
|
onHide,
|
||||||
popperOptions,
|
popperOptions,
|
||||||
}) => {
|
}) => {
|
||||||
@ -24,7 +25,11 @@ const Menu = ({
|
|||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<>
|
<>
|
||||||
<div className="menu__background" onClick={onHide} />
|
<div
|
||||||
|
className="menu__background"
|
||||||
|
data-testid={dataTestId}
|
||||||
|
onClick={onHide}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className={classnames('menu__container', className)}
|
className={classnames('menu__container', className)}
|
||||||
data-testid={className}
|
data-testid={className}
|
||||||
@ -45,6 +50,7 @@ Menu.propTypes = {
|
|||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
onHide: PropTypes.func.isRequired,
|
onHide: PropTypes.func.isRequired,
|
||||||
popperOptions: PropTypes.object,
|
popperOptions: PropTypes.object,
|
||||||
|
dataTestId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
Menu.defaultProps = {
|
Menu.defaultProps = {
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { fireEvent } from '@testing-library/react';
|
import { fireEvent, waitFor } from '@testing-library/react';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { renderWithProvider } from '../../../test/jest/rendering';
|
import { renderWithProvider } from '../../../test/jest/rendering';
|
||||||
import * as Actions from '../../store/actions';
|
import mockState from '../../../test/data/mock-state.json';
|
||||||
|
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
|
||||||
|
import {
|
||||||
|
addNftVerifyOwnership,
|
||||||
|
ignoreTokens,
|
||||||
|
setNewCollectibleAddedMessage,
|
||||||
|
updateCollectibleDropDownState,
|
||||||
|
} from '../../store/actions';
|
||||||
import AddCollectible from '.';
|
import AddCollectible from '.';
|
||||||
|
|
||||||
const VALID_ADDRESS = '0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9';
|
const VALID_ADDRESS = '0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9';
|
||||||
@ -10,9 +19,39 @@ const INVALID_ADDRESS = 'aoinsafasdfa';
|
|||||||
const VALID_TOKENID = '1201';
|
const VALID_TOKENID = '1201';
|
||||||
const INVALID_TOKENID = 'abcde';
|
const INVALID_TOKENID = 'abcde';
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useHistory: jest.fn(
|
||||||
|
jest.fn().mockReturnValue({
|
||||||
|
push: jest.fn(),
|
||||||
|
location: {
|
||||||
|
state: {
|
||||||
|
tokenAddress: '0xTokenAddress',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../store/actions.ts', () => ({
|
||||||
|
addNftVerifyOwnership: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue(jest.fn().mockResolvedValue()),
|
||||||
|
getTokenStandardAndDetails: jest.fn().mockResolvedValue(),
|
||||||
|
ignoreTokens: jest.fn().mockReturnValue(jest.fn().mockResolvedValue()),
|
||||||
|
setNewCollectibleAddedMessage: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue(jest.fn().mockResolvedValue()),
|
||||||
|
updateCollectibleDropDownState: jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue(jest.fn().mockResolvedValue()),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('AddCollectible', () => {
|
describe('AddCollectible', () => {
|
||||||
const store = configureMockStore([])({
|
const store = configureMockStore([thunk])(mockState);
|
||||||
metamask: { provider: { chainId: '0x1' } },
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enable the "Add" button when valid entries are input into both Address and TokenId fields', () => {
|
it('should enable the "Add" button when valid entries are input into both Address and TokenId fields', () => {
|
||||||
@ -53,7 +92,7 @@ describe('AddCollectible', () => {
|
|||||||
expect(getByText('Add')).not.toBeEnabled();
|
expect(getByText('Add')).not.toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call addNftVerifyOwnership action with correct values (tokenId should not be in scientific notation)', () => {
|
it('should call addNftVerifyOwnership, updateCollectibleDropDownState, setNewCollectibleAddedMessage, and ignoreTokens action with correct values (tokenId should not be in scientific notation)', async () => {
|
||||||
const { getByTestId, getByText } = renderWithProvider(
|
const { getByTestId, getByText } = renderWithProvider(
|
||||||
<AddCollectible />,
|
<AddCollectible />,
|
||||||
store,
|
store,
|
||||||
@ -65,15 +104,76 @@ describe('AddCollectible', () => {
|
|||||||
fireEvent.change(getByTestId('token-id'), {
|
fireEvent.change(getByTestId('token-id'), {
|
||||||
target: { value: LARGE_TOKEN_ID },
|
target: { value: LARGE_TOKEN_ID },
|
||||||
});
|
});
|
||||||
const addCollectibleVerifyOwnershipSpy = jest.spyOn(
|
|
||||||
Actions,
|
|
||||||
'addNftVerifyOwnership',
|
|
||||||
);
|
|
||||||
|
|
||||||
fireEvent.click(getByText('Add'));
|
fireEvent.click(getByText('Add'));
|
||||||
expect(addCollectibleVerifyOwnershipSpy).toHaveBeenCalledWith(
|
|
||||||
'0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9',
|
await waitFor(() => {
|
||||||
'9007199254740992',
|
expect(addNftVerifyOwnership).toHaveBeenCalledWith(
|
||||||
|
'0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9',
|
||||||
|
'9007199254740992',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(updateCollectibleDropDownState).toHaveBeenCalledWith({
|
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
|
||||||
|
'0x5': {
|
||||||
|
'0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9': true,
|
||||||
|
'0x495f947276749Ce646f68AC8c248420045cb7b5e': false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setNewCollectibleAddedMessage).toHaveBeenCalledWith('success');
|
||||||
|
|
||||||
|
expect(ignoreTokens).toHaveBeenCalledWith({
|
||||||
|
dontShowLoadingIndicator: true,
|
||||||
|
tokensToIgnore: '0xTokenAddress',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error message and click close on failure message', async () => {
|
||||||
|
addNftVerifyOwnership.mockImplementation(() =>
|
||||||
|
jest.fn().mockRejectedValue(new Error('error')),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { getByTestId, getByText, queryByTitle } = renderWithProvider(
|
||||||
|
<AddCollectible />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
fireEvent.change(getByTestId('address'), {
|
||||||
|
target: { value: VALID_ADDRESS },
|
||||||
|
});
|
||||||
|
const LARGE_TOKEN_ID = Number.MAX_SAFE_INTEGER + 1;
|
||||||
|
fireEvent.change(getByTestId('token-id'), {
|
||||||
|
target: { value: LARGE_TOKEN_ID },
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(getByText('Add'));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(setNewCollectibleAddedMessage).toHaveBeenCalledWith('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
const addCollectibleClose = queryByTitle('Close');
|
||||||
|
|
||||||
|
fireEvent.click(addCollectibleClose);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should route to default route when cancel button is clicked', () => {
|
||||||
|
const { queryByTestId } = renderWithProvider(<AddCollectible />, store);
|
||||||
|
|
||||||
|
const cancelButton = queryByTestId('page-container-footer-cancel');
|
||||||
|
fireEvent.click(cancelButton);
|
||||||
|
|
||||||
|
expect(useHistory().push).toHaveBeenCalledWith(DEFAULT_ROUTE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should route to default route when close button is clicked', () => {
|
||||||
|
const { queryByLabelText } = renderWithProvider(<AddCollectible />, store);
|
||||||
|
|
||||||
|
const closeButton = queryByLabelText('close');
|
||||||
|
fireEvent.click(closeButton);
|
||||||
|
|
||||||
|
expect(useHistory().push).toHaveBeenCalledWith(DEFAULT_ROUTE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -406,7 +406,7 @@ exports[`Security Tab should match snapshot 1`] = `
|
|||||||
class="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense"
|
class="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value="dweb.link"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user