3box Replacement (#15243)
* Backup user data Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Tests for prependZero (utils.js) Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Fix advancedtab test Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> backup controller tests Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Lint fixes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Backup controller don't have a store. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Restore from file. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Advanced Tab tests Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Lint fix Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> e2e tests for backup unit tests for restore. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Fix comments on PR. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> restore style Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Lint fixes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> We should move the exportAsFile to a utility file in the shared/ directory Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Move export as file to shared folder Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Refactor create download folder methods Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Lint fixes. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Move the backup/restore buttons closer to 3box Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Change descriptions Add to search Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> refactor code to use if instead of && Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Lint fixes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Restore button should change cursor to pointer. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Fix restore not uploading same file twice. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> Do not backup these items in preferences identities lostIdentities selectedAddress Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> lint fixes. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Only update what is needed. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Fixed test for search Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * remove txError as it currently does nothing. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Remove dispatch, not needed since we're not dispatching any actions. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Event should be title case. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Make backup/restore normal async functions rename event as per product suggestion. Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Use success Actionable message for success message and danger for error message Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * change event name to match with backup Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * Lint fixes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * lint fixes Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com> * fix e2e Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>feature/default_network_editable
parent
d255fcdefb
commit
4f34e72085
@ -0,0 +1,77 @@ |
||||
import { exportAsFile } from '../../../shared/modules/export-utils'; |
||||
import { prependZero } from '../../../shared/modules/string-utils'; |
||||
|
||||
export default class BackupController { |
||||
constructor(opts = {}) { |
||||
const { |
||||
preferencesController, |
||||
addressBookController, |
||||
trackMetaMetricsEvent, |
||||
} = opts; |
||||
|
||||
this.preferencesController = preferencesController; |
||||
this.addressBookController = addressBookController; |
||||
this._trackMetaMetricsEvent = trackMetaMetricsEvent; |
||||
} |
||||
|
||||
async restoreUserData(jsonString) { |
||||
const existingPreferences = this.preferencesController.store.getState(); |
||||
const { preferences, addressBook } = JSON.parse(jsonString); |
||||
if (preferences) { |
||||
preferences.identities = existingPreferences.identities; |
||||
preferences.lostIdentities = existingPreferences.lostIdentities; |
||||
preferences.selectedAddress = existingPreferences.selectedAddress; |
||||
|
||||
this.preferencesController.store.updateState(preferences); |
||||
} |
||||
|
||||
if (addressBook) { |
||||
this.addressBookController.update(addressBook, true); |
||||
} |
||||
|
||||
if (preferences && addressBook) { |
||||
this._trackMetaMetricsEvent({ |
||||
event: 'User Data Imported', |
||||
category: 'Backup', |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async backupUserData() { |
||||
const userData = { |
||||
preferences: { ...this.preferencesController.store.getState() }, |
||||
addressBook: { ...this.addressBookController.state }, |
||||
}; |
||||
|
||||
/** |
||||
* We can remove these properties since we will won't be restoring identities from backup |
||||
*/ |
||||
delete userData.preferences.identities; |
||||
delete userData.preferences.lostIdentities; |
||||
delete userData.preferences.selectedAddress; |
||||
|
||||
const result = JSON.stringify(userData); |
||||
|
||||
const date = new Date(); |
||||
|
||||
const prefixZero = (num) => prependZero(num, 2); |
||||
|
||||
/* |
||||
* userData.YYYY_MM_DD_HH_mm_SS e.g userData.2022_01_13_13_45_56 |
||||
* */ |
||||
const userDataFileName = `MetaMaskUserData.${date.getFullYear()}_${prefixZero( |
||||
date.getMonth() + 1, |
||||
)}_${prefixZero(date.getDay())}_${prefixZero(date.getHours())}_${prefixZero( |
||||
date.getMinutes(), |
||||
)}_${prefixZero(date.getDay())}.json`;
|
||||
|
||||
exportAsFile(userDataFileName, result); |
||||
|
||||
this._trackMetaMetricsEvent({ |
||||
event: 'User Data Exported', |
||||
category: 'Backup', |
||||
}); |
||||
|
||||
return result; |
||||
} |
||||
} |
@ -0,0 +1,118 @@ |
||||
import { strict as assert } from 'assert'; |
||||
import sinon from 'sinon'; |
||||
import BackupController from './backup'; |
||||
|
||||
function getMockController() { |
||||
const mcState = { |
||||
getSelectedAddress: sinon.stub().returns('0x01'), |
||||
selectedAddress: '0x01', |
||||
identities: { |
||||
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B': { |
||||
address: '0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B', |
||||
lastSelected: 1655380342907, |
||||
name: 'Account 3', |
||||
}, |
||||
}, |
||||
lostIdentities: { |
||||
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435': { |
||||
address: '0xfd59bbe569376e3d3e4430297c3c69ea93f77435', |
||||
lastSelected: 1655379648197, |
||||
name: 'Ledger 1', |
||||
}, |
||||
}, |
||||
update: (store) => (mcState.store = store), |
||||
}; |
||||
|
||||
mcState.store = { |
||||
getState: sinon.stub().returns(mcState), |
||||
updateState: (store) => (mcState.store = store), |
||||
}; |
||||
|
||||
return mcState; |
||||
} |
||||
|
||||
const jsonData = `{"preferences":{"frequentRpcListDetail":[{"chainId":"0x539","nickname":"Localhost 8545","rpcPrefs":{},"rpcUrl":"http://localhost:8545","ticker":"ETH"},{"chainId":"0x38","nickname":"Binance Smart Chain Mainnet","rpcPrefs":{"blockExplorerUrl":"https://bscscan.com"},"rpcUrl":"https://bsc-dataseed1.binance.org","ticker":"BNB"},{"chainId":"0x61","nickname":"Binance Smart Chain Testnet","rpcPrefs":{"blockExplorerUrl":"https://testnet.bscscan.com"},"rpcUrl":"https://data-seed-prebsc-1-s1.binance.org:8545","ticker":"tBNB"},{"chainId":"0x89","nickname":"Polygon Mainnet","rpcPrefs":{"blockExplorerUrl":"https://polygonscan.com"},"rpcUrl":"https://polygon-rpc.com","ticker":"MATIC"}],"useBlockie":false,"useNonceField":false,"usePhishDetect":true,"dismissSeedBackUpReminder":false,"useTokenDetection":false,"useCollectibleDetection":false,"openSeaEnabled":false,"advancedGasFee":null,"featureFlags":{"sendHexData":true,"showIncomingTransactions":true},"knownMethodData":{},"currentLocale":"en","forgottenPassword":false,"preferences":{"hideZeroBalanceTokens":false,"showFiatInTestnets":false,"showTestNetworks":true,"useNativeCurrencyAsPrimaryCurrency":true},"ipfsGateway":"dweb.link","infuraBlocked":false,"ledgerTransportType":"webhid","theme":"light","customNetworkListEnabled":false,"textDirection":"auto"},"addressBook":{"addressBook":{"0x61":{"0x42EB768f2244C8811C63729A21A3569731535f06":{"address":"0x42EB768f2244C8811C63729A21A3569731535f06","chainId":"0x61","isEns":false,"memo":"","name":""}}}}}`; |
||||
|
||||
describe('BackupController', function () { |
||||
const getBackupController = () => { |
||||
return new BackupController({ |
||||
preferencesController: getMockController(), |
||||
addressBookController: getMockController(), |
||||
trackMetaMetricsEvent: sinon.stub(), |
||||
}); |
||||
}; |
||||
|
||||
describe('constructor', function () { |
||||
it('should setup correctly', async function () { |
||||
const backupController = getBackupController(); |
||||
const selectedAddress = |
||||
backupController.preferencesController.getSelectedAddress(); |
||||
assert.equal(selectedAddress, '0x01'); |
||||
}); |
||||
|
||||
it('should restore backup', async function () { |
||||
const backupController = getBackupController(); |
||||
backupController.restoreUserData(jsonData); |
||||
// check Preferences backup
|
||||
assert.equal( |
||||
backupController.preferencesController.store.frequentRpcListDetail[0] |
||||
.chainId, |
||||
'0x539', |
||||
); |
||||
assert.equal( |
||||
backupController.preferencesController.store.frequentRpcListDetail[1] |
||||
.chainId, |
||||
'0x38', |
||||
); |
||||
// make sure identities are not lost after restore
|
||||
assert.equal( |
||||
backupController.preferencesController.store.identities[ |
||||
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B' |
||||
].lastSelected, |
||||
1655380342907, |
||||
); |
||||
assert.equal( |
||||
backupController.preferencesController.store.identities[ |
||||
'0x295e26495CEF6F69dFA69911d9D8e4F3bBadB89B' |
||||
].name, |
||||
'Account 3', |
||||
); |
||||
assert.equal( |
||||
backupController.preferencesController.store.lostIdentities[ |
||||
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435' |
||||
].lastSelected, |
||||
1655379648197, |
||||
); |
||||
assert.equal( |
||||
backupController.preferencesController.store.lostIdentities[ |
||||
'0xfd59bbe569376e3d3e4430297c3c69ea93f77435' |
||||
].name, |
||||
'Ledger 1', |
||||
); |
||||
// make sure selected address is not lost after restore
|
||||
assert.equal( |
||||
backupController.preferencesController.store.selectedAddress, |
||||
'0x01', |
||||
); |
||||
// check address book backup
|
||||
assert.equal( |
||||
backupController.addressBookController.store.addressBook['0x61'][ |
||||
'0x42EB768f2244C8811C63729A21A3569731535f06' |
||||
].chainId, |
||||
'0x61', |
||||
); |
||||
assert.equal( |
||||
backupController.addressBookController.store.addressBook['0x61'][ |
||||
'0x42EB768f2244C8811C63729A21A3569731535f06' |
||||
].address, |
||||
'0x42EB768f2244C8811C63729A21A3569731535f06', |
||||
); |
||||
assert.equal( |
||||
backupController.addressBookController.store.addressBook['0x61'][ |
||||
'0x42EB768f2244C8811C63729A21A3569731535f06' |
||||
].isEns, |
||||
false, |
||||
); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,19 @@ |
||||
import { getRandomFileName } from '../../ui/helpers/utils/util'; |
||||
|
||||
export function exportAsFile(filename, data, type = 'text/csv') { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
filename = filename || getRandomFileName(); |
||||
// source: https://stackoverflow.com/a/33542499 by Ludovic Feltz
|
||||
const blob = new window.Blob([data], { type }); |
||||
if (window.navigator.msSaveOrOpenBlob) { |
||||
window.navigator.msSaveBlob(blob, filename); |
||||
} else { |
||||
const elem = window.document.createElement('a'); |
||||
elem.target = '_blank'; |
||||
elem.href = window.URL.createObjectURL(blob); |
||||
elem.download = filename; |
||||
document.body.appendChild(elem); |
||||
elem.click(); |
||||
document.body.removeChild(elem); |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
const { strict: assert } = require('assert'); |
||||
const { promises: fs } = require('fs'); |
||||
const { |
||||
convertToHexValue, |
||||
withFixtures, |
||||
createDownloadFolder, |
||||
} = require('../helpers'); |
||||
|
||||
const downloadsFolder = `${process.cwd()}/test-artifacts/downloads`; |
||||
|
||||
const backupExists = async () => { |
||||
const date = new Date(); |
||||
|
||||
const prependZero = (num, maxLength) => { |
||||
return num.toString().padStart(maxLength, '0'); |
||||
}; |
||||
|
||||
const prefixZero = (num) => prependZero(num, 2); |
||||
|
||||
/* |
||||
* userData.YYYY_MM_DD_HH_mm_SS e.g userData.2022_01_13_13_45_56 |
||||
* */ |
||||
const userDataFileName = `MetaMaskUserData.${date.getFullYear()}_${prefixZero( |
||||
date.getMonth() + 1, |
||||
)}_${prefixZero(date.getDay())}_${prefixZero(date.getHours())}_${prefixZero( |
||||
date.getMinutes(), |
||||
)}_${prefixZero(date.getDay())}.json`;
|
||||
|
||||
try { |
||||
const backup = `${downloadsFolder}/${userDataFileName}`; |
||||
await fs.access(backup); |
||||
return true; |
||||
} catch (e) { |
||||
return false; |
||||
} |
||||
}; |
||||
|
||||
describe('Backup', function () { |
||||
const ganacheOptions = { |
||||
accounts: [ |
||||
{ |
||||
secretKey: |
||||
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', |
||||
balance: convertToHexValue(25000000000000000000), |
||||
}, |
||||
], |
||||
}; |
||||
it('should create backup for the account', async function () { |
||||
await withFixtures( |
||||
{ |
||||
fixtures: 'imported-account', |
||||
ganacheOptions, |
||||
title: this.test.title, |
||||
failOnConsoleError: false, |
||||
}, |
||||
async ({ driver }) => { |
||||
await createDownloadFolder(downloadsFolder); |
||||
await driver.navigate(); |
||||
await driver.fill('#password', 'correct horse battery staple'); |
||||
await driver.press('#password', driver.Key.ENTER); |
||||
|
||||
// Download user settings
|
||||
await driver.clickElement('.account-menu__icon'); |
||||
await driver.clickElement({ text: 'Settings', tag: 'div' }); |
||||
await driver.clickElement({ text: 'Advanced', tag: 'div' }); |
||||
await driver.clickElement({ |
||||
text: 'Backup', |
||||
tag: 'button', |
||||
}); |
||||
|
||||
// Verify download
|
||||
let fileExists; |
||||
await driver.wait(async () => { |
||||
fileExists = await backupExists(); |
||||
return fileExists === true; |
||||
}, 10000); |
||||
assert.equal(fileExists, true); |
||||
}, |
||||
); |
||||
}); |
||||
}); |
Loading…
Reference in new issue