Merge pull request #1730 from isocolsky/history
Refactor rate service as model
This commit is contained in:
commit
692852617f
13 changed files with 361 additions and 122 deletions
1
copay.js
1
copay.js
|
|
@ -12,6 +12,7 @@ module.exports.logger = require('./js/log');
|
||||||
// components
|
// components
|
||||||
var Async = module.exports.Async = require('./js/models/Async');
|
var Async = module.exports.Async = require('./js/models/Async');
|
||||||
var Insight = module.exports.Insight = require('./js/models/Insight');
|
var Insight = module.exports.Insight = require('./js/models/Insight');
|
||||||
|
var RateService = module.exports.RateService = require('./js/models/RateService');
|
||||||
|
|
||||||
module.exports.Identity = require('./js/models/Identity');
|
module.exports.Identity = require('./js/models/Identity');
|
||||||
module.exports.Wallet = require('./js/models/Wallet');
|
module.exports.Wallet = require('./js/models/Wallet');
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(res, function (r) {
|
_.each(res, function(r) {
|
||||||
r.ts = r.minedTs || r.sentTs;
|
r.ts = r.minedTs || r.sentTs;
|
||||||
if (r.action === 'sent' && r.peerActions) {
|
if (r.action === 'sent' && r.peerActions) {
|
||||||
r.actionList = controllerUtils.getActionList(r.peerActions);
|
r.actionList = controllerUtils.getActionList(r.peerActions);
|
||||||
|
|
@ -82,14 +82,6 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
return w.getNetworkName().substring(0, 4);
|
return w.getNetworkName().substring(0, 4);
|
||||||
};
|
};
|
||||||
$scope.amountAlternative = function(amount, txIndex, cb) {
|
|
||||||
var w = $rootScope.wallet;
|
|
||||||
rateService.whenAvailable(function() {
|
|
||||||
var valueSat = amount * w.settings.unitToSatoshi;
|
|
||||||
$scope.alternativeCurrency[txIndex] = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
|
|
||||||
return cb ? cb() : null;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Autoload transactions
|
// Autoload transactions
|
||||||
$scope.getTransactions();
|
$scope.getTransactions();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
$scope.rateService = rateService;
|
$scope.rateService = rateService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rateService.whenAvailable(function() {
|
rateService.whenAvailable(function() {
|
||||||
$scope.isRateAvailable = true;
|
$scope.isRateAvailable = true;
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,9 @@ Identity.create = function(opts, cb) {
|
||||||
opts = _.extend({}, opts);
|
opts = _.extend({}, opts);
|
||||||
|
|
||||||
var iden = new Identity(opts);
|
var iden = new Identity(opts);
|
||||||
iden.store(_.extend(opts, {failIfExists: true}), function(err) {
|
iden.store(_.extend(opts, {
|
||||||
|
failIfExists: true
|
||||||
|
}), function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
return cb(null, iden);
|
return cb(null, iden);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
139
js/models/RateService.js
Normal file
139
js/models/RateService.js
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('util');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var log = require('../log');
|
||||||
|
var preconditions = require('preconditions').singleton();
|
||||||
|
var request = require('request');
|
||||||
|
|
||||||
|
/*
|
||||||
|
This class lets interfaces with BitPay's exchange rate API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var RateService = function(opts) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
opts = opts || {};
|
||||||
|
self.request = opts.request || request;
|
||||||
|
|
||||||
|
self.SAT_TO_BTC = 1 / 1e8;
|
||||||
|
self.BTC_TO_SAT = 1e8;
|
||||||
|
self.UNAVAILABLE_ERROR = 'Service is not available - check for service.isAvailable() or use service.whenAvailable()';
|
||||||
|
self.UNSUPPORTED_CURRENCY_ERROR = 'Currency not supported';
|
||||||
|
|
||||||
|
self._isAvailable = false;
|
||||||
|
self._rates = {};
|
||||||
|
self._alternatives = [];
|
||||||
|
self._queued = [];
|
||||||
|
|
||||||
|
self._fetchCurrencies();
|
||||||
|
};
|
||||||
|
|
||||||
|
var _instance;
|
||||||
|
RateService.singleton = function(opts) {
|
||||||
|
if (!_instance) {
|
||||||
|
_instance = new RateService(opts);
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
RateService.prototype._fetchCurrencies = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
log.info('Fetching exchange rates');
|
||||||
|
|
||||||
|
var backoffSeconds = 5;
|
||||||
|
var updateFrequencySeconds = 3600;
|
||||||
|
var rateServiceUrl = 'https://bitpay.com/api/rates';
|
||||||
|
|
||||||
|
self.request.get({
|
||||||
|
url: rateServiceUrl,
|
||||||
|
json: true
|
||||||
|
}, function(err, res, body) {
|
||||||
|
if (err || !body) {
|
||||||
|
backoffSeconds *= 1.5;
|
||||||
|
setTimeout(retrieve, backoffSeconds * 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_.each(body, function(currency) {
|
||||||
|
self._rates[currency.code] = currency.rate;
|
||||||
|
self._alternatives.push({
|
||||||
|
name: currency.name,
|
||||||
|
isoCode: currency.code,
|
||||||
|
rate: currency.rate
|
||||||
|
});
|
||||||
|
});
|
||||||
|
self._isAvailable = true;
|
||||||
|
_.each(self._queued, function(callback) {
|
||||||
|
setTimeout(callback, 1);
|
||||||
|
});
|
||||||
|
setTimeout(function() {
|
||||||
|
self._fetchCurrencies()
|
||||||
|
}, updateFrequencySeconds * 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype._getRate = function(code) {
|
||||||
|
return this._rates[code];
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype._getHistoricRate = function(code, date, cb) {
|
||||||
|
// TODO (isocolsky): implement with a remote call
|
||||||
|
return cb(new Error('Not implemented'));
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype._getAlternatives = function() {
|
||||||
|
return this._alternatives;
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.isAvailable = function() {
|
||||||
|
return this._isAvailable;
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.whenAvailable = function(callback) {
|
||||||
|
if (this.isAvailable()) {
|
||||||
|
setTimeout(callback, 1);
|
||||||
|
} else {
|
||||||
|
this._queued.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.toFiat = function(satoshis, code) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
return satoshis * this.SAT_TO_BTC * this._getRate(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.toFiatHistoric = function(satoshis, code, date, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self._getHistoricRate(code, date, function(err, rate) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
return cb(null, satoshis * self.SAT_TO_BTC * rate);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.fromFiat = function(amount, code) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
return amount / this._getRate(code) * this.BTC_TO_SAT;
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.listAlternatives = function() {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.map(this._getAlternatives(), function(item) {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
isoCode: item.isoCode
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = RateService;
|
||||||
|
|
@ -2866,19 +2866,23 @@ Wallet.prototype.getTransactionHistory = function(cb) {
|
||||||
type: 'out'
|
type: 'out'
|
||||||
});
|
});
|
||||||
|
|
||||||
var proposal = _.findWhere(proposals, {
|
|
||||||
sentTxid: tx.txid
|
|
||||||
});
|
|
||||||
tx.comment = proposal ? proposal.comment : undefined;
|
|
||||||
tx.labelTo = firstOut ? firstOut.label : undefined;
|
tx.labelTo = firstOut ? firstOut.label : undefined;
|
||||||
tx.addressTo = firstOut ? firstOut.address : undefined;
|
tx.addressTo = firstOut ? firstOut.address : undefined;
|
||||||
tx.amountSat = Math.abs(amount);
|
tx.amountSat = Math.abs(amount);
|
||||||
tx.amount = tx.amountSat * satToUnit;
|
tx.amount = tx.amountSat * satToUnit;
|
||||||
tx.sentTs = proposal ? proposal.sentTs : undefined;
|
|
||||||
tx.minedTs = !_.isNaN(tx.time) ? tx.time * 1000 : undefined;
|
tx.minedTs = !_.isNaN(tx.time) ? tx.time * 1000 : undefined;
|
||||||
tx.merchant = proposal ? proposal.merchant : undefined;
|
|
||||||
tx.peerActions = proposal ? proposal.peerActions : undefined;
|
var proposal = _.findWhere(proposals, {
|
||||||
tx.finallyRejected = proposal ? proposal.finallyRejected : undefined;
|
sentTxid: tx.txid
|
||||||
|
});
|
||||||
|
|
||||||
|
if (proposal) {
|
||||||
|
tx.comment = proposal.comment;
|
||||||
|
tx.sentTs = proposal.sentTs;
|
||||||
|
tx.merchant = proposal.merchant;
|
||||||
|
tx.peerActions = proposal.peerActions;
|
||||||
|
tx.finallyRejected = proposal.finallyRejected;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (addresses.length > 0) {
|
if (addresses.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -324,6 +324,17 @@ angular.module('copayApp.services')
|
||||||
};
|
};
|
||||||
|
|
||||||
root.updateTxs = function(opts) {
|
root.updateTxs = function(opts) {
|
||||||
|
function computeAlternativeAmount(w, tx, cb) {
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
_.each(tx.outs, function(out) {
|
||||||
|
var valueSat = out.value * w.settings.unitToSatoshi;
|
||||||
|
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
|
||||||
|
out.alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||||
|
});
|
||||||
|
if (cb) return cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var w = opts.wallet || $rootScope.wallet;
|
var w = opts.wallet || $rootScope.wallet;
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
opts = opts || $rootScope.txsOpts || {};
|
opts = opts || $rootScope.txsOpts || {};
|
||||||
|
|
@ -364,6 +375,9 @@ angular.module('copayApp.services')
|
||||||
i.fee = i.builder.feeSat * satToUnit;
|
i.fee = i.builder.feeSat * satToUnit;
|
||||||
i.missingSignatures = tx.countInputMissingSignatures(0);
|
i.missingSignatures = tx.countInputMissingSignatures(0);
|
||||||
i.actionList = getActionList(i.peerActions);
|
i.actionList = getActionList(i.peerActions);
|
||||||
|
if (i.isPending) {
|
||||||
|
computeAlternativeAmount(w, i);
|
||||||
|
}
|
||||||
txs.push(i);
|
txs.push(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MINS_IN_HOUR = 60;
|
angular.module('copayApp.services').factory('rateService', function(request) {
|
||||||
var MILLIS_IN_SECOND = 1000;
|
return copay.RateService.singleton({
|
||||||
|
request: request
|
||||||
var RateService = function(request) {
|
|
||||||
this.isAvailable = false;
|
|
||||||
this.UNAVAILABLE_ERROR = 'Service is not available - check for service.isAvailable or use service.whenAvailable';
|
|
||||||
this.SAT_TO_BTC = 1 / 1e8;
|
|
||||||
this.BTC_TO_SAT = 1e8;
|
|
||||||
var rateServiceConfig = config.rate;
|
|
||||||
var updateFrequencySeconds = rateServiceConfig.updateFrequencySeconds || 60 * MINS_IN_HOUR;
|
|
||||||
var rateServiceUrl = rateServiceConfig.url || 'https://bitpay.com/api/rates';
|
|
||||||
this.queued = [];
|
|
||||||
this.alternatives = [];
|
|
||||||
var self = this;
|
|
||||||
var backoffSeconds = 5;
|
|
||||||
var retrieve = function() {
|
|
||||||
request.get({
|
|
||||||
url: rateServiceUrl,
|
|
||||||
json: true
|
|
||||||
}, function(err, response, listOfCurrencies) {
|
|
||||||
if (err) {
|
|
||||||
backoffSeconds *= 1.5;
|
|
||||||
setTimeout(retrieve, backoffSeconds * MILLIS_IN_SECOND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var rates = {};
|
|
||||||
listOfCurrencies.forEach(function(element) {
|
|
||||||
rates[element.code] = element.rate;
|
|
||||||
self.alternatives.push({
|
|
||||||
name: element.name,
|
|
||||||
isoCode: element.code,
|
|
||||||
rate: element.rate
|
|
||||||
});
|
|
||||||
});
|
|
||||||
self.isAvailable = true;
|
|
||||||
self.rates = rates;
|
|
||||||
self.queued.forEach(function(callback) {
|
|
||||||
setTimeout(callback, 1);
|
|
||||||
});
|
|
||||||
setTimeout(retrieve, updateFrequencySeconds * MILLIS_IN_SECOND);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
retrieve();
|
|
||||||
};
|
|
||||||
|
|
||||||
RateService.prototype.whenAvailable = function(callback) {
|
|
||||||
if (this.isAvailable) {
|
|
||||||
setTimeout(callback, 1);
|
|
||||||
} else {
|
|
||||||
this.queued.push(callback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
RateService.prototype.toFiat = function(satoshis, code) {
|
|
||||||
if (!this.isAvailable) {
|
|
||||||
throw new Error(this.UNAVAILABLE_ERROR);
|
|
||||||
}
|
|
||||||
return satoshis * this.SAT_TO_BTC * this.rates[code];
|
|
||||||
};
|
|
||||||
|
|
||||||
RateService.prototype.fromFiat = function(amount, code) {
|
|
||||||
if (!this.isAvailable) {
|
|
||||||
throw new Error(this.UNAVAILABLE_ERROR);
|
|
||||||
}
|
|
||||||
return amount / this.rates[code] * this.BTC_TO_SAT;
|
|
||||||
};
|
|
||||||
|
|
||||||
RateService.prototype.listAlternatives = function() {
|
|
||||||
if (!this.isAvailable) {
|
|
||||||
throw new Error(this.UNAVAILABLE_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
var alts = [];
|
|
||||||
this.alternatives.forEach(function(element) {
|
|
||||||
alts.push({
|
|
||||||
name: element.name,
|
|
||||||
isoCode: element.isoCode
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return alts;
|
});
|
||||||
};
|
|
||||||
|
|
||||||
angular.module('copayApp.services').service('rateService', RateService);
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,6 @@ describe('PayPro (in Wallet) model', function() {
|
||||||
c.network.getHexNonces = sinon.stub();
|
c.network.getHexNonces = sinon.stub();
|
||||||
c.network.send = sinon.stub();
|
c.network.send = sinon.stub();
|
||||||
|
|
||||||
|
|
||||||
return new Wallet(c);
|
return new Wallet(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
179
test/RateService.js
Normal file
179
test/RateService.js
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var RateService = copay.RateService;
|
||||||
|
|
||||||
|
describe('RateService model', function() {
|
||||||
|
before(function() {
|
||||||
|
sinon.stub(RateService.prototype, '_fetchCurrencies').returns();
|
||||||
|
});
|
||||||
|
after(function() {});
|
||||||
|
|
||||||
|
it('should create an instance', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
should.exist(rs);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#toFiat', function() {
|
||||||
|
it('should throw error when unavailable', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(false);
|
||||||
|
(function() {
|
||||||
|
rs.toFiat(10000, 'USD');
|
||||||
|
}).should.throw;
|
||||||
|
});
|
||||||
|
it('should return current valuation', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(true);
|
||||||
|
var getRateStub = sinon.stub(rs, '_getRate')
|
||||||
|
getRateStub.withArgs('USD').returns(300.00);
|
||||||
|
getRateStub.withArgs('EUR').returns(250.00);
|
||||||
|
var params = [{
|
||||||
|
satoshis: 0,
|
||||||
|
code: 'USD',
|
||||||
|
expected: '0.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 1e8,
|
||||||
|
code: 'USD',
|
||||||
|
expected: '300.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 10000,
|
||||||
|
code: 'USD',
|
||||||
|
expected: '0.03'
|
||||||
|
}, {
|
||||||
|
satoshis: 20000,
|
||||||
|
code: 'EUR',
|
||||||
|
expected: '0.05'
|
||||||
|
}, ];
|
||||||
|
|
||||||
|
_.each(params, function(p) {
|
||||||
|
rs.toFiat(p.satoshis, p.code).toFixed(2).should.equal(p.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#toFiatHistoric', function() {
|
||||||
|
it('should return historic valuation', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(true);
|
||||||
|
var today = Date.now();
|
||||||
|
var yesterday = today - 24 * 3600;
|
||||||
|
var getHistoricalRateStub = sinon.stub(rs, '_getHistoricRate');
|
||||||
|
getHistoricalRateStub.withArgs('USD', today).yields(null, 300.00);
|
||||||
|
getHistoricalRateStub.withArgs('USD', yesterday).yields(null, 250.00);
|
||||||
|
getHistoricalRateStub.withArgs('EUR', today).yields(null, 250.00);
|
||||||
|
getHistoricalRateStub.withArgs('EUR', yesterday).yields(null, 200.00);
|
||||||
|
var params = [{
|
||||||
|
satoshis: 0,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '0.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 1e8,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '300.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 10000,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '0.03'
|
||||||
|
}, {
|
||||||
|
satoshis: 0,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '0.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 1e8,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '300.00'
|
||||||
|
}, {
|
||||||
|
satoshis: 10000,
|
||||||
|
code: 'USD',
|
||||||
|
date: today,
|
||||||
|
expected: '0.03'
|
||||||
|
}, {
|
||||||
|
satoshis: 20000,
|
||||||
|
code: 'EUR',
|
||||||
|
date: today,
|
||||||
|
expected: '0.05'
|
||||||
|
}, {
|
||||||
|
satoshis: 20000,
|
||||||
|
code: 'EUR',
|
||||||
|
date: yesterday,
|
||||||
|
expected: '0.04'
|
||||||
|
}, ];
|
||||||
|
|
||||||
|
_.each(params, function(p) {
|
||||||
|
rs.toFiatHistoric(p.satoshis, p.code, p.date, function(err, rate) {
|
||||||
|
rate.toFixed(2).should.equal(p.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#fromFiat', function() {
|
||||||
|
it('should throw error when unavailable', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(false);
|
||||||
|
(function() {
|
||||||
|
rs.fromFiat(300, 'USD');
|
||||||
|
}).should.throw;
|
||||||
|
});
|
||||||
|
it('should return current valuation', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(true);
|
||||||
|
var getRateStub = sinon.stub(rs, '_getRate')
|
||||||
|
getRateStub.withArgs('USD').returns(300.00);
|
||||||
|
getRateStub.withArgs('EUR').returns(250.00);
|
||||||
|
var params = [{
|
||||||
|
amount: 0,
|
||||||
|
code: 'USD',
|
||||||
|
expected: 0
|
||||||
|
}, {
|
||||||
|
amount: 300.00,
|
||||||
|
code: 'USD',
|
||||||
|
expected: 1e8
|
||||||
|
}, {
|
||||||
|
amount: 600.00,
|
||||||
|
code: 'USD',
|
||||||
|
expected: 2e8
|
||||||
|
}, {
|
||||||
|
amount: 250.00,
|
||||||
|
code: 'EUR',
|
||||||
|
expected: 1e8
|
||||||
|
}, ];
|
||||||
|
|
||||||
|
_.each(params, function(p) {
|
||||||
|
rs.fromFiat(p.amount, p.code).should.equal(p.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#listAlternatives', function() {
|
||||||
|
it('should throw error when unavailable', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(false);
|
||||||
|
(function() {
|
||||||
|
rs.listAlternatives();
|
||||||
|
}).should.throw;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return list of available currencies', function() {
|
||||||
|
var rs = new RateService();
|
||||||
|
rs.isAvailable = sinon.stub().returns(true);
|
||||||
|
sinon.stub(rs, '_getAlternatives').returns([{
|
||||||
|
name: 'United States Dollar',
|
||||||
|
isoCode: 'USD',
|
||||||
|
rate: 300.00,
|
||||||
|
}, {
|
||||||
|
name: 'European Union Euro',
|
||||||
|
isoCode: 'EUR',
|
||||||
|
rate: 250.00,
|
||||||
|
}, ])
|
||||||
|
|
||||||
|
var list = rs.listAlternatives();
|
||||||
|
list.should.exist;
|
||||||
|
list.length.should.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -56,8 +56,8 @@ var addCopayers = function(w) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Wallet model', function() {
|
|
||||||
|
|
||||||
|
describe('Wallet model', function() {
|
||||||
it('should fail to create an instance', function() {
|
it('should fail to create an instance', function() {
|
||||||
(function() {
|
(function() {
|
||||||
new Wallet(walletConfig)
|
new Wallet(walletConfig)
|
||||||
|
|
@ -102,8 +102,6 @@ describe('Wallet model', function() {
|
||||||
c.network.peerFromCopayer = sinon.stub().returns('xxxx');
|
c.network.peerFromCopayer = sinon.stub().returns('xxxx');
|
||||||
c.network.send = sinon.stub();
|
c.network.send = sinon.stub();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c.addressBook = {
|
c.addressBook = {
|
||||||
'2NFR2kzH9NUdp8vsXTB4wWQtTtzhpKxsyoJ': {
|
'2NFR2kzH9NUdp8vsXTB4wWQtTtzhpKxsyoJ': {
|
||||||
label: 'John',
|
label: 'John',
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ describe("Unit: Controllers", function() {
|
||||||
var server;
|
var server;
|
||||||
|
|
||||||
beforeEach(module('copayApp'));
|
beforeEach(module('copayApp'));
|
||||||
beforeEach(module('copayApp.controllers'));
|
beforeEach(module('copayApp.controllers'));
|
||||||
beforeEach(module(function($provide) {
|
beforeEach(module(function($provide) {
|
||||||
$provide.value('request', {
|
$provide.value('request', {
|
||||||
'get': function(_, cb) {
|
'get': function(_, cb) {
|
||||||
cb(null, null, [{
|
cb(null, null, [{
|
||||||
name: 'USD Dollars',
|
name: 'USD Dollars',
|
||||||
code: 'USD',
|
code: 'USD',
|
||||||
rate: 2
|
rate: 2
|
||||||
|
|
@ -71,7 +71,7 @@ describe("Unit: Controllers", function() {
|
||||||
w.createTx = sinon.stub().yields(null);
|
w.createTx = sinon.stub().yields(null);
|
||||||
w.sendTx = sinon.stub().yields(null);
|
w.sendTx = sinon.stub().yields(null);
|
||||||
w.requiresMultipleSignatures = sinon.stub().returns(true);
|
w.requiresMultipleSignatures = sinon.stub().returns(true);
|
||||||
w.getTxProposals = sinon.stub().returns([1,2,3]);
|
w.getTxProposals = sinon.stub().returns([1, 2, 3]);
|
||||||
|
|
||||||
|
|
||||||
$rootScope.wallet = w;
|
$rootScope.wallet = w;
|
||||||
|
|
@ -172,17 +172,6 @@ describe("Unit: Controllers", function() {
|
||||||
scope.getTransactions();
|
scope.getTransactions();
|
||||||
expect(scope.blockchain_txs).to.be.empty;
|
expect(scope.blockchain_txs).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call amountAlternative and return a value', function() {
|
|
||||||
var cb = sinon.spy();
|
|
||||||
var s1 = sinon.stub(scope, 'amountAlternative');
|
|
||||||
s1.onCall(0).returns(1000);
|
|
||||||
s1.onCall(1).returns(2000);
|
|
||||||
expect(s1(100, 0, cb)).equal(1000);
|
|
||||||
expect(s1(200, 1, cb)).equal(2000);
|
|
||||||
sinon.assert.callCount(scope.amountAlternative, 2);
|
|
||||||
s1.restore();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Send Controller', function() {
|
describe('Send Controller', function() {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<div class="large-3 medium-3 small-4 columns">
|
<div class="large-3 medium-3 small-4 columns">
|
||||||
<div class="size-12">
|
<div class="size-12">
|
||||||
<span>{{out.value |noFractionNumber}} {{$root.wallet.settings.unitName}}</span>
|
<span>{{out.value |noFractionNumber}} {{$root.wallet.settings.unitName}}</span>
|
||||||
|
<span>{{out.alternativeAmount|noFractionNumber}} {{out.alternativeIsoCode}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-1 medium-1 small-1 columns fi-arrow-right"></div>
|
<div class="large-1 medium-1 small-1 columns fi-arrow-right"></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue