commit
59ab595b5e
@ -1,4 +1,4 @@ |
||||
{ |
||||
"presets": [["env"], "react", "stage-0"], |
||||
"presets": [["env", { "targets": { "browsers": [">0.25%", "not ie 11", "not op_mini all"] } } ], "react", "stage-0"], |
||||
"plugins": ["transform-runtime", "transform-async-to-generator", "transform-class-properties"] |
||||
} |
||||
|
@ -1,3 +0,0 @@ |
||||
{ |
||||
"generator-mocha": {} |
||||
} |
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 795 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.5 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,5 @@ |
||||
window.onload = function() { |
||||
if (window.location.pathname === '/phishing.html') { |
||||
document.getElementById('esdbLink').innerHTML = '<b>To read more about this scam, navigate to: <a href="https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '"> https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '</a></b>' |
||||
} |
||||
} |
@ -0,0 +1,202 @@ |
||||
const inherits = require('util').inherits |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../../ui/app/actions') |
||||
const Tooltip = require('./components/tooltip.js') |
||||
const ethUtil = require('ethereumjs-util') |
||||
const Copyable = require('./components/copyable') |
||||
const addressSummary = require('./util').addressSummary |
||||
|
||||
|
||||
module.exports = connect(mapStateToProps)(AddSuggestedTokenScreen) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
identities: state.metamask.identities, |
||||
suggestedTokens: state.metamask.suggestedTokens, |
||||
} |
||||
} |
||||
|
||||
inherits(AddSuggestedTokenScreen, Component) |
||||
function AddSuggestedTokenScreen () { |
||||
this.state = { |
||||
warning: null, |
||||
} |
||||
Component.call(this) |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.render = function () { |
||||
const state = this.state |
||||
const props = this.props |
||||
const { warning } = state |
||||
const key = Object.keys(props.suggestedTokens)[0] |
||||
const { address, symbol, decimals } = props.suggestedTokens[key] |
||||
|
||||
return ( |
||||
h('.flex-column.flex-grow', [ |
||||
|
||||
// subtitle and nav
|
||||
h('.section-title.flex-row.flex-center', [ |
||||
h('h2.page-subtitle', 'Add Suggested Token'), |
||||
]), |
||||
|
||||
h('.error', { |
||||
style: { |
||||
display: warning ? 'block' : 'none', |
||||
padding: '0 20px', |
||||
textAlign: 'center', |
||||
}, |
||||
}, warning), |
||||
|
||||
// conf view
|
||||
h('.flex-column.flex-justify-center.flex-grow.select-none', [ |
||||
h('.flex-space-around', { |
||||
style: { |
||||
padding: '20px', |
||||
}, |
||||
}, [ |
||||
|
||||
h('div', [ |
||||
h(Tooltip, { |
||||
position: 'top', |
||||
title: 'The contract of the actual token contract. Click for more info.', |
||||
}, [ |
||||
h('a', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address', |
||||
target: '_blank', |
||||
}, [ |
||||
h('span', 'Token Contract Address '), |
||||
h('i.fa.fa-question-circle'), |
||||
]), |
||||
]), |
||||
]), |
||||
|
||||
h('div', { |
||||
style: { display: 'flex' }, |
||||
}, [ |
||||
h(Copyable, { |
||||
value: ethUtil.toChecksumAddress(address), |
||||
}, [ |
||||
h('span#token-address', { |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
display: 'flex', |
||||
}, |
||||
}, addressSummary(address, 24, 4, false)), |
||||
]), |
||||
]), |
||||
|
||||
h('div', [ |
||||
h('span', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
}, 'Token Symbol'), |
||||
]), |
||||
|
||||
h('div', { style: {display: 'flex'} }, [ |
||||
h('p#token_symbol', { |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
}, |
||||
}, symbol), |
||||
]), |
||||
|
||||
h('div', [ |
||||
h('span', { |
||||
style: { fontWeight: 'bold', paddingRight: '10px'}, |
||||
}, 'Decimals of Precision'), |
||||
]), |
||||
|
||||
h('div', { style: {display: 'flex'} }, [ |
||||
h('p#token_decimals', { |
||||
type: 'number', |
||||
style: { |
||||
width: 'inherit', |
||||
flex: '1 0 auto', |
||||
height: '30px', |
||||
margin: '8px', |
||||
}, |
||||
}, decimals), |
||||
]), |
||||
|
||||
h('button', { |
||||
style: { |
||||
alignSelf: 'center', |
||||
margin: '8px', |
||||
}, |
||||
onClick: (event) => { |
||||
this.props.dispatch(actions.removeSuggestedTokens()) |
||||
}, |
||||
}, 'Cancel'), |
||||
|
||||
h('button', { |
||||
style: { |
||||
alignSelf: 'center', |
||||
margin: '8px', |
||||
}, |
||||
onClick: (event) => { |
||||
const valid = this.validateInputs({ address, symbol, decimals }) |
||||
if (!valid) return |
||||
|
||||
this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals)) |
||||
.then(() => { |
||||
this.props.dispatch(actions.removeSuggestedTokens()) |
||||
}) |
||||
}, |
||||
}, 'Add'), |
||||
]), |
||||
]), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.componentWillMount = function () { |
||||
if (typeof global.ethereumProvider === 'undefined') return |
||||
} |
||||
|
||||
AddSuggestedTokenScreen.prototype.validateInputs = function (opts) { |
||||
let msg = '' |
||||
const identitiesList = Object.keys(this.props.identities) |
||||
const { address, symbol, decimals } = opts |
||||
const standardAddress = ethUtil.addHexPrefix(address).toLowerCase() |
||||
|
||||
const validAddress = ethUtil.isValidAddress(address) |
||||
if (!validAddress) { |
||||
msg += 'Address is invalid.' |
||||
} |
||||
|
||||
const validDecimals = decimals >= 0 && decimals <= 36 |
||||
if (!validDecimals) { |
||||
msg += 'Decimals must be at least 0, and not over 36. ' |
||||
} |
||||
|
||||
const symbolLen = symbol.trim().length |
||||
const validSymbol = symbolLen > 0 && symbolLen < 10 |
||||
if (!validSymbol) { |
||||
msg += 'Symbol must be between 0 and 10 characters.' |
||||
} |
||||
|
||||
const ownAddress = identitiesList.includes(standardAddress) |
||||
if (ownAddress) { |
||||
msg = 'Personal address detected. Input the token contract address.' |
||||
} |
||||
|
||||
const isValid = validAddress && validDecimals && !ownAddress |
||||
|
||||
if (!isValid) { |
||||
this.setState({ |
||||
warning: msg, |
||||
}) |
||||
} else { |
||||
this.setState({ warning: null }) |
||||
} |
||||
|
||||
return isValid |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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.') |
||||
}) |
||||
|
||||
}) |
@ -1,33 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
// Main Views
|
||||
const TxView = require('./components/tx-view') |
||||
const WalletView = require('./components/wallet-view') |
||||
|
||||
module.exports = AccountAndTransactionDetails |
||||
|
||||
inherits(AccountAndTransactionDetails, Component) |
||||
function AccountAndTransactionDetails () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
AccountAndTransactionDetails.prototype.render = function () { |
||||
return h('div.account-and-transaction-details', [ |
||||
// wallet
|
||||
h(WalletView, { |
||||
style: { |
||||
}, |
||||
responsiveDisplayClassname: '.lap-visible', |
||||
}, [ |
||||
]), |
||||
|
||||
// transaction
|
||||
h(TxView, { |
||||
style: { |
||||
}, |
||||
}, [ |
||||
]), |
||||
]) |
||||
} |
||||
|
@ -1,267 +0,0 @@ |
||||
const Component = require('react').Component |
||||
const PropTypes = require('prop-types') |
||||
const h = require('react-hyperscript') |
||||
const inherits = require('util').inherits |
||||
const connect = require('react-redux').connect |
||||
const actions = require('../actions') |
||||
const CoinbaseForm = require('./coinbase-form') |
||||
const ShapeshiftForm = require('./shapeshift-form') |
||||
const Loading = require('./loading-screen') |
||||
const AccountPanel = require('./account-panel') |
||||
const RadioList = require('./custom-radio-list') |
||||
const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util') |
||||
|
||||
BuyButtonSubview.contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
module.exports = connect(mapStateToProps)(BuyButtonSubview) |
||||
|
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
identity: state.appState.identity, |
||||
account: state.metamask.accounts[state.appState.buyView.buyAddress], |
||||
warning: state.appState.warning, |
||||
buyView: state.appState.buyView, |
||||
network: state.metamask.network, |
||||
provider: state.metamask.provider, |
||||
context: state.appState.currentView.context, |
||||
isSubLoading: state.appState.isSubLoading, |
||||
} |
||||
} |
||||
|
||||
inherits(BuyButtonSubview, Component) |
||||
function BuyButtonSubview () { |
||||
Component.call(this) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.render = function () { |
||||
return ( |
||||
h('div', { |
||||
style: { |
||||
width: '100%', |
||||
}, |
||||
}, [ |
||||
this.headerSubview(), |
||||
this.primarySubview(), |
||||
]) |
||||
) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.headerSubview = function () { |
||||
const props = this.props |
||||
const isLoading = props.isSubLoading |
||||
return ( |
||||
|
||||
h('.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
}, |
||||
}, [ |
||||
|
||||
// header bar (back button, label)
|
||||
h('.flex-row', { |
||||
style: { |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
}, [ |
||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', { |
||||
onClick: this.backButtonContext.bind(this), |
||||
style: { |
||||
position: 'absolute', |
||||
left: '10px', |
||||
}, |
||||
}), |
||||
h('h2.text-transform-uppercase.flex-center', { |
||||
style: { |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, this.context.t('depositEth')), |
||||
]), |
||||
|
||||
// loading indication
|
||||
h('div', { |
||||
style: { |
||||
position: 'absolute', |
||||
top: '57vh', |
||||
left: '49vw', |
||||
}, |
||||
}, [ |
||||
isLoading && h(Loading), |
||||
]), |
||||
|
||||
// account panel
|
||||
h('div', { |
||||
style: { |
||||
width: '80%', |
||||
}, |
||||
}, [ |
||||
h(AccountPanel, { |
||||
showFullAddress: true, |
||||
identity: props.identity, |
||||
account: props.account, |
||||
}), |
||||
]), |
||||
|
||||
h('.flex-row', { |
||||
style: { |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
}, [ |
||||
h('h3.text-transform-uppercase.flex-center', { |
||||
style: { |
||||
paddingLeft: '15px', |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, this.context.t('selectService')), |
||||
]), |
||||
|
||||
]) |
||||
|
||||
) |
||||
} |
||||
|
||||
|
||||
BuyButtonSubview.prototype.primarySubview = function () { |
||||
const props = this.props |
||||
const network = props.network |
||||
|
||||
switch (network) { |
||||
case 'loading': |
||||
return |
||||
|
||||
case '1': |
||||
return this.mainnetSubview() |
||||
|
||||
// Ropsten, Rinkeby, Kovan
|
||||
case '3': |
||||
case '4': |
||||
case '42': |
||||
const networkName = getNetworkDisplayName(network) |
||||
const label = `${networkName} ${this.context.t('testFaucet')}` |
||||
return ( |
||||
h('div.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
margin: '20px 50px', |
||||
}, |
||||
}, [ |
||||
h('button.text-transform-uppercase', { |
||||
onClick: () => this.props.dispatch(actions.buyEth({ network })), |
||||
style: { |
||||
marginTop: '15px', |
||||
}, |
||||
}, label), |
||||
// Kovan only: Dharma loans beta
|
||||
network === '42' ? ( |
||||
h('button.text-transform-uppercase', { |
||||
onClick: () => this.navigateTo('https://borrow.dharma.io/'), |
||||
style: { |
||||
marginTop: '15px', |
||||
}, |
||||
}, this.context.t('borrowDharma')) |
||||
) : null, |
||||
]) |
||||
) |
||||
|
||||
default: |
||||
return ( |
||||
h('h2.error', this.context.t('unknownNetworkId')) |
||||
) |
||||
|
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.mainnetSubview = function () { |
||||
const props = this.props |
||||
|
||||
return ( |
||||
|
||||
h('.flex-column', { |
||||
style: { |
||||
alignItems: 'center', |
||||
}, |
||||
}, [ |
||||
|
||||
h('.flex-row.selected-exchange', { |
||||
style: { |
||||
position: 'relative', |
||||
right: '35px', |
||||
marginTop: '20px', |
||||
marginBottom: '20px', |
||||
}, |
||||
}, [ |
||||
h(RadioList, { |
||||
defaultFocus: props.buyView.subview, |
||||
labels: [ |
||||
'Coinbase', |
||||
'ShapeShift', |
||||
], |
||||
subtext: { |
||||
'Coinbase': `${this.context.t('crypto')}/${this.context.t('fiat')} (${this.context.t('usaOnly')})`, |
||||
'ShapeShift': this.context.t('crypto'), |
||||
}, |
||||
onClick: this.radioHandler.bind(this), |
||||
}), |
||||
]), |
||||
|
||||
h('h3.text-transform-uppercase', { |
||||
style: { |
||||
paddingLeft: '15px', |
||||
fontFamily: 'Montserrat Light', |
||||
width: '100vw', |
||||
background: 'rgb(235, 235, 235)', |
||||
color: 'rgb(174, 174, 174)', |
||||
paddingTop: '4px', |
||||
paddingBottom: '4px', |
||||
}, |
||||
}, props.buyView.subview), |
||||
|
||||
this.formVersionSubview(), |
||||
]) |
||||
|
||||
) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.formVersionSubview = function () { |
||||
const network = this.props.network |
||||
if (network === '1') { |
||||
if (this.props.buyView.formView.coinbase) { |
||||
return h(CoinbaseForm, this.props) |
||||
} else if (this.props.buyView.formView.shapeshift) { |
||||
return h(ShapeshiftForm, this.props) |
||||
} |
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.navigateTo = function (url) { |
||||
global.platform.openWindow({ url }) |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.backButtonContext = function () { |
||||
if (this.props.context === 'confTx') { |
||||
this.props.dispatch(actions.showConfTxPage({transForward: false})) |
||||
} else { |
||||
this.props.dispatch(actions.goHome()) |
||||
} |
||||
} |
||||
|
||||
BuyButtonSubview.prototype.radioHandler = function (event) { |
||||
switch (event.target.title) { |
||||
case 'Coinbase': |
||||
return this.props.dispatch(actions.coinBaseSubview()) |
||||
case 'ShapeShift': |
||||
return this.props.dispatch(actions.shapeShiftSubview(this.props.provider.type)) |
||||
} |
||||
} |
@ -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') |
||||
}) |
||||
}) |
@ -1 +0,0 @@ |
||||
export { default } from './confirm-page-container-error.component' |
@ -1,4 +1,3 @@ |
||||
export { default } from './confirm-page-container-content.component' |
||||
export { default as ConfirmPageContainerSummary } from './confirm-page-container-summary' |
||||
export { default as ConfirmPageContainerError } from './confirm-page-container-error' |
||||
export { default as ConfirmPageContainerWarning } from './confirm-page-container-warning' |
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue