From 5c62fdeaa91e7be2e5d66d336fa290ae8593e615 Mon Sep 17 00:00:00 2001 From: Everton Fraga Date: Fri, 13 Jan 2017 18:15:53 -0200 Subject: [PATCH] Adding tests for URL bar --- .eslintrc.yml | 9 +- README.md | 2 + customProtocols.js | 9 +- errorPages/400.html | 2 +- errorPages/404.html | 19 +++ errorPages/500.html | 19 +++ .../styles/networkIndicator.import.less | 8 +- .../popupWindows/onboardingScreen.html | 23 ++- .../popupWindows/onboardingScreen.js | 78 ++++++---- .../templates/popupWindows/splashScreen.js | 8 +- interface/client/templates/views/webview.js | 10 +- interface/client/templates/webviewEvents.js | 27 ++++ interface/i18n/mist.en.i18n.json | 7 +- main.js | 2 +- modules/clientBinaryManager.js | 2 + modules/dbSync.js | 36 ++--- modules/ethereumNode.js | 9 +- modules/ipc/dechunker.js | 5 +- modules/ipc/ipcProviderBackend.js | 14 +- modules/ipc/methods/eth_sendTransaction.js | 3 +- modules/ipcCommunicator.js | 12 +- modules/menuItems.js | 3 +- modules/nodeSync.js | 2 +- modules/preloader/browser.js | 2 +- modules/preloader/include/getFavicon.js | 9 +- modules/preloader/include/getMetaTags.js | 11 +- modules/preloader/include/mistAPI.js | 2 +- modules/preloader/include/openExternal.js | 9 +- modules/preloader/include/openPopup.js | 11 +- modules/preloader/mistUI.js | 49 ++++--- modules/preloader/walletMain.js | 5 +- package.json | 1 + tests/_base.js | 25 +++- tests/mist/basic.test.js | 135 ++++++++---------- 34 files changed, 346 insertions(+), 222 deletions(-) create mode 100644 errorPages/404.html create mode 100644 errorPages/500.html diff --git a/.eslintrc.yml b/.eslintrc.yml index b0d68de7d..59576da16 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,7 +7,6 @@ settings: import/core-modules: ## don't lint for these missing packages in package.json - electron ## 'electron' is only needed as devDependency / global installation - rules: # "off" or 0 - turn the rule off # "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code) @@ -22,7 +21,13 @@ rules: no-underscore-dangle: off comma-dangle: - error - - only-multiline + - only-multiline ## no comma after last item if one line, though allow comma if multiline + import/no-extraneous-dependencies: ## checks if required modules are missing in packages.json + - error + - devDependencies: ## declares files, whose imports belong to devDependencies + - "**/scripts/build-dist.js" + - "**/tests/_base.js" + - "**/*.test.js" globals: # don't warn about missing declarations i18n: true diff --git a/README.md b/README.md index a894b4b4f..b9f59744d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/ethereum/mist](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/mist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status develop branch](https://travis-ci.org/ethereum/mist.svg?branch=develop)](https://travis-ci.org/ethereum/mist) [![Code Climate](https://codeclimate.com/github/ethereum/mist/badges/gpa.svg)](https://codeclimate.com/github/ethereum/mist) +[![dependencies Status](https://david-dm.org/ethereum/mist/status.svg)](https://david-dm.org/ethereum/mist) +[![devDependencies Status](https://david-dm.org/ethereum/mist/dev-status.svg)](https://david-dm.org/ethereum/mist?type=dev) The Mist browser is the tool of choice to browse and use Ðapps. diff --git a/customProtocols.js b/customProtocols.js index b0b9fb2af..05fd52ef0 100644 --- a/customProtocols.js +++ b/customProtocols.js @@ -1,6 +1,4 @@ -const electron = require('electron'); -const protocol = electron.protocol; -const path = require('path'); +const { protocol } = require('electron'); protocol.registerHttpProtocol('mist', (request, callback) => { @@ -19,8 +17,9 @@ protocol.registerHttpProtocol('mist', (request, callback) => { callback(call); }, (error) => { - if (error) - { console.error('Failed to register protocol'); } + if (error) { + console.error('Failed to register protocol'); + } }); diff --git a/errorPages/400.html b/errorPages/400.html index 06d5acc2d..e4ceb706d 100644 --- a/errorPages/400.html +++ b/errorPages/400.html @@ -7,7 +7,7 @@ background-color: #f0f0f0; color: #ACACAC; text-shadow: 0 -1px #fff; - font: 20px Helvetica Neue, Arial; + font: 20px Source Sans Pro, Helvetica Neue, Arial; font-weight: 200; text-align: center; padding: 10px; diff --git a/errorPages/404.html b/errorPages/404.html new file mode 100644 index 000000000..c0eafb982 --- /dev/null +++ b/errorPages/404.html @@ -0,0 +1,19 @@ + + + Error 404 + + + + ﴾๏๏﴿

+ URL not found. + + \ No newline at end of file diff --git a/errorPages/500.html b/errorPages/500.html new file mode 100644 index 000000000..57aee2e60 --- /dev/null +++ b/errorPages/500.html @@ -0,0 +1,19 @@ + + + Error 500 + + + + (ノಠ益ಠ)ノ

+ Oops.. Something went wrong! + + \ No newline at end of file diff --git a/interface/client/styles/networkIndicator.import.less b/interface/client/styles/networkIndicator.import.less index c46672228..0cc4165a0 100644 --- a/interface/client/styles/networkIndicator.import.less +++ b/interface/client/styles/networkIndicator.import.less @@ -1,3 +1,4 @@ +// Applies to both onboarding and splash screen .network-indicator { position: absolute; top: 10px; @@ -9,8 +10,10 @@ opacity: 0.8; color: @colorGrey; text-transform: uppercase; +} - .unknown { +// Applies only to the splash screen +.splash-screen .network-indicator .unknown { position: absolute; top: 155px; text-align: center; @@ -21,5 +24,4 @@ font-weight: normal; text-transform: none; color: #fff; - } -} +} \ No newline at end of file diff --git a/interface/client/templates/popupWindows/onboardingScreen.html b/interface/client/templates/popupWindows/onboardingScreen.html index 4774ffbab..b1b227334 100644 --- a/interface/client/templates/popupWindows/onboardingScreen.html +++ b/interface/client/templates/popupWindows/onboardingScreen.html @@ -113,27 +113,24 @@

{{i18n "mist.popupWindows.onboarding.learnIt"}}

diff --git a/interface/client/templates/popupWindows/onboardingScreen.js b/interface/client/templates/popupWindows/onboardingScreen.js index 93d955790..50b6f5b4a 100644 --- a/interface/client/templates/popupWindows/onboardingScreen.js +++ b/interface/client/templates/popupWindows/onboardingScreen.js @@ -38,12 +38,12 @@ Template['popupWindows_onboardingScreen'].onCreated(function(){ if(syncing === true) { web3.reset(true); - } else if(_.isObject(syncing)) { // loads syncing data and adds it to old by using 'extend' var oldData = TemplateVar.get(template, 'syncing'); + TemplateVar.set(template, 'syncing', _.extend(oldData||{}, syncing||{})); - + } else { TemplateVar.set(template, 'syncing', false); } @@ -78,7 +78,7 @@ Template['popupWindows_onboardingScreen'].helpers({ return (account) ? account.toLowerCase() : ''; }, /** - Updates the Sync Message live + Updates the Sync Data @method syncStatus */ @@ -86,42 +86,61 @@ Template['popupWindows_onboardingScreen'].helpers({ // This functions loops trhough numbers while waiting for the node to respond var template = Template.instance(); + Meteor.clearInterval(template._intervalId); // Create an interval to quickly iterate trough the numbers template._intervalId = Meteor.setInterval(function(){ // load the sync information var syncing = TemplateVar.get(template, 'syncing'); - - // Calculates a block t display that is always getting 1% closer to target - syncing._displayBlock = (syncing._displayBlock + (syncing.currentBlock - syncing._displayBlock) / 100 )|| syncing.currentBlock; - syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates/syncing.knownStates - syncing._displayStatesDownload) / 100 ) || syncing.pulledStates/syncing.knownStates; + if (syncing) { + // If it's syncing, then it's not ready + TemplateVar.set(template, 'readyToLaunch', false); + // Calculates a block t display that is always getting a few % closer to target + syncing._displayBlock = (syncing._displayBlock + 2*(syncing.currentBlock - syncing._displayBlock) / 100 ) || Number(syncing.startingBlock); - // Calculates progress - syncing.progress = Math.round(((syncing._displayBlock - syncing.startingBlock) / (syncing.highestBlock - syncing.startingBlock)) * 100); - - // Makes fancy strings - syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0'); - syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0'); - syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0'); - syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload*10000)/100).format('0.00'); + syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates/(1 +syncing.knownStates) - syncing._displayStatesDownload) / 100 ) || Number(syncing.pulledStates)/Number(syncing.knownStates + 1); - // Saves the data back to the object - TemplateVar.set(template, 'syncing', syncing); + // Calculates progress + syncing.progress = 100 * (syncing._displayBlock - syncing.startingBlock) / (1 + Number(syncing.highestBlock) - syncing.startingBlock); + + // Makes fancy strings + syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0'); + syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0'); + syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0'); + syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload*10000)/100).format('0.00'); + + // Saves the data back to the object + TemplateVar.set(template, 'syncing', syncing); - // Only show states if they are less than 50% downloaded - if (Math.round(1000*Number(syncing._displayStatesDownload)) !== Math.round(1000*Number(syncing.pulledStates/syncing.knownStates))) { - TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing)); - } else { - TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing)); - } + // If it's close enough, show the synced button + + if (Number(syncing.highestBlock) - syncing.currentBlock < 100 ) { + TemplateVar.set(template, 'readyToLaunch', true); + } + + // Only show states if they are changing + if (Math.round(1000*Number(syncing._displayStatesDownload)) !== Math.round(1000*Number(syncing.pulledStates/(syncing.knownStates+1)))) { + TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing)); + } else if (syncing.displayBlock == '0') { + TemplateVar.set(template, "syncStatusMessageLive", ''); + } else { + TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing)); + } + } }, 50); + }, + /** + Updates the Sync Message live - return TemplateVar.get(template, "syncStatusMessageLive"); + @method syncStatusMessage + */ + 'syncStatusMessage' : function() { + return TemplateVar.get("syncStatusMessageLive"); } }); @@ -136,12 +155,14 @@ Template['popupWindows_onboardingScreen'].events({ if(TemplateVar.get('testnet')) { ipc.send('onBoarding_changeNet', false); TemplateVar.set('testnet', false); + TemplateVar.set('syncing', null); } }, 'click .start-testnet': function(e, template){ if(!TemplateVar.get('testnet')) { ipc.send('onBoarding_changeNet', true); TemplateVar.set('testnet', true); + TemplateVar.set('syncing', null); } TemplateVar.set('currentActive','testnet'); @@ -156,15 +177,18 @@ Template['popupWindows_onboardingScreen'].events({ }, 'click .goto-tutorial-1': function(){ TemplateVar.set('currentActive','tutorial-1'); - TemplateVar.set('readyToLaunch', true); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, 'click .goto-tutorial-2': function(){ TemplateVar.set('currentActive','tutorial-2'); - TemplateVar.set('readyToLaunch', true); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, 'click .goto-tutorial-3': function(){ TemplateVar.set('currentActive','tutorial-3'); - TemplateVar.set('readyToLaunch', true); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, /** Start the application diff --git a/interface/client/templates/popupWindows/splashScreen.js b/interface/client/templates/popupWindows/splashScreen.js index 8d51aaa57..08244ab3c 100644 --- a/interface/client/templates/popupWindows/splashScreen.js +++ b/interface/client/templates/popupWindows/splashScreen.js @@ -189,9 +189,9 @@ Template['popupWindows_splashScreen'].helpers({ syncData._displayKnownStates = Number(syncData.knownStates || 0); } else { // Increment each them slowly to match target number - syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 10; - syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 10; - syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 10; + syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 100; + syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 100; + syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 100; } // Create the fancy strings @@ -224,7 +224,7 @@ Template['popupWindows_splashScreen'].helpers({ } } - }, 100); + }, 10); return TemplateVar.get(template, "syncStatusMessageLive"); } diff --git a/interface/client/templates/views/webview.js b/interface/client/templates/views/webview.js index b64aa6ec6..e28d4b848 100644 --- a/interface/client/templates/views/webview.js +++ b/interface/client/templates/views/webview.js @@ -60,6 +60,10 @@ Template['views_webview'].onRendered(function(){ webviewLoadStop.call(this, tabId, e); }); + // show error pages + webview.addEventListener('did-fail-load', showError.bind(webview, tabId)); + webview.addEventListener('crashed', showError.bind(webview, tabId)); + // navigate page, and redirect to browser tab if necessary webview.addEventListener('will-navigate', webviewLoadStart.bind(webview, tabId)); webview.addEventListener('did-get-redirect-request', webviewLoadStart.bind(webview, tabId)); @@ -122,6 +126,10 @@ Template['views_webview'].helpers({ }}); } + // allow error pages + if(url && url.indexOf('file://'+ dirname + '/errorPages/') === 0) { + return url; + } // CHECK URL and throw error if not allowed if(!Helpers.sanitizeUrl(url, true)) { @@ -137,7 +145,7 @@ Template['views_webview'].helpers({ return 'file://'+ dirname + '/errorPages/400.html'; } - // remove redirect + // add url if(url) { template.url = url; Tabs.update(this._id, {$set: { diff --git a/interface/client/templates/webviewEvents.js b/interface/client/templates/webviewEvents.js index 26e10bfc8..332676911 100644 --- a/interface/client/templates/webviewEvents.js +++ b/interface/client/templates/webviewEvents.js @@ -1,4 +1,31 @@ +showError = function(tabId, e){ + if(e.isMainFrame || e.killed) { + var url, + path = 'file://'+ dirname + '/errorPages/'; + + if(e.killed) { + e.errorCode = 500; + } + + switch(e.errorCode) { + case -105: + url = path +'404.html'; + break; + case 500: + url = path +'500.html'; + break; + } + + if(url) { + Tabs.update(tabId, {$set: { + redirect: url + }}); + } + } +}; + + webviewChangeUrl = function(tabId, e){ if(e.type === 'did-navigate-in-page' && !e.isMainFrame) return; diff --git a/interface/i18n/mist.en.i18n.json b/interface/i18n/mist.en.i18n.json index 2b0b04f29..39df06f00 100644 --- a/interface/i18n/mist.en.i18n.json +++ b/interface/i18n/mist.en.i18n.json @@ -64,6 +64,10 @@ "ethereumNode": "Ethereum Node", "network": "Network", "mainNetwork": "Main Network", + "nodeMode": "Chain download", + "fullNode": "Store full blockchain", + "lightNode": "Use light Node (experimental!)", + "mainNetwork": "Main Network", "startMining": "⛏ Start Mining (Testnet only)", "stopMining": "⛏ Stop Mining" }, @@ -226,7 +230,7 @@ "lookupDataExplainer": "Look this up on the internet" }, "onboarding": { - "description" : "Ethereum is a public blockchain that features a turing complete programming for building solid, decentralized applications.", + "description" : "Ethereum is a platform for decentralized blockchain apps with a fully featured programming language", "goToTestnet" : "Use the test network", "goToTestnetDescription" : "Test the technology freely in a sandboxed testnet, without using real ether.", "gotoMainnet" : "Use the main network", @@ -253,6 +257,7 @@ "downloadingBlocks": "Downloading blocks", "syncMessage": "Block __displayBlock__ of __highestBlockString__", "syncMessageWithStates": "Block __displayBlock__ of __highestBlockString__ (Chain structure __statesPercent__%)", + "startingSync": "Getting ready to sync..", "tutorial1Description" : "

Now the only thing left to do is wait for the download to finish. Here's some reading suggestions:

Make your own money

Make a cryptocurrency with a fixed market supply, tokens representing real world assets, etc

", "tutorial2Description" : "

Create a crowdsale

Raise funds for a common goal, fully trustable without a third party. Sidestep the hurdle of traditional funding system and go directly to the source by funding an organization via the blockchain.

", "tutorial3Description" : "

Create a blockchain organization

Create an autonomous organization with rules on spending money and making decisions for you and your investors.

", diff --git a/main.js b/main.js index 302a1662e..db79233e8 100644 --- a/main.js +++ b/main.js @@ -415,7 +415,7 @@ onReady = () => { splashWindow.show(); } - if (Settings.inAutoTestMode) { + if (!Settings.inAutoTestMode) { return syncResultPromise; } diff --git a/modules/clientBinaryManager.js b/modules/clientBinaryManager.js index 7cb5c5e1a..af86061d1 100644 --- a/modules/clientBinaryManager.js +++ b/modules/clientBinaryManager.js @@ -73,6 +73,8 @@ class Manager extends EventEmitter { log.warn('Error fetching client binaries config from repo', err); }) .then((latestConfig) => { + if(!latestConfig) return; + let localConfig; let skipedVersion; const nodeVersion = latestConfig.clients[nodeType].version; diff --git a/modules/dbSync.js b/modules/dbSync.js index eda6e81f8..b21f9b22d 100644 --- a/modules/dbSync.js +++ b/modules/dbSync.js @@ -14,13 +14,13 @@ exports.backendSyncInit = function () { ipc.on('dbSync-add', (event, args) => { let collName = args.collName, - coll = db.getCollection('UI_'+ collName); + coll = db.getCollection(`UI_${collName}`); log.trace('dbSync-add', collName, args._id); const _id = args._id; - if (!coll.by("_id", _id)) { + if (!coll.by('_id', _id)) { args.fields._id = _id; coll.insert(args.fields); } @@ -28,12 +28,12 @@ exports.backendSyncInit = function () { ipc.on('dbSync-changed', (event, args) => { let collName = args.collName, - coll = db.getCollection('UI_'+ collName); + coll = db.getCollection(`UI_${collName}`); log.trace('dbSync-changed', collName, args._id); const _id = args._id; - const item = coll.by("_id", _id); + const item = coll.by('_id', _id); if (item) { for (const k in args.fields) { @@ -50,12 +50,12 @@ exports.backendSyncInit = function () { ipc.on('dbSync-removed', (event, args) => { let collName = args.collName, - coll = db.getCollection('UI_'+ collName); + coll = db.getCollection(`UI_${collName}`); log.trace('dbSync-removed', collName, args._id); const _id = args._id; - const item = coll.by("_id", _id); + const item = coll.by('_id', _id); if (item) { coll.remove(item); @@ -67,7 +67,7 @@ exports.backendSyncInit = function () { // Get all data (synchronous) ipc.on('dbSync-reloadSync', (event, args) => { let collName = args.collName, - coll = db.getCollection('UI_'+ collName), + coll = db.getCollection(`UI_${collName}`), docs = coll.find(); log.debug('dbSync-reloadSync, no. of docs:', collName, docs.length); @@ -88,15 +88,15 @@ exports.backendSyncInit = function () { }); }; -var syncDataFromBackend = function(coll){ - let ipc = ipcRenderer; +const syncDataFromBackend = function (coll) { + const ipc = ipcRenderer; - let collName = coll._name; + const collName = coll._name; console.debug('Load collection data from backend: ', collName); return new Promise((resolve, reject) => { - let dataJson = ipc.sendSync('dbSync-reloadSync', { + const dataJson = ipc.sendSync('dbSync-reloadSync', { collName, }); @@ -105,8 +105,9 @@ var syncDataFromBackend = function(coll){ coll.remove({}); - if(!dataJson.length) + if (!dataJson.length) { resolve(); + } // we do inserts slowly, to avoid race conditions when it comes // to updating the UI @@ -120,10 +121,11 @@ var syncDataFromBackend = function(coll){ record.redirect = null; } - if(record._id) + if (record._id) { coll.upsert(record._id, record); - else + } else { coll.insert(record); + } } catch (err) { console.error(err.toString()); } @@ -138,7 +140,7 @@ var syncDataFromBackend = function(coll){ } catch (err) { reject(err); } - }) + }); }; exports.syncDataFromBackend = syncDataFromBackend; @@ -146,8 +148,8 @@ exports.frontendSyncInit = function (coll) { let ipc = ipcRenderer, syncDoneResolver; - let collName = coll._name; - + const collName = coll._name; + coll.onceSynced = new Promise((resolve, reject) => { syncDoneResolver = resolve; }); diff --git a/modules/ethereumNode.js b/modules/ethereumNode.js index 2932dac0a..b37c67e8e 100644 --- a/modules/ethereumNode.js +++ b/modules/ethereumNode.js @@ -315,13 +315,14 @@ class EthereumNode extends EventEmitter { this._network = network; this._type = nodeType; - let client = ClientBinaryManager.getClient(nodeType); + const client = ClientBinaryManager.getClient(nodeType); let binPath; - if(client) + if (client) { binPath = client.binPath; - else + } else { throw new Error(`Node "${nodeType}" binPath is not available.`); + } log.info(`Start node using ${binPath}`); @@ -358,7 +359,7 @@ class EthereumNode extends EventEmitter { // START MAINNET else { args = (nodeType === 'geth') - ? ['--fast', '--cache', '1024'] + ? ['--fast', '--cache', '1024'] : ['--unsafe-transactions']; } diff --git a/modules/ipc/dechunker.js b/modules/ipc/dechunker.js index a77e54866..3bf74b22c 100644 --- a/modules/ipc/dechunker.js +++ b/modules/ipc/dechunker.js @@ -39,8 +39,9 @@ module.exports = class Dechunker { return _.each(dechunkedData, (data) => { // prepend the last chunk - if (this.lastChunk) - { data = this.lastChunk + data; } + if (this.lastChunk) { + data = this.lastChunk + data; + } let result = data; diff --git a/modules/ipc/ipcProviderBackend.js b/modules/ipc/ipcProviderBackend.js index 8ce1b4040..794fc500e 100644 --- a/modules/ipc/ipcProviderBackend.js +++ b/modules/ipc/ipcProviderBackend.js @@ -100,8 +100,7 @@ class IpcProviderBackend { socket.destroy().finally(() => { - if(!owner.isDestroyed()) - owner.send(`ipcProvider-${ev}`, JSON.stringify(data)); + if (!owner.isDestroyed()) { owner.send(`ipcProvider-${ev}`, JSON.stringify(data)); } }); delete this._connections[ownerId]; @@ -110,8 +109,9 @@ class IpcProviderBackend { }); socket.on('connect', (data) => { - if(!owner.isDestroyed()) + if (!owner.isDestroyed()) { owner.send('ipcProvider-connect', JSON.stringify(data)); + } }); // pass notifications back up the chain @@ -124,8 +124,9 @@ class IpcProviderBackend { data = this._makeResponsePayload(data, data); } - if(!owner.isDestroyed()) + if (!owner.isDestroyed()) { owner.send('ipcProvider-data', JSON.stringify(data)); + } }); } }) @@ -172,8 +173,9 @@ class IpcProviderBackend { } }) .then(() => { - if(!owner.isDestroyed()) + if (!owner.isDestroyed()) { owner.send('ipcProvider-setWritable', true); + } return this._connections[ownerId]; }); @@ -356,7 +358,7 @@ class IpcProviderBackend { if (isSync) { event.returnValue = returnValue; - } else if(!event.sender.isDestroyed()) { + } else if (!event.sender.isDestroyed()) { event.sender.send('ipcProvider-data', returnValue); } }); diff --git a/modules/ipc/methods/eth_sendTransaction.js b/modules/ipc/methods/eth_sendTransaction.js index fc90f073f..8f37ddc10 100644 --- a/modules/ipc/methods/eth_sendTransaction.js +++ b/modules/ipc/methods/eth_sendTransaction.js @@ -70,8 +70,7 @@ module.exports = class extends BaseProcessor { ipc.once('backendAction_unlockedAccountAndSentTransaction', (ev, err, result) => { if (Windows.getById(ev.sender.id) === modalWindow - && !modalWindow.isClosed) - { + && !modalWindow.isClosed) { if (err || !result) { this._log.debug('Confirmation error', err); diff --git a/modules/ipcCommunicator.js b/modules/ipcCommunicator.js index a833daea5..0c527e56e 100644 --- a/modules/ipcCommunicator.js +++ b/modules/ipcCommunicator.js @@ -62,7 +62,7 @@ ipc.on('backendAction_windowCallback', (e, value1, value2, value3) => { const windowId = e.sender.id; const senderWindow = Windows.getById(windowId); - if(senderWindow.callback) { + if (senderWindow.callback) { senderWindow.callback(value1, value2, value3); } }); @@ -100,10 +100,11 @@ ipc.on('backendAction_setLanguage', (e, lang) => { ipc.on('backendAction_stopWebviewNavigation', (e, id) => { console.log('webcontent ID', id); - var webContent = webContents.fromId(id); + const webContent = webContents.fromId(id); - if(webContent && !webContent.isDestroyed()) + if (webContent && !webContent.isDestroyed()) { webContent.stop(); + } e.returnValue = true; }); @@ -126,8 +127,9 @@ ipc.on('backendAction_importPresaleFile', (e, path, pw) => { }); nodeProcess.stdout.on('data', (data) => { var data = data.toString(); - if (data) - { log.info('Imported presale: ', data); } + if (data) { + log.info('Imported presale: ', data); + } if (/Decryption failed|not equal to expected addr|could not decrypt/.test(data)) { e.sender.send('uiAction_importedPresaleFile', 'Decryption Failed'); diff --git a/modules/menuItems.js b/modules/menuItems.js index 39b26a8c2..812ebf349 100644 --- a/modules/menuItems.js +++ b/modules/menuItems.js @@ -13,8 +13,7 @@ const ClientBinaryManager = require('./clientBinaryManager'); const switchForSystem = function (options) { if (process.platform in options) { return options[process.platform]; - } - else if ('default' in options) { + } else if ('default' in options) { return options.default; } }; diff --git a/modules/nodeSync.js b/modules/nodeSync.js index 294ffca8e..558b19584 100644 --- a/modules/nodeSync.js +++ b/modules/nodeSync.js @@ -149,7 +149,7 @@ class NodeSync extends EventEmitter { const diff = now - +blockResult.timestamp; - log.debug(`Last block: ${blockResult.number}, ${diff}s ago`); + log.debug(`Last block: ${Number(blockResult.number)}, ${diff}s ago`); // need sync if > 1 minute if (diff > 60) { diff --git a/modules/preloader/browser.js b/modules/preloader/browser.js index 92b1241e5..1ba237466 100644 --- a/modules/preloader/browser.js +++ b/modules/preloader/browser.js @@ -18,7 +18,7 @@ ipcRenderer.sendToHost('setWebviewId'); ipcRenderer.send('ipcProvider-destroy'); // Security -process.on('loaded',function () { +process.on('loaded', function () { Object.freeze(window.JSON); // Object.freeze(window.Function); // Object.freeze(window.Function.prototype); diff --git a/modules/preloader/include/getFavicon.js b/modules/preloader/include/getFavicon.js index 6e67db48f..be7e26f2d 100644 --- a/modules/preloader/include/getFavicon.js +++ b/modules/preloader/include/getFavicon.js @@ -12,10 +12,11 @@ const { ipcRenderer } = require('electron'); function DOMContentLoaded(event) { const icon = document.querySelector('link[rel="apple-touch-icon"]') || document.querySelector('link[type="image/x-icon"]') || document.querySelector('link[rel="shortcut"]') || document.querySelector('link[rel="shortcut icon"]') || document.querySelector('link[rel="icon"]'); - if (icon) - { ipcRenderer.sendToHost('favicon', icon.href); } - else - { ipcRenderer.sendToHost('favicon', null); } + if (icon) { + ipcRenderer.sendToHost('favicon', icon.href); + } else { + ipcRenderer.sendToHost('favicon', null); + } document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false); } diff --git a/modules/preloader/include/getMetaTags.js b/modules/preloader/include/getMetaTags.js index 233a1260b..6186ddade 100644 --- a/modules/preloader/include/getMetaTags.js +++ b/modules/preloader/include/getMetaTags.js @@ -4,7 +4,7 @@ Gest the meta[name="ethereum-dapp-url-bar-style"] meta tag @module getMetaTags */ -const { ipcRenderer } = require('electron'); +const { ipcRenderer } = require('electron'); module.export = (function () { document.addEventListener('DOMContentLoaded', DOMContentLoaded, false); @@ -12,10 +12,11 @@ module.export = (function () { function DOMContentLoaded(event) { const appBar = document.querySelector('meta[name="ethereum-dapp-url-bar-style"]'); - if (appBar) - { ipcRenderer.sendToHost('appBar', appBar.content); } - else - { ipcRenderer.sendToHost('appBar', null); } + if (appBar) { + ipcRenderer.sendToHost('appBar', appBar.content); + } else { + ipcRenderer.sendToHost('appBar', null); + } document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false); } diff --git a/modules/preloader/include/mistAPI.js b/modules/preloader/include/mistAPI.js index bd2389128..996dcbf57 100644 --- a/modules/preloader/include/mistAPI.js +++ b/modules/preloader/include/mistAPI.js @@ -102,7 +102,7 @@ module.exports = () => { @param {Function} callback Change the callback to be called when the menu is pressed. */ add(id, options, callback) { - var args = Array.prototype.slice.call(arguments); + const args = Array.prototype.slice.call(arguments); callback = _.isFunction(args[args.length - 1]) ? args.pop() : null; options = _.isObject(args[args.length - 1]) ? args.pop() : null; id = _.isString(args[args.length - 1]) || _.isFinite(args[args.length - 1]) ? args.pop() : null; diff --git a/modules/preloader/include/openExternal.js b/modules/preloader/include/openExternal.js index 962eebb19..b4f484d64 100644 --- a/modules/preloader/include/openExternal.js +++ b/modules/preloader/include/openExternal.js @@ -12,10 +12,11 @@ document.addEventListener('click', (e) => { let node = false; - if (e.target.nodeName === 'A') - { node = e.target; } - else if (e.target.parentNode && e.target.parentNode.nodeName === 'A') - { node = e.target.parentNode; } + if (e.target.nodeName === 'A') { + node = e.target; + } else if (e.target.parentNode && e.target.parentNode.nodeName === 'A') { + node = e.target.parentNode; + } // open in browser if (node && node.attributes.target && node.attributes.target.value === '_blank') { diff --git a/modules/preloader/include/openPopup.js b/modules/preloader/include/openPopup.js index 54a1e1278..ddaa73720 100644 --- a/modules/preloader/include/openPopup.js +++ b/modules/preloader/include/openPopup.js @@ -10,10 +10,11 @@ const { remote } = require('electron'); document.addEventListener('click', (e) => { let node = false; - if (e.target.nodeName === 'A') - { node = e.target; } - else if (e.target.parentNode && e.target.parentNode.nodeName === 'A') - { node = e.target.parentNode; } + if (e.target.nodeName === 'A') { + node = e.target; + } else if (e.target.parentNode && e.target.parentNode.nodeName === 'A') { + node = e.target.parentNode; + } // open popup if (node && node.attributes.target && node.attributes.target.value === '_popup') { @@ -25,6 +26,6 @@ document.addEventListener('click', (e) => { nodeIntegration: false, } }); - win.loadURL(node.href, true); + win.loadURL(node.href); } }, false); diff --git a/modules/preloader/mistUI.js b/modules/preloader/mistUI.js index b1dd85be2..6f401ab6f 100644 --- a/modules/preloader/mistUI.js +++ b/modules/preloader/mistUI.js @@ -56,11 +56,12 @@ ipcRenderer.on('uiAction_windowMessage', (e, type, id, error, value) => { } // forward to the webview (TODO: remove and manage in the ipcCommunicator?) - var tab = Tabs.findOne({ webviewId: id }); - if(tab) { - webview = $('webview[data-id='+ tab._id +']')[0]; - if(webview) + const tab = Tabs.findOne({ webviewId: id }); + if (tab) { + webview = $(`webview[data-id=${tab._id}]`)[0]; + if (webview) { webview.send('uiAction_windowMessage', type, error, value); + } } }); @@ -73,20 +74,22 @@ ipcRenderer.on('uiAction_enableBlurOverlay', (e, value) => { ipcRenderer.on('uiAction_toggleWebviewDevTool', (e, id) => { const webview = Helpers.getWebview(id); - if (!webview) - { return; } + if (!webview) { + return; + } - if (webview.isDevToolsOpened()) - { webview.closeDevTools(); } - else - { webview.openDevTools(); } + if (webview.isDevToolsOpened()) { + webview.closeDevTools(); + } else { + webview.openDevTools(); + } }); // randomize accounts and drop half // also certainly remove the web3.ethbase one -var randomizeAccounts = (acc, coinbase) => { - var accounts = _.shuffle(acc); +const randomizeAccounts = (acc, coinbase) => { + let accounts = _.shuffle(acc); accounts = _.rest(accounts, (accounts.length / 2).toFixed(0)); accounts = _.without(accounts, coinbase); return accounts; @@ -117,7 +120,7 @@ ipcRenderer.on('uiAction_runTests', (e, type) => { // update the permissions, when accounts change Tracker.autorun(() => { - var accountList = _.pluck(EthAccounts.find({}, { fields: { address: 1 } }).fetch(), 'address'); + const accountList = _.pluck(EthAccounts.find({}, { fields: { address: 1 } }).fetch(), 'address'); Tabs.update('tests', { $set: { 'permissions.accounts': randomizeAccounts(accountList, coinbase), @@ -136,18 +139,21 @@ const menu = new Menu(); // menu.append(new MenuItem({ type: 'separator' })); menu.append(new MenuItem({ label: i18n.t('mist.rightClick.reload'), accelerator: 'Command+R', click() { const webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if (webview) - { webview.reloadIgnoringCache(); } + if (webview) { + webview.reloadIgnoringCache(); + } } })); menu.append(new MenuItem({ label: i18n.t('mist.rightClick.openDevTools'), click() { const webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if (webview) - { webview.openDevTools(); } + if (webview) { + webview.openDevTools(); + } } })); menu.append(new MenuItem({ label: i18n.t('mist.rightClick.inspectElements'), click() { const webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if (webview) - { webview.inspectElement(currentMousePosition.x, currentMousePosition.y); } + if (webview) { + webview.inspectElement(currentMousePosition.x, currentMousePosition.y); + } } })); @@ -167,7 +173,8 @@ document.addEventListener('keydown', (e) => { // RELOAD current webview if (e.metaKey && e.keyCode === 82) { const webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if (webview) - { webview.reloadIgnoringCache(); } + if (webview) { + webview.reloadIgnoringCache(); + } } }, false); diff --git a/modules/preloader/walletMain.js b/modules/preloader/walletMain.js index 9b4909579..7bb9115f8 100644 --- a/modules/preloader/walletMain.js +++ b/modules/preloader/walletMain.js @@ -17,6 +17,7 @@ setTimeout(() => { }, 1000); setTimeout(() => { - if (document.getElementsByTagName('html')[0]) - { document.getElementsByTagName('html')[0].className = window.platform; } + if (document.getElementsByTagName('html')[0]) { + document.getElementsByTagName('html')[0].className = window.platform; + } }, 500); diff --git a/package.json b/package.json index fff594d84..2608274c4 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "url": "https://github.com/ethereum/mist.git" }, "scripts": { + "postinstall": "cd interface && yarn", "ci": "gulp --platform=linux" }, "main": "main.js", diff --git a/tests/_base.js b/tests/_base.js index 5a23b6f27..5c9f664ce 100644 --- a/tests/_base.js +++ b/tests/_base.js @@ -100,7 +100,7 @@ exports.mocha = function (_module, options) { requireName: 'electronRequire', startTimeout: 10000, waitTimeout: 10000, - quitTimeout: 10000, + quitTimeout: 3000, path: appPath, args: [ '--mode', options.app, @@ -119,9 +119,6 @@ exports.mocha = function (_module, options) { yield this.client.waitUntilWindowLoaded(); - // wait a small amount of time to ensure main app window is ready with data - yield Q.delay(8000); - // console.log(this.app.chromeDriver.logLines); /* @@ -131,6 +128,26 @@ exports.mocha = function (_module, options) { this[key] = genomatic.bind(Utils[key], this); } + // Loop over windows trying to select Main Window + let app = this; + let selectMainWindow = function* (mainWindowSearch) { + let windowHandles = (yield app.client.windowHandles()).value; + + for (let handle in windowHandles) { + yield app.client.window(windowHandles[handle]); + const windowUrl = yield app.client.getUrl(); + const isMainWindow = mainWindowSearch.test(windowUrl); + if (isMainWindow) return true; + } + + // not main window. try again after 1 second. + yield Q.delay(1000); + yield selectMainWindow(mainWindowSearch); + } + + const mainWindowSearch = (options.app === 'wallet') ? /^file:\/\/\/$/ : /interface\/index\.html$/; + yield selectMainWindow(mainWindowSearch); + this.mainWindowHandle = (yield this.client.windowHandle()).value; }, diff --git a/tests/mist/basic.test.js b/tests/mist/basic.test.js index 20c277aaa..cd8af0dd1 100644 --- a/tests/mist/basic.test.js +++ b/tests/mist/basic.test.js @@ -2,114 +2,93 @@ const _ = require('underscore'); const Q = require('bluebird'); const fs = require('fs'); const path = require('path'); - +const should = require('chai').should(); const test = require('../_base').mocha(module, { app: 'mist', }); - -test.title = function* () { +test['Check for Mist title'] = function* () { yield this.client.window(this.mainWindowHandle); - - (yield this.client.getTitle()).should.eql('Ethereum Wallet'); + (yield this.client.getTitle()).should.eql('Mist'); }; - -test['account balances'] = function* () { - const web3 = this.web3; +test['Sanity Check: main window is focused'] = function* () { const client = this.client; + yield client.window(this.mainWindowHandle); - const realBalances = this.getRealAccountBalances(); - const appBalances = this.getUiAccountBalances(); - - appBalances.should.eql(realBalances); + (yield client.getUrl()).should.match(/interface\/index\.html$/); }; - -// test['create account'] = function*() { -// const web3 = this.web3; -// const client = this.client; - -// const originalBalances = yield this.getRealAccountBalances(); - -// yield _createNewAccount.call(this); - -// const realBalances = yield this.getRealAccountBalances(); -// const appBalances = yield this.getUiAccountBalances(); - -// _.keys(realBalances).length.should.eql(_.keys(originalBalances).length + 1); -// appBalances.should.eql(realBalances); -// }; - - -test['deposit into account'] = function* () { - const web3 = this.web3; +test['Browser bar should render urls with separators'] = function* () { const client = this.client; + yield client.window(this.mainWindowHandle); - const accounts = web3.eth.accounts; - - yield _createNewAccount.call(this); - - const newAccount = _.difference(web3.eth.accounts, accounts)[0]; - - yield this.openAccountInUi(newAccount); - - // links - const accLinks = yield this.getUiElements('.dapp-actionbar li'); - yield client.elementIdClick(accLinks[0].ELEMENT); + yield client.setValue('#url-input', 'http://example.com/page?param=value'); + yield client.submitForm('form.url'); - // fill in send form and submit - yield _completeSendForm.call(this, 1); + yield client.waitUntil(() => { + return client.getText('.url-breadcrumb').then((e) => { + return e === 'example.com ▸ page'; + }); + }, 3000, 'expected breadcrumb to render as HTML encoded'); +}; - // do some mining - yield this.startMining(); - yield Q.delay(10000); - yield this.stopMining(); +test['Browser bar should not render script tags on breadcrumb view'] = function* () { + const client = this.client; + yield client.window(this.mainWindowHandle); - // check balances - const realBalances = yield this.getRealAccountBalances(); + yield client.setValue('#url-input', ''); + yield client.submitForm('form.url'); - realBalances[newAccount].should.eql(1); + yield client.waitUntil(() => { + return client.getText('.url-breadcrumb').then((e) => { + // HTML encoded version of input + return e === '%3Cscript%3Ealert%28%29%3C ▸ script%3E'; + }); + }, 1000, 'expected breadcrumb to render as HTML encoded'); }; - -const _createNewAccount = function* () { +test['Browser bar should not render script tags in disguise on breadcrumb view'] = function* () { const client = this.client; + yield client.window(this.mainWindowHandle); - // open password window - yield this.openAndFocusNewWindow(() => { - return client.click('button.create.account'); - }); + yield client.setValue('#url-input', '<script>alert()</script>'); + yield client.submitForm('form.url'); - // enter password - yield client.setValue('form .password', '1234'); - yield client.click('form button.ok'); + yield client.waitUntil(() => { + return client.getText('.url-breadcrumb').then((e) => { + return e === '%3Cscript%3Ealert%28%29%3C ▸ script%3E'; + }); + }, 1000, 'expected breadcrumb to render as HTML encoded'); +}; - // re-enter password - yield client.setValue('form .password-repeat', '1234'); - yield client.click('form button.ok'); +test['Browser bar should not render arbitrary code as HTML'] = function* () { + const client = this.client; + yield client.window(this.mainWindowHandle); - yield Q.delay(10000); + yield client.setValue('#url-input', '