After Width: | Height: | Size: 795 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,12 @@ |
|||||||
|
const TRANSACTION_TYPE_CANCEL = 'cancel' |
||||||
|
const TRANSACTION_TYPE_RETRY = 'retry' |
||||||
|
const TRANSACTION_TYPE_STANDARD = 'standard' |
||||||
|
|
||||||
|
const TRANSACTION_STATUS_APPROVED = 'approved' |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
TRANSACTION_TYPE_CANCEL, |
||||||
|
TRANSACTION_TYPE_RETRY, |
||||||
|
TRANSACTION_TYPE_STANDARD, |
||||||
|
TRANSACTION_STATUS_APPROVED, |
||||||
|
} |
@ -1,254 +0,0 @@ |
|||||||
const ethUtil = require('ethereumjs-util') |
|
||||||
const normalize = require('eth-sig-util').normalize |
|
||||||
const { |
|
||||||
MAINNET_RPC_URL, |
|
||||||
ROPSTEN_RPC_URL, |
|
||||||
KOVAN_RPC_URL, |
|
||||||
RINKEBY_RPC_URL, |
|
||||||
} = require('../controllers/network/enums') |
|
||||||
|
|
||||||
/* The config-manager is a convenience object |
|
||||||
* wrapping a pojo-migrator. |
|
||||||
* |
|
||||||
* It exists mostly to allow the creation of |
|
||||||
* convenience methods to access and persist |
|
||||||
* particular portions of the state. |
|
||||||
*/ |
|
||||||
module.exports = ConfigManager |
|
||||||
function ConfigManager (opts) { |
|
||||||
// ConfigManager is observable and will emit updates
|
|
||||||
this._subs = [] |
|
||||||
this.store = opts.store |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setConfig = function (config) { |
|
||||||
var data = this.getData() |
|
||||||
data.config = config |
|
||||||
this.setData(data) |
|
||||||
this._emitUpdates(config) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getConfig = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.config |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setData = function (data) { |
|
||||||
this.store.putState(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getData = function () { |
|
||||||
return this.store.getState() |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setPasswordForgotten = function (passwordForgottenState) { |
|
||||||
const data = this.getData() |
|
||||||
data.forgottenPassword = passwordForgottenState |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getPasswordForgotten = function (passwordForgottenState) { |
|
||||||
const data = this.getData() |
|
||||||
return data.forgottenPassword |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setWallet = function (wallet) { |
|
||||||
var data = this.getData() |
|
||||||
data.wallet = wallet |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setVault = function (encryptedString) { |
|
||||||
var data = this.getData() |
|
||||||
data.vault = encryptedString |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getVault = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.vault |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getKeychains = function () { |
|
||||||
return this.getData().keychains || [] |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setKeychains = function (keychains) { |
|
||||||
var data = this.getData() |
|
||||||
data.keychains = keychains |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getSelectedAccount = function () { |
|
||||||
var config = this.getConfig() |
|
||||||
return config.selectedAccount |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setSelectedAccount = function (address) { |
|
||||||
var config = this.getConfig() |
|
||||||
config.selectedAccount = ethUtil.addHexPrefix(address) |
|
||||||
this.setConfig(config) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getWallet = function () { |
|
||||||
return this.getData().wallet |
|
||||||
} |
|
||||||
|
|
||||||
// Takes a boolean
|
|
||||||
ConfigManager.prototype.setShowSeedWords = function (should) { |
|
||||||
var data = this.getData() |
|
||||||
data.showSeedWords = should |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
ConfigManager.prototype.getShouldShowSeedWords = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.showSeedWords |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setSeedWords = function (words) { |
|
||||||
var data = this.getData() |
|
||||||
data.seedWords = words |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getSeedWords = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.seedWords |
|
||||||
} |
|
||||||
ConfigManager.prototype.setRpcTarget = function (rpcUrl) { |
|
||||||
var config = this.getConfig() |
|
||||||
config.provider = { |
|
||||||
type: 'rpc', |
|
||||||
rpcTarget: rpcUrl, |
|
||||||
} |
|
||||||
this.setConfig(config) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setProviderType = function (type) { |
|
||||||
var config = this.getConfig() |
|
||||||
config.provider = { |
|
||||||
type: type, |
|
||||||
} |
|
||||||
this.setConfig(config) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.useEtherscanProvider = function () { |
|
||||||
var config = this.getConfig() |
|
||||||
config.provider = { |
|
||||||
type: 'etherscan', |
|
||||||
} |
|
||||||
this.setConfig(config) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getProvider = function () { |
|
||||||
var config = this.getConfig() |
|
||||||
return config.provider |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getCurrentRpcAddress = function () { |
|
||||||
var provider = this.getProvider() |
|
||||||
if (!provider) return null |
|
||||||
switch (provider.type) { |
|
||||||
|
|
||||||
case 'mainnet': |
|
||||||
return MAINNET_RPC_URL |
|
||||||
|
|
||||||
case 'ropsten': |
|
||||||
return ROPSTEN_RPC_URL |
|
||||||
|
|
||||||
case 'kovan': |
|
||||||
return KOVAN_RPC_URL |
|
||||||
|
|
||||||
case 'rinkeby': |
|
||||||
return RINKEBY_RPC_URL |
|
||||||
|
|
||||||
default: |
|
||||||
return provider && provider.rpcTarget ? provider.rpcTarget : RINKEBY_RPC_URL |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//
|
|
||||||
// Tx
|
|
||||||
//
|
|
||||||
|
|
||||||
ConfigManager.prototype.getTxList = function () { |
|
||||||
var data = this.getData() |
|
||||||
if (data.transactions !== undefined) { |
|
||||||
return data.transactions |
|
||||||
} else { |
|
||||||
return [] |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setTxList = function (txList) { |
|
||||||
var data = this.getData() |
|
||||||
data.transactions = txList |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
// wallet nickname methods
|
|
||||||
|
|
||||||
ConfigManager.prototype.getWalletNicknames = function () { |
|
||||||
var data = this.getData() |
|
||||||
const nicknames = ('walletNicknames' in data) ? data.walletNicknames : {} |
|
||||||
return nicknames |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.nicknameForWallet = function (account) { |
|
||||||
const address = normalize(account) |
|
||||||
const nicknames = this.getWalletNicknames() |
|
||||||
return nicknames[address] |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { |
|
||||||
const address = normalize(account) |
|
||||||
const nicknames = this.getWalletNicknames() |
|
||||||
nicknames[address] = nickname |
|
||||||
var data = this.getData() |
|
||||||
data.walletNicknames = nicknames |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
// observable
|
|
||||||
|
|
||||||
ConfigManager.prototype.getSalt = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.salt |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setSalt = function (salt) { |
|
||||||
var data = this.getData() |
|
||||||
data.salt = salt |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.subscribe = function (fn) { |
|
||||||
this._subs.push(fn) |
|
||||||
var unsubscribe = this.unsubscribe.bind(this, fn) |
|
||||||
return unsubscribe |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.unsubscribe = function (fn) { |
|
||||||
var index = this._subs.indexOf(fn) |
|
||||||
if (index !== -1) this._subs.splice(index, 1) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype._emitUpdates = function (state) { |
|
||||||
this._subs.forEach(function (handler) { |
|
||||||
handler(state) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.setLostAccounts = function (lostAccounts) { |
|
||||||
var data = this.getData() |
|
||||||
data.lostAccounts = lostAccounts |
|
||||||
this.setData(data) |
|
||||||
} |
|
||||||
|
|
||||||
ConfigManager.prototype.getLostAccounts = function () { |
|
||||||
var data = this.getData() |
|
||||||
return data.lostAccounts || [] |
|
||||||
} |
|
@ -1,50 +0,0 @@ |
|||||||
const version = 5 |
|
||||||
|
|
||||||
/* |
|
||||||
|
|
||||||
This is an incomplete migration bc it requires post-decrypted data |
|
||||||
which we dont have access to at the time of this writing. |
|
||||||
|
|
||||||
*/ |
|
||||||
|
|
||||||
const ObservableStore = require('obs-store') |
|
||||||
const ConfigManager = require('../../app/scripts/lib/config-manager') |
|
||||||
const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator') |
|
||||||
const KeyringController = require('eth-keyring-controller') |
|
||||||
|
|
||||||
const password = 'obviously not correct' |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
version, |
|
||||||
|
|
||||||
migrate: function (versionedData) { |
|
||||||
versionedData.meta.version = version |
|
||||||
|
|
||||||
const store = new ObservableStore(versionedData.data) |
|
||||||
const configManager = new ConfigManager({ store }) |
|
||||||
const idStoreMigrator = new IdentityStoreMigrator({ configManager }) |
|
||||||
const keyringController = new KeyringController({ |
|
||||||
configManager: configManager, |
|
||||||
}) |
|
||||||
|
|
||||||
// attempt to migrate to multiVault
|
|
||||||
return idStoreMigrator.migratedVaultForPassword(password) |
|
||||||
.then((result) => { |
|
||||||
// skip if nothing to migrate
|
|
||||||
if (!result) return Promise.resolve(versionedData) |
|
||||||
delete versionedData.data.wallet |
|
||||||
// create new keyrings
|
|
||||||
const privKeys = result.lostAccounts.map(acct => acct.privateKey) |
|
||||||
return Promise.all([ |
|
||||||
keyringController.restoreKeyring(result.serialized), |
|
||||||
keyringController.restoreKeyring({ type: 'Simple Key Pair', data: privKeys }), |
|
||||||
]).then(() => { |
|
||||||
return keyringController.persistAllKeyrings(password) |
|
||||||
}).then(() => { |
|
||||||
// copy result on to state object
|
|
||||||
versionedData.data = store.get() |
|
||||||
return Promise.resolve(versionedData) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}, |
|
||||||
} |
|
@ -0,0 +1,59 @@ |
|||||||
|
window.onload = function () { |
||||||
|
if (window.location.pathname === '/phishing.html') { |
||||||
|
const {hostname} = parseHash() |
||||||
|
document.getElementById('esdbLink').innerHTML = '<b>To read more about this scam, navigate to: <a href="https://etherscamdb.info/domain/' + hostname + '"> https://etherscamdb.info/domain/' + hostname + '</a></b>' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const querystring = require('querystring') |
||||||
|
const dnode = require('dnode') |
||||||
|
const { EventEmitter } = require('events') |
||||||
|
const PortStream = require('extension-port-stream') |
||||||
|
const extension = require('extensionizer') |
||||||
|
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex |
||||||
|
const { getEnvironmentType } = require('./lib/util') |
||||||
|
const ExtensionPlatform = require('./platforms/extension') |
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', start) |
||||||
|
|
||||||
|
function start () { |
||||||
|
const windowType = getEnvironmentType(window.location.href) |
||||||
|
|
||||||
|
global.platform = new ExtensionPlatform() |
||||||
|
global.METAMASK_UI_TYPE = windowType |
||||||
|
|
||||||
|
const extensionPort = extension.runtime.connect({ name: windowType }) |
||||||
|
const connectionStream = new PortStream(extensionPort) |
||||||
|
const mx = setupMultiplex(connectionStream) |
||||||
|
setupControllerConnection(mx.createStream('controller'), (err, metaMaskController) => { |
||||||
|
if (err) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const suspect = parseHash() |
||||||
|
const unsafeContinue = () => { |
||||||
|
window.location.href = suspect.href |
||||||
|
} |
||||||
|
const continueLink = document.getElementById('unsafe-continue') |
||||||
|
continueLink.addEventListener('click', () => { |
||||||
|
metaMaskController.whitelistPhishingDomain(suspect.hostname) |
||||||
|
unsafeContinue() |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function setupControllerConnection (connectionStream, cb) { |
||||||
|
const eventEmitter = new EventEmitter() |
||||||
|
const accountManagerDnode = dnode({ |
||||||
|
sendUpdate (state) { |
||||||
|
eventEmitter.emit('update', state) |
||||||
|
}, |
||||||
|
}) |
||||||
|
connectionStream.pipe(accountManagerDnode).pipe(connectionStream) |
||||||
|
accountManagerDnode.once('remote', (accountManager) => cb(null, accountManager)) |
||||||
|
} |
||||||
|
|
||||||
|
function parseHash () { |
||||||
|
const hash = window.location.hash.substring(1) |
||||||
|
return querystring.parse(hash) |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
{ "isInitialized": true, |
||||||
|
"provider": { "type": "rpc", "rpcTarget": "http://localhost:8545" }, |
||||||
|
"network": "loading", |
||||||
|
"accounts": { |
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { |
||||||
|
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", |
||||||
|
"balance": "0x0" |
||||||
|
}, |
||||||
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": { |
||||||
|
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b", |
||||||
|
"balance": "0x0" |
||||||
|
} |
||||||
|
}, |
||||||
|
"currentBlockGasLimit": "", |
||||||
|
"unapprovedTxs": {}, |
||||||
|
"selectedAddressTxList": [], |
||||||
|
"computedBalances": {}, |
||||||
|
"unapprovedMsgs": {}, |
||||||
|
"unapprovedMsgCount": 0, |
||||||
|
"unapprovedPersonalMsgs": {}, |
||||||
|
"unapprovedPersonalMsgCount": 0, |
||||||
|
"unapprovedTypedMessages": {}, |
||||||
|
"unapprovedTypedMessagesCount": 0, |
||||||
|
"isUnlocked": true, |
||||||
|
"keyringTypes": [ "Simple Key Pair", "HD Key Tree" ], |
||||||
|
"keyrings":[ |
||||||
|
{ "type": "HD Key Tree", |
||||||
|
"accounts": [ |
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"type": "Simple Key Pair", |
||||||
|
"accounts": [ |
||||||
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b" |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"frequentRpcList": [], |
||||||
|
"currentAccountTab": "history", |
||||||
|
"tokens": [], |
||||||
|
"useBlockie": false, |
||||||
|
"featureFlags": {}, |
||||||
|
"currentLocale": null, |
||||||
|
"identities": { |
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { |
||||||
|
"name": "Account 1", |
||||||
|
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" |
||||||
|
}, |
||||||
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": { |
||||||
|
"name": "Account 2", |
||||||
|
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b" |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
"lostIdentities": {}, |
||||||
|
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc", |
||||||
|
"recentBlocks": [], |
||||||
|
"addressBook": [], |
||||||
|
"currentCurrency": "usd", |
||||||
|
"conversionRate": 288.45, |
||||||
|
"conversionDate": 1506444677, |
||||||
|
"nextUnreadNotice": null, |
||||||
|
"noActiveNotices": true, |
||||||
|
"shapeShiftTxList": [], |
||||||
|
"infuraNetworkStatus": {}, |
||||||
|
"lostAccounts": [], |
||||||
|
"seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium", |
||||||
|
"forgottenPassword": null |
||||||
|
} |
@ -0,0 +1,286 @@ |
|||||||
|
const path = require('path') |
||||||
|
const assert = require('assert') |
||||||
|
const webdriver = require('selenium-webdriver') |
||||||
|
const { By, until } = webdriver |
||||||
|
const { |
||||||
|
delay, |
||||||
|
buildChromeWebDriver, |
||||||
|
buildFirefoxWebdriver, |
||||||
|
installWebExt, |
||||||
|
getExtensionIdChrome, |
||||||
|
getExtensionIdFirefox, |
||||||
|
} = require('../func') |
||||||
|
const { |
||||||
|
checkBrowserForConsoleErrors, |
||||||
|
closeAllWindowHandlesExcept, |
||||||
|
findElement, |
||||||
|
findElements, |
||||||
|
loadExtension, |
||||||
|
openNewPage, |
||||||
|
verboseReportOnFailure, |
||||||
|
waitUntilXWindowHandles, |
||||||
|
} = require('./helpers') |
||||||
|
|
||||||
|
describe('MetaMask', function () { |
||||||
|
let extensionId |
||||||
|
let driver |
||||||
|
|
||||||
|
const tinyDelayMs = 200 |
||||||
|
const regularDelayMs = tinyDelayMs * 2 |
||||||
|
const largeDelayMs = regularDelayMs * 2 |
||||||
|
|
||||||
|
this.timeout(0) |
||||||
|
this.bail(true) |
||||||
|
|
||||||
|
before(async function () { |
||||||
|
switch (process.env.SELENIUM_BROWSER) { |
||||||
|
case 'chrome': { |
||||||
|
const extPath = path.resolve('dist/chrome') |
||||||
|
driver = buildChromeWebDriver(extPath) |
||||||
|
extensionId = await getExtensionIdChrome(driver) |
||||||
|
await driver.get(`chrome-extension://${extensionId}/popup.html`) |
||||||
|
break |
||||||
|
} |
||||||
|
case 'firefox': { |
||||||
|
const extPath = path.resolve('dist/firefox') |
||||||
|
driver = buildFirefoxWebdriver() |
||||||
|
await installWebExt(driver, extPath) |
||||||
|
await delay(700) |
||||||
|
extensionId = await getExtensionIdFirefox(driver) |
||||||
|
await driver.get(`moz-extension://${extensionId}/popup.html`) |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
afterEach(async function () { |
||||||
|
if (process.env.SELENIUM_BROWSER === 'chrome') { |
||||||
|
const errors = await checkBrowserForConsoleErrors(driver) |
||||||
|
if (errors.length) { |
||||||
|
const errorReports = errors.map(err => err.message) |
||||||
|
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}` |
||||||
|
console.error(new Error(errorMessage)) |
||||||
|
} |
||||||
|
} |
||||||
|
if (this.currentTest.state === 'failed') { |
||||||
|
await verboseReportOnFailure(driver, this.currentTest) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
after(async function () { |
||||||
|
await driver.quit() |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
describe('New UI setup', async function () { |
||||||
|
it('switches to first tab', async function () { |
||||||
|
await delay(tinyDelayMs) |
||||||
|
const [firstTab] = await driver.getAllWindowHandles() |
||||||
|
await driver.switchTo().window(firstTab) |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('selects the new UI option', async () => { |
||||||
|
try { |
||||||
|
const overlay = await findElement(driver, By.css('.full-flex-height')) |
||||||
|
await driver.wait(until.stalenessOf(overlay)) |
||||||
|
} catch (e) {} |
||||||
|
|
||||||
|
let button |
||||||
|
try { |
||||||
|
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) |
||||||
|
} catch (e) { |
||||||
|
await loadExtension(driver, extensionId) |
||||||
|
await delay(largeDelayMs) |
||||||
|
button = await findElement(driver, By.xpath("//button[contains(text(), 'Try it now')]")) |
||||||
|
} |
||||||
|
await button.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
// Close all other tabs
|
||||||
|
const [tab0, tab1, tab2] = await driver.getAllWindowHandles() |
||||||
|
await driver.switchTo().window(tab0) |
||||||
|
await delay(tinyDelayMs) |
||||||
|
|
||||||
|
let selectedUrl = await driver.getCurrentUrl() |
||||||
|
await delay(tinyDelayMs) |
||||||
|
if (tab0 && selectedUrl.match(/popup.html/)) { |
||||||
|
await closeAllWindowHandlesExcept(driver, tab0) |
||||||
|
} else if (tab1) { |
||||||
|
await driver.switchTo().window(tab1) |
||||||
|
selectedUrl = await driver.getCurrentUrl() |
||||||
|
await delay(tinyDelayMs) |
||||||
|
if (selectedUrl.match(/popup.html/)) { |
||||||
|
await closeAllWindowHandlesExcept(driver, tab1) |
||||||
|
} else if (tab2) { |
||||||
|
await driver.switchTo().window(tab2) |
||||||
|
selectedUrl = await driver.getCurrentUrl() |
||||||
|
selectedUrl.match(/popup.html/) && await closeAllWindowHandlesExcept(driver, tab2) |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new Error('popup.html not found') |
||||||
|
} |
||||||
|
await delay(regularDelayMs) |
||||||
|
const [appTab] = await driver.getAllWindowHandles() |
||||||
|
await driver.switchTo().window(appTab) |
||||||
|
await delay(tinyDelayMs) |
||||||
|
|
||||||
|
await loadExtension(driver, extensionId) |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const continueBtn = await findElement(driver, By.css('.welcome-screen__button')) |
||||||
|
await continueBtn.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
describe('Going through the first time flow', () => { |
||||||
|
it('accepts a secure password', async () => { |
||||||
|
const passwordBox = await findElement(driver, By.css('.create-password #create-password')) |
||||||
|
const passwordBoxConfirm = await findElement(driver, By.css('.create-password #confirm-password')) |
||||||
|
const button = await findElement(driver, By.css('.create-password button')) |
||||||
|
|
||||||
|
await passwordBox.sendKeys('correct horse battery staple') |
||||||
|
await passwordBoxConfirm.sendKeys('correct horse battery staple') |
||||||
|
await button.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the unique image screen', async () => { |
||||||
|
const nextScreen = await findElement(driver, By.css('.unique-image button')) |
||||||
|
await nextScreen.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the ToS', async () => { |
||||||
|
// terms of use
|
||||||
|
const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled() |
||||||
|
assert.equal(canClickThrough, false, 'disabled continue button') |
||||||
|
const bottomOfTos = await findElement(driver, By.linkText('Attributions')) |
||||||
|
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos) |
||||||
|
await delay(regularDelayMs) |
||||||
|
const acceptTos = await findElement(driver, By.css('.tou button')) |
||||||
|
driver.wait(until.elementIsEnabled(acceptTos)) |
||||||
|
await acceptTos.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the privacy notice', async () => { |
||||||
|
// privacy notice
|
||||||
|
const nextScreen = await findElement(driver, By.css('.tou button')) |
||||||
|
await nextScreen.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the phishing notice', async () => { |
||||||
|
// phishing notice
|
||||||
|
const noticeElement = await driver.findElement(By.css('.markdown')) |
||||||
|
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement) |
||||||
|
await delay(regularDelayMs) |
||||||
|
const nextScreen = await findElement(driver, By.css('.tou button')) |
||||||
|
await nextScreen.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
let seedPhrase |
||||||
|
|
||||||
|
it('reveals the seed phrase', async () => { |
||||||
|
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') |
||||||
|
await driver.wait(until.elementLocated(byRevealButton, 10000)) |
||||||
|
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) |
||||||
|
await revealSeedPhraseButton.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
seedPhrase = await driver.findElement(By.css('.backup-phrase__secret-words')).getText() |
||||||
|
assert.equal(seedPhrase.split(' ').length, 12) |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const nextScreen = await findElement(driver, By.css('.backup-phrase button')) |
||||||
|
await nextScreen.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
async function clickWordAndWait (word) { |
||||||
|
const xpathClass = 'backup-phrase__confirm-seed-option backup-phrase__confirm-seed-option--unselected' |
||||||
|
const xpath = `//button[@class='${xpathClass}' and contains(text(), '${word}')]` |
||||||
|
const word0 = await findElement(driver, By.xpath(xpath), 10000) |
||||||
|
|
||||||
|
await word0.click() |
||||||
|
await delay(tinyDelayMs) |
||||||
|
} |
||||||
|
|
||||||
|
async function retypeSeedPhrase (words, wasReloaded, count = 0) { |
||||||
|
try { |
||||||
|
if (wasReloaded) { |
||||||
|
const byRevealButton = By.css('.backup-phrase__secret-blocker .backup-phrase__reveal-button') |
||||||
|
await driver.wait(until.elementLocated(byRevealButton, 10000)) |
||||||
|
const revealSeedPhraseButton = await findElement(driver, byRevealButton, 10000) |
||||||
|
await revealSeedPhraseButton.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const nextScreen = await findElement(driver, By.css('.backup-phrase button')) |
||||||
|
await nextScreen.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
} |
||||||
|
|
||||||
|
for (let i = 0; i < 12; i++) { |
||||||
|
await clickWordAndWait(words[i]) |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
if (count > 2) { |
||||||
|
throw e |
||||||
|
} else { |
||||||
|
await loadExtension(driver, extensionId) |
||||||
|
await retypeSeedPhrase(words, true, count + 1) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
it('can retype the seed phrase', async () => { |
||||||
|
const words = seedPhrase.split(' ') |
||||||
|
|
||||||
|
await retypeSeedPhrase(words) |
||||||
|
|
||||||
|
const confirm = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`)) |
||||||
|
await confirm.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('clicks through the deposit modal', async () => { |
||||||
|
const byBuyModal = By.css('span .modal') |
||||||
|
const buyModal = await driver.wait(until.elementLocated(byBuyModal)) |
||||||
|
const closeModal = await findElement(driver, By.css('.page-container__header-close')) |
||||||
|
await closeModal.click() |
||||||
|
await driver.wait(until.stalenessOf(buyModal)) |
||||||
|
await delay(regularDelayMs) |
||||||
|
}) |
||||||
|
|
||||||
|
it('switches to localhost', async () => { |
||||||
|
const networkDropdown = await findElement(driver, By.css('.network-name')) |
||||||
|
await networkDropdown.click() |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
const [localhost] = await findElements(driver, By.xpath(`//span[contains(text(), 'Localhost')]`)) |
||||||
|
await localhost.click() |
||||||
|
await delay(largeDelayMs * 2) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
describe('Drizzle', () => { |
||||||
|
it('should be able to detect our eth address', async () => { |
||||||
|
await openNewPage(driver, 'http://127.0.0.1:3000/') |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
await waitUntilXWindowHandles(driver, 2) |
||||||
|
const windowHandles = await driver.getAllWindowHandles() |
||||||
|
const dapp = windowHandles[1] |
||||||
|
|
||||||
|
await driver.switchTo().window(dapp) |
||||||
|
await delay(regularDelayMs) |
||||||
|
|
||||||
|
|
||||||
|
const addressElement = await findElement(driver, By.css(`.pure-u-1-1 h4`)) |
||||||
|
const addressText = await addressElement.getText() |
||||||
|
assert(addressText.match(/^0x[a-fA-F0-9]{40}$/)) |
||||||
|
}) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,20 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
set -e |
||||||
|
set -u |
||||||
|
set -o pipefail |
||||||
|
|
||||||
|
export PATH="$PATH:./node_modules/.bin" |
||||||
|
|
||||||
|
npm run ganache:start -- -b 2 >> /dev/null 2>&1 & |
||||||
|
sleep 5 |
||||||
|
cd test/e2e/beta/ |
||||||
|
rm -rf drizzle-test |
||||||
|
mkdir drizzle-test && cd drizzle-test |
||||||
|
npm install truffle |
||||||
|
truffle unbox drizzle |
||||||
|
echo "Deploying contracts for Drizzle test..." |
||||||
|
truffle compile && truffle migrate |
||||||
|
BROWSER=none npm start >> /dev/null 2>&1 & |
||||||
|
cd ../../../../ |
||||||
|
mocha test/e2e/beta/drizzle.spec |
@ -1,9 +0,0 @@ |
|||||||
const ObservableStore = require('obs-store') |
|
||||||
const clone = require('clone') |
|
||||||
const ConfigManager = require('../../app/scripts/lib/config-manager') |
|
||||||
const firstTimeState = require('../../app/scripts/first-time-state') |
|
||||||
|
|
||||||
module.exports = function () { |
|
||||||
const store = new ObservableStore(clone(firstTimeState)) |
|
||||||
return new ConfigManager({ store }) |
|
||||||
} |
|
@ -0,0 +1,42 @@ |
|||||||
|
const { shallow, mount } = require('enzyme') |
||||||
|
import { BrowserRouter } from 'react-router-dom' |
||||||
|
import { shape } from 'prop-types' |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
shallowWithStore, |
||||||
|
mountWithStore, |
||||||
|
mountWithRouter, |
||||||
|
} |
||||||
|
|
||||||
|
function shallowWithStore (component, store) { |
||||||
|
const context = { |
||||||
|
store, |
||||||
|
} |
||||||
|
return shallow(component, {context}) |
||||||
|
} |
||||||
|
|
||||||
|
function mountWithStore (component, store) { |
||||||
|
const context = { |
||||||
|
store, |
||||||
|
} |
||||||
|
return mount(component, {context}) |
||||||
|
} |
||||||
|
|
||||||
|
function mountWithRouter (node) { |
||||||
|
|
||||||
|
// Instantiate router context
|
||||||
|
const router = { |
||||||
|
history: new BrowserRouter().history, |
||||||
|
route: { |
||||||
|
location: {}, |
||||||
|
match: {}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const createContext = () => ({ |
||||||
|
context: { router, t: () => {} }, |
||||||
|
childContextTypes: { router: shape({}), t: () => {} }, |
||||||
|
}) |
||||||
|
|
||||||
|
return mount(node, createContext()) |
||||||
|
} |
@ -1,20 +0,0 @@ |
|||||||
const { shallow, mount } = require('enzyme') |
|
||||||
|
|
||||||
module.exports = { |
|
||||||
shallowWithStore, |
|
||||||
mountWithStore, |
|
||||||
} |
|
||||||
|
|
||||||
function shallowWithStore (component, store) { |
|
||||||
const context = { |
|
||||||
store, |
|
||||||
} |
|
||||||
return shallow(component, {context}) |
|
||||||
} |
|
||||||
|
|
||||||
function mountWithStore (component, store) { |
|
||||||
const context = { |
|
||||||
store, |
|
||||||
} |
|
||||||
return mount(component, {context}) |
|
||||||
} |
|
@ -0,0 +1,33 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const cleanErrorStack = require('../../../app/scripts/lib/cleanErrorStack') |
||||||
|
|
||||||
|
describe('Clean Error Stack', () => { |
||||||
|
|
||||||
|
const testMessage = 'Test Message' |
||||||
|
const testError = new Error(testMessage) |
||||||
|
const undefinedErrorName = new Error(testMessage) |
||||||
|
const blankErrorName = new Error(testMessage) |
||||||
|
const blankMsgError = new Error() |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
undefinedErrorName.name = undefined |
||||||
|
blankErrorName.name = '' |
||||||
|
}) |
||||||
|
|
||||||
|
it('tests error with message', () => { |
||||||
|
assert.equal(cleanErrorStack(testError), 'Error: Test Message') |
||||||
|
}) |
||||||
|
|
||||||
|
it('tests error with undefined name', () => { |
||||||
|
assert.equal(cleanErrorStack(undefinedErrorName).toString(), 'Error: Test Message') |
||||||
|
}) |
||||||
|
|
||||||
|
it('tests error with blank name', () => { |
||||||
|
assert.equal(cleanErrorStack(blankErrorName).toString(), 'Test Message') |
||||||
|
}) |
||||||
|
|
||||||
|
it('tests error with blank message', () => { |
||||||
|
assert.equal(cleanErrorStack(blankMsgError), 'Error') |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
@ -1,112 +0,0 @@ |
|||||||
const assert = require('assert') |
|
||||||
const configManagerGen = require('../lib/mock-config-manager') |
|
||||||
|
|
||||||
describe('config-manager', function () { |
|
||||||
var configManager |
|
||||||
|
|
||||||
beforeEach(function () { |
|
||||||
configManager = configManagerGen() |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#setConfig', function () { |
|
||||||
it('should set the config key', function () { |
|
||||||
var testConfig = { |
|
||||||
provider: { |
|
||||||
type: 'rpc', |
|
||||||
rpcTarget: 'foobar', |
|
||||||
}, |
|
||||||
} |
|
||||||
configManager.setConfig(testConfig) |
|
||||||
var result = configManager.getData() |
|
||||||
|
|
||||||
assert.equal(result.config.provider.type, testConfig.provider.type) |
|
||||||
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget) |
|
||||||
}) |
|
||||||
|
|
||||||
it('setting wallet should not overwrite config', function () { |
|
||||||
var testConfig = { |
|
||||||
provider: { |
|
||||||
type: 'rpc', |
|
||||||
rpcTarget: 'foobar', |
|
||||||
}, |
|
||||||
} |
|
||||||
configManager.setConfig(testConfig) |
|
||||||
|
|
||||||
var testWallet = { |
|
||||||
name: 'this is my fake wallet', |
|
||||||
} |
|
||||||
configManager.setWallet(testWallet) |
|
||||||
|
|
||||||
var result = configManager.getData() |
|
||||||
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set') |
|
||||||
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget) |
|
||||||
|
|
||||||
testConfig.provider.type = 'something else!' |
|
||||||
configManager.setConfig(testConfig) |
|
||||||
|
|
||||||
result = configManager.getData() |
|
||||||
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set') |
|
||||||
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget) |
|
||||||
assert.equal(result.config.provider.type, testConfig.provider.type) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('wallet nicknames', function () { |
|
||||||
it('should return null when no nicknames are saved', function () { |
|
||||||
var nick = configManager.nicknameForWallet('0x0') |
|
||||||
assert.equal(nick, null, 'no nickname returned') |
|
||||||
}) |
|
||||||
|
|
||||||
it('should persist nicknames', function () { |
|
||||||
var account = '0x0' |
|
||||||
var nick1 = 'foo' |
|
||||||
var nick2 = 'bar' |
|
||||||
configManager.setNicknameForWallet(account, nick1) |
|
||||||
|
|
||||||
var result1 = configManager.nicknameForWallet(account) |
|
||||||
assert.equal(result1, nick1) |
|
||||||
|
|
||||||
configManager.setNicknameForWallet(account, nick2) |
|
||||||
var result2 = configManager.nicknameForWallet(account) |
|
||||||
assert.equal(result2, nick2) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('rpc manipulations', function () { |
|
||||||
it('changing rpc should return a different rpc', function () { |
|
||||||
var firstRpc = 'first' |
|
||||||
var secondRpc = 'second' |
|
||||||
|
|
||||||
configManager.setRpcTarget(firstRpc) |
|
||||||
var firstResult = configManager.getCurrentRpcAddress() |
|
||||||
assert.equal(firstResult, firstRpc) |
|
||||||
|
|
||||||
configManager.setRpcTarget(secondRpc) |
|
||||||
var secondResult = configManager.getCurrentRpcAddress() |
|
||||||
assert.equal(secondResult, secondRpc) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('transactions', function () { |
|
||||||
beforeEach(function () { |
|
||||||
configManager.setTxList([]) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#getTxList', function () { |
|
||||||
it('when new should return empty array', function () { |
|
||||||
var result = configManager.getTxList() |
|
||||||
assert.ok(Array.isArray(result)) |
|
||||||
assert.equal(result.length, 0) |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
describe('#setTxList', function () { |
|
||||||
it('saves the submitted data to the tx list', function () { |
|
||||||
var target = [{ foo: 'bar' }] |
|
||||||
configManager.setTxList(target) |
|
||||||
var result = configManager.getTxList() |
|
||||||
assert.equal(result[0].foo, 'bar') |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
||||||
}) |
|
@ -0,0 +1,36 @@ |
|||||||
|
import React from 'react' |
||||||
|
import assert from 'assert' |
||||||
|
import thunk from 'redux-thunk' |
||||||
|
import configureMockStore from 'redux-mock-store' |
||||||
|
import { mount } from 'enzyme' |
||||||
|
|
||||||
|
import IdenticonComponent from '../../../../../ui/app/components/identicon' |
||||||
|
|
||||||
|
describe('Identicon Component', () => { |
||||||
|
|
||||||
|
const state = { |
||||||
|
metamask: { |
||||||
|
useBlockie: false, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const middlewares = [thunk] |
||||||
|
const mockStore = configureMockStore(middlewares) |
||||||
|
const store = mockStore(state) |
||||||
|
|
||||||
|
it('renders default eth_logo identicon with no props', () => { |
||||||
|
const wrapper = mount(<IdenticonComponent store={store}/>) |
||||||
|
assert.equal(wrapper.find('img.balance-icon').prop('src'), './images/eth_logo.svg') |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders custom image and add className props', () => { |
||||||
|
const wrapper = mount(<IdenticonComponent store={store} className={'test-image'} image={'test-image'} />) |
||||||
|
assert.equal(wrapper.find('img.test-image').prop('className'), 'test-image identicon') |
||||||
|
assert.equal(wrapper.find('img.test-image').prop('src'), 'test-image') |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders div with address prop', () => { |
||||||
|
const wrapper = mount(<IdenticonComponent store={store} className={'test-address'} address={'0xTest'} />) |
||||||
|
assert.equal(wrapper.find('div.test-address').prop('className'), 'test-address identicon') |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,69 @@ |
|||||||
|
import React from 'react' |
||||||
|
import assert from 'assert' |
||||||
|
import thunk from 'redux-thunk' |
||||||
|
import { Provider } from 'react-redux' |
||||||
|
import configureMockStore from 'redux-mock-store' |
||||||
|
import { mount } from 'enzyme' |
||||||
|
|
||||||
|
import TokenCell from '../../../../../ui/app/components/token-cell' |
||||||
|
import Identicon from '../../../../../ui/app/components/identicon' |
||||||
|
|
||||||
|
describe('Token Cell', () => { |
||||||
|
let wrapper |
||||||
|
|
||||||
|
const state = { |
||||||
|
metamask: { |
||||||
|
network: 'test', |
||||||
|
currentCurrency: 'usd', |
||||||
|
selectedTokenAddress: '0xToken', |
||||||
|
selectedAddress: '0xAddress', |
||||||
|
contractExchangeRates: { |
||||||
|
'0xAnotherToken': 0.015, |
||||||
|
}, |
||||||
|
conversionRate: 7.00, |
||||||
|
}, |
||||||
|
appState: { |
||||||
|
sidebar: { |
||||||
|
isOpen: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
const middlewares = [thunk] |
||||||
|
const mockStore = configureMockStore(middlewares) |
||||||
|
const store = mockStore(state) |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
wrapper = mount( |
||||||
|
<Provider store={store}> |
||||||
|
<TokenCell |
||||||
|
address={'0xAnotherToken'} |
||||||
|
symbol={'TEST'} |
||||||
|
string={'5.000'} |
||||||
|
network={22} |
||||||
|
currentCurrency={'usd'} |
||||||
|
image={'./test-image'} |
||||||
|
/> |
||||||
|
</Provider> |
||||||
|
) |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders Identicon with props from token cell', () => { |
||||||
|
assert.equal(wrapper.find(Identicon).prop('address'), '0xAnotherToken') |
||||||
|
assert.equal(wrapper.find(Identicon).prop('network'), 'test') |
||||||
|
assert.equal(wrapper.find(Identicon).prop('image'), './test-image') |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders token balance', () => { |
||||||
|
assert.equal(wrapper.find('.token-list-item__token-balance').text(), '5.000') |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders token symbol', () => { |
||||||
|
assert.equal(wrapper.find('.token-list-item__token-symbol').text(), 'TEST') |
||||||
|
}) |
||||||
|
|
||||||
|
it('renders converted fiat amount', () => { |
||||||
|
assert.equal(wrapper.find('.token-list-item__fiat-amount').text(), '0.52 USD') |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
@ -0,0 +1,175 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const selectors = require('../../../../ui/app/selectors') |
||||||
|
const mockState = require('../../../data/mock-state.json') |
||||||
|
const Eth = require('ethjs') |
||||||
|
|
||||||
|
const { createTestProviderTools } = require('../../../stub/provider') |
||||||
|
const provider = createTestProviderTools({ scaffold: {}}).provider |
||||||
|
|
||||||
|
describe('Selectors', function () { |
||||||
|
|
||||||
|
describe('#getSelectedAddress', function () { |
||||||
|
let state |
||||||
|
beforeEach(function () { |
||||||
|
state = { |
||||||
|
metamask: { |
||||||
|
accounts: { |
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': { |
||||||
|
'balance': '0x0', |
||||||
|
'address': '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns first account if selectedAddress is undefined', function () { |
||||||
|
assert.equal(selectors.getSelectedAddress(state), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns selectedAddress', function () { |
||||||
|
assert.equal(selectors.getSelectedAddress(mockState), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
it('returns selected identity', function () { |
||||||
|
const identity = selectors.getSelectedIdentity(mockState) |
||||||
|
assert.equal(identity.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
assert.equal(identity.name, 'Test Account') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns selected account', function () { |
||||||
|
const account = selectors.getSelectedAccount(mockState) |
||||||
|
assert.equal(account.balance, '0x0') |
||||||
|
assert.equal(account.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns selected token from first token list', function () { |
||||||
|
const token = selectors.getSelectedToken(mockState) |
||||||
|
assert.equal(token.address, '0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d') |
||||||
|
assert.equal(token.symbol, 'TEST') |
||||||
|
assert.equal(token.decimals, '0') |
||||||
|
}) |
||||||
|
|
||||||
|
describe('#getSelectedTokenExchangeRate', function () { |
||||||
|
it('returns token exchange rate for first token', function () { |
||||||
|
const tokenRate = selectors.getSelectedTokenExchangeRate(mockState) |
||||||
|
assert.equal(tokenRate, '0.00039345803819379796') |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
describe('#getTokenExchangeRate', function () { |
||||||
|
let missingTokenRate |
||||||
|
|
||||||
|
beforeEach(function () { |
||||||
|
missingTokenRate = { |
||||||
|
metamask: { |
||||||
|
'contractExchangeRates': {}, |
||||||
|
}, |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns 0 token exchange rate for a token not in state', function () { |
||||||
|
const tokenRate = selectors.getTokenExchangeRate(missingTokenRate, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5') |
||||||
|
assert.equal(tokenRate, 0) |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns token exchange rate for specified token in state', function () { |
||||||
|
const tokenRate = selectors.getTokenExchangeRate(mockState, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5') |
||||||
|
assert.equal(tokenRate, 0.00008189274407698049) |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
it('returns conversionRate from state', function () { |
||||||
|
assert.equal(selectors.conversionRateSelector(mockState), 556.12) |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns address book from state', function () { |
||||||
|
const addressBook = selectors.getAddressBook(mockState) |
||||||
|
assert.equal(addressBook[0].address, '0xc42edfcc21ed14dda456aa0756c153f7985d8813') |
||||||
|
assert.equal(addressBook[0].name, '') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns accounts with balance, address, and name from identity and accounts in state', function () { |
||||||
|
const accountsWithSendEther = selectors.accountsWithSendEtherInfoSelector(mockState) |
||||||
|
assert.equal(accountsWithSendEther.length, 2) |
||||||
|
assert.equal(accountsWithSendEther[0].balance, '0x0') |
||||||
|
assert.equal(accountsWithSendEther[0].address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
assert.equal(accountsWithSendEther[0].name, 'Test Account') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns selected account with balance, address, and name from accountsWithSendEtherInfoSelector', function () { |
||||||
|
const currentAccountwithSendEther = selectors.getCurrentAccountWithSendEtherInfo(mockState) |
||||||
|
assert.equal(currentAccountwithSendEther.balance, '0x0') |
||||||
|
assert.equal(currentAccountwithSendEther.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
assert.equal(currentAccountwithSendEther.name, 'Test Account') |
||||||
|
}) |
||||||
|
|
||||||
|
describe('#transactionSelector', function () { |
||||||
|
it('returns transactions from state', function () { |
||||||
|
selectors.transactionsSelector(mockState) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getGasIsLoading', () => { |
||||||
|
const gasIsLoading = selectors.getGasIsLoading(mockState) |
||||||
|
assert.equal(gasIsLoading, false) |
||||||
|
}) |
||||||
|
|
||||||
|
describe('Send From', () => { |
||||||
|
it('#getSendFrom', () => { |
||||||
|
const sendFrom = selectors.getSendFrom(mockState) |
||||||
|
assert.equal(sendFrom, '0xc42edfcc21ed14dda456aa0756c153f7985d8813') |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getForceGasMin', () => { |
||||||
|
const forceGasMin = selectors.getForceGasMin(mockState) |
||||||
|
assert.equal(forceGasMin, null) |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getSendAmount', () => { |
||||||
|
const sendAmount = selectors.getSendAmount(mockState) |
||||||
|
assert.equal(sendAmount, '1bc16d674ec80000') |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getSendMaxModeState', () => { |
||||||
|
const sendMaxModeState = selectors.getSendMaxModeState(mockState) |
||||||
|
assert.equal(sendMaxModeState, false) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getCurrentCurrency', () => { |
||||||
|
const currentCurrency = selectors.getCurrentCurrency(mockState) |
||||||
|
assert.equal(currentCurrency, 'usd') |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getSelectedTokenToFiatRate', () => { |
||||||
|
const selectedTokenToFiatRate = selectors.getSelectedTokenToFiatRate(mockState) |
||||||
|
assert.equal(selectedTokenToFiatRate, '0.21880988420033493') |
||||||
|
}) |
||||||
|
|
||||||
|
describe('#getSelectedTokenContract', () => { |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
global.eth = new Eth(provider) |
||||||
|
}) |
||||||
|
|
||||||
|
it('', () => { |
||||||
|
const selectedTokenContract = selectors.getSelectedTokenContract(mockState) |
||||||
|
assert(selectedTokenContract.abi) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getCurrentViewContext', () => { |
||||||
|
const currentViewContext = selectors.getCurrentViewContext(mockState) |
||||||
|
assert.equal(currentViewContext, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc') |
||||||
|
}) |
||||||
|
|
||||||
|
it('#getTotalUnapprovedCount', () => { |
||||||
|
const totalUnapprovedCount = selectors.getTotalUnapprovedCount(mockState) |
||||||
|
assert.equal(totalUnapprovedCount, 1) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,26 @@ |
|||||||
|
const assert = require('assert') |
||||||
|
const etherscanNetworkPrefix = require('../../../ui/lib/etherscan-prefix-for-network') |
||||||
|
|
||||||
|
describe('Etherscan Network Prefix', () => { |
||||||
|
|
||||||
|
it('returns empy string as default value', () => { |
||||||
|
assert.equal(etherscanNetworkPrefix(), '') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns empty string as a prefix for networkId of 1', () => { |
||||||
|
assert.equal(etherscanNetworkPrefix(1), '') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns ropsten as prefix for networkId of 3', () => { |
||||||
|
assert.equal(etherscanNetworkPrefix(3), 'ropsten.') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returns rinkeby as prefix for networkId of 4', () => { |
||||||
|
assert.equal(etherscanNetworkPrefix(4), 'rinkeby.') |
||||||
|
}) |
||||||
|
|
||||||
|
it('returs kovan as prefix for networkId of 42', () => { |
||||||
|
assert.equal(etherscanNetworkPrefix(42), 'kovan.') |
||||||
|
}) |
||||||
|
|
||||||
|
}) |
@ -0,0 +1,25 @@ |
|||||||
|
import React, { PureComponent } from 'react' |
||||||
|
import PropTypes from 'prop-types' |
||||||
|
import classnames from 'classnames' |
||||||
|
|
||||||
|
export default class Card extends PureComponent { |
||||||
|
static propTypes = { |
||||||
|
className: PropTypes.string, |
||||||
|
overrideClassName: PropTypes.bool, |
||||||
|
title: PropTypes.string, |
||||||
|
children: PropTypes.node, |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { className, overrideClassName, title } = this.props |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={classnames({ 'card': !overrideClassName }, className)}> |
||||||
|
<div className="card__title"> |
||||||
|
{ title } |
||||||
|
</div> |
||||||
|
{ this.props.children } |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './card.component' |
@ -0,0 +1,11 @@ |
|||||||
|
.card { |
||||||
|
border-radius: 4px; |
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08); |
||||||
|
padding: 8px; |
||||||
|
|
||||||
|
&__title { |
||||||
|
border-bottom: 1px solid #d8d8d8; |
||||||
|
padding-bottom: 4px; |
||||||
|
text-transform: capitalize; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
import React from 'react' |
||||||
|
import assert from 'assert' |
||||||
|
import { shallow } from 'enzyme' |
||||||
|
import Card from '../card.component' |
||||||
|
|
||||||
|
describe('Card Component', () => { |
||||||
|
it('should render a card with a title and child element', () => { |
||||||
|
const wrapper = shallow( |
||||||
|
<Card |
||||||
|
title="Test" |
||||||
|
className="card-test-class" |
||||||
|
> |
||||||
|
<div className="child-test-class">Child</div> |
||||||
|
</Card> |
||||||
|
) |
||||||
|
|
||||||
|
assert.ok(wrapper.hasClass('card-test-class')) |
||||||
|
const title = wrapper.find('.card__title') |
||||||
|
assert.ok(title) |
||||||
|
assert.equal(title.text(), 'Test') |
||||||
|
const child = wrapper.find('.child-test-class') |
||||||
|
assert.ok(child) |
||||||
|
assert.equal(child.text(), 'Child') |
||||||
|
}) |
||||||
|
}) |