Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: NftGrid view #12983

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions app/components/UI/CollectibleContracts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ const CollectibleContracts = ({
)
);
}, [favoriteCollectibles, collectibles, onItemPress]);

const onRefresh = useCallback(async () => {
requestAnimationFrame(async () => {
setRefreshing(true);
Expand Down
49 changes: 49 additions & 0 deletions app/components/UI/NftGrid/NftGrid.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useTheme } from '@react-navigation/native';
import { StyleSheet } from 'react-native';

const styleSheet = () => {
const { colors } = useTheme();

Check failure on line 5 in app/components/UI/NftGrid/NftGrid.styles.ts

View workflow job for this annotation

GitHub Actions / scripts (lint)

React Hook "useTheme" is called in function "styleSheet" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
return StyleSheet.create({
collectibleIcon: {
width: '100%',
aspectRatio: 1,
},
collectibleCard: {
flexBasis: '33%',
padding: 10,
marginBottom: 10,
justifyContent: 'center',
alignItems: 'center',
},
footer: {
// flex: 1,
alignItems: 'center',
// marginTop: 8,
},
spinner: {
marginBottom: 8,
},
emptyContainer: {
flex: 1,
alignItems: 'center',
outline: 'solid red 2px',
},
emptyImageContainer: {
width: 30,
height: 30,
// marginTop: 30,
// marginBottom: 12,
tintColor: colors.background,
},
headingMd: {
marginTop: 10,
},
emptyText: {
// color: colors.text.alternative,
marginBottom: 8,
fontSize: 14,
},
});
};

export default styleSheet;
158 changes: 158 additions & 0 deletions app/components/UI/NftGrid/NftGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import NftGridFooter from './NftGridFooter';
import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors';
import { StackNavigationProp } from '@react-navigation/stack';
import NftGridEmpty from './NftGridEmpty';
import NftGridItem from './NftGridItem';
import { Nft } from '@metamask/assets-controllers';
import renderWithProvider, {
DeepPartial,
} from '../../../util/test/renderWithProvider';
import { RootState } from '../../../reducers';
import { useNavigation } from '@react-navigation/native';

jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useNavigation: jest.fn(),
}));

type MockNavigation = StackNavigationProp<
{
AddAsset: { assetType: string };
[key: string]: object | undefined;
},
'AddAsset'
>;

const mockNavigation: MockNavigation = {
push: jest.fn(),
navigate: jest.fn(),
goBack: jest.fn(),
pop: jest.fn(),
replace: jest.fn(),
reset: jest.fn(),
popToTop: jest.fn(),
isFocused: jest.fn(),
canGoBack: jest.fn(),
setParams: jest.fn(),
getParent: jest.fn(),
} as unknown as MockNavigation;

const mockState: DeepPartial<RootState> = {
engine: {
backgroundState: {
PreferencesController: {
isIpfsGatewayEnabled: true,
},
},
},
} as unknown as RootState;

describe('NftGridFooter', () => {
it('renders without crashing', () => {
const { getByText } = render(<NftGridFooter navigation={mockNavigation} />);
expect(getByText('Don’t see your NFT?')).toBeTruthy();
expect(getByText('Import NFTs')).toBeTruthy();
});

it('calls navigation.push when the button is pressed', () => {
const { getByTestId } = render(
<NftGridFooter navigation={mockNavigation} />,
);
const button = getByTestId(WalletViewSelectorsIDs.IMPORT_NFT_BUTTON);
fireEvent.press(button);
expect(mockNavigation.push).toHaveBeenCalledWith('AddAsset', {
assetType: 'collectible',
});
});

it('matches the snapshot', () => {
const tree = render(<NftGridFooter navigation={mockNavigation} />).toJSON();
expect(tree).toMatchSnapshot();
});
});

describe('NftGridEmpty', () => {
it('renders without crashing', () => {
const { getByText, getByTestId } = render(
<NftGridEmpty navigation={mockNavigation} />,
);
expect(getByText('No NFTs yet')).toBeTruthy();
expect(getByText('Learn more')).toBeTruthy();
expect(getByTestId(WalletViewSelectorsIDs.IMPORT_NFT_BUTTON)).toBeTruthy();
});

it('calls navigation.navigate when the button is pressed', () => {
const { getByTestId } = render(
<NftGridEmpty navigation={mockNavigation} />,
);
const button = getByTestId(WalletViewSelectorsIDs.IMPORT_NFT_BUTTON);
fireEvent.press(button);
expect(mockNavigation.navigate).toHaveBeenCalledWith('Webview', {
screen: 'SimpleWebview',
params: {
url: 'https://support.metamask.io/nfts/nft-tokens-in-your-metamask-wallet/',
},
});
});

it('logs "goToLearnMore" when the learn_more text is pressed', () => {
const consoleSpy = jest.spyOn(console, 'log');
const { getByText } = render(<NftGridEmpty navigation={mockNavigation} />);
const learnMoreText = getByText('Learn more');
fireEvent.press(learnMoreText);

// TODO: actually test for learn more redirect
expect(consoleSpy).toHaveBeenCalledWith('goToLearnMore');
consoleSpy.mockRestore();
});

it('matches the snapshot', () => {
const tree = render(<NftGridEmpty navigation={mockNavigation} />).toJSON();
expect(tree).toMatchSnapshot();
});
});

describe('NftGridItem', () => {
const mockNft: Nft = {
address: '0x123',
tokenId: '1',
name: 'Test NFT',
image: 'https://example.com/image.png',
collection: {
name: 'Test Collection',
},
description: '',
standard: 'erc721',
};

const mockNavigate = jest.fn();
(useNavigation as jest.Mock).mockReturnValue({
navigate: mockNavigate,
goBack: jest.fn(),
push: jest.fn(),
replace: jest.fn(),
reset: jest.fn(),
popToTop: jest.fn(),
});

it('renders correctly with a valid nft', () => {
const { getByText, getByTestId } = renderWithProvider(
<NftGridItem nft={mockNft} navigation={mockNavigation} />,
{ state: mockState },
);

expect(getByTestId(mockNft.name as string)).toBeTruthy();
expect(getByText('Test NFT')).toBeTruthy();
expect(getByText('Test Collection')).toBeTruthy();
});

it('matches the snapshot', () => {
const tree = renderWithProvider(
<NftGridItem nft={mockNft} navigation={mockNavigation} />,
{ state: mockState },
).toJSON();
expect(tree).toMatchSnapshot();
});
});
Loading
Loading