Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2827,10 +2827,10 @@ export default class MetamaskController extends EventEmitter {
),
// PreferencesController
setSelectedAddress: (address) => {
this.preferencesController.setSelectedAddress(address);
const account = this.accountsController.getAccountByAddress(address);
if (account) {
this.accountsController.setSelectedAccount(account.id);
this.preferencesController.setSelectedAddress(address);
} else {
throw new Error(`No account found for address: ${address}`);
}
Expand Down Expand Up @@ -2875,8 +2875,13 @@ export default class MetamaskController extends EventEmitter {
///: END:ONLY_INCLUDE_IF

// AccountsController
setSelectedInternalAccount:
accountsController.setSelectedAccount.bind(accountsController),
setSelectedInternalAccount: (id) => {
const account = this.accountsController.getAccount(id);
if (account) {
this.preferencesController.setSelectedAddress(account.address);
this.accountsController.setSelectedAccount(id);
}
},

setAccountName:
accountsController.setAccountName.bind(accountsController),
Expand Down
92 changes: 92 additions & 0 deletions test/e2e/tests/nft/import-nft.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const {
defaultGanacheOptions,
withFixtures,
unlockWallet,
findAnotherAccountFromAccountList,
waitForAccountRendered,
} = require('../../helpers');
const { SMART_CONTRACTS } = require('../../seeder/smart-contracts');
const FixtureBuilder = require('../../fixture-builder');
Expand Down Expand Up @@ -57,6 +59,96 @@ describe('Import NFT', function () {
);
});

it('should continue to display an imported NFT after importing, adding a new account, and switching back', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: defaultGanacheOptions,
smartContract,
title: this.test.fullTitle(),
},
async ({ driver, _, contractRegistry }) => {
const contractAddress =
contractRegistry.getContractAddress(smartContract);
await unlockWallet(driver);

// After login, go to NFTs tab, open the import NFT form
await driver.clickElement('[data-testid="home__nfts-tab"]');
await driver.clickElement({ text: 'Import NFT', tag: 'button' });

// Enter a valid NFT that belongs to user and check success message appears
await driver.fill('#address', contractAddress);
await driver.fill('#token-id', '1');
await driver.clickElement(
'[data-testid="import-nfts-modal-import-button"]',
);

const newNftNotification = await driver.findElement({
text: 'NFT was successfully added!',
tag: 'h6',
});
assert.equal(await newNftNotification.isDisplayed(), true);

// Check the imported NFT and its image are displayed in the NFT tab
const importedNft = await driver.waitForSelector({
css: 'h5',
text: 'TestDappNFTs',
});
const importedNftImage = await driver.findElement(
'.nft-item__container',
);
assert.equal(await importedNft.isDisplayed(), true);
assert.equal(await importedNftImage.isDisplayed(), true);

await driver.clickElement({ text: 'Tokens', tag: 'button' });
await driver.clickElement('[data-testid="account-menu-icon"]');
await driver.clickElement(
'[data-testid="multichain-account-menu-popover-action-button"]',
);
await driver.clickElement({ text: 'Add a new account', tag: 'button' });

// set account name
await driver.fill('[placeholder="Account 2"]', '2nd account');
await driver.delay(400);
await driver.clickElement({ text: 'Create', tag: 'button' });

await driver.isElementPresent({
tag: 'span',
text: '2nd account',
});

const accountOneSelector = await findAnotherAccountFromAccountList(
driver,
1,
'Account 1',
);
await waitForAccountRendered(driver);
await driver.clickElement(accountOneSelector);

await driver.clickElement({ text: 'NFTs', tag: 'button' });
const nftIsStillDisplayed = await driver.isElementPresentAndVisible({
css: 'h5',
text: 'TestDappNFTs',
});
const nftImageIsStillDisplayed =
await driver.isElementPresentAndVisible('.nft-item__container');
assert.equal(
nftIsStillDisplayed,
true,
'Nft is no longer displayed after adding an account and switching back to account 1',
);
assert.equal(
nftImageIsStillDisplayed,
true,
'Nft image is no longer displayed after adding an account and switching back to account 1',
);
},
);
});

it('should not be able to import an NFT that does not belong to user', async function () {
await withFixtures(
{
Expand Down
9 changes: 6 additions & 3 deletions ui/ducks/metamask/metamask.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
checkNetworkAndAccountSupports1559,
getAddressBook,
getSelectedNetworkClientId,
getSelectedInternalAccount,
} from '../../selectors';
import * as actionConstants from '../../store/actionConstants';
import { updateTransactionGasFees } from '../../store/actions';
Expand Down Expand Up @@ -271,19 +272,21 @@ export function getNftsDropdownState(state) {

export const getNfts = (state) => {
const {
metamask: { allNfts, selectedAddress },
metamask: { allNfts },
} = state;
const { address: selectedAddress } = getSelectedInternalAccount(state);

const { chainId } = getProviderConfig(state);

return allNfts?.[selectedAddress]?.[chainId] ?? [];
};

export const getNftContracts = (state) => {
const {
metamask: { allNftContracts, selectedAddress },
metamask: { allNftContracts },
} = state;
const { address: selectedAddress } = getSelectedInternalAccount(state);
const { chainId } = getProviderConfig(state);

return allNftContracts?.[selectedAddress]?.[chainId] ?? [];
};

Expand Down
7 changes: 7 additions & 0 deletions ui/store/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -889,15 +889,20 @@ describe('Actions', () => {
const setSelectedInternalAccountSpy = sinon
.stub()
.callsFake((_, cb) => cb());
const setSelectedAddressSpy = sinon.stub().callsFake((_, cb) => cb());

background.getApi.returns({
setSelectedInternalAccount: setSelectedInternalAccountSpy,
setSelectedAddress: setSelectedAddressSpy,
});

setBackgroundConnection(background.getApi());

await store.dispatch(actions.setSelectedAccount('0x123'));
expect(setSelectedInternalAccountSpy.callCount).toStrictEqual(1);
expect(setSelectedInternalAccountSpy.calledWith('mock-id')).toBe(true);
expect(setSelectedAddressSpy.callCount).toStrictEqual(1);
expect(setSelectedAddressSpy.calledWith('0x123')).toBe(true);
});

it('displays warning if setSelectedAccount throws', async () => {
Expand Down Expand Up @@ -936,9 +941,11 @@ describe('Actions', () => {
const setSelectedInternalAccountSpy = sinon
.stub()
.callsFake((_, cb) => cb(new Error('error')));
const setSelectedAddressSpy = sinon.stub().callsFake((_, cb) => cb());

background.getApi.returns({
setSelectedInternalAccount: setSelectedInternalAccountSpy,
setSelectedAddress: setSelectedAddressSpy,
});

setBackgroundConnection(background.getApi());
Expand Down
1 change: 1 addition & 0 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,7 @@ export function setSelectedAccount(
!currentTabIsConnectedToNextAddress;

try {
await _setSelectedAddress(address);
await _setSelectedInternalAccount(internalAccount.id);
await forceUpdateMetamaskState(dispatch);
} catch (error) {
Expand Down