aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/data/2-state.json70
-rw-r--r--test/data/mock-state.json1251
-rw-r--r--test/e2e/beta/drizzle.spec.js286
-rw-r--r--test/e2e/beta/from-import-beta-ui.spec.js6
-rw-r--r--test/e2e/beta/helpers.js7
-rw-r--r--test/e2e/beta/metamask-beta-ui.spec.js235
-rwxr-xr-xtest/e2e/beta/run-all.sh4
-rwxr-xr-xtest/e2e/beta/run-drizzle.sh20
-rw-r--r--test/e2e/func.js48
-rw-r--r--test/e2e/metamask.spec.js76
-rw-r--r--test/helper.js14
-rw-r--r--test/integration/lib/add-token.js4
-rw-r--r--test/integration/lib/confirm-sig-requests.js2
-rw-r--r--test/integration/lib/currency-localization.js8
-rw-r--r--test/integration/lib/send-new-ui.js8
-rw-r--r--test/integration/lib/tx-list-items.js29
-rw-r--r--test/lib/createTxMeta.js16
-rw-r--r--test/lib/mock-config-manager.js9
-rw-r--r--test/lib/mock-encryptor.js2
-rw-r--r--test/lib/render-helpers.js42
-rw-r--r--test/lib/shallow-with-store.js20
-rw-r--r--test/unit/app/cleanErrorStack.spec.js33
-rw-r--r--test/unit/app/controllers/blacklist-controller-test.js15
-rw-r--r--test/unit/app/controllers/currency-controller-test.js3
-rw-r--r--test/unit/app/controllers/detect-tokens-test.js51
-rw-r--r--test/unit/app/controllers/metamask-controller-test.js248
-rw-r--r--test/unit/app/controllers/network-contoller-test.js7
-rw-r--r--test/unit/app/controllers/notice-controller-test.js7
-rw-r--r--test/unit/app/controllers/preferences-controller-test.js140
-rw-r--r--test/unit/app/controllers/transactions/nonce-tracker-test.js9
-rw-r--r--test/unit/app/controllers/transactions/pending-tx-test.js78
-rw-r--r--test/unit/app/controllers/transactions/tx-controller-test.js100
-rw-r--r--test/unit/components/balance-component-test.js3
-rw-r--r--test/unit/components/binary-renderer-test.js2
-rw-r--r--test/unit/config-manager-test.js115
-rw-r--r--test/unit/development/sample-changelog.md4
-rw-r--r--test/unit/localhostState.js21
-rw-r--r--test/unit/responsive/components/dropdown-test.js2
-rw-r--r--test/unit/ui/add-token.spec.js2
-rw-r--r--test/unit/ui/app/actions.spec.js1468
-rw-r--r--test/unit/ui/app/components/identicon.spec.js36
-rw-r--r--test/unit/ui/app/components/token-cell.spec.js69
-rw-r--r--test/unit/ui/app/selectors.spec.js175
-rw-r--r--test/unit/ui/etherscan-prefix-for-network.spec.js26
44 files changed, 4251 insertions, 520 deletions
diff --git a/test/data/2-state.json b/test/data/2-state.json
new file mode 100644
index 000000000..d41a403ff
--- /dev/null
+++ b/test/data/2-state.json
@@ -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
+} \ No newline at end of file
diff --git a/test/data/mock-state.json b/test/data/mock-state.json
new file mode 100644
index 000000000..7e083c60e
--- /dev/null
+++ b/test/data/mock-state.json
@@ -0,0 +1,1251 @@
+{
+ "metamask": {
+ "network": "4",
+ "identities": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "name": "Test Account"
+ },
+ "0xc42edfcc21ed14dda456aa0756c153f7985d8813": {
+ "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "name": "Test Account 2"
+ }
+ },
+ "unapprovedTxs": {
+ "8393540981007587": {
+ "id": 8393540981007587,
+ "time": 1536268017676,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "value": "0x0",
+ "gas": "0x5208",
+ "gasPrice": "0x3b9aca00"
+ },
+ "history": [
+ {
+ "id": 8393540981007587,
+ "time": 1536268017676,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "value": "0x0",
+ "gas": "0x5208",
+ "gasPrice": "0x3b9aca00"
+ }
+ },
+ [
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false,
+ "timestamp": 1536268017685
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0x5208"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "MetaMask",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1536268017686
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": true,
+ "estimatedGas": "0x5208",
+ "origin": "MetaMask"
+ }
+ },
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "accounts": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "balance": "0x0",
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ },
+ "0xc42edfcc21ed14dda456aa0756c153f7985d8813": {
+ "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "balance": "0x0"
+ }
+ },
+ "tokens": [
+ {
+ "address": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d",
+ "symbol": "TEST",
+ "decimals": "0"
+ },
+ {
+ "address": "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5",
+ "decimals": "8",
+ "symbol": "TEST2"
+ },
+ {
+ "address": "0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4",
+ "symbol": "META",
+ "decimals": "18"
+ }
+ ],
+ "contractExchangeRates": {
+ "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d": 0.00039345803819379796,
+ "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5": 0.00008189274407698049
+ },
+ "currentCurrency": "usd",
+ "conversionRate": 556.12,
+ "addressBook": [
+ {
+ "address": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "name": ""
+ }
+ ],
+ "selectedTokenAddress": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d",
+ "unapprovedMsgs": {},
+ "unapprovedMsgCount": 0,
+ "unapprovedPersonalMsgs": {},
+ "unapprovedPersonalMsgCount": 0,
+ "unapprovedTypedMessages": {},
+ "unapprovedTypedMessagesCount": 0,
+ "send": {
+ "gasLimit": "0x5208",
+ "gasPrice": "0xee6b2800",
+ "gasTotal": "0x4c65c6294000",
+ "tokenBalance": null,
+ "from": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
+ "to": "",
+ "amount": "1bc16d674ec80000",
+ "memo": "",
+ "errors": {},
+ "maxModeOn": false,
+ "editingTransactionId": null,
+ "forceGasMin": null,
+ "toNickname": ""
+ },
+ "selectedAddressTxList": [
+ {
+ "id": 3387511061307736,
+ "time": 1528133130531,
+ "status": "confirmed",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x3b9aca00",
+ "nonce": "0xb5"
+ },
+ "history": [
+ {
+ "id": 3387511061307736,
+ "time": 1528133130531,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x3b9aca00"
+ }
+ },
+ [
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false,
+ "timestamp": 1528133130666
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0xcf08"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "MetaMask",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133130667
+ }
+ ],
+ [],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "approved",
+ "note": "txStateManager: setting status to approved",
+ "timestamp": 1528133131716
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/txParams/nonce",
+ "value": "0xb5",
+ "note": "transactions#approveTransaction",
+ "timestamp": 1528133131806
+ },
+ {
+ "op": "add",
+ "path": "/nonceDetails",
+ "value": {
+ "params": {
+ "highestLocallyConfirmed": 0,
+ "highestSuggested": 181,
+ "nextNetworkNonce": 181
+ },
+ "local": {
+ "name": "local",
+ "nonce": 181,
+ "details": {
+ "startPoint": 181,
+ "highest": 181
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 181,
+ "details": {
+ "baseCount": 181
+ }
+ }
+ }
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "signed",
+ "note": "transactions#publishTransaction",
+ "timestamp": 1528133131825
+ },
+ {
+ "op": "add",
+ "path": "/rawTx",
+ "value": "0xf86c81b5843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba03f879cd33a31180da38545d0f809822e00ddf35954d8b0ece83bacf22347ce54a06ad050487978e425ca6a014ed55ea8e9a190069863ed96a0eefa88d729ea1eda"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "add",
+ "path": "/hash",
+ "value": "0x516b77569173a04c76fdb6545cf279ebd0c75f5d25d6e4ce019925205f0e3709",
+ "note": "transactions#setTxHash",
+ "timestamp": 1528133131951
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/submittedTime",
+ "value": 1528133131951,
+ "note": "txStateManager - add submitted time stamp",
+ "timestamp": 1528133131952
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "submitted",
+ "note": "txStateManager: setting status to submitted",
+ "timestamp": 1528133131955
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/firstRetryBlockNumber",
+ "value": "0x24af6b",
+ "note": "transactions/pending-tx-tracker#event: tx:block-update",
+ "timestamp": 1528133134414
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "confirmed",
+ "note": "txStateManager: setting status to confirmed",
+ "timestamp": 1528133158516
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": true,
+ "estimatedGas": "0xcf08",
+ "origin": "MetaMask",
+ "nonceDetails": {
+ "params": {
+ "highestLocallyConfirmed": 0,
+ "highestSuggested": 181,
+ "nextNetworkNonce": 181
+ },
+ "local": {
+ "name": "local",
+ "nonce": 181,
+ "details": {
+ "startPoint": 181,
+ "highest": 181
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 181,
+ "details": {
+ "baseCount": 181
+ }
+ }
+ },
+ "rawTx": "0xf86c81b5843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba03f879cd33a31180da38545d0f809822e00ddf35954d8b0ece83bacf22347ce54a06ad050487978e425ca6a014ed55ea8e9a190069863ed96a0eefa88d729ea1eda",
+ "hash": "0x516b77569173a04c76fdb6545cf279ebd0c75f5d25d6e4ce019925205f0e3709",
+ "submittedTime": 1528133131951,
+ "firstRetryBlockNumber": "0x24af6b"
+ },
+ {
+ "id": 3387511061307737,
+ "time": 1528133149983,
+ "status": "confirmed",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x3b9aca00",
+ "nonce": "0xb6"
+ },
+ "history": [
+ {
+ "id": 3387511061307737,
+ "time": 1528133149983,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x3b9aca00"
+ }
+ },
+ [
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false,
+ "timestamp": 1528133150011
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0xcf08"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "MetaMask",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133150013
+ }
+ ],
+ [],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "approved",
+ "note": "txStateManager: setting status to approved",
+ "timestamp": 1528133151102
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/txParams/nonce",
+ "value": "0xb6",
+ "note": "transactions#approveTransaction",
+ "timestamp": 1528133151189
+ },
+ {
+ "op": "add",
+ "path": "/nonceDetails",
+ "value": {
+ "params": {
+ "highestLocallyConfirmed": 0,
+ "highestSuggested": 181,
+ "nextNetworkNonce": 181
+ },
+ "local": {
+ "name": "local",
+ "nonce": 182,
+ "details": {
+ "startPoint": 181,
+ "highest": 182
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 181,
+ "details": {
+ "baseCount": 181
+ }
+ }
+ }
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "signed",
+ "note": "transactions#publishTransaction",
+ "timestamp": 1528133151203
+ },
+ {
+ "op": "add",
+ "path": "/rawTx",
+ "value": "0xf86c81b6843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba0692deaabf0d79544d41e7c475ad43760679a4f25d0fee908b1da308db1a291a7a0384db85fc6c843ea25986a0760f3c50ab6504fc559fc71fc7f23f60950eb316d"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "add",
+ "path": "/hash",
+ "value": "0x9271b266d05022cfa841362fae43763ebafcee540d84278b0157ef4a68d4e26f",
+ "note": "transactions#setTxHash",
+ "timestamp": 1528133151342
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/submittedTime",
+ "value": 1528133151347,
+ "note": "txStateManager - add submitted time stamp",
+ "timestamp": 1528133151347
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "submitted",
+ "note": "txStateManager: setting status to submitted",
+ "timestamp": 1528133151368
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/firstRetryBlockNumber",
+ "value": "0x24af6d",
+ "note": "transactions/pending-tx-tracker#event: tx:block-update",
+ "timestamp": 1528133158532
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "confirmed",
+ "note": "txStateManager: setting status to confirmed",
+ "timestamp": 1528133190636
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": true,
+ "estimatedGas": "0xcf08",
+ "origin": "MetaMask",
+ "nonceDetails": {
+ "params": {
+ "highestLocallyConfirmed": 0,
+ "highestSuggested": 181,
+ "nextNetworkNonce": 181
+ },
+ "local": {
+ "name": "local",
+ "nonce": 182,
+ "details": {
+ "startPoint": 181,
+ "highest": 182
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 181,
+ "details": {
+ "baseCount": 181
+ }
+ }
+ },
+ "rawTx": "0xf86c81b6843b9aca0082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba0692deaabf0d79544d41e7c475ad43760679a4f25d0fee908b1da308db1a291a7a0384db85fc6c843ea25986a0760f3c50ab6504fc559fc71fc7f23f60950eb316d",
+ "hash": "0x9271b266d05022cfa841362fae43763ebafcee540d84278b0157ef4a68d4e26f",
+ "submittedTime": 1528133151347,
+ "firstRetryBlockNumber": "0x24af6d"
+ },
+ {
+ "id": 3387511061307738,
+ "time": 1528133180635,
+ "status": "confirmed",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x12a05f200",
+ "nonce": "0xb7"
+ },
+ "history": [
+ {
+ "id": 3387511061307738,
+ "time": 1528133180635,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x92e659448c48fc926ec942d0da1459260d36bb33",
+ "value": "0x1bc16d674ec80000",
+ "gas": "0xcf08",
+ "gasPrice": "0x12a05f200"
+ }
+ },
+ [
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false,
+ "timestamp": 1528133180720
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0xcf08"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "MetaMask",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133180722
+ }
+ ],
+ [],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "approved",
+ "note": "txStateManager: setting status to approved",
+ "timestamp": 1528133181623
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/txParams/nonce",
+ "value": "0xb7",
+ "note": "transactions#approveTransaction",
+ "timestamp": 1528133181726
+ },
+ {
+ "op": "add",
+ "path": "/nonceDetails",
+ "value": {
+ "params": {
+ "highestLocallyConfirmed": 182,
+ "highestSuggested": 182,
+ "nextNetworkNonce": 182
+ },
+ "local": {
+ "name": "local",
+ "nonce": 183,
+ "details": {
+ "startPoint": 182,
+ "highest": 183
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 182,
+ "details": {
+ "baseCount": 182
+ }
+ }
+ }
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "signed",
+ "note": "transactions#publishTransaction",
+ "timestamp": 1528133181749
+ },
+ {
+ "op": "add",
+ "path": "/rawTx",
+ "value": "0xf86d81b785012a05f20082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba086f9846798be6988c39a5cf85f0dbe267e59ca0b96a6a7077e92cba33e10a258a064ffa52ac90c238ce21e6f085283216191b185a1eccd7daae6e2ab66ba26ada0"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "add",
+ "path": "/hash",
+ "value": "0x4e061e977c099735bc9e5203e717f7d9dccb3fcb2f82031a12a3ed326f95d43b",
+ "note": "transactions#setTxHash",
+ "timestamp": 1528133181885
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/submittedTime",
+ "value": 1528133181885,
+ "note": "txStateManager - add submitted time stamp",
+ "timestamp": 1528133181885
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "submitted",
+ "note": "txStateManager: setting status to submitted",
+ "timestamp": 1528133181888
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/firstRetryBlockNumber",
+ "value": "0x24af6f",
+ "note": "transactions/pending-tx-tracker#event: tx:block-update",
+ "timestamp": 1528133190653
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "confirmed",
+ "note": "txStateManager: setting status to confirmed",
+ "timestamp": 1528133222745
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": true,
+ "estimatedGas": "0xcf08",
+ "origin": "MetaMask",
+ "nonceDetails": {
+ "params": {
+ "highestLocallyConfirmed": 182,
+ "highestSuggested": 182,
+ "nextNetworkNonce": 182
+ },
+ "local": {
+ "name": "local",
+ "nonce": 183,
+ "details": {
+ "startPoint": 182,
+ "highest": 183
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 182,
+ "details": {
+ "baseCount": 182
+ }
+ }
+ },
+ "rawTx": "0xf86d81b785012a05f20082cf089492e659448c48fc926ec942d0da1459260d36bb33881bc16d674ec80000802ba086f9846798be6988c39a5cf85f0dbe267e59ca0b96a6a7077e92cba33e10a258a064ffa52ac90c238ce21e6f085283216191b185a1eccd7daae6e2ab66ba26ada0",
+ "hash": "0x4e061e977c099735bc9e5203e717f7d9dccb3fcb2f82031a12a3ed326f95d43b",
+ "submittedTime": 1528133181885,
+ "firstRetryBlockNumber": "0x24af6f"
+ },
+ {
+ "id": 3387511061307739,
+ "time": 1528133223918,
+ "status": "confirmed",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0xfe2149773b3513703e79ad23d05a778a185016ee",
+ "value": "0xaa87bee538000",
+ "data": "0xea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de33",
+ "gasPrice": "0x3b9aca00",
+ "gas": "0x6169e",
+ "nonce": "0xb8"
+ },
+ "history": [
+ {
+ "id": 3387511061307739,
+ "time": 1528133223918,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0xfe2149773b3513703e79ad23d05a778a185016ee",
+ "value": "0xaa87bee538000",
+ "data": "0xea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de33",
+ "gasPrice": "0x3b9aca00"
+ }
+ },
+ [
+ {
+ "op": "add",
+ "path": "/txParams/gas",
+ "value": "0x6169e",
+ "timestamp": 1528133225488
+ },
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": false
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "40f14"
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/estimatedGas",
+ "value": "40f14",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133225492
+ },
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "crypko.ai"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "approved",
+ "note": "txStateManager: setting status to approved",
+ "timestamp": 1528133227279
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/txParams/nonce",
+ "value": "0xb8",
+ "note": "transactions#approveTransaction",
+ "timestamp": 1528133227374
+ },
+ {
+ "op": "add",
+ "path": "/nonceDetails",
+ "value": {
+ "params": {
+ "highestLocallyConfirmed": 184,
+ "highestSuggested": 184,
+ "nextNetworkNonce": 184
+ },
+ "local": {
+ "name": "local",
+ "nonce": 184,
+ "details": {
+ "startPoint": 184,
+ "highest": 184
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 184,
+ "details": {
+ "baseCount": 184
+ }
+ }
+ }
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "signed",
+ "note": "transactions#publishTransaction",
+ "timestamp": 1528133227405
+ },
+ {
+ "op": "add",
+ "path": "/rawTx",
+ "value": "0xf8b181b8843b9aca008306169e94fe2149773b3513703e79ad23d05a778a185016ee870aa87bee538000b844ea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de332ca07bb2efbb8529d67606f9f89e7934c594a31d50c7d24a3286c20a2944a3b8c2a9a07b55ebd8aa28728ce0e38dd3b3503b78fccedae80053626d8649c68346c7c49c"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "add",
+ "path": "/hash",
+ "value": "0x466ae7d4b7c270121f0a8d68fbc6c9091ffc4aa976a553a5bfa56a79cf9f63dd",
+ "note": "transactions#setTxHash",
+ "timestamp": 1528133227534
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/submittedTime",
+ "value": 1528133227538,
+ "note": "txStateManager - add submitted time stamp",
+ "timestamp": 1528133227538
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "submitted",
+ "note": "txStateManager: setting status to submitted",
+ "timestamp": 1528133227543
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/firstRetryBlockNumber",
+ "value": "0x24af72",
+ "note": "transactions/pending-tx-tracker#event: tx:block-update",
+ "timestamp": 1528133238980
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "confirmed",
+ "note": "txStateManager: setting status to confirmed",
+ "timestamp": 1528133255035
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": false,
+ "estimatedGas": "40f14",
+ "origin": "crypko.ai",
+ "nonceDetails": {
+ "params": {
+ "highestLocallyConfirmed": 184,
+ "highestSuggested": 184,
+ "nextNetworkNonce": 184
+ },
+ "local": {
+ "name": "local",
+ "nonce": 184,
+ "details": {
+ "startPoint": 184,
+ "highest": 184
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 184,
+ "details": {
+ "baseCount": 184
+ }
+ }
+ },
+ "rawTx": "0xf8b181b8843b9aca008306169e94fe2149773b3513703e79ad23d05a778a185016ee870aa87bee538000b844ea94496b000000000000000000000000000000000000000000000000000000000001e1eb000000000000000000000000000000000000000000000000000000000001de332ca07bb2efbb8529d67606f9f89e7934c594a31d50c7d24a3286c20a2944a3b8c2a9a07b55ebd8aa28728ce0e38dd3b3503b78fccedae80053626d8649c68346c7c49c",
+ "hash": "0x466ae7d4b7c270121f0a8d68fbc6c9091ffc4aa976a553a5bfa56a79cf9f63dd",
+ "submittedTime": 1528133227538,
+ "firstRetryBlockNumber": "0x24af72"
+ },
+ {
+ "id": 3387511061307740,
+ "time": 1528133291381,
+ "status": "confirmed",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d",
+ "value": "0x0",
+ "data": "0xa9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb330000000000000000000000000000000000000000000000000000000000000002",
+ "gas": "0xd508",
+ "gasPrice": "0x3b9aca00",
+ "nonce": "0xb9"
+ },
+ "history": [
+ {
+ "id": 3387511061307740,
+ "time": 1528133291381,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d",
+ "value": "0x0",
+ "data": "0xa9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb330000000000000000000000000000000000000000000000000000000000000002",
+ "gas": "0xd508",
+ "gasPrice": "0x3b9aca00"
+ }
+ },
+ [
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false,
+ "timestamp": 1528133291486
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0xd508"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "MetaMask",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133291486
+ }
+ ],
+ [],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "approved",
+ "note": "txStateManager: setting status to approved",
+ "timestamp": 1528133293588
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/txParams/nonce",
+ "value": "0xb9",
+ "note": "transactions#approveTransaction",
+ "timestamp": 1528133293706
+ },
+ {
+ "op": "add",
+ "path": "/nonceDetails",
+ "value": {
+ "params": {
+ "highestLocallyConfirmed": 185,
+ "highestSuggested": 185,
+ "nextNetworkNonce": 185
+ },
+ "local": {
+ "name": "local",
+ "nonce": 185,
+ "details": {
+ "startPoint": 185,
+ "highest": 185
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 185,
+ "details": {
+ "baseCount": 185
+ }
+ }
+ }
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "signed",
+ "note": "transactions#publishTransaction",
+ "timestamp": 1528133293724
+ },
+ {
+ "op": "add",
+ "path": "/rawTx",
+ "value": "0xf8a981b9843b9aca0082d50894108cf70c7d384c552f42c07c41c0e1e46d77ea0d80b844a9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb3300000000000000000000000000000000000000000000000000000000000000022ca04f05310490d3e3a9a159ae25f52cec9afb0a69527d30be832aaae12e64ff056ea075f81a5220bed481e764bab8830c57169c59fe528ca9cf3442f47f7618a9b4a9"
+ }
+ ],
+ [],
+ [
+ {
+ "op": "add",
+ "path": "/hash",
+ "value": "0x3680dc9815cd05b620b6dd0017d949604ca7d92f051d5542fc8a5ecaa876af09",
+ "note": "transactions#setTxHash",
+ "timestamp": 1528133293853
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/submittedTime",
+ "value": 1528133293859,
+ "note": "txStateManager - add submitted time stamp",
+ "timestamp": 1528133293862
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "submitted",
+ "note": "txStateManager: setting status to submitted",
+ "timestamp": 1528133293867
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/firstRetryBlockNumber",
+ "value": "0x24af76",
+ "note": "transactions/pending-tx-tracker#event: tx:block-update",
+ "timestamp": 1528133295200
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "confirmed",
+ "note": "txStateManager: setting status to confirmed",
+ "timestamp": 1528133327522
+ }
+ ]
+ ],
+ "gasPriceSpecified": true,
+ "gasLimitSpecified": true,
+ "estimatedGas": "0xd508",
+ "origin": "MetaMask",
+ "nonceDetails": {
+ "params": {
+ "highestLocallyConfirmed": 185,
+ "highestSuggested": 185,
+ "nextNetworkNonce": 185
+ },
+ "local": {
+ "name": "local",
+ "nonce": 185,
+ "details": {
+ "startPoint": 185,
+ "highest": 185
+ }
+ },
+ "network": {
+ "name": "network",
+ "nonce": 185,
+ "details": {
+ "baseCount": 185
+ }
+ }
+ },
+ "rawTx": "0xf8a981b9843b9aca0082d50894108cf70c7d384c552f42c07c41c0e1e46d77ea0d80b844a9059cbb00000000000000000000000092e659448c48fc926ec942d0da1459260d36bb3300000000000000000000000000000000000000000000000000000000000000022ca04f05310490d3e3a9a159ae25f52cec9afb0a69527d30be832aaae12e64ff056ea075f81a5220bed481e764bab8830c57169c59fe528ca9cf3442f47f7618a9b4a9",
+ "hash": "0x3680dc9815cd05b620b6dd0017d949604ca7d92f051d5542fc8a5ecaa876af09",
+ "submittedTime": 1528133293859,
+ "firstRetryBlockNumber": "0x24af76"
+ },
+ {
+ "id": 3387511061307741,
+ "time": 1528133318440,
+ "status": "rejected",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": false,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "value": "0x0",
+ "gasPrice": "0x3b9aca00",
+ "gas": "0x5208"
+ },
+ "history": [
+ {
+ "id": 3387511061307741,
+ "time": 1528133318440,
+ "status": "unapproved",
+ "metamaskNetworkId": "4",
+ "loadingDefaults": true,
+ "txParams": {
+ "from": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62",
+ "to": "0x3b222de3aaba8ec9771ca9e9af5d8ed757fb7f62"
+ }
+ },
+ [
+ {
+ "op": "add",
+ "path": "/txParams/value",
+ "value": "0x0",
+ "timestamp": 1528133319641
+ },
+ {
+ "op": "add",
+ "path": "/txParams/gasPrice",
+ "value": "0x3b9aca00"
+ },
+ {
+ "op": "add",
+ "path": "/txParams/gas",
+ "value": "0x5208"
+ },
+ {
+ "op": "replace",
+ "path": "/loadingDefaults",
+ "value": false
+ },
+ {
+ "op": "add",
+ "path": "/gasPriceSpecified",
+ "value": false
+ },
+ {
+ "op": "add",
+ "path": "/gasLimitSpecified",
+ "value": false
+ },
+ {
+ "op": "add",
+ "path": "/simpleSend",
+ "value": true
+ },
+ {
+ "op": "add",
+ "path": "/estimatedGas",
+ "value": "0x5208"
+ }
+ ],
+ [
+ {
+ "op": "add",
+ "path": "/origin",
+ "value": "tmashuang.github.io",
+ "note": "#newUnapprovedTransaction - adding the origin",
+ "timestamp": 1528133319642
+ }
+ ],
+ [
+ {
+ "op": "replace",
+ "path": "/status",
+ "value": "rejected",
+ "note": "txStateManager: setting status to rejected",
+ "timestamp": 1528133320924
+ }
+ ]
+ ],
+ "gasPriceSpecified": false,
+ "gasLimitSpecified": false,
+ "simpleSend": true,
+ "estimatedGas": "0x5208",
+ "origin": "tmashuang.github.io"
+ }
+ ]
+ },
+ "appState": {
+ "gasIsLoading": false,
+ "currentView": {
+ "name": "accountDetail",
+ "detailView": null,
+ "context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ }
+ }
+} \ No newline at end of file
diff --git a/test/e2e/beta/drizzle.spec.js b/test/e2e/beta/drizzle.spec.js
new file mode 100644
index 000000000..ff4b4b74d
--- /dev/null
+++ b/test/e2e/beta/drizzle.spec.js
@@ -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}$/))
+ })
+ })
+})
diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js
index 1261b6f95..32aaa29a6 100644
--- a/test/e2e/beta/from-import-beta-ui.spec.js
+++ b/test/e2e/beta/from-import-beta-ui.spec.js
@@ -314,12 +314,12 @@ describe('Using MetaMask with an existing account', function () {
})
it('finds the transaction in the transactions list', async function () {
- const transactions = await findElements(driver, By.css('.tx-list-item'))
+ const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 1)
- const txValues = await findElements(driver, By.css('.tx-list-value'))
+ const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
assert.equal(txValues.length, 1)
- assert.equal(await txValues[0].getText(), '1 ETH')
+ assert.equal(await txValues[0].getText(), '-1 ETH')
})
})
diff --git a/test/e2e/beta/helpers.js b/test/e2e/beta/helpers.js
index d90cd5d66..4055d8155 100644
--- a/test/e2e/beta/helpers.js
+++ b/test/e2e/beta/helpers.js
@@ -2,8 +2,8 @@ const fs = require('fs')
const mkdirp = require('mkdirp')
const pify = require('pify')
const assert = require('assert')
-const {until} = require('selenium-webdriver')
const { delay } = require('../func')
+const { until } = require('selenium-webdriver')
module.exports = {
assertElementNotPresent,
@@ -126,10 +126,7 @@ async function assertElementNotPresent (webdriver, driver, by) {
try {
dataTab = await findElement(driver, by, 4000)
} catch (err) {
- console.log(err)
assert(err instanceof webdriver.error.NoSuchElementError || err instanceof webdriver.error.TimeoutError)
}
- if (dataTab) {
- assert(false, 'Data tab should not be present')
- }
+ assert.ok(!dataTab, 'Found element that should not be present')
}
diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js
index 43300bda6..8d1ecac0d 100644
--- a/test/e2e/beta/metamask-beta-ui.spec.js
+++ b/test/e2e/beta/metamask-beta-ui.spec.js
@@ -225,19 +225,9 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
}
- await clickWordAndWait(words[0])
- await clickWordAndWait(words[1])
- await clickWordAndWait(words[2])
- await clickWordAndWait(words[3])
- await clickWordAndWait(words[4])
- await clickWordAndWait(words[5])
- await clickWordAndWait(words[6])
- await clickWordAndWait(words[7])
- await clickWordAndWait(words[8])
- await clickWordAndWait(words[9])
- await clickWordAndWait(words[10])
- await clickWordAndWait(words[11])
-
+ for (let i = 0; i < 12; i++) {
+ await clickWordAndWait(words[i])
+ }
} catch (e) {
if (count > 2) {
throw e
@@ -281,6 +271,17 @@ describe('MetaMask', function () {
await driver.wait(until.stalenessOf(accountModal))
await delay(regularDelayMs)
})
+ it('show account details dropdown menu', async () => {
+
+ const {width, height} = await driver.manage().window().getSize()
+ driver.manage().window().setSize(320, 480)
+ await driver.findElement(By.css('div.menu-bar__open-in-browser')).click()
+ const options = await driver.findElements(By.css('div.menu.account-details-dropdown div.menu__item'))
+ assert.equal(options.length, 3) // HD Wallet type does not have to show the Remove Account option
+ await delay(regularDelayMs)
+ driver.manage().window().setSize(width, height)
+
+ })
})
describe('Log out an log back in', () => {
@@ -414,12 +415,12 @@ describe('MetaMask', function () {
})
it('finds the transaction in the transactions list', async function () {
- const transactions = await findElements(driver, By.css('.tx-list-item'))
+ const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 1)
if (process.env.SELENIUM_BROWSER !== 'firefox') {
- const txValues = await findElement(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues, /1\sETH/), 10000)
+ const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues, /-1\sETH/), 10000)
}
})
})
@@ -457,16 +458,11 @@ describe('MetaMask', function () {
})
it('finds the transaction in the transactions list', async function () {
- const transactions = await findElements(driver, By.css('.tx-list-item'))
+ const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 2)
- await findElement(driver, By.xpath(`//span[contains(text(), 'Submitted')]`))
-
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
-
- const txValues = await findElement(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues, /3\sETH/), 10000)
+ const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues, /-3\sETH/), 10000)
})
})
@@ -489,9 +485,9 @@ describe('MetaMask', function () {
await driver.switchTo().window(extension)
await delay(regularDelayMs)
- const txListItem = await findElement(driver, By.xpath(`//span[contains(text(), 'Contract Deployment')]`))
+ const txListItem = await findElement(driver, By.xpath(`//div[contains(text(), 'Contract Deployment')]`))
await txListItem.click()
- await delay(regularDelayMs)
+ await delay(largeDelayMs)
})
it('displays the contract creation data', async () => {
@@ -513,15 +509,15 @@ describe('MetaMask', function () {
it('confirms a deploy contract transaction', async () => {
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
await confirmButton.click()
- await delay(regularDelayMs)
-
- await findElement(driver, By.xpath(`//span[contains(text(), 'Submitted')]`))
+ await delay(largeDelayMs)
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 3
+ }, 10000)
- const txAccounts = await findElements(driver, By.css('.tx-list-account'))
- assert.equal(await txAccounts[0].getText(), 'Contract Deployment')
+ const txAction = await findElements(driver, By.css('.transaction-list-item__action'))
+ await driver.wait(until.elementTextMatches(txAction[0], /Contract\sDeployment/), 10000)
await delay(regularDelayMs)
})
@@ -542,9 +538,9 @@ describe('MetaMask', function () {
await driver.switchTo().window(extension)
await delay(largeDelayMs)
- await findElements(driver, By.css('.tx-list-pending-item-container'))
- const [txListValue] = await findElements(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txListValue, /4\sETH/), 10000)
+ await findElements(driver, By.css('.transaction-list-item'))
+ const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txListValue, /-4\sETH/), 10000)
await txListValue.click()
await delay(regularDelayMs)
@@ -572,15 +568,17 @@ describe('MetaMask', function () {
await confirmButton.click()
await delay(regularDelayMs)
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 4
+ }, 10000)
- const txValues = await findElement(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues, /4\sETH/), 10000)
+ const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues[0], /-4\sETH/), 10000)
- const txAccounts = await findElements(driver, By.css('.tx-list-account'))
- const firstTxAddress = await txAccounts[0].getText()
- assert(firstTxAddress.match(/^0x\w{8}\.{3}\w{4}$/))
+ // const txAccounts = await findElements(driver, By.css('.tx-list-account'))
+ // const firstTxAddress = await txAccounts[0].getText()
+ // assert(firstTxAddress.match(/^0x\w{8}\.{3}\w{4}$/))
})
it('calls and confirms a contract method where ETH is received', async () => {
@@ -594,7 +592,7 @@ describe('MetaMask', function () {
await driver.switchTo().window(extension)
await delay(regularDelayMs)
- const txListItem = await findElement(driver, By.css('.tx-list-item'))
+ const txListItem = await findElement(driver, By.css('.transaction-list-item'))
await txListItem.click()
await delay(regularDelayMs)
@@ -602,18 +600,20 @@ describe('MetaMask', function () {
await confirmButton.click()
await delay(regularDelayMs)
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 5
+ }, 10000)
- const txValues = await findElement(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues, /0\sETH/), 10000)
+ const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues, /-0\sETH/), 10000)
await closeAllWindowHandlesExcept(driver, [extension, dapp])
await driver.switchTo().window(extension)
})
it('renders the correct ETH balance', async () => {
- const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
+ const balance = await findElement(driver, By.css('.transaction-view-balance__primary-balance'))
await delay(regularDelayMs)
if (process.env.SELENIUM_BROWSER !== 'firefox') {
await driver.wait(until.elementTextMatches(balance, /^92.*ETH.*$/), 10000)
@@ -626,20 +626,21 @@ describe('MetaMask', function () {
describe('Add a custom token from a dapp', () => {
it('creates a new token', async () => {
- const windowHandles = await driver.getAllWindowHandles()
+ let windowHandles = await driver.getAllWindowHandles()
const extension = windowHandles[0]
const dapp = windowHandles[1]
await delay(regularDelayMs * 2)
await driver.switchTo().window(dapp)
- await delay(regularDelayMs)
+ await delay(regularDelayMs * 2)
const createToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Create Token')]`))
await createToken.click()
- await delay(regularDelayMs)
+ await delay(largeDelayMs)
- await driver.switchTo().window(extension)
- await loadExtension(driver, extensionId)
+ windowHandles = await driver.getAllWindowHandles()
+ const popup = windowHandles[2]
+ await driver.switchTo().window(popup)
await delay(regularDelayMs)
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
@@ -657,18 +658,17 @@ describe('MetaMask', function () {
await closeAllWindowHandlesExcept(driver, [extension, dapp])
await delay(regularDelayMs)
await driver.switchTo().window(extension)
- await delay(regularDelayMs)
-
+ await delay(largeDelayMs)
})
it('clicks on the Add Token button', async () => {
- const addToken = await findElement(driver, By.xpath(`//button[contains(text(), 'Add Token')]`))
+ const addToken = await driver.findElement(By.css('.wallet-view__add-token-button'))
await addToken.click()
await delay(regularDelayMs)
})
it('picks the newly created Test token', async () => {
- const addCustomToken = await findElement(driver, By.xpath("//div[contains(text(), 'Custom Token')]"))
+ const addCustomToken = await findElement(driver, By.xpath("//li[contains(text(), 'Custom Token')]"))
await addCustomToken.click()
await delay(regularDelayMs)
@@ -686,7 +686,7 @@ describe('MetaMask', function () {
})
it('renders the balance for the new token', async () => {
- const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
+ const balance = await findElement(driver, By.css('.transaction-view-balance .transaction-view-balance__token-balance'))
await driver.wait(until.elementTextMatches(balance, /^100\s*TST\s*$/))
const tokenAmount = await balance.getText()
assert.ok(/^100\s*TST\s*$/.test(tokenAmount))
@@ -755,21 +755,25 @@ describe('MetaMask', function () {
})
it('finds the transaction in the transactions list', async function () {
- const transactions = await findElements(driver, By.css('.tx-list-item'))
+ const transactions = await findElements(driver, By.css('.transaction-list-item'))
assert.equal(transactions.length, 1)
- const txValues = await findElements(driver, By.css('.tx-list-value'))
+ const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
assert.equal(txValues.length, 1)
// test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved,
// or possibly until we use latest version of firefox in the tests
if (process.env.SELENIUM_BROWSER !== 'firefox') {
- await driver.wait(until.elementTextMatches(txValues[0], /50\sTST/), 10000)
+ await driver.wait(until.elementTextMatches(txValues[0], /-50\sTST/), 10000)
}
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- const tx = await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed|Failed/), 10000)
- assert.equal(await tx.getText(), 'Confirmed')
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 1
+ }, 10000)
+ const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
+ const tx = await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken|Failed/), 10000)
+ assert.equal(await tx.getText(), 'Sent Tokens')
})
})
@@ -792,9 +796,9 @@ describe('MetaMask', function () {
await driver.switchTo().window(extension)
await delay(largeDelayMs)
- await findElements(driver, By.css('.tx-list-pending-item-container'))
- const [txListValue] = await findElements(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txListValue, /7\sTST/), 10000)
+ await findElements(driver, By.css('.transaction-list__pending-transactions'))
+ const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txListValue, /-7\sTST/), 10000)
await txListValue.click()
await delay(regularDelayMs)
@@ -841,25 +845,28 @@ describe('MetaMask', function () {
})
it('finds the transaction in the transactions list', async function () {
- const transactions = await findElements(driver, By.css('.tx-list-item'))
- assert.equal(transactions.length, 2)
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 2
+ }, 10000)
- const txValues = await findElements(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues[0], /7\sTST/))
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
+ const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues[0], /-7\sTST/))
+ const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
+ await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/))
const walletBalance = await findElement(driver, By.css('.wallet-balance'))
await walletBalance.click()
const tokenListItems = await findElements(driver, By.css('.token-list-item'))
await tokenListItems[0].click()
+ await delay(regularDelayMs)
// test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved,
// or possibly until we use latest version of firefox in the tests
if (process.env.SELENIUM_BROWSER !== 'firefox') {
- const tokenBalanceAmount = await findElement(driver, By.css('.token-balance__amount'))
- assert.equal(await tokenBalanceAmount.getText(), '43')
+ const tokenBalanceAmount = await findElement(driver, By.css('.transaction-view-balance__token-balance'))
+ assert.equal(await tokenBalanceAmount.getText(), '43 TST')
}
})
})
@@ -883,9 +890,14 @@ describe('MetaMask', function () {
await driver.switchTo().window(extension)
await delay(regularDelayMs)
- const [txListItem] = await findElements(driver, By.css('.tx-list-item'))
- const [txListValue] = await findElements(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txListValue, /0\sETH/))
+ driver.wait(async () => {
+ const pendingTxes = await findElements(driver, By.css('.transaction-list__pending-transactions .transaction-list-item'))
+ return pendingTxes.length === 1
+ }, 10000)
+
+ const [txListItem] = await findElements(driver, By.css('.transaction-list-item'))
+ const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txListValue, /-7\sTST/))
await txListItem.click()
await delay(regularDelayMs)
})
@@ -956,10 +968,15 @@ describe('MetaMask', function () {
})
it('finds the transaction in the transactions list', async function () {
- const txValues = await findElements(driver, By.css('.tx-list-value'))
- await driver.wait(until.elementTextMatches(txValues[0], /0\sETH/))
- const txStatuses = await findElements(driver, By.css('.tx-list-status'))
- await driver.wait(until.elementTextMatches(txStatuses[0], /Confirmed/))
+ driver.wait(async () => {
+ const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
+ return confirmedTxes.length === 3
+ }, 10000)
+
+ const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
+ await driver.wait(until.elementTextMatches(txValues[0], /-7\sTST/))
+ const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
+ await driver.wait(until.elementTextMatches(txStatuses[0], /Approve/))
})
})
@@ -1009,9 +1026,59 @@ describe('MetaMask', function () {
})
it('renders the balance for the chosen token', async () => {
- const balance = await findElement(driver, By.css('.tx-view .balance-display .token-amount'))
+ const balance = await findElement(driver, By.css('.transaction-view-balance__token-balance'))
await driver.wait(until.elementTextMatches(balance, /0\sBAT/))
await delay(regularDelayMs)
})
})
+
+ describe('Stores custom RPC history', () => {
+ const customRpcUrls = [
+ 'https://mainnet.infura.io/1',
+ 'https://mainnet.infura.io/2',
+ 'https://mainnet.infura.io/3',
+ 'https://mainnet.infura.io/4',
+ ]
+
+ customRpcUrls.forEach(customRpcUrl => {
+ it(`creates custom RPC: ${customRpcUrl}`, async () => {
+ const networkDropdown = await findElement(driver, By.css('.network-name'))
+ await networkDropdown.click()
+ await delay(regularDelayMs)
+
+ const customRpcButton = await findElement(driver, By.xpath(`//span[contains(text(), 'Custom RPC')]`))
+ await customRpcButton.click()
+ await delay(regularDelayMs)
+
+ const customRpcInput = await findElement(driver, By.css('input[placeholder="New RPC URL"]'))
+ await customRpcInput.clear()
+ await customRpcInput.sendKeys(customRpcUrl)
+
+ const customRpcSave = await findElement(driver, By.css('.settings-tab__rpc-save-button'))
+ await customRpcSave.click()
+ await delay(largeDelayMs * 2)
+ })
+ })
+
+ it('selects another provider', async () => {
+ const networkDropdown = await findElement(driver, By.css('.network-name'))
+ await networkDropdown.click()
+ await delay(regularDelayMs)
+
+ const customRpcButton = await findElement(driver, By.xpath(`//span[contains(text(), 'Main Ethereum Network')]`))
+ await customRpcButton.click()
+ await delay(largeDelayMs * 2)
+ })
+
+ it('finds all recent RPCs in history', async () => {
+ const networkDropdown = await findElement(driver, By.css('.network-name'))
+ await networkDropdown.click()
+ await delay(regularDelayMs)
+
+ // only recent 3 are found and in correct order (most recent at the top)
+ const customRpcs = await findElements(driver, By.xpath(`//span[contains(text(), 'https://mainnet.infura.io/')]`))
+
+ assert.equal(customRpcs.length, customRpcUrls.length)
+ })
+ })
})
diff --git a/test/e2e/beta/run-all.sh b/test/e2e/beta/run-all.sh
index 7da61e504..c51f19fdf 100755
--- a/test/e2e/beta/run-all.sh
+++ b/test/e2e/beta/run-all.sh
@@ -6,5 +6,5 @@ set -o pipefail
export PATH="$PATH:./node_modules/.bin"
-shell-parallel -s 'npm run ganache:start' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
-shell-parallel -s 'npm run ganache:start -- -d' -x 'sleep 5 && static-server test/e2e/beta/contract-test/ --port 8080' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
+shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test --port 8080' -x 'sleep 5 && mocha test/e2e/beta/metamask-beta-ui.spec'
+shell-parallel -s 'npm run ganache:start -- -d -b 2' -x 'sleep 5 && mocha test/e2e/beta/from-import-beta-ui.spec'
diff --git a/test/e2e/beta/run-drizzle.sh b/test/e2e/beta/run-drizzle.sh
new file mode 100755
index 000000000..7bfffd7e6
--- /dev/null
+++ b/test/e2e/beta/run-drizzle.sh
@@ -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
diff --git a/test/e2e/func.js b/test/e2e/func.js
index 7b1730959..13dfb82f9 100644
--- a/test/e2e/func.js
+++ b/test/e2e/func.js
@@ -1,14 +1,19 @@
require('chromedriver')
require('geckodriver')
-const fs = require('fs')
+const fs = require('fs-extra')
const os = require('os')
const path = require('path')
+const pify = require('pify')
+const prependFile = pify(require('prepend-file'))
const webdriver = require('selenium-webdriver')
const Command = require('selenium-webdriver/lib/command').Command
const By = webdriver.By
module.exports = {
delay,
+ createModifiedTestBuild,
+ setupBrowserAndExtension,
+ verboseReportOnFailure,
buildChromeWebDriver,
buildFirefoxWebdriver,
installWebExt,
@@ -20,6 +25,37 @@ function delay (time) {
return new Promise(resolve => setTimeout(resolve, time))
}
+async function createModifiedTestBuild ({ browser, srcPath }) {
+ // copy build to test-builds directory
+ const extPath = path.resolve(`test-builds/${browser}`)
+ await fs.ensureDir(extPath)
+ await fs.copy(srcPath, extPath)
+ // inject METAMASK_TEST_CONFIG setting default test network
+ const config = { NetworkController: { provider: { type: 'localhost' } } }
+ await prependFile(`${extPath}/background.js`, `window.METAMASK_TEST_CONFIG=${JSON.stringify(config)};\n`)
+ return { extPath }
+}
+
+async function setupBrowserAndExtension ({ browser, extPath }) {
+ let driver, extensionId, extensionUri
+
+ if (browser === 'chrome') {
+ driver = buildChromeWebDriver(extPath)
+ extensionId = await getExtensionIdChrome(driver)
+ extensionUri = `chrome-extension://${extensionId}/home.html`
+ } else if (browser === 'firefox') {
+ driver = buildFirefoxWebdriver()
+ await installWebExt(driver, extPath)
+ await delay(700)
+ extensionId = await getExtensionIdFirefox(driver)
+ extensionUri = `moz-extension://${extensionId}/home.html`
+ } else {
+ throw new Error(`Unknown Browser "${browser}"`)
+ }
+
+ return { driver, extensionId, extensionUri }
+}
+
function buildChromeWebDriver (extPath) {
const tmpProfile = fs.mkdtempSync(path.join(os.tmpdir(), 'mm-chrome-profile'))
return new webdriver.Builder()
@@ -61,3 +97,13 @@ async function installWebExt (driver, extension) {
return await driver.schedule(cmd, 'installWebExt(' + extension + ')')
}
+
+async function verboseReportOnFailure ({ browser, driver, title }) {
+ const artifactDir = `./test-artifacts/${browser}/${title}`
+ const filepathBase = `${artifactDir}/test-failure`
+ await fs.ensureDir(artifactDir)
+ const screenshot = await driver.takeScreenshot()
+ await fs.writeFile(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
+ const htmlSource = await driver.getPageSource()
+ await fs.writeFile(`${filepathBase}-dom.html`, htmlSource)
+}
diff --git a/test/e2e/metamask.spec.js b/test/e2e/metamask.spec.js
index ac7600f09..13af6cb22 100644
--- a/test/e2e/metamask.spec.js
+++ b/test/e2e/metamask.spec.js
@@ -1,49 +1,41 @@
-const fs = require('fs')
-const mkdirp = require('mkdirp')
const path = require('path')
const assert = require('assert')
-const pify = require('pify')
-const webdriver = require('selenium-webdriver')
-const { By, Key, until } = webdriver
-const { delay, buildChromeWebDriver, buildFirefoxWebdriver, installWebExt, getExtensionIdChrome, getExtensionIdFirefox } = require('./func')
+const { By, Key, until } = require('selenium-webdriver')
+const { delay, createModifiedTestBuild, setupBrowserAndExtension, verboseReportOnFailure } = require('./func')
describe('Metamask popup page', function () {
- let driver, accountAddress, tokenAddress, extensionId
+ const browser = process.env.SELENIUM_BROWSER
+ let driver, accountAddress, tokenAddress, extensionUri
this.timeout(0)
before(async function () {
- if (process.env.SELENIUM_BROWSER === 'chrome') {
- const extPath = path.resolve('dist/chrome')
- driver = buildChromeWebDriver(extPath)
- extensionId = await getExtensionIdChrome(driver)
- await driver.get(`chrome-extension://${extensionId}/popup.html`)
-
- } else if (process.env.SELENIUM_BROWSER === '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`)
- }
+ const srcPath = path.resolve(`dist/${browser}`)
+ const { extPath } = await createModifiedTestBuild({ browser, srcPath })
+ const installResult = await setupBrowserAndExtension({ browser, extPath })
+ driver = installResult.driver
+ extensionUri = installResult.extensionUri
+
+ await driver.get(extensionUri)
+ await delay(300)
})
afterEach(async function () {
// logs command not supported in firefox
// https://github.com/SeleniumHQ/selenium/issues/2910
- if (process.env.SELENIUM_BROWSER === 'chrome') {
+ if (browser === 'chrome') {
// check for console errors
const errors = await checkBrowserForConsoleErrors()
if (errors.length) {
const errorReports = errors.map(err => err.message)
const errorMessage = `Errors found in browser console:\n${errorReports.join('\n')}`
- this.test.error(new Error(errorMessage))
+ console.error(new Error(errorMessage))
+
}
}
// gather extra data if test failed
if (this.currentTest.state === 'failed') {
- await verboseReportOnFailure(this.currentTest)
+ await verboseReportOnFailure({ browser, driver, title: this.currentTest.title })
}
})
@@ -54,7 +46,6 @@ describe('Metamask popup page', function () {
describe('Setup', function () {
it('switches to Chrome extensions list', async function () {
- await delay(300)
const windowHandles = await driver.getAllWindowHandles()
await driver.switchTo().window(windowHandles[0])
})
@@ -98,6 +89,7 @@ describe('Metamask popup page', function () {
it('allows the button to be clicked when scrolled to the bottom of TOU', async () => {
const button = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div.flex-column.flex-center.flex-grow > button'))
await button.click()
+ await delay(300)
})
it('shows privacy notice', async () => {
@@ -108,7 +100,6 @@ describe('Metamask popup page', function () {
})
it('shows phishing notice', async () => {
- await delay(300)
const noticeHeader = await driver.findElement(By.css('.terms-header')).getText()
assert.equal(noticeHeader, 'PHISHING WARNING', 'shows phishing warning')
const element = await driver.findElement(By.css('.markdown'))
@@ -210,17 +201,17 @@ describe('Metamask popup page', function () {
})
it('balance renders', async function () {
- await delay(200)
+ await delay(500)
const balance = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > div.ether-balance.ether-balance-amount > div > div > div:nth-child(1) > div:nth-child(1)'))
assert.equal(await balance.getText(), '100.000')
await delay(200)
})
it('sends transaction', async function () {
- const sendButton = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(4)'))
- assert.equal(await sendButton.getText(), 'SEND')
- await sendButton.click()
- await delay(200)
+ const sendButton = await driver.findElement(By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(4)'))
+ assert.equal(await sendButton.getText(), 'SEND')
+ await sendButton.click()
+ await delay(200)
})
it('adds recipient address and amount', async function () {
@@ -295,11 +286,7 @@ describe('Metamask popup page', function () {
})
it('navigates back to MetaMask popup in the tab', async function () {
- if (process.env.SELENIUM_BROWSER === 'chrome') {
- await driver.get(`chrome-extension://${extensionId}/popup.html`)
- } else if (process.env.SELENIUM_BROWSER === 'firefox') {
- await driver.get(`moz-extension://${extensionId}/popup.html`)
- }
+ await driver.get(extensionUri)
await delay(700)
})
})
@@ -362,21 +349,4 @@ describe('Metamask popup page', function () {
return matchedErrorObjects
}
- async function verboseReportOnFailure (test) {
- let artifactDir
- if (process.env.SELENIUM_BROWSER === 'chrome') {
- artifactDir = `./test-artifacts/chrome/${test.title}`
- } else if (process.env.SELENIUM_BROWSER === 'firefox') {
- artifactDir = `./test-artifacts/firefox/${test.title}`
- }
- const filepathBase = `${artifactDir}/test-failure`
- await pify(mkdirp)(artifactDir)
- // capture screenshot
- const screenshot = await driver.takeScreenshot()
- await pify(fs.writeFile)(`${filepathBase}-screenshot.png`, screenshot, { encoding: 'base64' })
- // capture dom source
- const htmlSource = await driver.getPageSource()
- await pify(fs.writeFile)(`${filepathBase}-dom.html`, htmlSource)
- }
-
})
diff --git a/test/helper.js b/test/helper.js
index a3abbebf2..51f28de17 100644
--- a/test/helper.js
+++ b/test/helper.js
@@ -1,10 +1,21 @@
+const Ganache = require('ganache-core')
+const nock = require('nock')
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-15'
+nock.disableNetConnect()
+nock.enableNetConnect('localhost')
+
Enzyme.configure({ adapter: new Adapter() })
// disallow promises from swallowing errors
enableFailureOnUnhandledPromiseRejection()
+// ganache server
+const server = Ganache.server()
+server.listen(8545, () => {
+ console.log('Ganache Testrpc is running on "http://localhost:8545"')
+})
+
// logging util
var log = require('loglevel')
log.setDefaultLevel(5)
@@ -14,6 +25,9 @@ global.log = log
// polyfills
//
+// fetch
+global.fetch = require('isomorphic-fetch')
+
// dom
require('jsdom-global')()
diff --git a/test/integration/lib/add-token.js b/test/integration/lib/add-token.js
index 6de7574c4..bb9d0d10f 100644
--- a/test/integration/lib/add-token.js
+++ b/test/integration/lib/add-token.js
@@ -86,7 +86,7 @@ async function runAddTokenFlowTest (assert, done) {
$('button.btn-primary.btn--large')[0].click()
// Verify added token image
- let heroBalance = await queryAsync($, '.hero-balance')
+ let heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
assert.ok(heroBalance, 'rendered hero balance')
assert.ok(tokenImageUrl.indexOf(heroBalance.find('img').attr('src')) > -1, 'token added')
@@ -134,7 +134,7 @@ async function runAddTokenFlowTest (assert, done) {
// $('button.btn-primary--lg')[0].click()
// Verify added token image
- heroBalance = await queryAsync($, '.hero-balance')
+ heroBalance = await queryAsync($, '.transaction-view-balance__balance-container')
assert.ok(heroBalance, 'rendered hero balance')
assert.ok(heroBalance.find('.identicon')[0], 'token added')
}
diff --git a/test/integration/lib/confirm-sig-requests.js b/test/integration/lib/confirm-sig-requests.js
index dcc25c493..9c2ad7cf4 100644
--- a/test/integration/lib/confirm-sig-requests.js
+++ b/test/integration/lib/confirm-sig-requests.js
@@ -19,7 +19,7 @@ async function runConfirmSigRequestsTest (assert, done) {
selectState.val('confirm sig requests')
reactTriggerChange(selectState[0])
- const pendingRequestItem = $.find('.tx-list-item.tx-list-pending-item-container.tx-list-clickable')
+ const pendingRequestItem = $.find('.transaction-list-item .transaction-list-item__grid')
if (pendingRequestItem[0]) {
pendingRequestItem[0].click()
diff --git a/test/integration/lib/currency-localization.js b/test/integration/lib/currency-localization.js
index d42b7495d..8d5acf5d0 100644
--- a/test/integration/lib/currency-localization.js
+++ b/test/integration/lib/currency-localization.js
@@ -22,8 +22,8 @@ async function runCurrencyLocalizationTest (assert, done) {
await timeout(1000)
reactTriggerChange(selectState[0])
await timeout(1000)
- const txView = await queryAsync($, '.tx-view')
- const heroBalance = await findAsync($(txView), '.hero-balance')
- const fiatAmount = await findAsync($(heroBalance), '.fiat-amount')
- assert.equal(fiatAmount[0].textContent, '₱102,707.97')
+ const txView = await queryAsync($, '.transaction-view')
+ const heroBalance = await findAsync($(txView), '.transaction-view-balance__balance')
+ const fiatAmount = await findAsync($(heroBalance), '.transaction-view-balance__secondary-balance')
+ assert.equal(fiatAmount[0].textContent, '₱102,707.97 PHP')
}
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 406863ca6..ac1cc2e14 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -58,7 +58,7 @@ async function runSendFlowTest (assert, done) {
selectState.val('send new ui')
reactTriggerChange(selectState[0])
- const sendScreenButton = await queryAsync($, 'button.btn-primary.hero-balance-button')
+ const sendScreenButton = await queryAsync($, 'button.btn-primary.transaction-view-balance__button')
assert.ok(sendScreenButton[1], 'send screen button present')
sendScreenButton[1].click()
@@ -94,7 +94,7 @@ async function runSendFlowTest (assert, done) {
sendToDropdownList.children()[2].click()
const sendToAccountAddress = sendToFieldInput.val()
- assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address')
+ assert.equal(sendToAccountAddress, '0x2f8D4a878cFA04A6E60D46362f5644DeAb66572D', 'send to dropdown selects the correct address')
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountField.find('.currency-display')[0].click()
@@ -124,10 +124,10 @@ async function runSendFlowTest (assert, done) {
selectState.val('send edit')
reactTriggerChange(selectState[0])
- const confirmFromName = (await queryAsync($, '.sender-to-recipient__sender-name')).first()
+ const confirmFromName = (await queryAsync($, '.sender-to-recipient__name')).first()
assert.equal(confirmFromName[0].textContent, 'Send Account 4', 'confirm screen should show correct from name')
- const confirmToName = (await queryAsync($, '.sender-to-recipient__recipient-name')).last()
+ const confirmToName = (await queryAsync($, '.sender-to-recipient__name')).last()
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
const confirmScreenRowFiats = await queryAsync($, '.confirm-detail-row__fiat')
diff --git a/test/integration/lib/tx-list-items.js b/test/integration/lib/tx-list-items.js
index 9075efe03..ed4f82074 100644
--- a/test/integration/lib/tx-list-items.js
+++ b/test/integration/lib/tx-list-items.js
@@ -29,26 +29,25 @@ async function runTxListItemsTest (assert, done) {
assert.ok(metamaskLogo[0], 'metamask logo present')
metamaskLogo[0].click()
- const txListItems = await queryAsync($, '.tx-list-item')
+ const txListItems = await queryAsync($, '.transaction-list-item')
assert.equal(txListItems.length, 8, 'all tx list items are rendered')
- const unapprovedTx = txListItems[0]
- assert.equal($(unapprovedTx).hasClass('tx-list-pending-item-container'), true, 'unapprovedTx has the correct class')
-
- const retryTx = txListItems[1]
- const retryTxLink = await findAsync($(retryTx), '.tx-list-item-retry-container span')
- assert.equal(retryTxLink[0].textContent, 'Taking too long? Increase the gas price on your transaction', 'retryTx has expected link')
+ const retryTxGrid = await findAsync($(txListItems[2]), '.transaction-list-item__grid')
+ retryTxGrid[0].click()
+ const retryTxDetails = await findAsync($, '.transaction-list-item-details')
+ const headerButtons = await findAsync($(retryTxDetails[0]), '.transaction-list-item-details__header-button')
+ assert.equal(headerButtons[0].textContent, 'speed up')
const approvedTx = txListItems[2]
- const approvedTxRenderedStatus = await findAsync($(approvedTx), '.tx-list-status')
- assert.equal(approvedTxRenderedStatus[0].textContent, 'Approved', 'approvedTx has correct label')
+ const approvedTxRenderedStatus = await findAsync($(approvedTx), '.transaction-list-item__status')
+ assert.equal(approvedTxRenderedStatus[0].textContent, 'pending', 'approvedTx has correct label')
- const unapprovedMsg = txListItems[3]
- const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.tx-list-account')
+ const unapprovedMsg = txListItems[0]
+ const unapprovedMsgDescription = await findAsync($(unapprovedMsg), '.transaction-list-item__action')
assert.equal(unapprovedMsgDescription[0].textContent, 'Signature Request', 'unapprovedMsg has correct description')
const failedTx = txListItems[4]
- const failedTxRenderedStatus = await findAsync($(failedTx), '.tx-list-status')
+ const failedTxRenderedStatus = await findAsync($(failedTx), '.transaction-list-item__status')
assert.equal(failedTxRenderedStatus[0].textContent, 'Failed', 'failedTx has correct label')
const shapeShiftTx = txListItems[5]
@@ -56,10 +55,10 @@ async function runTxListItemsTest (assert, done) {
assert.equal(shapeShiftTxStatus[0].textContent, 'No deposits received', 'shapeShiftTx has correct status')
const confirmedTokenTx = txListItems[6]
- const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.tx-list-account')
- assert.equal(confirmedTokenTxAddress[0].textContent, '0xE7884118...81a9', 'confirmedTokenTx has correct address')
+ const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.transaction-list-item__status')
+ assert.equal(confirmedTokenTxAddress[0].textContent, 'Confirmed', 'confirmedTokenTx has correct address')
const rejectedTx = txListItems[7]
- const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status')
+ const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.transaction-list-item__status')
assert.equal(rejectedTxRenderedStatus[0].textContent, 'Rejected', 'rejectedTx has correct label')
}
diff --git a/test/lib/createTxMeta.js b/test/lib/createTxMeta.js
new file mode 100644
index 000000000..0e88e3cfb
--- /dev/null
+++ b/test/lib/createTxMeta.js
@@ -0,0 +1,16 @@
+const txStateHistoryHelper = require('../../app/scripts/controllers/transactions/lib/tx-state-history-helper')
+
+module.exports = createTxMeta
+
+function createTxMeta (partialMeta) {
+ const txMeta = Object.assign({
+ status: 'unapproved',
+ txParams: {},
+ }, partialMeta)
+ // initialize history
+ txMeta.history = []
+ // capture initial snapshot of txMeta for history
+ const snapshot = txStateHistoryHelper.snapshotFromTxMeta(txMeta)
+ txMeta.history.push(snapshot)
+ return txMeta
+}
diff --git a/test/lib/mock-config-manager.js b/test/lib/mock-config-manager.js
deleted file mode 100644
index 0cc6953bb..000000000
--- a/test/lib/mock-config-manager.js
+++ /dev/null
@@ -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 })
-}
diff --git a/test/lib/mock-encryptor.js b/test/lib/mock-encryptor.js
index 48aa9e52c..852c536c2 100644
--- a/test/lib/mock-encryptor.js
+++ b/test/lib/mock-encryptor.js
@@ -1,5 +1,5 @@
var mockHex = '0xabcdef0123456789'
-var mockKey = new Buffer(32)
+var mockKey = Buffer.alloc(32)
let cacheVal
module.exports = {
diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js
new file mode 100644
index 000000000..81f0e27aa
--- /dev/null
+++ b/test/lib/render-helpers.js
@@ -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())
+}
diff --git a/test/lib/shallow-with-store.js b/test/lib/shallow-with-store.js
deleted file mode 100644
index 9df10a3c5..000000000
--- a/test/lib/shallow-with-store.js
+++ /dev/null
@@ -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})
-}
diff --git a/test/unit/app/cleanErrorStack.spec.js b/test/unit/app/cleanErrorStack.spec.js
new file mode 100644
index 000000000..7a1ab1ed8
--- /dev/null
+++ b/test/unit/app/cleanErrorStack.spec.js
@@ -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')
+ })
+
+})
diff --git a/test/unit/app/controllers/blacklist-controller-test.js b/test/unit/app/controllers/blacklist-controller-test.js
index 085641777..7a14c02cc 100644
--- a/test/unit/app/controllers/blacklist-controller-test.js
+++ b/test/unit/app/controllers/blacklist-controller-test.js
@@ -8,6 +8,16 @@ describe('blacklist controller', function () {
blacklistController = new BlacklistController()
})
+ describe('whitelistDomain', function () {
+ it('should add hostname to the runtime whitelist', function () {
+ blacklistController.whitelistDomain('foo.com')
+ assert.deepEqual(blacklistController.store.getState().whitelist, ['foo.com'])
+
+ blacklistController.whitelistDomain('bar.com')
+ assert.deepEqual(blacklistController.store.getState().whitelist, ['bar.com', 'foo.com'])
+ })
+ })
+
describe('checkForPhishing', function () {
it('should not flag whitelisted values', function () {
const result = blacklistController.checkForPhishing('www.metamask.io')
@@ -37,5 +47,10 @@ describe('blacklist controller', function () {
const result = blacklistController.checkForPhishing('zero-faucet.metamask.io')
assert.equal(result, false)
})
+ it('should not flag whitelisted domain', function () {
+ blacklistController.whitelistDomain('metamask.com')
+ const result = blacklistController.checkForPhishing('metamask.com')
+ assert.equal(result, false)
+ })
})
})
diff --git a/test/unit/app/controllers/currency-controller-test.js b/test/unit/app/controllers/currency-controller-test.js
index 1941d1c43..7c4644d9f 100644
--- a/test/unit/app/controllers/currency-controller-test.js
+++ b/test/unit/app/controllers/currency-controller-test.js
@@ -1,6 +1,3 @@
-// polyfill fetch
-global.fetch = global.fetch || require('isomorphic-fetch')
-
const assert = require('assert')
const nock = require('nock')
const CurrencyController = require('../../../../app/scripts/controllers/currency')
diff --git a/test/unit/app/controllers/detect-tokens-test.js b/test/unit/app/controllers/detect-tokens-test.js
index d6c3fad8a..2acc53e92 100644
--- a/test/unit/app/controllers/detect-tokens-test.js
+++ b/test/unit/app/controllers/detect-tokens-test.js
@@ -1,4 +1,5 @@
const assert = require('assert')
+const nock = require('nock')
const sinon = require('sinon')
const ObservableStore = require('obs-store')
const DetectTokensController = require('../../../../app/scripts/controllers/detect-tokens')
@@ -6,15 +7,34 @@ const NetworkController = require('../../../../app/scripts/controllers/network/n
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
describe('DetectTokensController', () => {
- const sandbox = sinon.createSandbox()
- let clock, keyringMemStore, network, preferences
- beforeEach(async () => {
- keyringMemStore = new ObservableStore({ isUnlocked: false})
- network = new NetworkController({ provider: { type: 'mainnet' }})
- preferences = new PreferencesController({ network })
- })
- after(() => {
- sandbox.restore()
+ const sandbox = sinon.createSandbox()
+ let clock, keyringMemStore, network, preferences, controller
+
+ const noop = () => {}
+
+ const networkControllerProviderConfig = {
+ getAccounts: noop,
+ }
+
+ beforeEach(async () => {
+
+
+ nock('https://api.infura.io')
+ .get(/.*/)
+ .reply(200)
+
+ keyringMemStore = new ObservableStore({ isUnlocked: false})
+ network = new NetworkController()
+ preferences = new PreferencesController({ network })
+ controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
+
+ network.initializeProvider(networkControllerProviderConfig)
+
+ })
+
+ after(() => {
+ sandbox.restore()
+ nock.cleanAll()
})
it('should poll on correct interval', async () => {
@@ -26,7 +46,10 @@ describe('DetectTokensController', () => {
it('should be called on every polling period', async () => {
clock = sandbox.useFakeTimers()
+ const network = new NetworkController()
+ network.initializeProvider(networkControllerProviderConfig)
network.setProviderType('mainnet')
+ const preferences = new PreferencesController({ network })
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -44,8 +67,6 @@ describe('DetectTokensController', () => {
})
it('should not check tokens while in test network', async () => {
- network.setProviderType('rinkeby')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -58,7 +79,6 @@ describe('DetectTokensController', () => {
})
it('should only check and add tokens while in main network', async () => {
- network.setProviderType('mainnet')
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -75,7 +95,6 @@ describe('DetectTokensController', () => {
})
it('should not detect same token while in main network', async () => {
- network.setProviderType('mainnet')
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
@@ -93,8 +112,6 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when change address', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
var stub = sandbox.stub(controller, 'detectNewTokens')
@@ -103,8 +120,6 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when submit password', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.selectedAddress = '0x0'
var stub = sandbox.stub(controller, 'detectNewTokens')
@@ -113,8 +128,6 @@ describe('DetectTokensController', () => {
})
it('should not trigger detect new tokens when not open or not unlocked', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = false
var stub = sandbox.stub(controller, 'detectTokenBalance')
diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js
index 9f25cf376..17be2c028 100644
--- a/test/unit/app/controllers/metamask-controller-test.js
+++ b/test/unit/app/controllers/metamask-controller-test.js
@@ -3,16 +3,22 @@ const sinon = require('sinon')
const clone = require('clone')
const nock = require('nock')
const createThoughStream = require('through2').obj
-const MetaMaskController = require('../../../../app/scripts/metamask-controller')
const blacklistJSON = require('eth-phishing-detect/src/config')
-const firstTimeState = require('../../../../app/scripts/first-time-state')
+const MetaMaskController = require('../../../../app/scripts/metamask-controller')
+const firstTimeState = require('../../../unit/localhostState')
+const createTxMeta = require('../../../lib/createTxMeta')
+const EthQuery = require('eth-query')
const currentNetworkId = 42
const DEFAULT_LABEL = 'Account 1'
+const DEFAULT_LABEL_2 = 'Account 2'
const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
const TEST_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'
+const TEST_ADDRESS_2 = '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b'
+const TEST_ADDRESS_3 = '0xeb9e64b93097bc15f01f13eae97015c57ab64823'
const TEST_SEED_ALT = 'setup olympic issue mobile velvet surge alcohol burger horse view reopen gentle'
const TEST_ADDRESS_ALT = '0xc42edfcc21ed14dda456aa0756c153f7985d8813'
+const CUSTOM_RPC_URL = 'http://localhost:8545'
describe('MetaMaskController', function () {
let metamaskController
@@ -110,7 +116,7 @@ describe('MetaMaskController', function () {
}
const gasPrice = metamaskController.getGasPrice()
- assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price')
+ assert.equal(gasPrice, '0x174876e800', 'accurately estimates 65th percentile accepted gas price')
metamaskController.recentBlocksController = realRecentBlocksController
})
@@ -134,6 +140,9 @@ describe('MetaMaskController', function () {
describe('#createNewVaultAndRestore', function () {
it('should be able to call newVaultAndRestore despite a mistake.', async function () {
const password = 'what-what-what'
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
+
await metamaskController.createNewVaultAndRestore(password, TEST_SEED.slice(0, -1)).catch((e) => null)
await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
@@ -141,6 +150,9 @@ describe('MetaMaskController', function () {
})
it('should clear previous identities after vault restoration', async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
+
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED)
assert.deepEqual(metamaskController.getState().identities, {
[TEST_ADDRESS]: { address: TEST_ADDRESS, name: DEFAULT_LABEL },
@@ -156,6 +168,54 @@ describe('MetaMaskController', function () {
[TEST_ADDRESS_ALT]: { address: TEST_ADDRESS_ALT, name: DEFAULT_LABEL },
})
})
+
+ it('should restore any consecutive accounts with balances', async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.withArgs(TEST_ADDRESS).callsFake(() => {
+ return Promise.resolve('0x14ced5122ce0a000')
+ })
+ metamaskController.getBalance.withArgs(TEST_ADDRESS_2).callsFake(() => {
+ return Promise.resolve('0x0')
+ })
+ metamaskController.getBalance.withArgs(TEST_ADDRESS_3).callsFake(() => {
+ return Promise.resolve('0x14ced5122ce0a000')
+ })
+
+ await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED)
+ assert.deepEqual(metamaskController.getState().identities, {
+ [TEST_ADDRESS]: { address: TEST_ADDRESS, name: DEFAULT_LABEL },
+ [TEST_ADDRESS_2]: { address: TEST_ADDRESS_2, name: DEFAULT_LABEL_2 },
+ })
+ })
+ })
+
+ describe('#getBalance', () => {
+ it('should return the balance known by accountTracker', async () => {
+ const accounts = {}
+ const balance = '0x14ced5122ce0a000'
+ accounts[TEST_ADDRESS] = { balance: balance }
+
+ metamaskController.accountTracker.store.putState({ accounts: accounts })
+
+ const gotten = await metamaskController.getBalance(TEST_ADDRESS)
+
+ assert.equal(balance, gotten)
+ })
+
+ it('should ask the network for a balance when not known by accountTracker', async () => {
+ const accounts = {}
+ const balance = '0x14ced5122ce0a000'
+ const ethQuery = new EthQuery()
+ sinon.stub(ethQuery, 'getBalance').callsFake((account, callback) => {
+ callback(undefined, balance)
+ })
+
+ metamaskController.accountTracker.store.putState({ accounts: accounts })
+
+ const gotten = await metamaskController.getBalance(TEST_ADDRESS, ethQuery)
+
+ assert.equal(balance, gotten)
+ })
})
describe('#getApi', function () {
@@ -360,29 +420,19 @@ describe('MetaMaskController', function () {
})
describe('#setCustomRpc', function () {
- const customRPC = 'https://custom.rpc/'
let rpcTarget
beforeEach(function () {
-
- nock('https://custom.rpc')
- .post('/')
- .reply(200)
-
- rpcTarget = metamaskController.setCustomRpc(customRPC)
- })
-
- afterEach(function () {
- nock.cleanAll()
+ rpcTarget = metamaskController.setCustomRpc(CUSTOM_RPC_URL)
})
it('returns custom RPC that when called', async function () {
- assert.equal(await rpcTarget, customRPC)
+ assert.equal(await rpcTarget, CUSTOM_RPC_URL)
})
it('changes the network controller rpc', function () {
const networkControllerState = metamaskController.networkController.store.getState()
- assert.equal(networkControllerState.provider.rpcTarget, customRPC)
+ assert.equal(networkControllerState.provider.rpcTarget, CUSTOM_RPC_URL)
})
})
@@ -487,9 +537,10 @@ describe('MetaMaskController', function () {
getNetworkstub.returns(42)
metamaskController.txController.txStateManager._saveTxList([
- { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} },
- { id: 2, status: 'rejected', metamaskNetworkId: 32, txParams: {} },
- { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4'} },
+ createTxMeta({ id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} }),
+ createTxMeta({ id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} }),
+ createTxMeta({ id: 2, status: 'rejected', metamaskNetworkId: 32 }),
+ createTxMeta({ id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4'} }),
])
})
@@ -522,7 +573,7 @@ describe('MetaMaskController', function () {
assert(metamaskController.preferencesController.removeAddress.calledWith(addressToRemove))
})
it('should call accountTracker.removeAccount', async function () {
- assert(metamaskController.accountTracker.removeAccount.calledWith(addressToRemove))
+ assert(metamaskController.accountTracker.removeAccount.calledWith([addressToRemove]))
})
it('should call keyringController.removeAccount', async function () {
assert(metamaskController.keyringController.removeAccount.calledWith(addressToRemove))
@@ -533,22 +584,18 @@ describe('MetaMaskController', function () {
})
describe('#clearSeedWordCache', function () {
+ it('should set seed words to null', function (done) {
+ sandbox.stub(metamaskController.preferencesController, 'setSeedWords')
+ metamaskController.clearSeedWordCache((err) => {
+ if (err) {
+ done(err)
+ }
- it('should have set seed words', function () {
- metamaskController.configManager.setSeedWords('test words')
- const getConfigSeed = metamaskController.configManager.getSeedWords()
- assert.equal(getConfigSeed, 'test words')
- })
-
- it('should clear config seed phrase', function () {
- metamaskController.configManager.setSeedWords('test words')
- metamaskController.clearSeedWordCache((err, result) => {
- if (err) console.log(err)
+ assert.ok(metamaskController.preferencesController.setSeedWords.calledOnce)
+ assert.deepEqual(metamaskController.preferencesController.setSeedWords.args, [[null]])
+ done()
})
- const getConfigSeed = metamaskController.configManager.getSeedWords()
- assert.equal(getConfigSeed, null)
})
-
})
describe('#setCurrentLocale', function () {
@@ -566,14 +613,16 @@ describe('MetaMaskController', function () {
})
- describe('#newUnsignedMessage', function () {
+ describe('#newUnsignedMessage', () => {
let msgParams, metamaskMsgs, messages, msgId
const address = '0xc42edfcc21ed14dda456aa0756c153f7985d8813'
const data = '0x43727970746f6b697474696573'
- beforeEach(async function () {
+ beforeEach(async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED_ALT)
@@ -582,7 +631,10 @@ describe('MetaMaskController', function () {
'data': data,
}
- metamaskController.newUnsignedMessage(msgParams, noop)
+ const promise = metamaskController.newUnsignedMessage(msgParams)
+ // handle the promise so it doesn't throw an unhandledRejection
+ promise.then(noop).catch(noop)
+
metamaskMsgs = metamaskController.messageManager.getUnapprovedMsgs()
messages = metamaskController.messageManager.messages
msgId = Object.keys(metamaskMsgs)[0]
@@ -622,13 +674,16 @@ describe('MetaMaskController', function () {
describe('#newUnsignedPersonalMessage', function () {
- it('errors with no from in msgParams', function () {
+ it('errors with no from in msgParams', async () => {
const msgParams = {
'data': data,
}
- metamaskController.newUnsignedPersonalMessage(msgParams, function (error) {
+ try {
+ await metamaskController.newUnsignedPersonalMessage(msgParams)
+ assert.fail('should have thrown')
+ } catch (error) {
assert.equal(error.message, 'MetaMask Message Signature: from field is required.')
- })
+ }
})
let msgParams, metamaskPersonalMsgs, personalMessages, msgId
@@ -637,6 +692,8 @@ describe('MetaMaskController', function () {
const data = '0x43727970746f6b697474696573'
beforeEach(async function () {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED_ALT)
@@ -645,7 +702,10 @@ describe('MetaMaskController', function () {
'data': data,
}
- metamaskController.newUnsignedPersonalMessage(msgParams, noop)
+ const promise = metamaskController.newUnsignedPersonalMessage(msgParams)
+ // handle the promise so it doesn't throw an unhandledRejection
+ promise.then(noop).catch(noop)
+
metamaskPersonalMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs()
personalMessages = metamaskController.personalMessageManager.messages
msgId = Object.keys(metamaskPersonalMsgs)[0]
@@ -684,22 +744,27 @@ describe('MetaMaskController', function () {
describe('#setupUntrustedCommunication', function () {
let streamTest
- const phishingUrl = 'decentral.market'
+ const phishingUrl = 'myethereumwalletntw.com'
afterEach(function () {
streamTest.end()
})
- it('sets up phishing stream for untrusted communication ', async function () {
+ it('sets up phishing stream for untrusted communication ', async () => {
await metamaskController.blacklistController.updatePhishingList()
+ console.log(blacklistJSON.blacklist.includes(phishingUrl))
+
+ const { promise, resolve } = deferredPromise()
streamTest = createThoughStream((chunk, enc, cb) => {
- assert.equal(chunk.name, 'phishing')
+ if (chunk.name !== 'phishing') return cb()
assert.equal(chunk.data.hostname, phishingUrl)
- cb()
- })
- // console.log(streamTest)
- metamaskController.setupUntrustedCommunication(streamTest, phishingUrl)
+ resolve()
+ cb()
+ })
+ metamaskController.setupUntrustedCommunication(streamTest, phishingUrl)
+
+ await promise
})
})
@@ -724,25 +789,102 @@ describe('MetaMaskController', function () {
describe('#markAccountsFound', function () {
it('adds lost accounts to config manager data', function () {
metamaskController.markAccountsFound(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.deepEqual(configManagerData.lostAccounts, [])
+ const state = metamaskController.getState()
+ assert.deepEqual(state.lostAccounts, [])
})
})
describe('#markPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to true', function () {
metamaskController.markPasswordForgotten(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.equal(configManagerData.forgottenPassword, true)
+ const state = metamaskController.getState()
+ assert.equal(state.forgottenPassword, true)
})
})
describe('#unMarkPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to false', function () {
metamaskController.unMarkPasswordForgotten(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.equal(configManagerData.forgottenPassword, false)
+ const state = metamaskController.getState()
+ assert.equal(state.forgottenPassword, false)
+ })
+ })
+
+ describe('#_onKeyringControllerUpdate', function () {
+ it('should do nothing if there are no keyrings in state', async function () {
+ const addAddresses = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({keyrings: []})
+
+ assert.ok(addAddresses.notCalled)
+ assert.ok(syncWithAddresses.notCalled)
+ assert.deepEqual(metamaskController.getState(), oldState)
+ })
+
+ it('should update selected address if keyrings was locked', async function () {
+ const addAddresses = sinon.fake()
+ const getSelectedAddress = sinon.fake.returns('0x42')
+ const setSelectedAddress = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ getSelectedAddress,
+ setSelectedAddress,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({
+ isUnlocked: false,
+ keyrings: [{
+ accounts: ['0x1', '0x2'],
+ }],
+ })
+
+ assert.deepEqual(addAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(syncWithAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(setSelectedAddress.args, [['0x1']])
+ assert.deepEqual(metamaskController.getState(), oldState)
+ })
+
+ it('should NOT update selected address if already unlocked', async function () {
+ const addAddresses = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({
+ isUnlocked: true,
+ keyrings: [{
+ accounts: ['0x1', '0x2'],
+ }],
+ })
+
+ assert.deepEqual(addAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(syncWithAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(metamaskController.getState(), oldState)
})
})
})
+
+function deferredPromise () {
+ let resolve
+ const promise = new Promise(_resolve => { resolve = _resolve })
+ return { promise, resolve }
+}
diff --git a/test/unit/app/controllers/network-contoller-test.js b/test/unit/app/controllers/network-contoller-test.js
index e16fb104e..822311931 100644
--- a/test/unit/app/controllers/network-contoller-test.js
+++ b/test/unit/app/controllers/network-contoller-test.js
@@ -32,9 +32,10 @@ describe('# Network Controller', function () {
describe('#provider', function () {
it('provider should be updatable without reassignment', function () {
networkController.initializeProvider(networkControllerProviderConfig)
- const proxy = networkController._proxy
- proxy.setTarget({ test: true, on: () => {} })
- assert.ok(proxy.test)
+ const providerProxy = networkController.getProviderAndBlockTracker().provider
+ assert.equal(providerProxy.test, undefined)
+ providerProxy.setTarget({ test: true })
+ assert.equal(providerProxy.test, true)
})
})
describe('#getNetworkState', function () {
diff --git a/test/unit/app/controllers/notice-controller-test.js b/test/unit/app/controllers/notice-controller-test.js
index b3ae75080..834c88f7b 100644
--- a/test/unit/app/controllers/notice-controller-test.js
+++ b/test/unit/app/controllers/notice-controller-test.js
@@ -1,16 +1,11 @@
const assert = require('assert')
-const configManagerGen = require('../../../lib/mock-config-manager')
const NoticeController = require('../../../../app/scripts/notice-controller')
describe('notice-controller', function () {
var noticeController
beforeEach(function () {
- // simple localStorage polyfill
- const configManager = configManagerGen()
- noticeController = new NoticeController({
- configManager: configManager,
- })
+ noticeController = new NoticeController()
})
describe('notices', function () {
diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js
index 9b2c846bd..b5ccf3fb5 100644
--- a/test/unit/app/controllers/preferences-controller-test.js
+++ b/test/unit/app/controllers/preferences-controller-test.js
@@ -1,6 +1,7 @@
const assert = require('assert')
const ObservableStore = require('obs-store')
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
+const sinon = require('sinon')
describe('preferences controller', function () {
let preferencesController
@@ -339,5 +340,144 @@ describe('preferences controller', function () {
assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same network')
})
})
+
+ describe('on watchAsset', function () {
+ var stubNext, stubEnd, stubHandleWatchAssetERC20, asy, req, res
+ const sandbox = sinon.createSandbox()
+
+ beforeEach(() => {
+ req = {params: {}}
+ res = {}
+ asy = {next: () => {}, end: () => {}}
+ stubNext = sandbox.stub(asy, 'next')
+ stubEnd = sandbox.stub(asy, 'end').returns(0)
+ stubHandleWatchAssetERC20 = sandbox.stub(preferencesController, '_handleWatchAssetERC20')
+ })
+ after(() => {
+ sandbox.restore()
+ })
+
+ it('shouldn not do anything if method not corresponds', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ var stubNext = sandbox.stub(asy, 'next')
+ var stubEnd = sandbox.stub(asy, 'end').returns(0)
+ req.method = 'metamask'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.notCalled(stubEnd)
+ sandbox.assert.called(stubNext)
+ })
+ it('should do something if method is supported', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ var stubNext = sandbox.stub(asy, 'next')
+ var stubEnd = sandbox.stub(asy, 'end').returns(0)
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'someasset'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubEnd)
+ sandbox.assert.notCalled(stubNext)
+ })
+ it('should through error if method is supported but asset type is not', async function () {
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'someasset'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubEnd)
+ sandbox.assert.notCalled(stubHandleWatchAssetERC20)
+ sandbox.assert.notCalled(stubNext)
+ assert.deepEqual(res, {})
+ })
+ it('should trigger handle add asset if type supported', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'ERC20'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubHandleWatchAssetERC20)
+ })
+ })
+
+ describe('on watchAsset of type ERC20', function () {
+ var req
+
+ const sandbox = sinon.createSandbox()
+ beforeEach(() => {
+ req = {params: {type: 'ERC20'}}
+ })
+ after(() => {
+ sandbox.restore()
+ })
+
+ it('should add suggested token', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+ const image = 'someimage'
+ req.params.options = { address, symbol, decimals, image }
+
+ sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
+ preferencesController.showWatchAssetUi = async () => {}
+
+ await preferencesController._handleWatchAssetERC20(req.params.options)
+ const suggested = preferencesController.getSuggestedTokens()
+ assert.equal(Object.keys(suggested).length, 1, `one token added ${Object.keys(suggested)}`)
+
+ assert.equal(suggested[address].address, address, 'set address correctly')
+ assert.equal(suggested[address].symbol, symbol, 'set symbol correctly')
+ assert.equal(suggested[address].decimals, decimals, 'set decimals correctly')
+ assert.equal(suggested[address].image, image, 'set image correctly')
+ })
+
+ it('should add token correctly if user confirms', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+ const image = 'someimage'
+ req.params.options = { address, symbol, decimals, image }
+
+ sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
+ preferencesController.showWatchAssetUi = async () => {
+ await preferencesController.addToken(address, symbol, decimals, image)
+ }
+
+ await preferencesController._handleWatchAssetERC20(req.params.options)
+ const tokens = preferencesController.getTokens()
+ assert.equal(tokens.length, 1, `one token added`)
+ const added = tokens[0]
+ assert.equal(added.address, address, 'set address correctly')
+ assert.equal(added.symbol, symbol, 'set symbol correctly')
+ assert.equal(added.decimals, decimals, 'set decimals correctly')
+
+ const assetImages = preferencesController.getAssetImages()
+ assert.ok(assetImages[address], `set image correctly`)
+ })
+ })
+
+ describe('setPasswordForgotten', function () {
+ it('should default to false', function () {
+ const state = preferencesController.store.getState()
+ assert.equal(state.forgottenPassword, false)
+ })
+
+ it('should set the forgottenPassword property in state', function () {
+ assert.equal(preferencesController.store.getState().forgottenPassword, false)
+
+ preferencesController.setPasswordForgotten(true)
+
+ assert.equal(preferencesController.store.getState().forgottenPassword, true)
+ })
+ })
+
+ describe('setSeedWords', function () {
+ it('should default to null', function () {
+ const state = preferencesController.store.getState()
+ assert.equal(state.seedWords, null)
+ })
+
+ it('should set the seedWords property in state', function () {
+ assert.equal(preferencesController.store.getState().seedWords, null)
+
+ preferencesController.setSeedWords('foo bar baz')
+
+ assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz')
+ })
+ })
})
diff --git a/test/unit/app/controllers/transactions/nonce-tracker-test.js b/test/unit/app/controllers/transactions/nonce-tracker-test.js
index 6c0ac759f..51ac390e9 100644
--- a/test/unit/app/controllers/transactions/nonce-tracker-test.js
+++ b/test/unit/app/controllers/transactions/nonce-tracker-test.js
@@ -224,14 +224,15 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
providerResultStub.result = providerStub
const provider = {
sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
- _blockTracker: {
- getCurrentBlock: () => '0x11b568',
- },
+ }
+ const blockTracker = {
+ getCurrentBlock: () => '0x11b568',
+ getLatestBlock: async () => '0x11b568',
}
return new NonceTracker({
provider,
+ blockTracker,
getPendingTransactions,
getConfirmedTransactions,
})
}
-
diff --git a/test/unit/app/controllers/transactions/pending-tx-test.js b/test/unit/app/controllers/transactions/pending-tx-test.js
index 8bf2da6f8..ba15f1953 100644
--- a/test/unit/app/controllers/transactions/pending-tx-test.js
+++ b/test/unit/app/controllers/transactions/pending-tx-test.js
@@ -9,6 +9,7 @@ describe('PendingTransactionTracker', function () {
let pendingTxTracker, txMeta, txMetaNoHash, providerResultStub,
provider, txMeta3, txList, knownErrors
this.timeout(10000)
+
beforeEach(function () {
txMeta = {
id: 1,
@@ -40,7 +41,10 @@ describe('PendingTransactionTracker', function () {
getPendingTransactions: () => { return [] },
getCompletedTransactions: () => { return [] },
publishTransaction: () => {},
+ confirmTransaction: () => {},
})
+
+ pendingTxTracker._getBlock = (blockNumber) => { return {number: blockNumber, transactions: []} }
})
describe('_checkPendingTx state management', function () {
@@ -92,58 +96,6 @@ describe('PendingTransactionTracker', function () {
})
})
- describe('#checkForTxInBlock', function () {
- it('should return if no pending transactions', function () {
- // throw a type error if it trys to do anything on the block
- // thus failing the test
- const block = Proxy.revocable({}, {}).revoke()
- pendingTxTracker.checkForTxInBlock(block)
- })
- it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
- const block = Proxy.revocable({}, {}).revoke()
- pendingTxTracker.getPendingTransactions = () => [txMetaNoHash]
- pendingTxTracker.once('tx:failed', (txId, err) => {
- assert(txId, txMetaNoHash.id, 'should pass txId')
- done()
- })
- pendingTxTracker.checkForTxInBlock(block)
- })
- it('should emit \'txConfirmed\' if the tx is in the block', function (done) {
- const block = { transactions: [txMeta]}
- pendingTxTracker.getPendingTransactions = () => [txMeta]
- pendingTxTracker.once('tx:confirmed', (txId) => {
- assert(txId, txMeta.id, 'should pass txId')
- done()
- })
- pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
- pendingTxTracker.checkForTxInBlock(block)
- })
- })
- describe('#queryPendingTxs', function () {
- it('should call #_checkPendingTxs if their is no oldBlock', function (done) {
- let oldBlock
- const newBlock = { number: '0x01' }
- pendingTxTracker._checkPendingTxs = done
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- })
- it('should call #_checkPendingTxs if oldBlock and the newBlock have a diff of greater then 1', function (done) {
- const oldBlock = { number: '0x01' }
- const newBlock = { number: '0x03' }
- pendingTxTracker._checkPendingTxs = done
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- })
- it('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less', function (done) {
- const oldBlock = { number: '0x1' }
- const newBlock = { number: '0x2' }
- pendingTxTracker._checkPendingTxs = () => {
- const err = new Error('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less')
- done(err)
- }
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- done()
- })
- })
-
describe('#_checkPendingTx', function () {
it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
pendingTxTracker.once('tx:failed', (txId, err) => {
@@ -157,16 +109,6 @@ describe('PendingTransactionTracker', function () {
providerResultStub.eth_getTransactionByHash = null
pendingTxTracker._checkPendingTx(txMeta)
})
-
- it('should emit \'txConfirmed\'', function (done) {
- providerResultStub.eth_getTransactionByHash = {blockNumber: '0x01'}
- pendingTxTracker.once('tx:confirmed', (txId) => {
- assert(txId, txMeta.id, 'should pass txId')
- done()
- })
- pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
- pendingTxTracker._checkPendingTx(txMeta)
- })
})
describe('#_checkPendingTxs', function () {
@@ -180,19 +122,19 @@ describe('PendingTransactionTracker', function () {
})
})
- it('should warp all txMeta\'s in #_checkPendingTx', function (done) {
+ it('should warp all txMeta\'s in #updatePendingTxs', function (done) {
pendingTxTracker.getPendingTransactions = () => txList
pendingTxTracker._checkPendingTx = (tx) => { tx.resolve(tx) }
Promise.all(txList.map((tx) => tx.processed))
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker._checkPendingTxs()
+ pendingTxTracker.updatePendingTxs()
})
})
describe('#resubmitPendingTxs', function () {
- const blockStub = { number: '0x0' }
+ const blockNumberStub = '0x0'
beforeEach(function () {
const txMeta2 = txMeta3 = txMeta
txList = [txMeta, txMeta2, txMeta3].map((tx) => {
@@ -210,7 +152,7 @@ describe('PendingTransactionTracker', function () {
Promise.all(txList.map((tx) => tx.processed))
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
it('should not emit \'tx:failed\' if the txMeta throws a known txError', function (done) {
knownErrors = [
@@ -237,7 +179,7 @@ describe('PendingTransactionTracker', function () {
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
it('should emit \'tx:warning\' if it encountered a real error', function (done) {
pendingTxTracker.once('tx:warning', (txMeta, err) => {
@@ -255,7 +197,7 @@ describe('PendingTransactionTracker', function () {
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
})
describe('#_resubmitTx', function () {
diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js
index 26dc7b656..ea58aa560 100644
--- a/test/unit/app/controllers/transactions/tx-controller-test.js
+++ b/test/unit/app/controllers/transactions/tx-controller-test.js
@@ -1,4 +1,5 @@
const assert = require('assert')
+const EventEmitter = require('events')
const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx')
const ObservableStore = require('obs-store')
@@ -22,12 +23,14 @@ describe('Transaction Controller', function () {
}
provider = createTestProviderTools({ scaffold: providerResultStub }).provider
fromAccount = getTestAccounts()[0]
-
+ const blockTrackerStub = new EventEmitter()
+ blockTrackerStub.getCurrentBlock = noop
+ blockTrackerStub.getLatestBlock = noop
txController = new TransactionController({
provider,
networkStore: new ObservableStore(currentNetworkId),
txHistoryLimit: 10,
- blockTracker: { getCurrentBlock: noop, on: noop, once: noop },
+ blockTracker: blockTrackerStub,
signTransaction: (ethTx) => new Promise((resolve) => {
ethTx.sign(fromAccount.key)
resolve()
@@ -49,9 +52,9 @@ describe('Transaction Controller', function () {
describe('#getUnapprovedTxCount', function () {
it('should return the number of unapproved txs', function () {
txController.txStateManager._saveTxList([
- { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
])
const unapprovedTxCount = txController.getUnapprovedTxCount()
assert.equal(unapprovedTxCount, 3, 'should be 3')
@@ -61,9 +64,9 @@ describe('Transaction Controller', function () {
describe('#getPendingTxCount', function () {
it('should return the number of pending txs', function () {
txController.txStateManager._saveTxList([
- { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
])
const pendingTxCount = txController.getPendingTxCount()
assert.equal(pendingTxCount, 3, 'should be 3')
@@ -79,15 +82,15 @@ describe('Transaction Controller', function () {
'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
}
txController.txStateManager._saveTxList([
- {id: 0, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams},
- {id: 4, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams},
- {id: 5, status: 'approved', metamaskNetworkId: currentNetworkId, txParams},
- {id: 6, status: 'signed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams},
- {id: 8, status: 'failed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 0, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 4, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 5, status: 'approved', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 6, status: 'signed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 8, status: 'failed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
])
})
@@ -155,9 +158,19 @@ describe('Transaction Controller', function () {
})
describe('#addUnapprovedTransaction', function () {
+ const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
+
+ let getSelectedAddress
+ beforeEach(function () {
+ getSelectedAddress = sinon.stub(txController, 'getSelectedAddress').returns(selectedAddress)
+ })
+
+ afterEach(function () {
+ getSelectedAddress.restore()
+ })
it('should add an unapproved transaction and return a valid txMeta', function (done) {
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.then((txMeta) => {
assert(('id' in txMeta), 'should have a id')
assert(('time' in txMeta), 'should have a time stamp')
@@ -177,48 +190,58 @@ describe('Transaction Controller', function () {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.catch(done)
})
it('should fail if recipient is public', function (done) {
txController.networkStore = new ObservableStore(1)
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch((err) => {
if (err.message === 'Recipient is a public account') done()
else done(err)
})
})
+ it('should fail if the from address isn\'t the selected address', function (done) {
+ txController.addUnapprovedTransaction({from: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2'})
+ .then(function () {
+ assert.fail('transaction should not have been added')
+ done()
+ })
+ .catch(function () {
+ assert.ok('pass')
+ done()
+ })
+ })
+
it('should not fail if recipient is public but not on mainnet', function (done) {
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch(done)
})
})
describe('#addTxGasDefaults', function () {
- it('should add the tx defaults if their are none', function (done) {
+ it('should add the tx defaults if their are none', async () => {
const txMeta = {
- 'txParams': {
- 'from': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
- 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ txParams: {
+ from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ to: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
},
+ history: [],
}
- providerResultStub.eth_gasPrice = '4a817c800'
- providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }
- providerResultStub.eth_estimateGas = '5209'
- txController.addTxGasDefaults(txMeta)
- .then((txMetaWithDefaults) => {
- assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value')
- assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price')
- assert(txMetaWithDefaults.txParams.gas, 'should have added the gas field')
- done()
- })
- .catch(done)
+ providerResultStub.eth_gasPrice = '4a817c800'
+ providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }
+ providerResultStub.eth_estimateGas = '5209'
+
+ const txMetaWithDefaults = await txController.addTxGasDefaults(txMeta)
+ assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value')
+ assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price')
+ assert(txMetaWithDefaults.txParams.gas, 'should have added the gas field')
})
})
@@ -381,8 +404,9 @@ describe('Transaction Controller', function () {
})
it('should publish a tx, updates the rawTx when provided a one', async function () {
+ const rawTx = '0x477b2e6553c917af0db0388ae3da62965ff1a184558f61b749d1266b2e6d024c'
txController.txStateManager.addTx(txMeta)
- await txController.publishTransaction(txMeta.id)
+ await txController.publishTransaction(txMeta.id, rawTx)
const publishedTx = txController.txStateManager.getTx(1)
assert.equal(publishedTx.hash, hash)
assert.equal(publishedTx.status, 'submitted')
@@ -398,7 +422,7 @@ describe('Transaction Controller', function () {
data: '0x0',
}
txController.txStateManager._saveTxList([
- { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams },
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] },
])
txController.retryTransaction(1)
.then((txMeta) => {
diff --git a/test/unit/components/balance-component-test.js b/test/unit/components/balance-component-test.js
index 81e6fdf9e..aa9763b72 100644
--- a/test/unit/components/balance-component-test.js
+++ b/test/unit/components/balance-component-test.js
@@ -1,7 +1,7 @@
const assert = require('assert')
const h = require('react-hyperscript')
const { createMockStore } = require('redux-test-utils')
-const { shallowWithStore } = require('../../lib/shallow-with-store')
+const { shallowWithStore } = require('../../lib/render-helpers')
const BalanceComponent = require('../../../ui/app/components/balance-component')
const mockState = {
metamask: {
@@ -42,4 +42,3 @@ describe('BalanceComponent', function () {
})
})
-
diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js
index 7bf9250cc..e428c26ad 100644
--- a/test/unit/components/binary-renderer-test.js
+++ b/test/unit/components/binary-renderer-test.js
@@ -4,7 +4,7 @@ var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer')
describe('BinaryRenderer', function () {
let binaryRenderer
const message = 'Hello, world!'
- const buffer = new Buffer(message, 'utf8')
+ const buffer = Buffer.from(message, 'utf8')
const hex = buffer.toString('hex')
beforeEach(function () {
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
deleted file mode 100644
index b710e2dfb..000000000
--- a/test/unit/config-manager-test.js
+++ /dev/null
@@ -1,115 +0,0 @@
-// polyfill fetch
-global.fetch = global.fetch || require('isomorphic-fetch')
-
-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')
- })
- })
- })
-})
diff --git a/test/unit/development/sample-changelog.md b/test/unit/development/sample-changelog.md
index 8fc9d2145..fd980e375 100644
--- a/test/unit/development/sample-changelog.md
+++ b/test/unit/development/sample-changelog.md
@@ -67,7 +67,7 @@
- Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code.
- Add an extra px to address for Firefox clipping.
- Fix Firefox scrollbar.
-- Open metamask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
+- Open MetaMask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
- Fix bug that prevented eth_signTypedData from signing bytes.
- Further improve gas price estimation.
@@ -330,7 +330,7 @@ rollback to 3.10.0 due to bug
## 3.7.7 2017-6-8
-- Fix bug where metamask would show old data after computer being asleep or disconnected from the internet.
+- Fix bug where MetaMask would show old data after computer being asleep or disconnected from the internet.
## 3.7.6 2017-6-5
diff --git a/test/unit/localhostState.js b/test/unit/localhostState.js
new file mode 100644
index 000000000..f9fa157d7
--- /dev/null
+++ b/test/unit/localhostState.js
@@ -0,0 +1,21 @@
+
+/**
+ * @typedef {Object} FirstTimeState
+ * @property {Object} config Initial configuration parameters
+ * @property {Object} NetworkController Network controller state
+ */
+
+/**
+ * @type {FirstTimeState}
+ */
+const initialState = {
+ config: {},
+ NetworkController: {
+ provider: {
+ type: 'rpc',
+ rpcTarget: 'http://localhost:8545',
+ },
+ },
+}
+
+module.exports = initialState
diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js
index 493b01918..f3f236d90 100644
--- a/test/unit/responsive/components/dropdown-test.js
+++ b/test/unit/responsive/components/dropdown-test.js
@@ -6,7 +6,7 @@ const path = require('path')
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown
const { createMockStore } = require('redux-test-utils')
-const { mountWithStore } = require('../../../lib/shallow-with-store')
+const { mountWithStore } = require('../../../lib/render-helpers')
const mockState = {
metamask: {
diff --git a/test/unit/ui/add-token.spec.js b/test/unit/ui/add-token.spec.js
index 69b7fb620..f6b6155a0 100644
--- a/test/unit/ui/add-token.spec.js
+++ b/test/unit/ui/add-token.spec.js
@@ -1,7 +1,7 @@
const assert = require('assert')
const { createMockStore } = require('redux-test-utils')
const h = require('react-hyperscript')
-const { shallowWithStore } = require('../../lib/shallow-with-store')
+const { shallowWithStore } = require('../../lib/render-helpers')
const AddTokenScreen = require('../../../old-ui/app/add-token')
describe('Add Token Screen', function () {
diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js
new file mode 100644
index 000000000..748a58b32
--- /dev/null
+++ b/test/unit/ui/app/actions.spec.js
@@ -0,0 +1,1468 @@
+// Used to inspect long objects
+// util.inspect({JSON}, false, null))
+// const util = require('util')
+const assert = require('assert')
+const sinon = require('sinon')
+const clone = require('clone')
+const nock = require('nock')
+const fetchMock = require('fetch-mock')
+const configureStore = require('redux-mock-store').default
+const thunk = require('redux-thunk').default
+const EthQuery = require('eth-query')
+const Eth = require('ethjs')
+const KeyringController = require('eth-keyring-controller')
+
+const { createTestProviderTools } = require('../../../stub/provider')
+const provider = createTestProviderTools({ scaffold: {}}).provider
+
+const enLocale = require('../../../../app/_locales/en/messages.json')
+const actions = require('../../../../ui/app/actions')
+const MetaMaskController = require('../../../../app/scripts/metamask-controller')
+
+const firstTimeState = require('../../../unit/localhostState')
+const devState = require('../../../data/2-state.json')
+
+const middleware = [thunk]
+const mockStore = configureStore(middleware)
+
+describe('Actions', () => {
+
+ const noop = () => {}
+
+ let background, metamaskController
+
+ const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
+ const password = 'a-fake-password'
+ const importPrivkey = '4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'
+
+ beforeEach(async () => {
+
+
+ metamaskController = new MetaMaskController({
+ provider,
+ keyringController: new KeyringController({}),
+ showUnapprovedTx: noop,
+ showUnconfirmedMessage: noop,
+ encryptor: {
+ encrypt: function (password, object) {
+ this.object = object
+ return Promise.resolve('mock-encrypted')
+ },
+ decrypt: function () {
+ return Promise.resolve(this.object)
+ },
+ },
+ initState: clone(firstTimeState),
+ })
+
+ await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
+
+ await metamaskController.importAccountWithStrategy('Private Key', [ importPrivkey ])
+
+ background = metamaskController.getApi()
+
+ actions._setBackgroundConnection(background)
+
+ global.ethQuery = new EthQuery(provider)
+ })
+
+ describe('#tryUnlockMetamask', () => {
+
+ let submitPasswordSpy, verifySeedPhraseSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ verifySeedPhraseSpy.restore()
+ })
+
+ it('', async () => {
+
+ const store = mockStore({})
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ verifySeedPhraseSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.tryUnlockMetamask())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(verifySeedPhraseSpy.calledOnce)
+ })
+ })
+
+ it('errors on submitPassword will fail', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UNLOCK_IN_PROGRESS' },
+ { type: 'UNLOCK_FAILED', value: 'error in submitPassword' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error in submitPassword'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error and unlock failed when verifySeed fails', () => {
+ const store = mockStore({})
+ const displayWarningError = [ { type: 'DISPLAY_WARNING', value: 'error' } ]
+ const unlockFailedError = [ { type: 'UNLOCK_FAILED', value: 'error' } ]
+
+ verifySeedPhraseSpy = sinon.stub(background, 'verifySeedPhrase')
+ verifySeedPhraseSpy.callsFake(callback => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .catch(() => {
+ const actions = store.getActions()
+ const warning = actions.filter(action => action.type === 'DISPLAY_WARNING')
+ const unlockFailed = actions.filter(action => action.type === 'UNLOCK_FAILED')
+ assert.deepEqual(warning, displayWarningError)
+ assert.deepEqual(unlockFailed, unlockFailedError)
+ })
+ })
+ })
+
+ describe('#confirmSeedWords', () => {
+
+ let clearSeedWordCacheSpy
+
+ afterEach(() => {
+ clearSeedWordCacheSpy.restore()
+ })
+
+ it('shows account page after clearing seed word cache', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+
+ return store.dispatch(actions.confirmSeedWords())
+ .then(() => {
+ assert.equal(clearSeedWordCacheSpy.callCount, 1)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors in callback will display warning', () => {
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.confirmSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndRestore', () => {
+
+ let createNewVaultAndRestoreSpy, clearSeedWordCacheSpy
+
+ afterEach(() => {
+ createNewVaultAndRestoreSpy.restore()
+ })
+
+ it('clears seed words and restores new vault', () => {
+
+ const store = mockStore({})
+
+ createNewVaultAndRestoreSpy = sinon.spy(background, 'createNewVaultAndRestore')
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert(clearSeedWordCacheSpy.calledOnce)
+ assert(createNewVaultAndRestoreSpy.calledOnce)
+ })
+ })
+
+ it('errors when callback in clearSeedWordCache throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when callback in createNewVaultAndRestore throws', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
+
+ createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndKeychain', () => {
+
+ let createNewVaultAndKeychainSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ createNewVaultAndKeychainSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls createNewVaultAndKeychain and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ createNewVaultAndKeychainSpy = sinon.spy(background, 'createNewVaultAndKeychain')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert(createNewVaultAndKeychainSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays error and value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ createNewVaultAndKeychainSpy = sinon.stub(background, 'createNewVaultAndKeychain')
+ createNewVaultAndKeychainSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ it('errors when placeSeedWords throws', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords from background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error with value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeedWords', () => {
+ let submitPasswordSpy
+
+ it('calls submitPassword in background', () => {
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message then callback in background errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'verifySeedPhrase')
+ submitPasswordSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when submitPassword in background errors', () => {
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when placeSeedWords throw', () => {
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeAccount', () => {
+ let removeAccountSpy
+
+ afterEach(() => {
+ removeAccountSpy.restore()
+ })
+
+ it('calls removeAccount in background and expect actions to show account', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ removeAccountSpy = sinon.spy(background, 'removeAccount')
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .then(() => {
+ assert(removeAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error message when removeAccount callback errors', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ removeAccountSpy = sinon.stub(background, 'removeAccount')
+ removeAccountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#addNewKeyring', () => {
+ let addNewKeyringSpy
+
+ beforeEach(() => {
+ addNewKeyringSpy = sinon.stub(background, 'addNewKeyring')
+ })
+
+ afterEach(() => {
+ addNewKeyringSpy.restore()
+ })
+
+ it('', () => {
+ const privateKey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
+
+ const store = mockStore()
+ store.dispatch(actions.addNewKeyring('Simple Key Pair', [ privateKey ]))
+ assert(addNewKeyringSpy.calledOnce)
+ })
+
+ it('errors then addNewKeyring in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ addNewKeyringSpy.callsFake((type, opts, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.addNewKeyring())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ describe('#resetAccount', () => {
+
+ let resetAccountSpy
+
+ afterEach(() => {
+ resetAccountSpy.restore()
+ })
+
+ it('', () => {
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ resetAccountSpy = sinon.spy(background, 'resetAccount')
+
+ return store.dispatch(actions.resetAccount())
+ .then(() => {
+ assert(resetAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ resetAccountSpy = sinon.stub(background, 'resetAccount')
+ resetAccountSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.resetAccount())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#importNewAccount', () => {
+
+ let importAccountWithStrategySpy
+
+ afterEach(() => {
+ importAccountWithStrategySpy.restore()
+ })
+
+ it('calls importAccountWithStrategies in background', () => {
+ const store = mockStore()
+
+ importAccountWithStrategySpy = sinon.spy(background, 'importAccountWithStrategy')
+
+ const importPrivkey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
+
+ return store.dispatch(actions.importNewAccount('Private Key', [ importPrivkey ]))
+ .then(() => {
+ assert(importAccountWithStrategySpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when importAccount in background callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: 'This may take a while, please be patient.' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ importAccountWithStrategySpy = sinon.stub(background, 'importAccountWithStrategy')
+ importAccountWithStrategySpy.callsFake((strategy, args, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.importNewAccount())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#addNewAccount', () => {
+
+ let addNewAccountSpy
+
+ afterEach(() => {
+ addNewAccountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore({ metamask: devState })
+
+ addNewAccountSpy = sinon.spy(background, 'addNewAccount')
+
+ return store.dispatch(actions.addNewAccount())
+ .then(() => {
+ assert(addNewAccountSpy.calledOnce)
+ })
+ })
+ })
+
+ describe('#setCurrentCurrency', () => {
+
+ let setCurrentCurrencySpy
+
+ beforeEach(() => {
+ setCurrentCurrencySpy = sinon.stub(background, 'setCurrentCurrency')
+ })
+
+ afterEach(() => {
+ setCurrentCurrencySpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setCurrentCurrency('jpy'))
+ assert(setCurrentCurrencySpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setCurrentCurrencySpy.callsFake((currencyCode, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setCurrentCurrency())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#signMsg', () => {
+
+ let signMessageSpy, metamaskMsgs, msgId, messages
+
+ const msgParams = {
+ from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
+ }
+
+ beforeEach(() => {
+ metamaskController.newUnsignedMessage(msgParams, noop)
+ metamaskMsgs = metamaskController.messageManager.getUnapprovedMsgs()
+ messages = metamaskController.messageManager.messages
+ msgId = Object.keys(metamaskMsgs)[0]
+ messages[0].msgParams.metamaskId = parseInt(msgId)
+ })
+
+ afterEach(() => {
+ signMessageSpy.restore()
+ })
+
+ it('calls signMsg in background', () => {
+ const store = mockStore()
+
+ signMessageSpy = sinon.spy(background, 'signMessage')
+
+ return store.dispatch(actions.signMsg(msgParams))
+ .then(() => {
+ assert(signMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when signMessage in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UPDATE_METAMASK_STATE', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ signMessageSpy = sinon.stub(background, 'signMessage')
+ signMessageSpy.callsFake((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signMsg())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ })
+
+ describe('#signPersonalMsg', () => {
+
+ let signPersonalMessageSpy, metamaskMsgs, msgId, personalMessages
+
+ const msgParams = {
+ from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
+ }
+
+ beforeEach(() => {
+ metamaskController.newUnsignedPersonalMessage(msgParams, noop)
+ metamaskMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs()
+ personalMessages = metamaskController.personalMessageManager.messages
+ msgId = Object.keys(metamaskMsgs)[0]
+ personalMessages[0].msgParams.metamaskId = parseInt(msgId)
+ })
+
+ afterEach(() => {
+ signPersonalMessageSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ signPersonalMessageSpy = sinon.spy(background, 'signPersonalMessage')
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .then(() => {
+ assert(signPersonalMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'UPDATE_METAMASK_STATE', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ signPersonalMessageSpy = sinon.stub(background, 'signPersonalMessage')
+ signPersonalMessageSpy.callsFake((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ })
+
+ describe('#signTx', () => {
+
+ let sendTransactionSpy
+
+ beforeEach(() => {
+ global.ethQuery = new EthQuery(provider)
+ sendTransactionSpy = sinon.stub(global.ethQuery, 'sendTransaction')
+ })
+
+ afterEach(() => {
+ sendTransactionSpy.restore()
+ })
+
+ it('calls sendTransaction in global ethQuery', () => {
+ const store = mockStore()
+ store.dispatch(actions.signTx())
+ assert(sendTransactionSpy.calledOnce)
+ })
+
+ it('errors in when sendTransaction throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'SHOW_CONF_TX_PAGE', transForward: true, id: undefined },
+ ]
+ sendTransactionSpy.callsFake((txData, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.signTx())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#signTokenTx', () => {
+
+ let tokenSpy
+
+ beforeEach(() => {
+ global.eth = new Eth(provider)
+ tokenSpy = sinon.spy(global.eth, 'contract')
+ })
+
+ afterEach(() => {
+ tokenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.signTokenTx())
+ assert(tokenSpy.calledOnce)
+ })
+ })
+
+ describe('#lockMetamask', () => {
+ let backgroundSetLockedSpy
+
+ afterEach(() => {
+ backgroundSetLockedSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ backgroundSetLockedSpy = sinon.spy(background, 'setLocked')
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ assert(backgroundSetLockedSpy.calledOnce)
+ })
+ })
+
+ it('returns display warning error with value when setLocked in background callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'LOCK_METAMASK' },
+ ]
+ backgroundSetLockedSpy = sinon.stub(background, 'setLocked')
+ backgroundSetLockedSpy.callsFake(callback => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setSelectedAddress', () => {
+ let setSelectedAddressSpy
+
+ beforeEach(() => {
+ setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
+ })
+
+ afterEach(() => {
+ setSelectedAddressSpy.restore()
+ })
+
+ it('calls setSelectedAddress in background', () => {
+ const store = mockStore({ metamask: devState })
+
+ store.dispatch(actions.setSelectedAddress('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ assert(setSelectedAddressSpy.calledOnce)
+ })
+
+ it('errors when setSelectedAddress throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ setSelectedAddressSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setSelectedAddress())
+ assert.deepEqual(store.getActions(), expectedActions)
+
+ })
+ })
+
+ describe('#showAccountDetail', () => {
+ let setSelectedAddressSpy
+
+ beforeEach(() => {
+ setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
+ })
+
+ afterEach(() => {
+ setSelectedAddressSpy.restore()
+ })
+
+ it('#showAccountDetail', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.showAccountDetail())
+ assert(setSelectedAddressSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setSelectedAddressSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+
+ store.dispatch(actions.showAccountDetail())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#addToken', () => {
+ let addTokenSpy
+
+ beforeEach(() => {
+ addTokenSpy = sinon.stub(background, 'addToken')
+ })
+
+ afterEach(() => {
+ addTokenSpy.restore()
+ })
+
+ it('calls addToken in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.addToken())
+ .then(() => {
+ assert(addTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when addToken in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ addTokenSpy.callsFake((address, symbol, decimals, image, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.addToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeToken', () => {
+
+ let removeTokenSpy
+
+ beforeEach(() => {
+ removeTokenSpy = sinon.stub(background, 'removeToken')
+ })
+
+ afterEach(() => {
+ removeTokenSpy.restore()
+ })
+
+ it('calls removeToken in background', () => {
+ const store = mockStore()
+ store.dispatch(actions.removeToken())
+ .then(() => {
+ assert(removeTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when removeToken in background fails', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ removeTokenSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.removeToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markNoticeRead', () => {
+ let markNoticeReadSpy
+ const notice = {
+ id: 0,
+ read: false,
+ date: 'test date',
+ title: 'test title',
+ body: 'test body',
+ }
+
+ beforeEach(() => {
+ markNoticeReadSpy = sinon.stub(background, 'markNoticeRead')
+ })
+
+ afterEach(() => {
+ markNoticeReadSpy.restore()
+ })
+
+ it('calls markNoticeRead in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.markNoticeRead(notice))
+ .then(() => {
+ assert(markNoticeReadSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when markNoticeRead in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ markNoticeReadSpy.callsFake((notice, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.markNoticeRead())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setProviderType', () => {
+ let setProviderTypeSpy
+
+ beforeEach(() => {
+ setProviderTypeSpy = sinon.stub(background, 'setProviderType')
+ })
+
+ afterEach(() => {
+ setProviderTypeSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setProviderType())
+ assert(setProviderTypeSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setProviderTypeSpy.callsFake((type, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setProviderType())
+ assert(setProviderTypeSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#setRpcTarget', () => {
+ let setRpcTargetSpy
+
+ beforeEach(() => {
+ setRpcTargetSpy = sinon.stub(background, 'setCustomRpc')
+ })
+
+ afterEach(() => {
+ setRpcTargetSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setRpcTarget('http://localhost:8545'))
+ assert(setRpcTargetSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setRpcTargetSpy.callsFake((newRpc, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setRpcTarget())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#addToAddressBook', () => {
+ let addToAddressBookSpy
+
+ beforeEach(() => {
+ addToAddressBookSpy = sinon.stub(background, 'setAddressBook')
+ })
+
+ afterEach(() => {
+ addToAddressBookSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.addToAddressBook('test'))
+ assert(addToAddressBookSpy.calledOnce)
+ })
+ })
+
+ describe('#exportAccount', () => {
+ let submitPasswordSpy, exportAccountSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ exportAccountSpy.restore()
+ })
+
+ it('returns expected actions for successful action', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_PRIVATE_KEY', value: '7ec73b91bb20f209a7ff2d32f542c3420b4fccf14abcc7840d2eff0ebcb18505' },
+ ]
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ exportAccountSpy = sinon.spy(background, 'exportAccount')
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .then((result) => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(exportAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when first func callback errors', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'Incorrect Password.' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when second func callback errors', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'Had a problem exporting the account.' },
+ ]
+
+ exportAccountSpy = sinon.stub(background, 'exportAccount')
+ exportAccountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setAccountLabel', () => {
+ let setAccountLabelSpy
+
+ beforeEach(() => {
+ setAccountLabelSpy = sinon.stub(background, 'setAccountLabel')
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setAccountLabel('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', 'test'))
+ assert(setAccountLabelSpy.calledOnce)
+ })
+ })
+
+ describe('#pairUpdate', () => {
+ beforeEach(() => {
+
+ nock('https://shapeshift.io')
+ .defaultReplyHeaders({ 'access-control-allow-origin': '*' })
+ .get('/marketinfo/btc_eth')
+ .reply(200, {pair: 'BTC_ETH', rate: 25.68289016, minerFee: 0.00176, limit: 0.67748474, minimum: 0.00013569, maxLimit: 0.67758573})
+
+ nock('https://shapeshift.io')
+ .defaultReplyHeaders({ 'access-control-allow-origin': '*' })
+ .get('/coins')
+ .reply(200)
+ })
+
+ afterEach(() => {
+ nock.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ // issue with dispatch action in callback not showing
+ const expectedActions = [
+ { type: 'SHOW_SUB_LOADING_INDICATION' },
+ { type: 'HIDE_WARNING' },
+ ]
+
+ store.dispatch(actions.pairUpdate('btc'))
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#setFeatureFlag', () => {
+ let setFeatureFlagSpy
+
+ beforeEach(() => {
+ setFeatureFlagSpy = sinon.stub(background, 'setFeatureFlag')
+ })
+
+ afterEach(() => {
+ setFeatureFlagSpy.restore()
+ })
+
+ it('calls setFeatureFlag in the background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setFeatureFlag())
+ assert(setFeatureFlagSpy.calledOnce)
+ })
+
+ it('errors when setFeatureFlag in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ setFeatureFlagSpy.callsFake((feature, activated, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setFeatureFlag())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#updateNetworkNonce', () => {
+ let getTransactionCountSpy
+
+ afterEach(() => {
+ getTransactionCountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ getTransactionCountSpy = sinon.spy(global.ethQuery, 'getTransactionCount')
+
+ store.dispatch(actions.updateNetworkNonce())
+ .then(() => {
+ assert(getTransactionCountSpy.calledOnce)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ getTransactionCountSpy = sinon.stub(global.ethQuery, 'getTransactionCount')
+ getTransactionCountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateNetworkNonce())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setUseBlockie', () => {
+ let setUseBlockieSpy
+
+ beforeEach(() => {
+ setUseBlockieSpy = sinon.stub(background, 'setUseBlockie')
+ })
+
+ afterEach(() => {
+ setUseBlockieSpy.restore()
+ })
+
+ it('calls setUseBlockie in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setUseBlockie())
+ assert(setUseBlockieSpy.calledOnce)
+ })
+
+ it('errors when setUseBlockie in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'SET_USE_BLOCKIE', value: undefined },
+ ]
+
+ setUseBlockieSpy.callsFake((val, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setUseBlockie())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ describe('#updateCurrentLocale', () => {
+ let setCurrentLocaleSpy
+
+ beforeEach(() => {
+ fetchMock.get('*', enLocale)
+ })
+
+ afterEach(() => {
+ setCurrentLocaleSpy.restore()
+ fetchMock.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ setCurrentLocaleSpy = sinon.spy(background, 'setCurrentLocale')
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SET_CURRENT_LOCALE', value: 'en' },
+ { type: 'SET_LOCALE_MESSAGES', value: enLocale },
+ ]
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert(setCurrentLocaleSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setCurrentLocaleSpy = sinon.stub(background, 'setCurrentLocale')
+ setCurrentLocaleSpy.callsFake((key, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markPasswordForgotten', () => {
+ let markPasswordForgottenSpy
+
+ beforeEach(() => {
+ markPasswordForgottenSpy = sinon.stub(background, 'markPasswordForgotten')
+ })
+
+ afterEach(() => {
+ markPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.markPasswordForgotten())
+ assert(markPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+ describe('#unMarkPasswordForgotten', () => {
+ let unMarkPasswordForgottenSpy
+
+ beforeEach(() => {
+ unMarkPasswordForgottenSpy = sinon.stub(background, 'unMarkPasswordForgotten')
+ })
+
+ afterEach(() => {
+ unMarkPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.unMarkPasswordForgotten())
+ assert(unMarkPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+
+})
diff --git a/test/unit/ui/app/components/identicon.spec.js b/test/unit/ui/app/components/identicon.spec.js
new file mode 100644
index 000000000..a2f8d8246
--- /dev/null
+++ b/test/unit/ui/app/components/identicon.spec.js
@@ -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')
+ })
+})
diff --git a/test/unit/ui/app/components/token-cell.spec.js b/test/unit/ui/app/components/token-cell.spec.js
new file mode 100644
index 000000000..6145c6924
--- /dev/null
+++ b/test/unit/ui/app/components/token-cell.spec.js
@@ -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')
+ })
+
+})
diff --git a/test/unit/ui/app/selectors.spec.js b/test/unit/ui/app/selectors.spec.js
new file mode 100644
index 000000000..78c4267ee
--- /dev/null
+++ b/test/unit/ui/app/selectors.spec.js
@@ -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)
+ })
+})
diff --git a/test/unit/ui/etherscan-prefix-for-network.spec.js b/test/unit/ui/etherscan-prefix-for-network.spec.js
new file mode 100644
index 000000000..f0aeb8306
--- /dev/null
+++ b/test/unit/ui/etherscan-prefix-for-network.spec.js
@@ -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.')
+ })
+
+})