diff --git a/API.js b/API.js new file mode 100644 index 000000000..5b4a969ed --- /dev/null +++ b/API.js @@ -0,0 +1,184 @@ +var WalletFactory = require('./js/models/core/WalletFactory'); + +var API = function(opts) { + this._init(opts); +}; + +API.prototype._init = function(opts) { + var self = this; + + opts = opts || {}; + self.opts = opts; + + this.walletFactory = new WalletFactory(opts); +}; + +API._coerceArgTypes = function(args, argTypes) { + for (var i in args) { + var arg = args[i]; + var argType = argTypes[i][1]; + if (typeof arg == 'string') { + switch (argType) { + case 'object': + args[i] = JSON.parse(arg); + break; + case 'number': + args[i] = Number(arg); + break; + } + } + } + + return args; +}; + +API.prototype._command = function(command, args, callback) { + var self = this; + + if (!command || command[0] == "_") + return callback(new Error('invalid command')); + + if (!API._checkArgTypes(command, args)) { + var argTypes = API.prototype[command].argTypes; + API._coerceArgTypes(args, argTypes) + if (!API._checkArgTypes(command, args)) + throw new Error('invalid arguments'); + } + + if (typeof self["_cmd_" + command] == 'function') { + var f = API.prototype[command]; + if (f.argTypes[f.argTypes.length - 1][1] == 'function') + return self["_cmd_" + command].apply(self, args.concat([callback])); + else + return callback(null, self["_cmd_" + command].apply(self, args)); + }; + + return callback(new Error('invalid command')); +}; + +API._checkArgTypes = function(command, args) { + var f = API.prototype[command]; + + if (f.argTypes.length != args.length) { + + //if the function doesn't have a callback + if (!(f.argTypes.length == args.length + 1 && f.argTypes[f.argTypes.length - 1][1] == 'function')) + return false; + } + + for (var i in args) { + if (typeof args[i] != f.argTypes[i][1]) + return false; + } + return true; +}; + +function decorate(command, argTypes) { + var d = function() { + API.prototype._command.call(this, command, Array.prototype.slice.call(arguments, 0)); + }; + + d.argTypes = argTypes; + + return d; +}; + +API.prototype._cmd_echo = function(str, callback) { + var self = this; + + return callback(null, str); +}; + +API.prototype.echo = decorate('echo', [ + ['str', 'string'], + ['callback', 'function'] +]); + +API.prototype._cmd_echoNumber = function(num, callback) { + var self = this; + + return callback(null, num); +}; + +API.prototype.echoNumber = decorate('echoNumber', [ + ['num', 'number'], + ['callback', 'function'] +]); + +API.prototype._cmd_echoObject = function(obj, callback) { + var self = this; + + return callback(null, obj); +}; + +API.prototype.echoObject = decorate('echoObject', [ + ['obj', 'object'], + ['callback', 'function'] +]); + +/* +API.prototype.getBalance = function(callback) { + var self = this; + + return callback(null, self.wallet.getBalance([])); +}; + +API.prototype.getBalance.argTypes = + [ + ['callback', 'function'] + ]; +*/ + +API.prototype._cmd_getArgTypes = function(command, callback) { + var self = this; + + if (command[0] == '_' || typeof API.prototype[command] != 'function') + return callback(new Error('Invalid command')); + + var argTypes = API.prototype[command].argTypes; + + return callback(null, argTypes); +}; + +API.prototype.getArgTypes = decorate('getArgTypes', [ + ['command', 'string'], + ['callback', 'function'] +]); + +API.prototype._cmd_getCommands = function(callback) { + var self = this; + + var fs = []; + + for (var i in API.prototype) { + var f = API.prototype[i]; + if (typeof f == 'function' && i[0] != "_") + fs.push(i); + }; + + return callback(null, fs); +}; + +API.prototype.getCommands = decorate('getCommands', [ + ['callback', 'function'] +]); + +API.prototype._cmd_getWallets = function(callback) { + var self = this; + + return callback(null, self.walletFactory.getWallets()); +}; + +API.prototype.getWallets = decorate('getWallets', [ + ['callback', 'function'] +]); + +API.prototype._cmd_help = function(callback) { + this._cmd_getCommands.apply(this, arguments); +}; + +API.prototype.help = decorate('help', [ + ['callback', 'function'] +]); + +module.exports = API; diff --git a/js/models/blockchain/Insight.js b/js/models/blockchain/Insight.js index bafde9861..b0a0b7654 100644 --- a/js/models/blockchain/Insight.js +++ b/js/models/blockchain/Insight.js @@ -1,6 +1,5 @@ 'use strict'; -var imports = require('soop').imports(); var bitcore = require('bitcore'); var coinUtil = bitcore.util; var preconditions = require('preconditions').singleton(); @@ -286,4 +285,4 @@ Insight.prototype._request = function(options, callback) { } }; -module.exports = require('soop')(Insight); +module.exports = Insight; diff --git a/js/models/core/Message.js b/js/models/core/Message.js index b8b37f772..ad6324440 100644 --- a/js/models/core/Message.js +++ b/js/models/core/Message.js @@ -1,6 +1,5 @@ 'use strict'; -var imports = require('soop').imports(); var bitcore = require('bitcore'); /* Encrypted, authenticated messages to be shared between copayers */ @@ -146,4 +145,4 @@ Message._verify = function(pubkey, signature, payload) { return v; }; -module.exports = require('soop')(Message); +module.exports = Message; diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index 81dda34f5..2a4fa7542 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -1,8 +1,8 @@ 'use strict'; -var imports = require('soop').imports(); var http = require('http'); -var EventEmitter = imports.EventEmitter || require('events').EventEmitter; +var EventEmitter = require('events').EventEmitter; +var nodeUtil = require('util'); var async = require('async'); var preconditions = require('preconditions').singleton(); var parseBitcoinURI = require('./HDPath').parseBitcoinURI; @@ -64,6 +64,7 @@ function Wallet(opts) { this.network.setHexNonces(opts.networkNonces); } +nodeUtil.inherits(Wallet, EventEmitter); Wallet.builderOpts = { lockTime: null, @@ -72,7 +73,6 @@ Wallet.builderOpts = { feeSat: null, }; -Wallet.parent = EventEmitter; Wallet.prototype.log = function() { if (!this.verbose) return; if (console) @@ -1849,4 +1849,4 @@ Wallet.request = function(options, callback) { return ret; }; -module.exports = require('soop')(Wallet); +module.exports = Wallet; diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index cc56cd976..f40a02e4d 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -1,7 +1,5 @@ 'use strict'; -var imports = require('soop').imports(); - var TxProposals = require('./TxProposals'); var PublicKeyRing = require('./PublicKeyRing'); var PrivateKey = require('./PrivateKey'); @@ -235,7 +233,6 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras }); self.network.on('serverError', function() { - console.log('[WalletFactory.js.236]'); //TODO return cb('joinError'); }); @@ -260,4 +257,4 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras }); }; -module.exports = require('soop')(WalletFactory); +module.exports = WalletFactory; diff --git a/js/models/network/WebRTC.js b/js/models/network/WebRTC.js index ea88c890a..256bc3ec7 100644 --- a/js/models/network/WebRTC.js +++ b/js/models/network/WebRTC.js @@ -1,10 +1,9 @@ 'use strict'; -var imports = require('soop').imports(); -var EventEmitter = imports.EventEmitter || require('events').EventEmitter; +var EventEmitter = require('events').EventEmitter; var bitcore = require('bitcore'); var util = bitcore.util; -var extend = require('util')._extend; +var nodeUtil = require('util'); var Message = require('../core/Message'); /* * Emits @@ -39,7 +38,7 @@ function Network(opts) { this.cleanUp(); } -Network.parent = EventEmitter; +nodeUtil.inherits(Network, EventEmitter); Network.prototype.cleanUp = function() { this.started = false; @@ -64,7 +63,6 @@ Network.prototype.cleanUp = function() { this.removeAllListeners(); }; -Network.parent = EventEmitter; // Array helpers Network._arrayDiff = function(a, b) { @@ -524,4 +522,4 @@ Network.prototype.disconnect = function(cb, forced) { }); }; -module.exports = require('soop')(Network); +module.exports = Network; diff --git a/js/models/storage/File.js b/js/models/storage/File.js index abee8ee6b..ec0880145 100644 --- a/js/models/storage/File.js +++ b/js/models/storage/File.js @@ -1,6 +1,5 @@ 'use strict'; -var imports = require('soop').imports(); -var fs = imports.fs || require('fs'); +var fs = require('fs'); var CryptoJS = require('node-cryptojs-aes').CryptoJS; var passwords = []; @@ -37,6 +36,8 @@ Storage.prototype._decryptObj = function(base64) { Storage.prototype.load = function(walletId, callback) { var self = this; fs.readFile(walletId, function(err, base64) { + if (typeof base64 !== 'string') + base64 = base64.toString(); var data = self._decryptObj(base64); if (err) return callback(err); @@ -145,4 +146,4 @@ Storage.prototype.clearAll = function(callback) { this.save(callback); }; -module.exports = require('soop')(Storage); +module.exports = Storage; diff --git a/js/models/storage/LocalEncrypted.js b/js/models/storage/LocalEncrypted.js index 0ea61f217..da1d99742 100644 --- a/js/models/storage/LocalEncrypted.js +++ b/js/models/storage/LocalEncrypted.js @@ -1,6 +1,6 @@ 'use strict'; -var imports = require('soop').imports(); +var CryptoJS = require('node-cryptojs-aes').CryptoJS; var id = 0; @@ -215,4 +215,4 @@ Storage.prototype.import = function(base64) { return decryptedObj; }; -module.exports = require('soop')(Storage); +module.exports = Storage; diff --git a/package.json b/package.json index 0509e251e..d41d1a13a 100644 --- a/package.json +++ b/package.json @@ -36,34 +36,38 @@ "bitcoin" ], "devDependencies": { - "mocha-lcov-reporter": "0.0.1", - "travis-cov": "0.2.5", - "chai": "1.9.1", + "async": "0.9.0", + "blanket": "1.1.6", + "browser-pack": "2.0.1", + "browserify": "3.32.1", "buffertools": "2.0.1", + "chai": "1.9.1", + "cli-color": "0.3.2", "commander": "2.1.0", - "uglifyify": "1.2.3", - "soop": "0.1.5", - "grunt-contrib-watch": "0.5.3", - "istanbul": "0.2.10", - "grunt-mocha-test": "0.8.2", + "coveralls": "2.10.0", + "express": "4.0.0", "github-releases": "0.2.0", + "grunt-browserify": "2.0.8", + "grunt-contrib-watch": "0.5.3", "grunt-markdown": "0.5.0", "browser-pack": "2.0.1", "bitcore": "0.1.35", "node-cryptojs-aes": "0.4.0", "blanket": "1.1.6", "express": "4.0.0", + "grunt-mocha-test": "0.8.2", "grunt-shell": "0.6.4", - "karma-mocha": "0.1.3", - "async": "0.9.0", - "mocha": "1.18.2", - "browserify": "3.32.1", - "karma-phantomjs-launcher": "^0.1.4", - "coveralls": "2.10.0", - "grunt-browserify": "2.0.8", - "karma-chrome-launcher": "0.1.3", + "istanbul": "0.2.10", "karma": "0.12.9", - "cli-color": "0.3.2" + "karma-chrome-launcher": "0.1.3", + "karma-mocha": "0.1.3", + "karma-phantomjs-launcher": "^0.1.4", + "mocha": "1.18.2", + "mocha-lcov-reporter": "0.0.1", + "mock-fs": "^2.3.1", + "node-cryptojs-aes": "0.4.0", + "travis-cov": "0.2.5", + "uglifyify": "1.2.3" }, "main": "app.js", "homepage": "https://github.com/bitpay/copay", diff --git a/test/index.html b/test/index.html index f755dc486..0a0d77852 100644 --- a/test/index.html +++ b/test/index.html @@ -20,13 +20,11 @@ - + - diff --git a/test/mocks/FakeLocalStorage.js b/test/mocks/FakeLocalStorage.js index 6029b2601..e39280719 100644 --- a/test/mocks/FakeLocalStorage.js +++ b/test/mocks/FakeLocalStorage.js @@ -24,4 +24,4 @@ FakeLocalStorage.setItem = function(k, v) { this.length = Object.keys(ls).length; }; -module.exports = require('soop')(FakeLocalStorage); +module.exports = FakeLocalStorage; diff --git a/test/mocks/FakeNetwork.js b/test/mocks/FakeNetwork.js index 4bdf68487..4368e8de5 100644 --- a/test/mocks/FakeNetwork.js +++ b/test/mocks/FakeNetwork.js @@ -1,9 +1,9 @@ -var imports = require('soop').imports(); -var EventEmitter = imports.EventEmitter || require('events').EventEmitter; +var EventEmitter = require('events').EventEmitter; +var util = require('util'); function Network(opts) {} -Network.parent = EventEmitter; +util.inherits(Network, EventEmitter); Network.prototype.start = function(opts, cb) { // start! :D @@ -92,4 +92,4 @@ Network.prototype.iterateNonce = function() { }; -module.exports = require('soop')(Network); +module.exports = Network; diff --git a/test/mocks/FakeStorage.js b/test/mocks/FakeStorage.js index 3a4b426cc..56b88c2f4 100644 --- a/test/mocks/FakeStorage.js +++ b/test/mocks/FakeStorage.js @@ -119,4 +119,4 @@ FakeStorage.prototype.setFromObj = function(walletId, obj) { this.setName(walletId, obj.opts.name); }; -module.exports = require('soop')(FakeStorage); +module.exports = FakeStorage; diff --git a/test/mocks/FakeWallet.js b/test/mocks/FakeWallet.js index 58d58a458..5ca39a8cc 100644 --- a/test/mocks/FakeWallet.js +++ b/test/mocks/FakeWallet.js @@ -1,6 +1,4 @@ - -var is_browser = typeof process == 'undefined' - || typeof process.versions === 'undefined'; +var is_browser = typeof process == 'undefined' || typeof process.versions === 'undefined'; if (is_browser) { var copay = require('copay'); //browser } else { @@ -27,7 +25,11 @@ var FakeWallet = function() { createdTs: 1403102115, } }; - this.publicKeyRing = {isComplete: function(){ return true; }}; + this.publicKeyRing = { + isComplete: function() { + return true; + } + }; }; FakeWallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) { @@ -96,7 +98,5 @@ FakeWallet.prototype.disconnect = function() { this.disconnectCalled = 1; }; -// This mock is meant for karma, module.exports is not necesary. -try { - module.exports = require('soop')(FakeWallet); -} catch (e) {} +// TODO a try catch was here +module.exports = FakeWallet; diff --git a/test/test.storage.LocalEncrypted.js b/test/test.LocalEncrypted.js similarity index 89% rename from test/test.storage.LocalEncrypted.js rename to test/test.LocalEncrypted.js index a25f6cc92..5fcd6068c 100644 --- a/test/test.storage.LocalEncrypted.js +++ b/test/test.LocalEncrypted.js @@ -1,33 +1,10 @@ -//Crypto Mock -CryptoJS = {}; -CryptoJS.AES = {}; -CryptoJS.AES.encrypt = function(a) { - return a; -}; - -CryptoJS.enc = { - utf8: '' -}; - -CryptoJS.AES.decrypt = function(a) { - return a; -}; - - - - 'use strict'; +var copay = copay || require('../copay'); var chai = chai || require('chai'); var should = chai.should(); -var is_browser = typeof process == 'undefined' - || typeof process.versions === 'undefined'; -if (is_browser) { - var copay = require('copay'); //browser -} else { - var copay = require('../copay'); //node -} var LocalEncrypted = copay.StorageLocalEncrypted; + var fakeWallet = 'fake-wallet-id'; var timeStamp = Date.now(); var localMock = require('./mocks/FakeLocalStorage'); @@ -104,18 +81,6 @@ describe('Storage/LocalEncrypted model', function() { //encrypted.slice(0,6).should.equal("53616c"); }); }); - describe('#_decryptObj', function() { - it('should decrypt and Obj', function() { - var storage = new LocalEncrypted({ - password: 'password', - localStorage: localMock, - }); - storage._decryptObj('{"a":"2"}').should.deep.equal({ - a: "2" - }); - }); - }); - describe('#remove', function() { it('should remove an item', function() { diff --git a/test/test.storage.File.js b/test/test.storage.File.js index 23212871c..38e5789f1 100644 --- a/test/test.storage.File.js +++ b/test/test.storage.File.js @@ -7,29 +7,31 @@ var sinon = require('sinon'); var crypto = require('crypto'); var CryptoJS = require('node-cryptojs-aes').CryptoJS; +var mock = require('mock-fs'); + describe('Storage/File', function() { it('should exist', function() { should.exist(Storage); }); + var mockFS = function() { + var obj = { + "test": "test" + }; + var encryptedStr = CryptoJS.AES.encrypt(JSON.stringify(obj), 'password').toString(); + mock({ + 'myfilename': encryptedStr + }); + }; + describe('#load', function(done) { it('should call fs.readFile', function(done) { - var fs = {} - fs.readFile = function(filename, callback) { - filename.should.equal('myfilename'); - var obj = { - "test": "test" - }; - var encryptedStr = CryptoJS.AES.encrypt(JSON.stringify(obj), "password").toString(); - callback(null, encryptedStr); - }; - var Storage = require('soop').load('../js/models/storage/File.js', { - fs: fs - }); + mockFS(); var storage = new Storage({ password: 'password' }); storage.load('myfilename', function(err) { + mock.restore(); done(); }); }); @@ -37,18 +39,12 @@ describe('Storage/File', function() { describe('#save', function(done) { it('should call fs.writeFile', function(done) { - var fs = {} - fs.writeFile = function(filename, data, callback) { - filename.should.equal('myfilename'); - callback(); - }; - var Storage = require('soop').load('../js/models/storage/File.js', { - fs: fs - }); + mockFS(); var storage = new Storage({ password: 'password' }); storage.save('myfilename', function(err) { + mock.restore(); done(); }); }); diff --git a/util/build.js b/util/build.js index a8215e977..965e01b46 100644 --- a/util/build.js +++ b/util/build.js @@ -4,24 +4,9 @@ var fs = require('fs'); var browserify = require('browserify'); -var browserPack = require('browser-pack'); var exec = require('child_process').exec; -var sys = require('sys'); var puts = function(error, stdout, stderr) { if (error) console.log(error); - //sys.puts(stdout); - //sys.puts(stderr); -}; - -var pack = function(params) { - var file = require.resolve('soop'); - var dir = file.substr(0, file.length - String('soop.js').length); - var preludePath = dir + 'example/custom_prelude.js'; - params.raw = true; - params.sourceMapPrefix = '//#'; - params.prelude = fs.readFileSync(preludePath, 'utf8'); - params.preludePath = preludePath; - return browserPack(params); }; var createVersion = function() { @@ -34,7 +19,6 @@ var createBundle = function(opts) { opts.dir = opts.dir || 'js/'; var bopts = { - pack: pack, debug: true, standalone: 'copay', insertGlobals: true @@ -60,30 +44,6 @@ var createBundle = function(opts) { b.require('./js/models/core/Wallet', { expose: '../../js/models/core/Wallet' }); - b.require('./test/mocks/FakeStorage', { - expose: './mocks/FakeStorage' - }); - b.require('./test/mocks/FakeLocalStorage', { - expose: './mocks/FakeLocalStorage' - }); - b.require('./js/models/core/Message', { - expose: '../js/models/core/Message' - }); - b.require('./test/mocks/FakeBlockchain', { - expose: './mocks/FakeBlockchain' - }); - b.require('./test/mocks/FakeNetwork', { - expose: './mocks/FakeNetwork' - }); - b.require('./test/mocks/FakePayProServer', { - expose: './mocks/FakePayProServer' - }); - b.require('./test/mocks/FakePayProServer', { - expose: '../../mocks/FakePayProServer' - }); - b.require('./test/mocks/FakeBuilder', { - expose: './mocks/FakeBuilder' - }); b.require('./js/models/network/WebRTC', { expose: '../js/models/network/WebRTC' }); @@ -106,11 +66,34 @@ var createBundle = function(opts) { expose: '../config' }); - if (opts.dontminify) { + if (opts.debug) { //include dev dependencies b.require('sinon'); b.require('blanket'); - b.require('soop'); + b.require('./test/mocks/FakeStorage', { + expose: './mocks/FakeStorage' + }); + b.require('./test/mocks/FakeLocalStorage', { + expose: './mocks/FakeLocalStorage' + }); + b.require('./js/models/core/Message', { + expose: '../js/models/core/Message' + }); + b.require('./test/mocks/FakeBlockchain', { + expose: './mocks/FakeBlockchain' + }); + b.require('./test/mocks/FakeNetwork', { + expose: './mocks/FakeNetwork' + }); + b.require('./test/mocks/FakePayProServer', { + expose: './mocks/FakePayProServer' + }); + b.require('./test/mocks/FakePayProServer', { + expose: '../../mocks/FakePayProServer' + }); + b.require('./test/mocks/FakeBuilder', { + expose: './mocks/FakeBuilder' + }); } if (!opts.dontminify) {