# Conflicts: # app/_locales/ja/messages.json # package-lock.json messages.jsonのローカライズfeature/default_network_editable
commit
cc246528b5
@ -0,0 +1,19 @@ |
||||
[ |
||||
{ "code": "de", "name": "German" }, |
||||
{ "code": "en", "name": "English" }, |
||||
{ "code": "es", "name": "Spanish" }, |
||||
{ "code": "fr", "name": "French" }, |
||||
{ "code": "hn", "name": "Hindi" }, |
||||
{ "code": "it", "name": "Italian" }, |
||||
{ "code": "ja", "name": "Japanese" }, |
||||
{ "code": "ko", "name": "Korean" }, |
||||
{ "code": "nl", "name": "Dutch" }, |
||||
{ "code": "ph", "name": "Tagalog" }, |
||||
{ "code": "pt", "name": "Portuguese" }, |
||||
{ "code": "ru", "name": "Russian" }, |
||||
{ "code": "sl", "name": "Slovenian" }, |
||||
{ "code": "th", "name": "Thai" }, |
||||
{ "code": "vi", "name": "Vietnamese" }, |
||||
{ "code": "zh_CN", "name": "Mandarin" }, |
||||
{ "code": "zh_TW", "name": "Taiwanese" } |
||||
] |
@ -0,0 +1,27 @@ |
||||
const ethJsRpcSlug = 'Error: [ethjs-rpc] rpc error with payload ' |
||||
const errorLabelPrefix = 'Error: ' |
||||
|
||||
module.exports = extractEthjsErrorMessage |
||||
|
||||
|
||||
//
|
||||
// ethjs-rpc provides overly verbose error messages
|
||||
// if we detect this type of message, we extract the important part
|
||||
// Below is an example input and output
|
||||
//
|
||||
// Error: [ethjs-rpc] rpc error with payload {"id":3947817945380,"jsonrpc":"2.0","params":["0xf8eb8208708477359400830398539406012c8cf97bead5deae237070f9587f8e7a266d80b8843d7d3f5a0000000000000000000000000000000000000000000000000000000000081d1a000000000000000000000000000000000000000000000000001ff973cafa800000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000003f48025a04c32a9b630e0d9e7ff361562d850c86b7a884908135956a7e4a336fa0300d19ca06830776423f25218e8d19b267161db526e66895567147015b1f3fc47aef9a3c7"],"method":"eth_sendRawTransaction"} Error: replacement transaction underpriced
|
||||
//
|
||||
// Transaction Failed: replacement transaction underpriced
|
||||
//
|
||||
|
||||
|
||||
function extractEthjsErrorMessage(errorMessage) { |
||||
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug) |
||||
if (isEthjsRpcError) { |
||||
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length) |
||||
const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length) |
||||
return originalError |
||||
} else { |
||||
return errorMessage |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
const extension = require('extensionizer') |
||||
const promisify = require('pify') |
||||
const allLocales = require('../../_locales/index.json') |
||||
|
||||
const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-')) |
||||
|
||||
async function getFirstPreferredLangCode () { |
||||
const userPreferredLocaleCodes = await promisify( |
||||
extension.i18n.getAcceptLanguages, |
||||
{ errorFirst: false } |
||||
)() |
||||
const firstPreferredLangCode = userPreferredLocaleCodes |
||||
.map(code => code.toLowerCase()) |
||||
.find(code => existingLocaleCodes.includes(code)) |
||||
return firstPreferredLangCode || 'en' |
||||
} |
||||
|
||||
module.exports = getFirstPreferredLangCode |
@ -0,0 +1,33 @@ |
||||
const clone = require('clone') |
||||
|
||||
module.exports = getObjStructure |
||||
|
||||
// This will create an object that represents the structure of the given object
|
||||
// it replaces all values with the result of their type
|
||||
|
||||
// {
|
||||
// "data": {
|
||||
// "CurrencyController": {
|
||||
// "conversionDate": "number",
|
||||
// "conversionRate": "number",
|
||||
// "currentCurrency": "string"
|
||||
// }
|
||||
// }
|
||||
|
||||
function getObjStructure(obj) { |
||||
const structure = clone(obj) |
||||
return deepMap(structure, (value) => { |
||||
return value === null ? 'null' : typeof value |
||||
}) |
||||
} |
||||
|
||||
function deepMap(target = {}, visit) { |
||||
Object.entries(target).forEach(([key, value]) => { |
||||
if (typeof value === 'object' && value !== null) { |
||||
target[key] = deepMap(value, visit) |
||||
} else { |
||||
target[key] = visit(value) |
||||
} |
||||
}) |
||||
return target |
||||
} |
@ -0,0 +1,54 @@ |
||||
|
||||
const version = 23 |
||||
|
||||
/* |
||||
|
||||
This migration removes transactions that are no longer usefull down to 40 total |
||||
|
||||
*/ |
||||
|
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
try { |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
} catch (err) { |
||||
console.warn(`MetaMask Migration #${version}` + err.stack) |
||||
} |
||||
return Promise.resolve(versionedData) |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
|
||||
const { TransactionController } = newState |
||||
if (TransactionController && TransactionController.transactions) { |
||||
const transactions = newState.TransactionController.transactions |
||||
|
||||
if (transactions.length <= 40) return newState |
||||
|
||||
let reverseTxList = transactions.reverse() |
||||
let stripping = true |
||||
while (reverseTxList.length > 40 && stripping) { |
||||
let txIndex = reverseTxList.findIndex((txMeta) => { |
||||
return (txMeta.status === 'failed' || |
||||
txMeta.status === 'rejected' || |
||||
txMeta.status === 'confirmed' || |
||||
txMeta.status === 'dropped') |
||||
}) |
||||
if (txIndex < 0) stripping = false |
||||
else reverseTxList.splice(txIndex, 1) |
||||
} |
||||
|
||||
newState.TransactionController.transactions = reverseTxList.reverse() |
||||
} |
||||
return newState |
||||
} |
@ -0,0 +1,41 @@ |
||||
|
||||
const version = 24 |
||||
|
||||
/* |
||||
|
||||
This migration ensures that the from address in txParams is to lower case for |
||||
all unapproved transactions |
||||
|
||||
*/ |
||||
|
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: async function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
return versionedData |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
if (!newState.TransactionController) return newState |
||||
const transactions = newState.TransactionController.transactions |
||||
newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => { |
||||
if ( |
||||
txMeta.status === 'unapproved' && |
||||
txMeta.txParams && |
||||
txMeta.txParams.from |
||||
) { |
||||
txMeta.txParams.from = txMeta.txParams.from.toLowerCase() |
||||
} |
||||
return txMeta |
||||
}) |
||||
return newState |
||||
} |
@ -0,0 +1,61 @@ |
||||
// next version number
|
||||
const version = 25 |
||||
|
||||
/* |
||||
|
||||
normalizes txParams on unconfirmed txs |
||||
|
||||
*/ |
||||
const ethUtil = require('ethereumjs-util') |
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: async function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
return versionedData |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
|
||||
if (newState.TransactionController) { |
||||
if (newState.TransactionController.transactions) { |
||||
const transactions = newState.TransactionController.transactions |
||||
newState.TransactionController.transactions = transactions.map((txMeta) => { |
||||
if (txMeta.status !== 'unapproved') return txMeta |
||||
txMeta.txParams = normalizeTxParams(txMeta.txParams) |
||||
return txMeta |
||||
}) |
||||
} |
||||
} |
||||
|
||||
return newState |
||||
} |
||||
|
||||
function normalizeTxParams (txParams) { |
||||
// functions that handle normalizing of that key in txParams
|
||||
const whiteList = { |
||||
from: from => ethUtil.addHexPrefix(from).toLowerCase(), |
||||
to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(), |
||||
nonce: nonce => ethUtil.addHexPrefix(nonce), |
||||
value: value => ethUtil.addHexPrefix(value), |
||||
data: data => ethUtil.addHexPrefix(data), |
||||
gas: gas => ethUtil.addHexPrefix(gas), |
||||
gasPrice: gasPrice => ethUtil.addHexPrefix(gasPrice), |
||||
} |
||||
|
||||
// apply only keys in the whiteList
|
||||
const normalizedTxParams = {} |
||||
Object.keys(whiteList).forEach((key) => { |
||||
if (txParams[key]) normalizedTxParams[key] = whiteList[key](txParams[key]) |
||||
}) |
||||
|
||||
return normalizedTxParams |
||||
} |
@ -0,0 +1,29 @@ |
||||
// next version number
|
||||
const version = 0 |
||||
|
||||
/* |
||||
|
||||
description of migration and what it does |
||||
|
||||
*/ |
||||
|
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: async function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
return versionedData |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
// transform state here
|
||||
return newState |
||||
} |
@ -1,78 +0,0 @@ |
||||
const injectCss = require('inject-css') |
||||
const OldMetaMaskUiCss = require('../../old-ui/css') |
||||
const NewMetaMaskUiCss = require('../../ui/css') |
||||
const startPopup = require('./popup-core') |
||||
const PortStream = require('./lib/port-stream.js') |
||||
const isPopupOrNotification = require('./lib/is-popup-or-notification') |
||||
const extension = require('extensionizer') |
||||
const ExtensionPlatform = require('./platforms/extension') |
||||
const NotificationManager = require('./lib/notification-manager') |
||||
const notificationManager = new NotificationManager() |
||||
const setupRaven = require('./lib/setupRaven') |
||||
|
||||
// create platform global
|
||||
global.platform = new ExtensionPlatform() |
||||
|
||||
// setup sentry error reporting
|
||||
const release = global.platform.getVersion() |
||||
setupRaven({ release }) |
||||
|
||||
// inject css
|
||||
// const css = MetaMaskUiCss()
|
||||
// injectCss(css)
|
||||
|
||||
// identify window type (popup, notification)
|
||||
const windowType = isPopupOrNotification() |
||||
global.METAMASK_UI_TYPE = windowType |
||||
closePopupIfOpen(windowType) |
||||
|
||||
// setup stream to background
|
||||
const extensionPort = extension.runtime.connect({ name: windowType }) |
||||
const connectionStream = new PortStream(extensionPort) |
||||
|
||||
// start ui
|
||||
const container = document.getElementById('app-content') |
||||
startPopup({ container, connectionStream }, (err, store) => { |
||||
if (err) return displayCriticalError(err) |
||||
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
|
||||
// const firstTime = Object.keys(identities).length === 0
|
||||
const { isMascara, featureFlags = {} } = store.getState().metamask |
||||
let betaUIState = featureFlags.betaUI |
||||
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const useBetaCss = isMascara || firstTime || betaUIState
|
||||
const useBetaCss = isMascara || betaUIState |
||||
|
||||
let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss() |
||||
let deleteInjectedCss = injectCss(css) |
||||
let newBetaUIState |
||||
|
||||
store.subscribe(() => { |
||||
const state = store.getState() |
||||
newBetaUIState = state.metamask.featureFlags.betaUI |
||||
if (newBetaUIState !== betaUIState) { |
||||
deleteInjectedCss() |
||||
betaUIState = newBetaUIState |
||||
css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss() |
||||
deleteInjectedCss = injectCss(css) |
||||
} |
||||
if (state.appState.shouldClose) notificationManager.closePopup() |
||||
}) |
||||
}) |
||||
|
||||
|
||||
function closePopupIfOpen (windowType) { |
||||
if (windowType !== 'notification') { |
||||
// should close only chrome popup
|
||||
notificationManager.closePopup() |
||||
} |
||||
} |
||||
|
||||
function displayCriticalError (err) { |
||||
container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>' |
||||
container.style.height = '80px' |
||||
log.error(err.stack) |
||||
throw err |
||||
} |
@ -0,0 +1,84 @@ |
||||
const injectCss = require('inject-css') |
||||
const OldMetaMaskUiCss = require('../../old-ui/css') |
||||
const NewMetaMaskUiCss = require('../../ui/css') |
||||
const startPopup = require('./popup-core') |
||||
const PortStream = require('./lib/port-stream.js') |
||||
const isPopupOrNotification = require('./lib/is-popup-or-notification') |
||||
const extension = require('extensionizer') |
||||
const ExtensionPlatform = require('./platforms/extension') |
||||
const NotificationManager = require('./lib/notification-manager') |
||||
const notificationManager = new NotificationManager() |
||||
const setupRaven = require('./lib/setupRaven') |
||||
|
||||
start().catch(log.error) |
||||
|
||||
async function start() { |
||||
|
||||
// create platform global
|
||||
global.platform = new ExtensionPlatform() |
||||
|
||||
// setup sentry error reporting
|
||||
const release = global.platform.getVersion() |
||||
setupRaven({ release }) |
||||
|
||||
// inject css
|
||||
// const css = MetaMaskUiCss()
|
||||
// injectCss(css)
|
||||
|
||||
// identify window type (popup, notification)
|
||||
const windowType = isPopupOrNotification() |
||||
global.METAMASK_UI_TYPE = windowType |
||||
closePopupIfOpen(windowType) |
||||
|
||||
// setup stream to background
|
||||
const extensionPort = extension.runtime.connect({ name: windowType }) |
||||
const connectionStream = new PortStream(extensionPort) |
||||
|
||||
// start ui
|
||||
const container = document.getElementById('app-content') |
||||
startPopup({ container, connectionStream }, (err, store) => { |
||||
if (err) return displayCriticalError(err) |
||||
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const { isMascara, identities = {}, featureFlags = {} } = store.getState().metamask
|
||||
// const firstTime = Object.keys(identities).length === 0
|
||||
const { isMascara, featureFlags = {} } = store.getState().metamask |
||||
let betaUIState = featureFlags.betaUI |
||||
|
||||
// Code commented out until we begin auto adding users to NewUI
|
||||
// const useBetaCss = isMascara || firstTime || betaUIState
|
||||
const useBetaCss = isMascara || betaUIState |
||||
|
||||
let css = useBetaCss ? NewMetaMaskUiCss() : OldMetaMaskUiCss() |
||||
let deleteInjectedCss = injectCss(css) |
||||
let newBetaUIState |
||||
|
||||
store.subscribe(() => { |
||||
const state = store.getState() |
||||
newBetaUIState = state.metamask.featureFlags.betaUI |
||||
if (newBetaUIState !== betaUIState) { |
||||
deleteInjectedCss() |
||||
betaUIState = newBetaUIState |
||||
css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss() |
||||
deleteInjectedCss = injectCss(css) |
||||
} |
||||
if (state.appState.shouldClose) notificationManager.closePopup() |
||||
}) |
||||
}) |
||||
|
||||
|
||||
function closePopupIfOpen (windowType) { |
||||
if (windowType !== 'notification') { |
||||
// should close only chrome popup
|
||||
notificationManager.closePopup() |
||||
} |
||||
} |
||||
|
||||
function displayCriticalError (err) { |
||||
container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>' |
||||
container.style.height = '80px' |
||||
log.error(err.stack) |
||||
throw err |
||||
} |
||||
|
||||
} |
@ -0,0 +1,62 @@ |
||||
#!/usr/bin/env node
|
||||
const request = require('request-promise') |
||||
const VERSION = require('../dist/chrome/manifest.json').version |
||||
|
||||
start().catch(console.error) |
||||
|
||||
async function start() { |
||||
|
||||
const GITHUB_COMMENT_TOKEN = process.env.GITHUB_COMMENT_TOKEN |
||||
const CIRCLE_PULL_REQUEST = process.env.CIRCLE_PULL_REQUEST |
||||
console.log('CIRCLE_PULL_REQUEST', CIRCLE_PULL_REQUEST) |
||||
const CIRCLE_SHA1 = process.env.CIRCLE_SHA1 |
||||
console.log('CIRCLE_SHA1', CIRCLE_SHA1) |
||||
const CIRCLE_BUILD_NUM = process.env.CIRCLE_BUILD_NUM |
||||
console.log('CIRCLE_BUILD_NUM', CIRCLE_BUILD_NUM) |
||||
|
||||
if (!CIRCLE_PULL_REQUEST) { |
||||
console.warn(`No pull request detected for commit "${CIRCLE_SHA1}"`) |
||||
return |
||||
} |
||||
|
||||
const CIRCLE_PR_NUMBER = CIRCLE_PULL_REQUEST.split('/').pop() |
||||
const SHORT_SHA1 = CIRCLE_SHA1.slice(0,7) |
||||
const BUILD_LINK_BASE = `https://${CIRCLE_BUILD_NUM}-42009758-gh.circle-artifacts.com/0` |
||||
|
||||
const MASCARA = `${BUILD_LINK_BASE}/builds/mascara/home.html` |
||||
const CHROME = `${BUILD_LINK_BASE}/builds/metamask-chrome-${VERSION}.zip` |
||||
const FIREFOX = `${BUILD_LINK_BASE}/builds/metamask-firefox-${VERSION}.zip` |
||||
const EDGE = `${BUILD_LINK_BASE}/builds/metamask-edge-${VERSION}.zip` |
||||
const OPERA = `${BUILD_LINK_BASE}/builds/metamask-opera-${VERSION}.zip` |
||||
const WALKTHROUGH = `${BUILD_LINK_BASE}/test-artifacts/screens/walkthrough%20%28en%29.gif` |
||||
|
||||
const commentBody = ` |
||||
<details> |
||||
<summary> |
||||
Builds ready [${SHORT_SHA1}]: |
||||
<a href="${MASCARA}">mascara</a>, |
||||
<a href="${CHROME}">chrome</a>, |
||||
<a href="${FIREFOX}">firefox</a>, |
||||
<a href="${EDGE}">edge</a>, |
||||
<a href="${OPERA}">opera</a> |
||||
</summary> |
||||
<image src="${WALKTHROUGH}"> |
||||
</details> |
||||
` |
||||
|
||||
const JSON_PAYLOAD = JSON.stringify({ body: commentBody }) |
||||
const POST_COMMENT_URI = `https://api.github.com/repos/metamask/metamask-extension/issues/${CIRCLE_PR_NUMBER}/comments` |
||||
console.log(`Announcement:\n${commentBody}`) |
||||
console.log(`Posting to: ${POST_COMMENT_URI}`) |
||||
|
||||
await request({ |
||||
method: 'POST', |
||||
uri: POST_COMMENT_URI, |
||||
body: JSON_PAYLOAD, |
||||
headers: { |
||||
'User-Agent': 'metamaskbot', |
||||
'Authorization': `token ${GITHUB_COMMENT_TOKEN}`, |
||||
}, |
||||
}) |
||||
|
||||
} |
@ -0,0 +1,55 @@ |
||||
#!/usr/bin/env node
|
||||
const pify = require('pify') |
||||
const exec = pify(require('child_process').exec, { multiArgs: true }) |
||||
const VERSION = require('../dist/chrome/manifest.json').version |
||||
|
||||
start().catch(console.error) |
||||
|
||||
async function start(){ |
||||
const authWorked = await checkIfAuthWorks() |
||||
if (!authWorked) { |
||||
console.log(`Sentry auth failed...`) |
||||
} |
||||
// check if version exists or not
|
||||
const versionAlreadyExists = await checkIfVersionExists() |
||||
// abort if versions exists
|
||||
if (versionAlreadyExists) { |
||||
console.log(`Version "${VERSION}" already exists on Sentry, aborting sourcemap upload.`) |
||||
return |
||||
} |
||||
|
||||
// create sentry release
|
||||
console.log(`creating Sentry release for "${VERSION}"...`) |
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' new ${VERSION}`) |
||||
console.log(`removing any existing files from Sentry release "${VERSION}"...`) |
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} delete --all`) |
||||
// upload sentry source and sourcemaps
|
||||
console.log(`uploading source files Sentry release "${VERSION}"...`) |
||||
await exec(`for FILEPATH in ./dist/chrome/*.js; do [ -e $FILEPATH ] || continue; export FILE=\`basename $FILEPATH\` && echo uploading $FILE && sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload $FILEPATH metamask/$FILE; done;`) |
||||
console.log(`uploading sourcemaps Sentry release "${VERSION}"...`) |
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' files ${VERSION} upload-sourcemaps ./dist/sourcemaps/ --url-prefix 'sourcemaps'`) |
||||
console.log('all done!') |
||||
} |
||||
|
||||
async function checkIfAuthWorks() { |
||||
const itWorked = await doesNotFail(async () => { |
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' list`) |
||||
}) |
||||
return itWorked |
||||
} |
||||
|
||||
async function checkIfVersionExists() { |
||||
const versionAlreadyExists = await doesNotFail(async () => { |
||||
await exec(`sentry-cli releases --org 'metamask' --project 'metamask' info ${VERSION}`) |
||||
}) |
||||
return versionAlreadyExists |
||||
} |
||||
|
||||
async function doesNotFail(asyncFn) { |
||||
try { |
||||
await asyncFn() |
||||
return true |
||||
} catch (err) { |
||||
return false |
||||
} |
||||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,48 @@ |
||||
# QA Guide |
||||
|
||||
Steps to mark a full pass of QA complete. |
||||
* Browsers: Opera, Chrome, Firefox, Edge. |
||||
* OS: Ubuntu, Mac OSX, Windows |
||||
* Load older version of MetaMask and attempt to simulate updating the extension. |
||||
* Open Developer Console in background and popup, inspect errors. |
||||
* Watch the state logs |
||||
* Transactions (unapproved txs -> rejected/submitted -> confirmed) |
||||
* Nonces/LocalNonces |
||||
* Vault integrity |
||||
* create vault |
||||
* Log out |
||||
* Log in again |
||||
* Log out |
||||
* Restore from seed |
||||
* Create a second account |
||||
* Import a loose account (not related to HD Wallet) |
||||
* Import old existing vault seed phrase (pref with test Ether) |
||||
* Download State Logs, Priv key file, seed phrase file. |
||||
* Send Ether |
||||
* by address |
||||
* by ens name |
||||
* Web3 API Stability |
||||
* Create a contract from a Ðapp (remix) |
||||
* Load a Ðapp that reads using events/logs (ENS) |
||||
* Connect to MEW/MyCypto |
||||
* Send a transaction from any Ðapp |
||||
- MEW |
||||
- EtherDelta |
||||
- Leeroy |
||||
- Aragon |
||||
- (https://tmashuang.github.io/demo-dapp) |
||||
* Check account balances |
||||
* Token Management |
||||
* create a token with tokenfactory (http://tokenfactory.surge.sh/#/factory) |
||||
* Add that token to the token view |
||||
* Send that token to another metamask address. |
||||
* confirm the token arrived. |
||||
* Send a transaction and sign a message (https://danfinlay.github.io/js-eth-personal-sign-examples/) for each keyring type |
||||
* hd keyring |
||||
* imported keyring |
||||
* Change network from mainnet → ropsten → rinkeby → localhost (ganache) |
||||
* Ganache set blocktime to simulate retryTx in MetaMask |
||||
* Copy public key to clipboard |
||||
* Export private key |
||||
|
||||
* Explore changes in master, target features that have been changed and break. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,18 @@ |
||||
require('chromedriver') |
||||
const webdriver = require('selenium-webdriver') |
||||
|
||||
exports.delay = function delay (time) { |
||||
return new Promise(resolve => setTimeout(resolve, time)) |
||||
} |
||||
|
||||
|
||||
exports.buildWebDriver = function buildWebDriver (extPath) { |
||||
return new webdriver.Builder() |
||||
.withCapabilities({ |
||||
chromeOptions: { |
||||
args: [`load-extension=${extPath}`], |
||||
}, |
||||
}) |
||||
.forBrowser('chrome') |
||||
.build() |
||||
} |
@ -0,0 +1,145 @@ |
||||
const fs = require('fs') |
||||
const mkdirp = require('mkdirp') |
||||
const path = require('path') |
||||
const assert = require('assert') |
||||
const pify = require('pify') |
||||
const webdriver = require('selenium-webdriver') |
||||
const By = webdriver.By |
||||
const { delay, buildWebDriver } = require('./func') |
||||
|
||||
describe('Metamask popup page', function () { |
||||
let driver |
||||
this.seedPhase |
||||
this.accountAddress |
||||
this.timeout(0) |
||||
|
||||
before(async function () { |
||||
const extPath = path.resolve('dist/chrome') |
||||
driver = buildWebDriver(extPath) |
||||
await driver.get('chrome://extensions-frame') |
||||
const elems = await driver.findElements(By.css('.extension-list-item-wrapper')) |
||||
const extensionId = await elems[1].getAttribute('id') |
||||
await driver.get(`chrome-extension://${extensionId}/popup.html`) |
||||
await delay(500) |
||||
}) |
||||
|
||||
afterEach(async function () { |
||||
if (this.currentTest.state === 'failed') { |
||||
await verboseReportOnFailure(this.currentTest) |
||||
} |
||||
}) |
||||
|
||||
after(async function () { |
||||
await driver.quit() |
||||
}) |
||||
|
||||
describe('#onboarding', () => { |
||||
it('should open Metamask.io', async function () { |
||||
const tabs = await driver.getAllWindowHandles() |
||||
await driver.switchTo().window(tabs[0]) |
||||
await delay(300) |
||||
await setProviderType('localhost') |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should match title', async () => { |
||||
const title = await driver.getTitle() |
||||
assert.equal(title, 'MetaMask', 'title matches MetaMask') |
||||
}) |
||||
|
||||
it('should show privacy notice', async () => { |
||||
const privacy = await driver.findElement(By.css('.terms-header')).getText() |
||||
assert.equal(privacy, 'PRIVACY NOTICE', 'shows privacy notice') |
||||
driver.findElement(By.css('button')).click() |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should show terms of use', async () => { |
||||
await delay(300) |
||||
const terms = await driver.findElement(By.css('.terms-header')).getText() |
||||
assert.equal(terms, 'TERMS OF USE', 'shows terms of use') |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should be unable to continue without scolling throught the terms of use', async () => { |
||||
const button = await driver.findElement(By.css('button')).isEnabled() |
||||
assert.equal(button, false, 'disabled continue button') |
||||
const element = driver.findElement(By.linkText( |
||||
'Attributions' |
||||
)) |
||||
await driver.executeScript('arguments[0].scrollIntoView(true)', element) |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should be able to continue when scrolled to the bottom of terms of use', async () => { |
||||
const button = await driver.findElement(By.css('button')) |
||||
const buttonEnabled = await button.isEnabled() |
||||
await delay(500) |
||||
assert.equal(buttonEnabled, true, 'enabled continue button') |
||||
await button.click() |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should accept password with length of eight', async () => { |
||||
const passwordBox = await driver.findElement(By.id('password-box')) |
||||
const passwordBoxConfirm = await driver.findElement(By.id('password-box-confirm')) |
||||
const button = driver.findElement(By.css('button')) |
||||
|
||||
passwordBox.sendKeys('123456789') |
||||
passwordBoxConfirm.sendKeys('123456789') |
||||
await delay(500) |
||||
await button.click() |
||||
}) |
||||
|
||||
it('should show value was created and seed phrase', async () => { |
||||
await delay(700) |
||||
this.seedPhase = await driver.findElement(By.css('.twelve-word-phrase')).getText() |
||||
const continueAfterSeedPhrase = await driver.findElement(By.css('button')) |
||||
await continueAfterSeedPhrase.click() |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should show lock account', async () => { |
||||
await driver.findElement(By.css('.sandwich-expando')).click() |
||||
await delay(500) |
||||
await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)')).click() |
||||
}) |
||||
|
||||
it('should accept account password after lock', async () => { |
||||
await delay(500) |
||||
await driver.findElement(By.id('password-box')).sendKeys('123456789') |
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(500) |
||||
}) |
||||
|
||||
it('should show QR code option', async () => { |
||||
await delay(300) |
||||
await driver.findElement(By.css('.fa-ellipsis-h')).click() |
||||
await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div:nth-child(1) > flex-column > div.name-label > div > span > i > div > div > li:nth-child(3)')).click() |
||||
await delay(300) |
||||
}) |
||||
|
||||
it('should show the account address', async () => { |
||||
this.accountAddress = await driver.findElement(By.css('.ellip-address')).getText() |
||||
await driver.findElement(By.css('.fa-arrow-left')).click() |
||||
await delay(500) |
||||
}) |
||||
}) |
||||
|
||||
async function setProviderType(type) { |
||||
await driver.executeScript('window.metamask.setProviderType(arguments[0])', type) |
||||
} |
||||
|
||||
async function verboseReportOnFailure(test) { |
||||
const artifactDir = `./test-artifacts/${test.title}` |
||||
const filepathBase = `${artifactDir}/test-failure` |
||||
await pify(mkdirp)(artifactDir) |
||||
// capture screenshot
|
||||
const screenshot = await driver.takeScreenshot() |
||||
await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' }) |
||||
// capture dom source
|
||||
const htmlSource = await driver.getPageSource() |
||||
await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource) |
||||
} |
||||
|
||||
}) |
@ -0,0 +1,61 @@ |
||||
const reactTriggerChange = require('../../lib/react-trigger-change') |
||||
const { |
||||
timeout, |
||||
queryAsync, |
||||
findAsync, |
||||
} = require('../../lib/util') |
||||
|
||||
QUnit.module('tx list items') |
||||
|
||||
QUnit.test('renders list items successfully', (assert) => { |
||||
const done = assert.async() |
||||
runTxListItemsTest(assert).then(done).catch((err) => { |
||||
assert.notOk(err, `Error was thrown: ${err.stack}`) |
||||
done() |
||||
}) |
||||
}) |
||||
|
||||
async function runTxListItemsTest(assert, done) { |
||||
console.log('*** start runTxListItemsTest') |
||||
const selectState = await queryAsync($, 'select') |
||||
selectState.val('tx list items') |
||||
reactTriggerChange(selectState[0]) |
||||
|
||||
const metamaskLogo = await queryAsync($, '.left-menu-wrapper') |
||||
assert.ok(metamaskLogo[0], 'metamask logo present') |
||||
metamaskLogo[0].click() |
||||
|
||||
const txListItems = await queryAsync($, '.tx-list-item') |
||||
assert.equal(txListItems.length, 8, 'all tx list items are rendered') |
||||
|
||||
const unapprovedTx = txListItems[0] |
||||
assert.equal($(unapprovedTx).hasClass('tx-list-pending-item-container'), true, 'unapprovedTx has the correct class') |
||||
|
||||
const retryTx = txListItems[1] |
||||
const retryTxLink = await findAsync($(retryTx), '.tx-list-item-retry-link') |
||||
assert.equal(retryTxLink[0].textContent, 'Increase the gas price on your transaction', 'retryTx has expected link') |
||||
|
||||
const approvedTx = txListItems[2] |
||||
const approvedTxRenderedStatus = await findAsync($(approvedTx), '.tx-list-status') |
||||
assert.equal(approvedTxRenderedStatus[0].textContent, 'Approved', 'approvedTx has correct label') |
||||
|
||||
const unapprovedMsg = txListItems[3] |
||||
const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.tx-list-account') |
||||
assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description') |
||||
|
||||
const failedTx = txListItems[4] |
||||
const failedTxRenderedStatus = await findAsync($(failedTx), '.tx-list-status') |
||||
assert.equal(failedTxRenderedStatus[0].textContent, 'Failed', 'failedTx has correct label') |
||||
|
||||
const shapeShiftTx = txListItems[5] |
||||
const shapeShiftTxStatus = await findAsync($(shapeShiftTx), '.flex-column div:eq(1)') |
||||
assert.equal(shapeShiftTxStatus[0].textContent, 'No deposits received', 'shapeShiftTx has correct status') |
||||
|
||||
const confirmedTokenTx = txListItems[6] |
||||
const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.tx-list-account') |
||||
assert.equal(confirmedTokenTxAddress[0].textContent, '0xe7884118...81a9', 'confirmedTokenTx has correct address') |
||||
|
||||
const rejectedTx = txListItems[7] |
||||
const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status') |
||||
assert.equal(rejectedTxRenderedStatus[0].textContent, 'Rejected', 'rejectedTx has correct label') |
||||
} |
@ -0,0 +1,18 @@ |
||||
require('chromedriver') |
||||
const webdriver = require('selenium-webdriver') |
||||
|
||||
exports.delay = function delay (time) { |
||||
return new Promise(resolve => setTimeout(resolve, time)) |
||||
} |
||||
|
||||
|
||||
exports.buildWebDriver = function buildWebDriver (extPath) { |
||||
return new webdriver.Builder() |
||||
.withCapabilities({ |
||||
chromeOptions: { |
||||
args: [`load-extension=${extPath}`], |
||||
}, |
||||
}) |
||||
.forBrowser('chrome') |
||||
.build() |
||||
} |
@ -0,0 +1,230 @@ |
||||
const path = require('path') |
||||
const fs = require('fs') |
||||
const pify = require('pify') |
||||
const mkdirp = require('mkdirp') |
||||
const rimraf = require('rimraf') |
||||
const webdriver = require('selenium-webdriver') |
||||
const endOfStream = require('end-of-stream') |
||||
const GIFEncoder = require('gifencoder') |
||||
const pngFileStream = require('png-file-stream') |
||||
const sizeOfPng = require('image-size/lib/types/png') |
||||
const By = webdriver.By |
||||
const { delay, buildWebDriver } = require('./func') |
||||
const localesIndex = require('../../app/_locales/index.json') |
||||
|
||||
let driver |
||||
|
||||
captureAllScreens().catch((err) => { |
||||
try { |
||||
console.error(err) |
||||
verboseReportOnFailure() |
||||
driver.quit() |
||||
} catch (err) { |
||||
console.error(err) |
||||
} |
||||
process.exit(1) |
||||
}) |
||||
|
||||
async function captureAllScreens() { |
||||
let screenshotCount = 0 |
||||
|
||||
// common names
|
||||
let button |
||||
let tabs |
||||
let element |
||||
|
||||
await cleanScreenShotDir() |
||||
|
||||
// setup selenium and install extension
|
||||
const extPath = path.resolve('dist/chrome') |
||||
driver = buildWebDriver(extPath) |
||||
await driver.get('chrome://extensions-frame') |
||||
const elems = await driver.findElements(By.css('.extension-list-item-wrapper')) |
||||
const extensionId = await elems[1].getAttribute('id') |
||||
await driver.get(`chrome-extension://${extensionId}/home.html`) |
||||
await delay(500) |
||||
tabs = await driver.getAllWindowHandles() |
||||
await driver.switchTo().window(tabs[0]) |
||||
await delay(1000) |
||||
await setProviderType('localhost') |
||||
await delay(300) |
||||
|
||||
// click try new ui
|
||||
await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div.flex-row.flex-center.flex-grow > p')).click() |
||||
await delay(300) |
||||
|
||||
// close metamask homepage and extra home.html
|
||||
tabs = await driver.getAllWindowHandles() |
||||
// metamask homepage is opened on prod, not dev
|
||||
if (tabs.length > 2) { |
||||
await driver.switchTo().window(tabs[2]) |
||||
driver.close() |
||||
} |
||||
await driver.switchTo().window(tabs[1]) |
||||
driver.close() |
||||
await driver.switchTo().window(tabs[0]) |
||||
await delay(300) |
||||
await captureLanguageScreenShots('welcome-new-ui') |
||||
|
||||
// setup account
|
||||
await delay(1000) |
||||
await driver.findElement(By.css('body')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('welcome') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await captureLanguageScreenShots('create password') |
||||
|
||||
const passwordBox = await driver.findElement(By.css('input[type=password]:nth-of-type(1)')) |
||||
const passwordBoxConfirm = await driver.findElement(By.css('input[type=password]:nth-of-type(2)')) |
||||
passwordBox.sendKeys('123456789') |
||||
passwordBoxConfirm.sendKeys('123456789') |
||||
await delay(500) |
||||
await captureLanguageScreenShots('choose-password-filled') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(500) |
||||
await captureLanguageScreenShots('unique account image') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(500) |
||||
await captureLanguageScreenShots('privacy note') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('terms') |
||||
|
||||
await delay(300) |
||||
element = driver.findElement(By.linkText('Attributions')) |
||||
await driver.executeScript('arguments[0].scrollIntoView(true)', element) |
||||
await delay(300) |
||||
await captureLanguageScreenShots('terms-scrolled') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('secret backup phrase') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('secret backup phrase') |
||||
|
||||
await driver.findElement(By.css('.backup-phrase__reveal-button')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('secret backup phrase - reveal') |
||||
|
||||
await driver.findElement(By.css('button')).click() |
||||
await delay(300) |
||||
await captureLanguageScreenShots('confirm secret backup phrase') |
||||
|
||||
// finish up
|
||||
console.log('building gif...') |
||||
await generateGif() |
||||
await driver.quit() |
||||
return |
||||
|
||||
//
|
||||
// await button.click()
|
||||
// await delay(700)
|
||||
// this.seedPhase = await driver.findElement(By.css('.twelve-word-phrase')).getText()
|
||||
// await captureScreenShot('seed phrase')
|
||||
//
|
||||
// const continueAfterSeedPhrase = await driver.findElement(By.css('button'))
|
||||
// await continueAfterSeedPhrase.click()
|
||||
// await delay(300)
|
||||
// await captureScreenShot('main screen')
|
||||
//
|
||||
// await driver.findElement(By.css('.sandwich-expando')).click()
|
||||
// await delay(500)
|
||||
// await captureScreenShot('menu')
|
||||
|
||||
// await driver.findElement(By.css('#app-content > div > div:nth-child(3) > span > div > li:nth-child(3)')).click()
|
||||
// await captureScreenShot('main screen')
|
||||
// it('should accept account password after lock', async () => {
|
||||
// await delay(500)
|
||||
// await driver.findElement(By.id('password-box')).sendKeys('123456789')
|
||||
// await driver.findElement(By.css('button')).click()
|
||||
// await delay(500)
|
||||
// })
|
||||
//
|
||||
// it('should show QR code option', async () => {
|
||||
// await delay(300)
|
||||
// await driver.findElement(By.css('.fa-ellipsis-h')).click()
|
||||
// await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div:nth-child(1) > flex-column > div.name-label > div > span > i > div > div > li:nth-child(3)')).click()
|
||||
// await delay(300)
|
||||
// })
|
||||
//
|
||||
// it('should show the account address', async () => {
|
||||
// this.accountAddress = await driver.findElement(By.css('.ellip-address')).getText()
|
||||
// await driver.findElement(By.css('.fa-arrow-left')).click()
|
||||
// await delay(500)
|
||||
// })
|
||||
|
||||
async function captureLanguageScreenShots(label) { |
||||
const nonEnglishLocales = localesIndex.filter(localeMeta => localeMeta.code !== 'en') |
||||
// take english shot
|
||||
await captureScreenShot(`${label} (en)`) |
||||
for (let localeMeta of nonEnglishLocales) { |
||||
// set locale and take shot
|
||||
await setLocale(localeMeta.code) |
||||
await delay(300) |
||||
await captureScreenShot(`${label} (${localeMeta.code})`) |
||||
} |
||||
// return locale to english
|
||||
await setLocale('en') |
||||
await delay(300) |
||||
} |
||||
|
||||
async function setLocale(code) { |
||||
await driver.executeScript('window.metamask.updateCurrentLocale(arguments[0])', code) |
||||
} |
||||
|
||||
async function setProviderType(type) { |
||||
await driver.executeScript('window.metamask.setProviderType(arguments[0])', type) |
||||
} |
||||
|
||||
// cleanup
|
||||
await driver.quit() |
||||
|
||||
async function cleanScreenShotDir() { |
||||
await pify(rimraf)(`./test-artifacts/screens/`) |
||||
} |
||||
|
||||
async function captureScreenShot(label) { |
||||
const shotIndex = screenshotCount.toString().padStart(4, '0') |
||||
screenshotCount++ |
||||
const artifactDir = `./test-artifacts/screens/` |
||||
await pify(mkdirp)(artifactDir) |
||||
// capture screenshot
|
||||
const screenshot = await driver.takeScreenshot() |
||||
await pify(fs.writeFile)(`${artifactDir}/${shotIndex} - ${label}.png`, screenshot, { encoding: 'base64' }) |
||||
} |
||||
|
||||
async function generateGif(){ |
||||
// calculate screenshot size
|
||||
const screenshot = await driver.takeScreenshot() |
||||
const pngBuffer = Buffer.from(screenshot, 'base64') |
||||
const size = sizeOfPng.calculate(pngBuffer) |
||||
|
||||
// read only the english pngs into gif
|
||||
const encoder = new GIFEncoder(size.width, size.height) |
||||
const stream = pngFileStream('./test-artifacts/screens/* (en).png') |
||||
.pipe(encoder.createWriteStream({ repeat: 0, delay: 1000, quality: 10 })) |
||||
.pipe(fs.createWriteStream('./test-artifacts/screens/walkthrough (en).gif')) |
||||
|
||||
// wait for end
|
||||
await pify(endOfStream)(stream) |
||||
} |
||||
|
||||
} |
||||
|
||||
async function verboseReportOnFailure(test) { |
||||
const artifactDir = `./test-artifacts/${test.title}` |
||||
const filepathBase = `${artifactDir}/test-failure` |
||||
await pify(mkdirp)(artifactDir) |
||||
// capture screenshot
|
||||
const screenshot = await driver.takeScreenshot() |
||||
await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' }) |
||||
// capture dom source
|
||||
const htmlSource = await driver.getPageSource() |
||||
await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource) |
||||
} |
@ -0,0 +1,99 @@ |
||||
const assert = require('assert') |
||||
const migration23 = require('../../../app/scripts/migrations/023') |
||||
const properTime = (new Date()).getTime() |
||||
const storage = { |
||||
"meta": {}, |
||||
"data": { |
||||
"TransactionController": { |
||||
"transactions": [ |
||||
] |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
const transactions = [] |
||||
const transactions40 = [] |
||||
const transactions20 = [] |
||||
|
||||
const txStates = [ |
||||
'unapproved', |
||||
'approved', |
||||
'signed', |
||||
'submitted', |
||||
'confirmed', |
||||
'rejected', |
||||
'failed', |
||||
'dropped', |
||||
] |
||||
|
||||
const deletableTxStates = [ |
||||
'confirmed', |
||||
'rejected', |
||||
'failed', |
||||
'dropped', |
||||
] |
||||
|
||||
let nonDeletableCount = 0 |
||||
|
||||
let status |
||||
while (transactions.length <= 100) { |
||||
status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] |
||||
if (!deletableTxStates.find((s) => s === status)) nonDeletableCount++ |
||||
transactions.push({status}) |
||||
} |
||||
|
||||
while (transactions40.length < 40) { |
||||
status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] |
||||
transactions40.push({status}) |
||||
} |
||||
|
||||
while (transactions20.length < 20) { |
||||
status = txStates[Math.floor(Math.random() * Math.floor(txStates.length - 1))] |
||||
transactions20.push({status}) |
||||
} |
||||
|
||||
|
||||
|
||||
storage.data.TransactionController.transactions = transactions |
||||
|
||||
describe('storage is migrated successfully and the proper transactions are remove from state', () => { |
||||
it('should remove transactions that are unneeded', (done) => { |
||||
migration23.migrate(storage) |
||||
.then((migratedData) => { |
||||
let leftoverNonDeletableTxCount = 0 |
||||
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||
migratedTransactions.forEach((tx) => { |
||||
if (!deletableTxStates.find((s) => s === tx.status)) { |
||||
leftoverNonDeletableTxCount++ |
||||
} |
||||
}) |
||||
assert.equal(leftoverNonDeletableTxCount, nonDeletableCount, 'migration shouldnt delete transactions we want to keep') |
||||
assert((migratedTransactions.length >= 40), `should be equal or greater to 40 if they are non deletable states got ${migratedTransactions.length} transactions`) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
|
||||
it('should not remove any transactions because 40 is the expectable limit', (done) => { |
||||
storage.meta.version = 22 |
||||
storage.data.TransactionController.transactions = transactions40 |
||||
migration23.migrate(storage) |
||||
.then((migratedData) => { |
||||
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||
|
||||
assert.equal(migratedTransactions.length, 40, 'migration shouldnt delete when at limit') |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
|
||||
it('should not remove any transactions because 20 txs is under the expectable limit', (done) => { |
||||
storage.meta.version = 22 |
||||
storage.data.TransactionController.transactions = transactions20 |
||||
migration23.migrate(storage) |
||||
.then((migratedData) => { |
||||
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||
assert.equal(migratedTransactions.length, 20, 'migration shouldnt delete when under limit') |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
|
||||
}) |
@ -0,0 +1,49 @@ |
||||
const assert = require('assert') |
||||
const migration24 = require('../../../app/scripts/migrations/024') |
||||
const firstTimeState = { |
||||
meta: {}, |
||||
data: require('../../../app/scripts/first-time-state'), |
||||
} |
||||
const properTime = (new Date()).getTime() |
||||
const storage = { |
||||
"meta": {}, |
||||
"data": { |
||||
"TransactionController": { |
||||
"transactions": [ |
||||
] |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
const transactions = [] |
||||
|
||||
|
||||
while (transactions.length <= 10) { |
||||
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'unapproved' }) |
||||
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'confirmed' }) |
||||
} |
||||
|
||||
|
||||
storage.data.TransactionController.transactions = transactions |
||||
|
||||
describe('storage is migrated successfully and the txParams.from are lowercase', () => { |
||||
it('should lowercase the from for unapproved txs', (done) => { |
||||
migration24.migrate(storage) |
||||
.then((migratedData) => { |
||||
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||
migratedTransactions.forEach((tx) => { |
||||
if (tx.status === 'unapproved') assert.equal(tx.txParams.from, '0x8acce2391c0d510a6c5e5d8f819a678f79b7e675') |
||||
else assert.equal(tx.txParams.from, '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675') |
||||
}) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
|
||||
it('should migrate first time state', (done) => { |
||||
migration24.migrate(firstTimeState) |
||||
.then((migratedData) => { |
||||
assert.equal(migratedData.meta.version, 24) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
}) |
@ -0,0 +1,49 @@ |
||||
const assert = require('assert') |
||||
const migration25 = require('../../../app/scripts/migrations/025') |
||||
const firstTimeState = { |
||||
meta: {}, |
||||
data: require('../../../app/scripts/first-time-state'), |
||||
} |
||||
|
||||
const storage = { |
||||
"meta": {}, |
||||
"data": { |
||||
"TransactionController": { |
||||
"transactions": [ |
||||
] |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
const transactions = [] |
||||
|
||||
|
||||
while (transactions.length <= 10) { |
||||
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675', random: 'stuff', chainId: 2 }, status: 'unapproved' }) |
||||
transactions.push({ txParams: { from: '0x8aCce2391c0d510a6c5E5d8f819a678f79b7e675' }, status: 'confirmed' }) |
||||
} |
||||
|
||||
|
||||
storage.data.TransactionController.transactions = transactions |
||||
|
||||
describe('storage is migrated successfully and the txParams.from are lowercase', () => { |
||||
it('should lowercase the from for unapproved txs', (done) => { |
||||
migration25.migrate(storage) |
||||
.then((migratedData) => { |
||||
const migratedTransactions = migratedData.data.TransactionController.transactions |
||||
migratedTransactions.forEach((tx) => { |
||||
if (tx.status === 'unapproved') assert(!tx.txParams.random) |
||||
if (tx.status === 'unapproved') assert(!tx.txParams.chainId) |
||||
}) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
|
||||
it('should migrate first time state', (done) => { |
||||
migration25.migrate(firstTimeState) |
||||
.then((migratedData) => { |
||||
assert.equal(migratedData.meta.version, 25) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
}) |
@ -0,0 +1,17 @@ |
||||
const assert = require('assert') |
||||
const migrationTemplate = require('../../../app/scripts/migrations/template') |
||||
const properTime = (new Date()).getTime() |
||||
const storage = { |
||||
meta: {}, |
||||
data: {}, |
||||
} |
||||
|
||||
describe('storage is migrated successfully', () => { |
||||
it('should work', (done) => { |
||||
migrationTemplate.migrate(storage) |
||||
.then((migratedData) => { |
||||
assert.equal(migratedData.meta.version, 0) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
}) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue