Merge pull request #15746 from MetaMask/Version-v10.19.0
Version v10.19.0feature/default_network_editable
commit
0278de708d
@ -0,0 +1,68 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
set -e |
||||
set -u |
||||
set -o pipefail |
||||
|
||||
if [[ "${CI:-}" != 'true' ]] |
||||
then |
||||
printf '%s\n' 'CI environment variable must be set to true' |
||||
exit 1 |
||||
fi |
||||
|
||||
if [[ "${CIRCLECI:-}" != 'true' ]] |
||||
then |
||||
printf '%s\n' 'CIRCLECI environment variable must be set to true' |
||||
exit 1 |
||||
fi |
||||
|
||||
if [[ "${CIRCLE_BRANCH}" != "develop" ]] |
||||
then |
||||
printf 'This is not develop branch' |
||||
exit 0 |
||||
fi |
||||
|
||||
if [[ -z "${GITHUB_TOKEN:-}" ]] |
||||
then |
||||
printf '%s\n' 'GITHUB_TOKEN environment variable must be set' |
||||
exit 1 |
||||
elif [[ -z "${GITHUB_TOKEN_USER:-}" ]] |
||||
then |
||||
printf '%s\n' 'GITHUB_TOKEN_USER environment variable must be set' |
||||
exit 1 |
||||
fi |
||||
|
||||
mkdir temp |
||||
|
||||
git config --global user.email "metamaskbot@users.noreply.github.com" |
||||
|
||||
git config --global user.name "MetaMask Bot" |
||||
|
||||
git clone git@github.com:MetaMask/extension_bundlesize_stats.git temp |
||||
|
||||
if [[ -f "temp/stats/bundle_size_stats-${CIRCLE_SHA1}.json" ]] |
||||
then |
||||
printf 'Bundle size of the commit is already recorded' |
||||
cd .. |
||||
rm -rf temp |
||||
exit 0 |
||||
fi |
||||
|
||||
cp -R test-artifacts/chrome/mv3/bundle_size_stats.json temp/stats |
||||
|
||||
echo " bundle_size_stats-${CIRCLE_SHA1}.json" >> temp/stats/fileList.txt |
||||
|
||||
mv temp/stats/bundle_size_stats.json "temp/stats/bundle_size_stats-${CIRCLE_SHA1}.json" |
||||
|
||||
cd temp |
||||
|
||||
git add . |
||||
|
||||
git commit --message "Bundle size at commit: ${CIRCLE_SHA1}" |
||||
|
||||
repo_slug="$CIRCLE_PROJECT_USERNAME/extension_bundlesize_stats" |
||||
git push "https://$GITHUB_TOKEN_USER:$GITHUB_TOKEN@github.com/$repo_slug" main |
||||
|
||||
cd .. |
||||
|
||||
rm -rf temp |
@ -0,0 +1,307 @@ |
||||
import { Meta, Canvas, Story } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="Foundations / Shadow" /> |
||||
|
||||
# Shadow |
||||
|
||||
Shadows convey elevation of elements on a surface |
||||
|
||||
## Size |
||||
|
||||
There are 4 different sizes of shadow in MetaMask |
||||
|
||||
<div |
||||
style={{ |
||||
display: 'grid', |
||||
gap: '32px', |
||||
gridTemplateColumns: 'repeat(auto-fill, 200px)', |
||||
marginTop: 16, |
||||
marginBottom: 16, |
||||
}} |
||||
> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-background-default)', |
||||
boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
}} |
||||
> |
||||
XS |
||||
</div> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-background-default)', |
||||
boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
}} |
||||
> |
||||
SM |
||||
</div> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-background-default)', |
||||
boxShadow: 'var(--shadow-size-md) var(--color-shadow-default', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
}} |
||||
> |
||||
MD |
||||
</div> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-background-default)', |
||||
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
}} |
||||
> |
||||
LG |
||||
</div> |
||||
</div> |
||||
|
||||
| Size | CSS | |
||||
| ------ | ----------------------- | |
||||
| **XS** | `var(--shadow-size-xs)` | |
||||
| **SM** | `var(--shadow-size-sm)` | |
||||
| **MD** | `var(--shadow-size-md)` | |
||||
| **LG** | `var(--shadow-size-lg)` | |
||||
|
||||
## Color |
||||
|
||||
As well as the neutral colors for shadow 2 other colors exist that are used for the primary and error/danger button hover states |
||||
|
||||
<div |
||||
style={{ |
||||
display: 'grid', |
||||
gap: '32px', |
||||
gridTemplateColumns: 'repeat(auto-fill, 200px)', |
||||
marginTop: 16, |
||||
marginBottom: 16, |
||||
}} |
||||
> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-background-default)', |
||||
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
}} |
||||
color="default" |
||||
> |
||||
<span>Default</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-primary-default)', |
||||
boxShadow: 'var(--shadow-size-lg) var(--color-primary-shadow', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
color: 'var(--color-primary-inverse)', |
||||
}} |
||||
> |
||||
<span>Primary</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
height: 100, |
||||
backgroundColor: 'var(--color-error-default)', |
||||
boxShadow: 'var(--shadow-size-lg) var(--color-error-shadow', |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
color: 'var(--color-error-inverse)', |
||||
}} |
||||
> |
||||
<span>Error/Danger</span> |
||||
</div> |
||||
</div> |
||||
|
||||
| Color | CSS | |
||||
| ----------- | ----------------------------- | |
||||
| **neutral** | `var(--color-shadow-default)` | |
||||
| **primary** | `var(--color-primary-shadow)` | |
||||
| **danger** | `var(--color-error-shadow)` | |
||||
|
||||
## Example usage |
||||
|
||||
Using both size and color tokens, different shadows can be applied to components |
||||
|
||||
<div> |
||||
<div |
||||
style={{ |
||||
display: 'grid', |
||||
gap: '32px', |
||||
gridTemplateColumns: 'repeat(auto-fill, 200px)', |
||||
marginBottom: '64px', |
||||
}} |
||||
> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--shadow-size-xs) var(--color-shadow-default)', |
||||
}} |
||||
> |
||||
<span>Card</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--shadow-size-sm) var(--color-shadow-default)', |
||||
}} |
||||
> |
||||
<span>Dropdown</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--shadow-size-md) var(--color-shadow-default)', |
||||
}} |
||||
> |
||||
<span>Toast</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--shadow-size-lg) var(--color-shadow-default)', |
||||
}} |
||||
> |
||||
<span>Modal</span> |
||||
</div> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
display: 'grid', |
||||
gap: '32px', |
||||
gridTemplateColumns: 'repeat(auto-fill, 200px)', |
||||
}} |
||||
> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--component-button-primary-shadow)', |
||||
backgroundColor: 'var(--color-primary-default)', |
||||
color: 'var(--color-primary-inverse)', |
||||
}} |
||||
> |
||||
<span>Button Primary Hover</span> |
||||
</div> |
||||
<div |
||||
style={{ |
||||
borderRadius: '4px', |
||||
display: 'grid', |
||||
alignContent: 'center', |
||||
justifyContent: 'center', |
||||
spanAlign: 'center', |
||||
height: 100, |
||||
textAlign: 'center', |
||||
boxShadow: 'var(--component-button-danger-shadow)', |
||||
backgroundColor: 'var(--color-error-default)', |
||||
color: 'var(--color-error-inverse)', |
||||
}} |
||||
> |
||||
<span>Button Error/Danger Hover</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
| Component | JS | CSS | |
||||
| ------------------------ | ---------------------------------------------------------------- | --- | |
||||
| **Card** | `box-shadow: var(--shadow-size-xs) var(--color-shadow-default);` | |
||||
| **Dropdown** | `box-shadow: var(--shadow-size-sm) var(--color-shadow-default);` | |
||||
| **Toast** | `box-shadow: var(--shadow-size-md) var(--color-shadow-default);` | |
||||
| **Modal** | `box-shadow: var(--shadow-size-lg) var(--color-shadow-default);` | |
||||
| **Button Primary Hover** | `box-shadow: var(--shadow-size-sm) var(--color-primary-shadow);` | |
||||
| **Button Danger Hover** | `box-shadow: var(--shadow-size-sm) var(--color-error-shadow);` | |
||||
|
||||
## Takeaways |
||||
|
||||
- Try to avoid using static media queries in your code |
||||
- Try to use the provided SCSS mixins |
||||
|
||||
### ❌ Don't do this |
||||
|
||||
Don't use static media queries in your code |
||||
|
||||
```css |
||||
/** |
||||
* Don't do this |
||||
* Static box-shadows create inconsistency in elevation of elements |
||||
**/ |
||||
.card { |
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2); |
||||
} |
||||
``` |
||||
|
||||
### ✅ Do this |
||||
|
||||
Do use the provided shadow design token css variables |
||||
|
||||
```css |
||||
.card { |
||||
box-shadow: var(--shadow-size-xs) var(--color-shadow-default); |
||||
} |
||||
``` |
||||
|
||||
## References |
||||
|
||||
- [Shadow design tokens](https://metamask.github.io/design-tokens/?path=/docs/shadows-shadows--shadow) |
||||
- [Figma light theme colors library(shadows page)](https://www.figma.com/file/kdFzEC7xzSNw7cXteqgzDW/%5BColor%5D-Light-Theme?node-id=753%3A719)(internal use only) |
||||
- [Figma dark theme colors library(shadows page)](https://www.figma.com/file/rLKsoqpjyoKauYnFDcBIbO/%5BColor%5D-Dark-Theme?node-id=522%3A1022)(internal use only) |
@ -0,0 +1,125 @@ |
||||
import { Meta } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="Foundations / Breakpoints" /> |
||||
|
||||
# Breakpoints |
||||
|
||||
Breakpoints are used for responsive layout |
||||
|
||||
## Screen Sizes |
||||
|
||||
There are 4 screen sizes that make up the breakpoints for the MetaMask extension |
||||
|
||||
- base: `0px` |
||||
- sm: `576px` |
||||
- md: `768px` |
||||
- lg: `1280px` |
||||
|
||||
### SCSS |
||||
|
||||
There are Sass variables and mixins available for use for both min and max screens sizes |
||||
|
||||
### Variables |
||||
|
||||
```css |
||||
$screen-sm-max /* 575px */ |
||||
$screen-md-max /* 767px */ |
||||
$screen-lg-max /* 1279px */ |
||||
|
||||
$screen-sm-min /* 576px */ |
||||
$screen-md-min /* 768px */ |
||||
$screen-lg-min /* 1280px */ |
||||
``` |
||||
|
||||
### Mixins |
||||
|
||||
```css |
||||
/* Max screen size */ |
||||
@include screen-sm-max { |
||||
/* equivalent css @media screen and (max-width: 575px) */ |
||||
} |
||||
@include screen-md-max { |
||||
/* equivalent css @media screen and (max-width: 767px) */ |
||||
} |
||||
@include screen-lg-max { |
||||
/* equivalent css @media screen and (max-width: 1279px) */ |
||||
} |
||||
|
||||
/* Min screen size */ |
||||
@include screen-sm-min { |
||||
/* equivalent css @media screen and (min-width: 576px) */ |
||||
} |
||||
@include screen-md-min { |
||||
/* equivalent css @media screen and (min-width: 768px) */ |
||||
} |
||||
@include screen-lg-min { |
||||
/* equivalent css @media screen and (min-width: 1280px) */ |
||||
} |
||||
``` |
||||
|
||||
Migrating from the old sass variables to the new mixins looks like this |
||||
|
||||
```css |
||||
/* Max width */ |
||||
/* Instead of the media query and sass variable */ |
||||
@media screen and (max-width: $break-small) { |
||||
right: 16px; |
||||
} |
||||
|
||||
/* Use the sass mixin */ |
||||
@include screen-sm-max { |
||||
right: 16px; |
||||
} |
||||
|
||||
/* Min width */ |
||||
/* Instead of the media query and sass variable */ |
||||
@media screen and (min-width: $break-large) { |
||||
left: 16px; |
||||
} |
||||
|
||||
/* Use the sass mixin */ |
||||
@include screen-sm-min { |
||||
left: 16px; |
||||
} |
||||
``` |
||||
|
||||
## Takeaways |
||||
|
||||
- Try to avoid using static media queries in your code. |
||||
- Try to use the provided SCSS mixins |
||||
|
||||
### ❌ Don't do this |
||||
|
||||
Don't use static media queries in your code. |
||||
|
||||
```css |
||||
/** |
||||
* Don't do this |
||||
* Static media queries create inconsistency and could break the UI if we want to update them in future |
||||
**/ |
||||
.account-menu { |
||||
@media screen and (min-width: 769px) { |
||||
right: calc((100vw - 80vw) / 2); |
||||
} |
||||
|
||||
@media screen and (min-width: 1281px) { |
||||
right: calc((100vw - 65vw) / 2); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### ✅ Do this |
||||
|
||||
Do use the provided Sass mixins |
||||
|
||||
```css |
||||
.account-menu { |
||||
@include screen-md-min { |
||||
right: calc((100vw - 80vw) / 2); |
||||
} |
||||
|
||||
@include screen-lg-min { |
||||
right: calc((100vw - 65vw) / 2); |
||||
} |
||||
} |
||||
``` |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -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,241 @@ |
||||
import { strict as assert } from 'assert'; |
||||
import sinon from 'sinon'; |
||||
import proxyquire from 'proxyquire'; |
||||
import { ORIGIN_METAMASK } from '../../shared/constants/app'; |
||||
|
||||
const Ganache = require('../../test/e2e/ganache'); |
||||
|
||||
const ganacheServer = new Ganache(); |
||||
|
||||
const browserPolyfillMock = { |
||||
runtime: { |
||||
id: 'fake-extension-id', |
||||
onInstalled: { |
||||
addListener: () => undefined, |
||||
}, |
||||
onMessageExternal: { |
||||
addListener: () => undefined, |
||||
}, |
||||
getPlatformInfo: async () => 'mac', |
||||
}, |
||||
}; |
||||
|
||||
let loggerMiddlewareMock; |
||||
const createLoggerMiddlewareMock = () => (req, res, next) => { |
||||
if (loggerMiddlewareMock) { |
||||
loggerMiddlewareMock.requests.push(req); |
||||
next((cb) => { |
||||
loggerMiddlewareMock.responses.push(res); |
||||
cb(); |
||||
}); |
||||
return; |
||||
} |
||||
next(); |
||||
}; |
||||
|
||||
const TEST_SEED = |
||||
'debris dizzy just program just float decrease vacant alarm reduce speak stadium'; |
||||
|
||||
const MetaMaskController = proxyquire('./metamask-controller', { |
||||
'./lib/createLoggerMiddleware': { default: createLoggerMiddlewareMock }, |
||||
}).default; |
||||
|
||||
describe('MetaMaskController', function () { |
||||
let metamaskController; |
||||
const sandbox = sinon.createSandbox(); |
||||
const noop = () => undefined; |
||||
|
||||
before(async function () { |
||||
await ganacheServer.start(); |
||||
}); |
||||
|
||||
beforeEach(function () { |
||||
metamaskController = new MetaMaskController({ |
||||
showUserConfirmation: noop, |
||||
encryptor: { |
||||
encrypt(_, object) { |
||||
this.object = object; |
||||
return Promise.resolve('mock-encrypted'); |
||||
}, |
||||
decrypt() { |
||||
return Promise.resolve(this.object); |
||||
}, |
||||
}, |
||||
initLangCode: 'en_US', |
||||
platform: { |
||||
showTransactionNotification: () => undefined, |
||||
getVersion: () => 'foo', |
||||
}, |
||||
browser: browserPolyfillMock, |
||||
infuraProjectId: 'foo', |
||||
}); |
||||
}); |
||||
|
||||
afterEach(function () { |
||||
sandbox.restore(); |
||||
}); |
||||
|
||||
after(async function () { |
||||
await ganacheServer.quit(); |
||||
}); |
||||
|
||||
describe('#addNewAccount', function () { |
||||
it('two parallel calls with same accountCount give same result', async function () { |
||||
await metamaskController.createNewVaultAndKeychain('test@123'); |
||||
const [addNewAccountResult1, addNewAccountResult2] = await Promise.all([ |
||||
metamaskController.addNewAccount(1), |
||||
metamaskController.addNewAccount(1), |
||||
]); |
||||
assert.deepEqual( |
||||
Object.keys(addNewAccountResult1.identities), |
||||
Object.keys(addNewAccountResult2.identities), |
||||
); |
||||
}); |
||||
|
||||
it('two successive calls with same accountCount give same result', async function () { |
||||
await metamaskController.createNewVaultAndKeychain('test@123'); |
||||
const addNewAccountResult1 = await metamaskController.addNewAccount(1); |
||||
const addNewAccountResult2 = await metamaskController.addNewAccount(1); |
||||
assert.deepEqual( |
||||
Object.keys(addNewAccountResult1.identities), |
||||
Object.keys(addNewAccountResult2.identities), |
||||
); |
||||
}); |
||||
|
||||
it('two successive calls with different accountCount give different results', async function () { |
||||
await metamaskController.createNewVaultAndKeychain('test@123'); |
||||
const addNewAccountResult1 = await metamaskController.addNewAccount(1); |
||||
const addNewAccountResult2 = await metamaskController.addNewAccount(2); |
||||
assert.notDeepEqual(addNewAccountResult1, addNewAccountResult2); |
||||
}); |
||||
}); |
||||
|
||||
describe('#importAccountWithStrategy', function () { |
||||
it('two sequential calls with same strategy give same result', async function () { |
||||
let keyringControllerState1; |
||||
let keyringControllerState2; |
||||
const importPrivkey = |
||||
'4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'; |
||||
|
||||
await metamaskController.createNewVaultAndKeychain('test@123'); |
||||
await Promise.all([ |
||||
metamaskController.importAccountWithStrategy('Private Key', [ |
||||
importPrivkey, |
||||
]), |
||||
Promise.resolve(1).then(() => { |
||||
keyringControllerState1 = JSON.stringify( |
||||
metamaskController.keyringController.memStore.getState(), |
||||
); |
||||
metamaskController.importAccountWithStrategy('Private Key', [ |
||||
importPrivkey, |
||||
]); |
||||
}), |
||||
Promise.resolve(2).then(() => { |
||||
keyringControllerState2 = JSON.stringify( |
||||
metamaskController.keyringController.memStore.getState(), |
||||
); |
||||
}), |
||||
]); |
||||
assert.deepEqual(keyringControllerState1, keyringControllerState2); |
||||
}); |
||||
}); |
||||
|
||||
describe('#createNewVaultAndRestore', function () { |
||||
it('two successive calls with same inputs give same result', async function () { |
||||
const result1 = await metamaskController.createNewVaultAndRestore( |
||||
'test@123', |
||||
TEST_SEED, |
||||
); |
||||
const result2 = await metamaskController.createNewVaultAndRestore( |
||||
'test@123', |
||||
TEST_SEED, |
||||
); |
||||
assert.deepEqual(result1, result2); |
||||
}); |
||||
}); |
||||
|
||||
describe('#createNewVaultAndKeychain', function () { |
||||
it('two successive calls with same inputs give same result', async function () { |
||||
const result1 = await metamaskController.createNewVaultAndKeychain( |
||||
'test@123', |
||||
); |
||||
const result2 = await metamaskController.createNewVaultAndKeychain( |
||||
'test@123', |
||||
); |
||||
assert.notEqual(result1, undefined); |
||||
assert.deepEqual(result1, result2); |
||||
}); |
||||
}); |
||||
|
||||
describe('#addToken', function () { |
||||
const address = '0x514910771af9ca656af840dff83e8264ecf986ca'; |
||||
const symbol = 'LINK'; |
||||
const decimals = 18; |
||||
|
||||
it('two parallel calls with same token details give same result', async function () { |
||||
const supportsInterfaceStub = sinon |
||||
.stub() |
||||
.returns(Promise.resolve(false)); |
||||
sinon |
||||
.stub(metamaskController.tokensController, '_createEthersContract') |
||||
.callsFake(() => |
||||
Promise.resolve({ supportsInterface: supportsInterfaceStub }), |
||||
); |
||||
|
||||
const [token1, token2] = await Promise.all([ |
||||
metamaskController.getApi().addToken(address, symbol, decimals), |
||||
metamaskController.getApi().addToken(address, symbol, decimals), |
||||
]); |
||||
assert.deepEqual(token1, token2); |
||||
}); |
||||
}); |
||||
|
||||
describe('#addCustomNetwork', function () { |
||||
const customRpc = { |
||||
chainId: '0x1', |
||||
chainName: 'DUMMY_CHAIN_NAME', |
||||
rpcUrl: 'DUMMY_RPCURL', |
||||
ticker: 'DUMMY_TICKER', |
||||
blockExplorerUrl: 'DUMMY_EXPLORER', |
||||
}; |
||||
it('two successive calls with custom RPC details give same result', async function () { |
||||
await metamaskController.addCustomNetwork(customRpc); |
||||
const rpcList1Length = |
||||
metamaskController.preferencesController.store.getState() |
||||
.frequentRpcListDetail.length; |
||||
await metamaskController.addCustomNetwork(customRpc); |
||||
const rpcList2Length = |
||||
metamaskController.preferencesController.store.getState() |
||||
.frequentRpcListDetail.length; |
||||
assert.equal(rpcList1Length, rpcList2Length); |
||||
}); |
||||
}); |
||||
|
||||
describe('#updateTransactionSendFlowHistory', function () { |
||||
it('two sequential calls with same history give same result', async function () { |
||||
const recipientAddress = '0xc42edfcc21ed14dda456aa0756c153f7985d8813'; |
||||
|
||||
await metamaskController.createNewVaultAndKeychain('test@123'); |
||||
const accounts = await metamaskController.keyringController.getAccounts(); |
||||
const txMeta = await metamaskController.getApi().addUnapprovedTransaction( |
||||
{ |
||||
from: accounts[0], |
||||
to: recipientAddress, |
||||
}, |
||||
ORIGIN_METAMASK, |
||||
); |
||||
|
||||
const [transaction1, transaction2] = await Promise.all([ |
||||
metamaskController |
||||
.getApi() |
||||
.updateTransactionSendFlowHistory(txMeta.id, 2, ['foo1', 'foo2']), |
||||
Promise.resolve(1).then(() => |
||||
metamaskController |
||||
.getApi() |
||||
.updateTransactionSendFlowHistory(txMeta.id, 2, ['foo1', 'foo2']), |
||||
), |
||||
]); |
||||
assert.deepEqual(transaction1, transaction2); |
||||
}); |
||||
}); |
||||
}); |
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue