diff --git a/app/src/main/mockServer.js b/app/src/main/mockServer.js index a0f4a2c166..95f9d4dd97 100644 --- a/app/src/main/mockServer.js +++ b/app/src/main/mockServer.js @@ -3,11 +3,6 @@ let proxy = require('express-http-proxy') let randomBytes = require('crypto').pseudoRandomBytes let casual = require('casual') -let randomPubkey = () => ({ - type: 'ed25519', - data: randomBytes(32).toString('hex') -}) - let randomAddress = () => randomBytes(20).toString('hex') let randomTime = () => Date.now() - casual.integer(0, 32e7) @@ -23,24 +18,6 @@ let randomBondTx = (address, delegator) => ({ height: 1000 }) -let randomCandidate = () => ({ - address: randomAddress(), - owner: randomAddress(), // address - shares: casual.integer(1000, 1e7), - votingPower: casual.integer(10, 1e5), - since: casual.date('YYYY-MM-DD'), - description: { - name: casual.username, - website: casual.url, - details: casual.sentences(3) - }, - commissionRate: casual.double(0.005, 0.05), - commissionMax: casual.double(0.05, 0.25), - status: [ 'active', 'bonding', 'unbonding' ][Math.floor(Math.random() * 3)], - slashRatio: Math.random() < 0.9 ? casual.double(0.01, 0.5) : 0, - reDelegatingShares: casual.integer(1000, 1e7) -}) - module.exports = function (port = 8999) { let app = express() @@ -52,38 +29,6 @@ module.exports = function (port = 8999) { // }) // delegation mock API - let candidates = new Array(50).fill(0).map(randomPubkey) - app.get('/query/stake/candidates', (req, res) => { - res.json({ - height: 10000, - data: candidates - }) - }) - app.get('/query/stake/candidates/:pubkey', (req, res) => { - res.json({ - height: 10000, - data: { - pubkey: randomPubkey(), - owner: { - chain: 'gaia-1', - app: 'sig', - address: randomBytes(20).toString('hex') - }, - shares: Math.floor(Math.random() * 1e7), - voting_power: Math.floor(Math.random() * 1e5), - description: JSON.stringify({ - description: casual.sentences(3), - commission: casual.double(0.005, 0.05), - commissionMax: casual.double(0.05, 0.25), - commissionMaxRate: casual.double(0.005, 0.05), - url: casual.url, - keybaseID: casual.username, - country: casual.country, - startDate: casual.date('YYYY-MM-DD') - }) - } - }) - }) app.post('/tx/stake/delegate/:pubkey/:amount', (req, res) => { res.json({ 'type': 'sigs/one', @@ -150,9 +95,8 @@ module.exports = function (port = 8999) { } }) }) - app.get('/candidates', (req, res) => { - res.json(new Array(200).fill(0).map(randomCandidate)) - }) + + // tx history app.get('/tx/bondings/delegator/:address', (req, res) => { let { address } = req.params let txs = new Array(100).fill(0) diff --git a/app/src/renderer/components/common/AppMenu.vue b/app/src/renderer/components/common/AppMenu.vue index c6898820a4..bea41a16ae 100644 --- a/app/src/renderer/components/common/AppMenu.vue +++ b/app/src/renderer/components/common/AppMenu.vue @@ -7,7 +7,7 @@ menu.app-menu list-item(to="/wallet/transactions" exact @click.native="close" title="Transactions") part(title='Governance' v-if="config.devMode") list-item(to="/proposals" exact @click.native="close" title="Proposals") - part(title='Stake' v-if="config.devMode") + part(title='Stake') list-item(to="/staking" exact @click.native="close" title="Delegates") part(title='Monitor' v-if="config.devMode") list-item(to="/blockchain" exact @click.native="close" title="Blockchain") diff --git a/app/src/renderer/components/staking/LiDelegate.vue b/app/src/renderer/components/staking/LiDelegate.vue index f0aa2f7d48..ca9b115d20 100644 --- a/app/src/renderer/components/staking/LiDelegate.vue +++ b/app/src/renderer/components/staking/LiDelegate.vue @@ -7,16 +7,18 @@ transition(name='ts-li-delegate'): div(:class='styles') template i.fa.fa-check-square-o(v-if='inCart' @click='rm(delegate)') i.fa.fa-square-o(v-else @click='add(delegate)') - router-link(:to="{ name: 'delegate', params: { delegate: delegate.id }}") - | {{ delegate.keybaseID}} - .value {{ delegate.country }} + router-link(v-if="config.devMode" :to="{ name: 'delegate', params: { delegate: delegate.id }}") + //- | {{ delegate.keybaseID ? delegate.keybaseID : 'n/a'}} + | {{ delegate.id }} + a(v-else) {{ delegate.id }} + .value {{ delegate.country ? delegate.country : 'n/a' }} .value.voting_power.num.bar span {{ num.prettyInt(delegate.voting_power) }} .bar(:style='vpStyles') .value.delegated.num.bar.delegated span {{ num.prettyInt(delegate.shares) }} .bar(:style='sharesStyles') - .value {{ (delegate.commission * 100).toFixed(2) }}% + .value {{ delegate.commission ? (delegate.commission * 100).toFixed(2) + '%' : 'n/a' }} menu btn(theme='cosmos' v-if='inCart' icon='delete' value='Remove' size='sm' @click.native='rm(delegate)') @@ -36,7 +38,7 @@ export default { Btn }, computed: { - ...mapGetters(['shoppingCart', 'delegates']), + ...mapGetters(['shoppingCart', 'delegates', 'config']), styles () { let value = 'li-delegate' if (this.inCart) value += ' li-delegate-active ' diff --git a/app/src/renderer/components/staking/PageDelegates.vue b/app/src/renderer/components/staking/PageDelegates.vue index 9106de6555..8e2fd07f28 100644 --- a/app/src/renderer/components/staking/PageDelegates.vue +++ b/app/src/renderer/components/staking/PageDelegates.vue @@ -4,11 +4,14 @@ page(:title='pageTitle') a(@click='setSearch(true)') i.material-icons search .label Search - router-link(to='/staking/bond') + a(@click='updateDelegates()') + i.material-icons refresh + .label Refresh + router-link(v-if="config.devMode" to='/staking/bond') i.material-icons check_circle .label Bond Atoms modal-search(v-if="filters.delegates.search.visible" type="delegates") - template(v-if="filteredDelegates.length > 0") + template(v-if="delegates.length > 0") panel-sort(:sort='sort') li-delegate( v-for='i in filteredDelegates' @@ -42,7 +45,7 @@ export default { ToolBar }, computed: { - ...mapGetters(['delegates', 'filters', 'shoppingCart']), + ...mapGetters(['delegates', 'filters', 'shoppingCart', 'config']), pageTitle () { if (this.shoppingCart.length > 0) { return `Delegates (${this.shoppingCart.length} Selected)` @@ -54,7 +57,7 @@ export default { let query = this.filters.delegates.search.query let list = orderBy(this.delegates, [this.sort.property], [this.sort.order]) if (this.filters.delegates.search.visible) { - return list.filter(i => includes(i.keybaseID.toLowerCase(), query.toLowerCase())) + return list.filter(i => includes(JSON.stringify(i).toLowerCase(), query.toLowerCase())) } else { return list } @@ -63,10 +66,11 @@ export default { data: () => ({ query: '', sort: { - property: 'keybaseID', + property: 'id', order: 'asc', properties: [ - { id: 1, title: 'Keybase ID', value: 'keybaseID', initial: true }, + // { id: 1, title: 'Keybase ID', value: 'keybaseID', initial: true }, + { id: 1, title: 'Public Key', value: 'id', initial: true }, { id: 2, title: 'Country', value: 'country' }, { id: 3, title: 'Voting Power', value: 'voting_power' }, { id: 4, title: 'Delegated Power', value: 'shares' }, @@ -75,6 +79,7 @@ export default { } }), methods: { + updateDelegates () { this.$store.dispatch('getDelegates') }, setSearch (bool) { this.$store.commit('setSearchVisible', ['delegates', bool]) } }, mounted () { diff --git a/app/src/renderer/vuex/modules/delegates.js b/app/src/renderer/vuex/modules/delegates.js index d3c7963a95..5bc6a5bf53 100644 --- a/app/src/renderer/vuex/modules/delegates.js +++ b/app/src/renderer/vuex/modules/delegates.js @@ -1,17 +1,12 @@ -function pubkeyToString (pubkey) { - // go-wire key encoding, - // ed25519 keys prefixed w/ 01, secp256k1 w/ 02 - let type = pubkey.type === 'ed25519' ? '01' : '02' - return type + pubkey.data -} +import axios from 'axios' export default ({ dispatch, node }) => { const state = [] const mutations = { addDelegate (state, delegate) { - delegate.id = pubkeyToString(delegate.pubkey) - Object.assign(delegate, JSON.parse(delegate.description)) + delegate.id = delegate.pub_key.data + Object.assign(delegate, delegate.description) // return if we already have this delegate for (let existingDelegate of state) { @@ -30,7 +25,9 @@ export default ({ dispatch, node }) => { } }, async getDelegate ({ commit }, pubkey) { - let delegate = (await node.candidate(pubkeyToString(pubkey))).data + let delegate = (await axios.get('http://localhost:8998/query/stake/candidate/' + pubkey.data)).data.data + // TODO move into cosmos-sdk + // let delegate = (await node.candidate(pubkeyToString(pubkey))).data commit('addDelegate', delegate) } } diff --git a/test/unit/specs/LiDelegate.spec.js b/test/unit/specs/LiDelegate.spec.js index 79ccdc4f1c..15f8a6d863 100644 --- a/test/unit/specs/LiDelegate.spec.js +++ b/test/unit/specs/LiDelegate.spec.js @@ -15,7 +15,10 @@ describe('LiDelegate', () => { store = new Vuex.Store({ getters: { shoppingCart: () => shoppingCart.state.delegates, - delegates: () => delegates.state + delegates: () => delegates.state, + config: () => ({ + devMode: true + }) }, modules: { shoppingCart, @@ -24,26 +27,30 @@ describe('LiDelegate', () => { }) store.commit('addDelegate', { - pubkey: 'pubkeyX', - description: JSON.stringify({ - id: 'idX', + pub_key: { + type: 'ed25519', + data: 'pubkeyX' + }, + voting_power: 10000, + shares: 5000, + description: { description: 'descriptionX', - voting_power: 10000, - shares: 5000, keybaseID: 'keybaseX', country: 'USA' - }) + } }) store.commit('addDelegate', { - pubkey: 'pubkeyY', - description: JSON.stringify({ - id: 'idY', + pub_key: { + type: 'ed25519', + data: 'pubkeyY' + }, + voting_power: 30000, + shares: 10000, + description: { description: 'descriptionY', - voting_power: 30000, - shares: 10000, keybaseID: 'keybaseY', country: 'Canada' - }) + } }) delegate = store.state.delegates[0] diff --git a/test/unit/specs/PageDelegates.spec.js b/test/unit/specs/PageDelegates.spec.js index 6215380e3b..ca97b7267c 100644 --- a/test/unit/specs/PageDelegates.spec.js +++ b/test/unit/specs/PageDelegates.spec.js @@ -17,7 +17,10 @@ describe('PageDelegates', () => { getters: { shoppingCart: () => shoppingCart.state.delegates, delegates: () => delegates.state, - filters: () => filters.state + filters: () => filters.state, + config: () => ({ + devMode: true + }) }, modules: { shoppingCart, @@ -27,26 +30,30 @@ describe('PageDelegates', () => { }) store.commit('addDelegate', { - pubkey: 'pubkeyY', - description: JSON.stringify({ - id: 'idY', - description: 'descriptionY', - voting_power: 30000, - shares: 10000, - keybaseID: 'keybaseY', - country: 'Canada' - }) - }) - store.commit('addDelegate', { - pubkey: 'pubkeyX', - description: JSON.stringify({ - id: 'idX', + pub_key: { + type: 'ed25519', + data: 'pubkeyX' + }, + voting_power: 10000, + shares: 5000, + description: { description: 'descriptionX', - voting_power: 2000, - shares: 5000, keybaseID: 'keybaseX', country: 'USA' - }) + } + }) + store.commit('addDelegate', { + pub_key: { + type: 'ed25519', + data: 'pubkeyY' + }, + voting_power: 30000, + shares: 10000, + description: { + description: 'descriptionY', + keybaseID: 'keybaseY', + country: 'Canada' + } }) wrapper = mount(PageDelegates, { @@ -58,6 +65,7 @@ describe('PageDelegates', () => { }) jest.spyOn(store, 'commit') + jest.spyOn(store, 'dispatch') }) it('has the expected html structure', () => { @@ -69,19 +77,26 @@ describe('PageDelegates', () => { expect(wrapper.contains('.ni-modal-search')).toBe(true) }) + it('should refresh candidates on click', () => { + wrapper.findAll('.ni-tool-bar i').at(1).trigger('click') + expect(store.dispatch).toHaveBeenCalledWith('getDelegates') + }) + it('should sort the delegates by selected property', () => { - expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['idX', 'idY']) - wrapper.vm.sort = 'voting_power' - expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['idY', 'idX']) + expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['pubkeyX', 'pubkeyY']) + + wrapper.vm.sort.property = 'voting_power' + wrapper.vm.sort.order = 'desc' + expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['pubkeyY', 'pubkeyX']) }) it('should filter the delegates', () => { store.commit('setSearchVisible', ['delegates', true]) store.commit('setSearchQuery', ['delegates', 'baseX']) - expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['idX']) - expect(wrapper.html()).toMatchSnapshot() + expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['pubkeyX']) + expect(wrapper.vm.$el).toMatchSnapshot() store.commit('setSearchQuery', ['delegates', 'baseY']) - expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['idY']) + expect(wrapper.vm.filteredDelegates.map(x => x.id)).toEqual(['pubkeyY']) }) it('should show the amount of selected delegates', () => { @@ -96,7 +111,10 @@ describe('PageDelegates', () => { getters: { shoppingCart: () => shoppingCart.state, delegates: () => [], - filters: () => filters.state + filters: () => filters.state, + config: () => ({ + devMode: true + }) }, modules: { shoppingCart, diff --git a/test/unit/specs/__snapshots__/LiDelegate.spec.js.snap b/test/unit/specs/__snapshots__/LiDelegate.spec.js.snap index b7b49d639b..429c267078 100644 --- a/test/unit/specs/__snapshots__/LiDelegate.spec.js.snap +++ b/test/unit/specs/__snapshots__/LiDelegate.spec.js.snap @@ -21,7 +21,7 @@ exports[`LiDelegate has the expected html structure 1`] = ` - keybaseX + pubkeyX @@ -55,7 +55,7 @@ exports[`LiDelegate has the expected html structure 1`] = `
- NaN% + n/a
diff --git a/test/unit/specs/__snapshots__/PageDelegates.spec.js.snap b/test/unit/specs/__snapshots__/PageDelegates.spec.js.snap index 10498e26a3..0bc33968e9 100644 --- a/test/unit/specs/__snapshots__/PageDelegates.spec.js.snap +++ b/test/unit/specs/__snapshots__/PageDelegates.spec.js.snap @@ -54,6 +54,18 @@ exports[`PageDelegates has the expected html structure 1`] = ` Search + + + refresh + +
+ Refresh +
+
@@ -101,7 +113,7 @@ exports[`PageDelegates has the expected html structure 1`] = `
- Keybase ID + Public Key
- keybaseX + pubkeyX
@@ -174,11 +186,11 @@ exports[`PageDelegates has the expected html structure 1`] = ` class="value voting_power num bar" > - 2,000 + 10,000
- NaN% + n/a
@@ -240,7 +252,7 @@ exports[`PageDelegates has the expected html structure 1`] = ` - keybaseY + pubkeyY @@ -274,7 +286,7 @@ exports[`PageDelegates has the expected html structure 1`] = `
- NaN% + n/a
@@ -311,4 +323,352 @@ exports[`PageDelegates has the expected html structure 1`] = ` `; -exports[`PageDelegates should filter the delegates 1`] = `"
Delegates
search
Search
check_circle
Bond Atoms
help_outline
Keybase ID
Country
Voting Power
Delegated Power
Commission
keybaseX
USA
2,000
5,000
NaN%
keybaseY
Canada
30,000
10,000
NaN%
"`; +exports[`PageDelegates should filter the delegates 1`] = ` +
+
+
+
+
+
+ Delegates +
+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+ Public Key +
+
+
+
+ Country +
+
+
+
+ Voting Power +
+
+
+
+ Delegated Power +
+
+
+
+ Commission +
+
+
+
+
+
+
+
+ + + + pubkeyX + + +
+
+ USA +
+
+ + 10,000 + +
+
+
+ + 5,000 + +
+
+
+ n/a +
+
+ + + +
+
+
+
+
+
+ + + + pubkeyY + + +
+
+ Canada +
+
+ + 30,000 + +
+
+
+ + 10,000 + +
+
+
+ n/a +
+
+ + + +
+
+
+
+
+
+
+`;