mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into setup-admin-interface
This commit is contained in:
commit
a06815c841
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -344,7 +344,7 @@ jobs:
|
||||
report_name: Coverage Frontend
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 82
|
||||
min_coverage: 83
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
|
||||
@ -27,7 +27,6 @@
|
||||
"babel-preset-vue": "^2.0.2",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "^2.5.0",
|
||||
"chart.js": "^2.9.3",
|
||||
"d3": "^5.7.0",
|
||||
"datamaps": "^0.5.9",
|
||||
"date-fns": "^1.30.1",
|
||||
@ -64,7 +63,6 @@
|
||||
"vue": "^2.6.11",
|
||||
"vue-apollo": "^3.0.7",
|
||||
"vue-bootstrap-typeahead": "^0.2.6",
|
||||
"vue-chartjs": "^3.5.0",
|
||||
"vue-cli-plugin-i18n": "^1.0.1",
|
||||
"vue-clickaway": "^2.2.2",
|
||||
"vue-clipboard2": "^0.3.0",
|
||||
|
||||
45
frontend/src/components/Status.spec.js
Normal file
45
frontend/src/components/Status.spec.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Status from './Status'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('Status', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$n: jest.fn((n) => n),
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
balance: 1234,
|
||||
statusText: 'GDD',
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(Status, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('balance is pending', () => {
|
||||
it('it displays an en-dash', () => {
|
||||
expect(wrapper.find('div.gdd-status-div').text()).toEqual('— GDD')
|
||||
})
|
||||
})
|
||||
|
||||
describe('balance is loaded', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({
|
||||
pending: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('it displays the ammount of GDD', () => {
|
||||
expect(wrapper.find('div.gdd-status-div').text()).toEqual('1234 GDD')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
24
frontend/src/components/Status.vue
Normal file
24
frontend/src/components/Status.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="gdd-status">
|
||||
<div class="p-0 gdd-status-div">
|
||||
{{ pending ? '—' : $n(balance, 'decimal') }} {{ statusText }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Status',
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
pending: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
statusText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,8 +1,11 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Transaction from './Transaction'
|
||||
import Vue from 'vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const consoleErrorMock = jest.fn()
|
||||
|
||||
describe('Transaction', () => {
|
||||
let wrapper
|
||||
|
||||
@ -27,5 +30,206 @@ describe('Transaction', () => {
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div.gdt-transaction-list-item').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a collapse button', () => {
|
||||
expect(wrapper.find('button[type="button"].btn-secondary').text()).toBe('i')
|
||||
})
|
||||
|
||||
describe('no valid GDT entry type', () => {
|
||||
beforeEach(async () => {
|
||||
// disable throwing Errors on warnings to catch the warning
|
||||
Vue.config.warnHandler = (w) => {}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error = consoleErrorMock
|
||||
await wrapper.setProps({ gdtEntryType: 'NOT_VALID' })
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(consoleErrorMock).toBeCalledWith(
|
||||
expect.objectContaining({ message: 'no lines for this type: NOT_VALID' }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('default entry type FORM', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
amount: 100,
|
||||
date: '2021-05-02T17:20:11+00:00',
|
||||
comment: 'This is a comment',
|
||||
factor: 17,
|
||||
gdt: 1700,
|
||||
id: 42,
|
||||
})
|
||||
})
|
||||
|
||||
it('has the heart icon', () => {
|
||||
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the description gdt.contribution', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.contribution')
|
||||
})
|
||||
|
||||
it('renders the amount of euros', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('100 €')
|
||||
})
|
||||
|
||||
it('renders the amount of GDT', () => {
|
||||
expect(wrapper.findAll('div.row').at(1).text()).toContain('1700 GDT')
|
||||
})
|
||||
|
||||
it('renders the comment message', () => {
|
||||
expect(wrapper.findAll('div.row').at(2).text()).toContain('This is a comment')
|
||||
})
|
||||
|
||||
it('renders the date', () => {
|
||||
expect(wrapper.findAll('div.row').at(3).text()).toContain('Sun May 02 2021')
|
||||
})
|
||||
|
||||
it('does not show the collapse by default', () => {
|
||||
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
|
||||
})
|
||||
|
||||
describe('without comment', () => {
|
||||
it('does not render the message row', async () => {
|
||||
await wrapper.setProps({ comment: undefined })
|
||||
expect(wrapper.findAll('div.row').at(2).text()).toContain('form.date')
|
||||
})
|
||||
})
|
||||
/* how to open the collapse ?????
|
||||
describe('collapse is open', () => {
|
||||
beforeEach(async () => {
|
||||
//console.log(wrapper.html())
|
||||
await wrapper.find('div#gdt-collapse-42').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
await wrapper.vm.$nextTick()
|
||||
await flushPromises()
|
||||
//console.log(wrapper.find('[enteractiveclass="collapsing"]').html())
|
||||
})
|
||||
|
||||
it('shows the collapse', () => {
|
||||
//console.log(wrapper.html())
|
||||
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
describe('GdtEntryType.CVS', () => {
|
||||
it('behaves as default FORM', async () => {
|
||||
await wrapper.setProps({ gdtEntryType: 'CVS' })
|
||||
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GdtEntryType.ELOPAGE', () => {
|
||||
it('behaves as default FORM', async () => {
|
||||
await wrapper.setProps({ gdtEntryType: 'ELOPAGE' })
|
||||
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GdtEntryType.DIGISTORE', () => {
|
||||
it('behaves as default FORM', async () => {
|
||||
await wrapper.setProps({ gdtEntryType: 'DIGISTORE' })
|
||||
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GdtEntryType.CVS2', () => {
|
||||
it('behaves as default FORM', async () => {
|
||||
await wrapper.setProps({ gdtEntryType: 'CVS2' })
|
||||
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GdtEntryType.ELOPAGE_PUBLISHER', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
amount: 365.67,
|
||||
date: '2020-04-10T13:28:00+00:00',
|
||||
comment: 'This is a comment',
|
||||
gdtEntryType: 'ELOPAGE_PUBLISHER',
|
||||
factor: 22,
|
||||
gdt: 967.65,
|
||||
id: 42,
|
||||
})
|
||||
})
|
||||
|
||||
it('has the person-check icon', () => {
|
||||
expect(wrapper.find('svg.bi-person-check').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the description gdt.recruited-member', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.recruited-member')
|
||||
})
|
||||
|
||||
it('renders the percentage', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('5%')
|
||||
})
|
||||
|
||||
it('renders the amount of GDT', () => {
|
||||
expect(wrapper.findAll('div.row').at(1).text()).toContain('365.67 GDT')
|
||||
})
|
||||
|
||||
it('renders the comment message', () => {
|
||||
expect(wrapper.findAll('div.row').at(2).text()).toContain('This is a comment')
|
||||
})
|
||||
|
||||
it('renders the date', () => {
|
||||
expect(wrapper.findAll('div.row').at(3).text()).toContain('Fri Apr 10 2020')
|
||||
})
|
||||
|
||||
it('does not show the collapse by default', () => {
|
||||
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
|
||||
})
|
||||
|
||||
describe('without comment', () => {
|
||||
it('does not render the message row', async () => {
|
||||
await wrapper.setProps({ comment: undefined })
|
||||
expect(wrapper.findAll('div.row').at(2).text()).toContain('form.date')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GdtEntryType.GLOBAL_MODIFICATOR', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
amount: 123.45,
|
||||
date: '2020-03-12T13:28:00+00:00',
|
||||
comment: 'This is a comment',
|
||||
gdtEntryType: 'GLOBAL_MODIFICATOR',
|
||||
factor: 19,
|
||||
gdt: 61.23,
|
||||
id: 42,
|
||||
})
|
||||
})
|
||||
|
||||
it('has the gift icon', () => {
|
||||
expect(wrapper.find('svg.bi-gift').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the description gdt.gdt-received', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.gdt-received')
|
||||
})
|
||||
|
||||
it('renders the comment', () => {
|
||||
expect(wrapper.findAll('div.row').at(0).text()).toContain('This is a comment')
|
||||
})
|
||||
|
||||
it('renders the amount of GDT', () => {
|
||||
expect(wrapper.findAll('div.row').at(1).text()).toContain('61.23 GDT')
|
||||
})
|
||||
|
||||
it('renders the date', () => {
|
||||
expect(wrapper.findAll('div.row').at(2).text()).toContain('Thu Mar 12 2020')
|
||||
})
|
||||
|
||||
it('does not show the collapse by default', () => {
|
||||
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item gdt-transaction-list-item" v-b-toggle="'a' + date + ''">
|
||||
<div class="list-group-item gdt-transaction-list-item" v-b-toggle="collapseId">
|
||||
<!-- icon -->
|
||||
<div class="text-right" style="position: absolute">
|
||||
<b-icon
|
||||
:icon="getLinesByType(gdtEntryType).icon"
|
||||
:class="getLinesByType(gdtEntryType).iconclasses"
|
||||
></b-icon>
|
||||
<b-icon :icon="getLinesByType.icon" :class="getLinesByType.iconclasses"></b-icon>
|
||||
</div>
|
||||
|
||||
<!-- collaps Button -->
|
||||
@ -20,10 +17,10 @@
|
||||
<!-- type -->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
{{ getLinesByType(gdtEntryType).description }}
|
||||
{{ getLinesByType.description }}
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ getLinesByType(gdtEntryType).descriptiontext }}
|
||||
{{ getLinesByType.descriptiontext }}
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
@ -33,7 +30,7 @@
|
||||
{{ $t('gdt.credit') }}
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ getLinesByType(gdtEntryType).credittext }}
|
||||
{{ getLinesByType.credittext }}
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
@ -58,7 +55,7 @@
|
||||
</b-row>
|
||||
|
||||
<!-- collaps trancaction info-->
|
||||
<b-collapse :id="'a' + date + ''" class="mt-2 pb-4">
|
||||
<b-collapse :id="collapseId" class="mt-2 pb-4">
|
||||
<transaction-collapse
|
||||
:amount="amount"
|
||||
:gdtEntryType="gdtEntryType"
|
||||
@ -86,15 +83,17 @@ export default {
|
||||
gdtEntryType: { type: String, default: GdtEntryType.FORM },
|
||||
factor: { type: Number },
|
||||
gdt: { type: Number },
|
||||
id: { type: Number },
|
||||
},
|
||||
computed: {
|
||||
isGlobalModificator: function () {
|
||||
collapseId() {
|
||||
return 'gdt-collapse-' + String(this.id)
|
||||
},
|
||||
isGlobalModificator() {
|
||||
return this.gdtEntryType === GdtEntryType.GLOBAL_MODIFICATOR
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getLinesByType(givenType) {
|
||||
switch (givenType) {
|
||||
getLinesByType() {
|
||||
switch (this.gdtEntryType) {
|
||||
case GdtEntryType.FORM:
|
||||
case GdtEntryType.CVS:
|
||||
case GdtEntryType.ELOPAGE:
|
||||
@ -127,7 +126,7 @@ export default {
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error('no lines for this type: ' + givenType)
|
||||
throw new Error('no lines for this type: ' + this.gdtEntryType)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -2,8 +2,12 @@ import { mount } from '@vue/test-utils'
|
||||
import TransactionCollapse from './TransactionCollapse'
|
||||
import { GdtEntryType } from '../graphql/enums'
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const consoleErrorMock = jest.fn()
|
||||
|
||||
describe('TransactionCollapse', () => {
|
||||
let wrapper
|
||||
|
||||
@ -16,6 +20,31 @@ describe('TransactionCollapse', () => {
|
||||
return mount(TransactionCollapse, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('no valid GDT entry type', () => {
|
||||
beforeEach(async () => {
|
||||
// disable throwing Errors on warnings to catch the warning
|
||||
Vue.config.warnHandler = (w) => {}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error = consoleErrorMock
|
||||
const propsData = {
|
||||
amount: 100,
|
||||
gdt: 110,
|
||||
factor: 22,
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
}
|
||||
wrapper = Wrapper(propsData)
|
||||
await wrapper.setProps({ gdtEntryType: 'NOT_VALID' })
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(consoleErrorMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'no additional transaction info for this type: NOT_VALID',
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('mount with gdtEntryType: FORM', () => {
|
||||
beforeEach(() => {
|
||||
const propsData = {
|
||||
@ -24,7 +53,6 @@ describe('TransactionCollapse', () => {
|
||||
factor: 22,
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
}
|
||||
|
||||
wrapper = Wrapper(propsData)
|
||||
})
|
||||
|
||||
@ -41,23 +69,23 @@ describe('TransactionCollapse', () => {
|
||||
})
|
||||
|
||||
it('renders the component collapse-headline', () => {
|
||||
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.calculation')
|
||||
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.calculation')
|
||||
})
|
||||
|
||||
it('renders the component collapse-first', () => {
|
||||
expect(wrapper.find('#collapse-first').text()).toBe('gdt.factor')
|
||||
expect(wrapper.find('.collapse-first').text()).toBe('gdt.factor')
|
||||
})
|
||||
|
||||
it('renders the component collapse-second', () => {
|
||||
expect(wrapper.find('#collapse-second').text()).toBe('gdt.formula')
|
||||
expect(wrapper.find('.collapse-second').text()).toBe('gdt.formula')
|
||||
})
|
||||
|
||||
it('renders the component collapse-firstMath', () => {
|
||||
expect(wrapper.find('#collapse-firstMath').text()).toBe('22 GDT pro €')
|
||||
expect(wrapper.find('.collapse-firstMath').text()).toBe('22 GDT pro €')
|
||||
})
|
||||
|
||||
it('renders the component collapse-secondMath', () => {
|
||||
expect(wrapper.find('#collapse-secondMath').text()).toBe('100 € * 22 GDT / € = 110 GDT')
|
||||
expect(wrapper.find('.collapse-secondMath').text()).toBe('100 € * 22 GDT / € = 110 GDT')
|
||||
})
|
||||
})
|
||||
|
||||
@ -86,23 +114,23 @@ describe('TransactionCollapse', () => {
|
||||
})
|
||||
|
||||
it('renders the component collapse-headline', () => {
|
||||
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.conversion-gdt-euro')
|
||||
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.conversion-gdt-euro')
|
||||
})
|
||||
|
||||
it('renders the component collapse-first', () => {
|
||||
expect(wrapper.find('#collapse-first').text()).toBe('gdt.raise')
|
||||
expect(wrapper.find('.collapse-first').text()).toBe('gdt.raise')
|
||||
})
|
||||
|
||||
it('renders the component collapse-second', () => {
|
||||
expect(wrapper.find('#collapse-second').text()).toBe('gdt.conversion')
|
||||
expect(wrapper.find('.collapse-second').text()).toBe('gdt.conversion')
|
||||
})
|
||||
|
||||
it('renders the component collapse-firstMath', () => {
|
||||
expect(wrapper.find('#collapse-firstMath').text()).toBe('2200 %')
|
||||
expect(wrapper.find('.collapse-firstMath').text()).toBe('2200 %')
|
||||
})
|
||||
|
||||
it('renders the component collapse-secondMath', () => {
|
||||
expect(wrapper.find('#collapse-secondMath').text()).toBe('100 GDT * 2200 % = 2200 GDT')
|
||||
expect(wrapper.find('.collapse-secondMath').text()).toBe('100 GDT * 2200 % = 2200 GDT')
|
||||
})
|
||||
})
|
||||
|
||||
@ -131,23 +159,23 @@ describe('TransactionCollapse', () => {
|
||||
})
|
||||
|
||||
it('renders the component collapse-headline', () => {
|
||||
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.publisher')
|
||||
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.publisher')
|
||||
})
|
||||
|
||||
it('renders the component collapse-first', () => {
|
||||
expect(wrapper.find('#collapse-first').text()).toBe('')
|
||||
expect(wrapper.find('.collapse-first').text()).toBe('')
|
||||
})
|
||||
|
||||
it('renders the component collapse-second', () => {
|
||||
expect(wrapper.find('#collapse-second').text()).toBe('')
|
||||
expect(wrapper.find('.collapse-second').text()).toBe('')
|
||||
})
|
||||
|
||||
it('renders the component collapse-firstMath', () => {
|
||||
expect(wrapper.find('#collapse-firstMath').text()).toBe('')
|
||||
expect(wrapper.find('.collapse-firstMath').text()).toBe('')
|
||||
})
|
||||
|
||||
it('renders the component collapse-secondMath', () => {
|
||||
expect(wrapper.find('#collapse-secondMath').text()).toBe('')
|
||||
expect(wrapper.find('.collapse-secondMath').text()).toBe('')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -4,19 +4,19 @@
|
||||
style="border: 0px; background-color: #f1f1f1"
|
||||
>
|
||||
<b-row class="gdt-list-collapse-header-text text-center pb-3">
|
||||
<b-col id="collapse-headline">
|
||||
<b>{{ getLinesByType(gdtEntryType).headline }}</b>
|
||||
<b-col class="collapse-headline">
|
||||
<b>{{ getLinesByType.headline }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="gdt-list-collapse-box--all">
|
||||
<b-col cols="6" class="text-right collapse-col-left">
|
||||
<div id="collapse-first">{{ getLinesByType(gdtEntryType).first }}</div>
|
||||
<div id="collapse-second">{{ getLinesByType(gdtEntryType).second }}</div>
|
||||
<div class="collapse-first">{{ getLinesByType.first }}</div>
|
||||
<div class="collapse-second">{{ getLinesByType.second }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6" class="collapse-col-right">
|
||||
<div id="collapse-firstMath">{{ getLinesByType(gdtEntryType).firstMath }}</div>
|
||||
<div id="collapse-secondMath">
|
||||
{{ getLinesByType(gdtEntryType).secondMath }}
|
||||
<div class="collapse-firstMath">{{ getLinesByType.firstMath }}</div>
|
||||
<div class="collapse-secondMath">
|
||||
{{ getLinesByType.secondMath }}
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@ -33,9 +33,9 @@ export default {
|
||||
factor: { type: Number },
|
||||
gdt: { type: Number },
|
||||
},
|
||||
methods: {
|
||||
getLinesByType(givenType) {
|
||||
switch (givenType) {
|
||||
computed: {
|
||||
getLinesByType() {
|
||||
switch (this.gdtEntryType) {
|
||||
case GdtEntryType.FORM:
|
||||
case GdtEntryType.CVS:
|
||||
case GdtEntryType.ELOPAGE:
|
||||
@ -80,7 +80,7 @@ export default {
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error('no additional transaction info for this type: ' + givenType)
|
||||
throw new Error('no additional transaction info for this type: ' + this.gdtEntryType)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -97,8 +97,9 @@
|
||||
"credit": "Gutschrift",
|
||||
"factor": "Faktor",
|
||||
"formula": "Berechungsformel",
|
||||
"funding": "Zu den Förderbeiträgen",
|
||||
"gdt-received": "Gradido Transform (GDT) erhalten",
|
||||
"no-transactions": "Du hast zur Zeit keine Transaktionen",
|
||||
"no-transactions": "Du hast noch keine Gradido Transform (GDT).",
|
||||
"publisher": "Dein geworbenes Mitglied hat einen Beitrag bezahlt",
|
||||
"raise": "Erhöhung",
|
||||
"recruited-member": "Geworbenes Mitglied"
|
||||
@ -109,6 +110,7 @@
|
||||
"logout": "Abmelden",
|
||||
"members_area": "Mitgliederbereich",
|
||||
"message": "hallo gradido !!",
|
||||
"overview": "Übersicht",
|
||||
"privacy_policy": "Datenschutzerklärung",
|
||||
"send": "Senden",
|
||||
"settings": {
|
||||
|
||||
@ -97,8 +97,9 @@
|
||||
"credit": "Credit",
|
||||
"factor": "Factor",
|
||||
"formula": "Calculation formula",
|
||||
"funding": "Regarding the funding contributions",
|
||||
"gdt-received": "Gradido Transform (GDT) received",
|
||||
"no-transactions": "You currently have no transactions",
|
||||
"no-transactions": "You do not have Gradido Transform (GDT) yet.",
|
||||
"publisher": "A member you referred has paid a contribution",
|
||||
"raise": "Increase",
|
||||
"recruited-member": "Recruited Member"
|
||||
@ -109,6 +110,7 @@
|
||||
"logout": "Logout",
|
||||
"members_area": "Member's area",
|
||||
"message": "hello gradido !!",
|
||||
"overview": "Overview",
|
||||
"privacy_policy": "Privacy policy",
|
||||
"send": "Send",
|
||||
"settings": {
|
||||
|
||||
@ -6,6 +6,7 @@ import { loadAllRules } from './validation-rules'
|
||||
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
|
||||
import VueApollo from 'vue-apollo'
|
||||
import CONFIG from './config'
|
||||
|
||||
import addNavigationGuards from './routes/guards'
|
||||
|
||||
import { store } from './store/store'
|
||||
|
||||
@ -49,8 +49,8 @@ describe('router', () => {
|
||||
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
|
||||
})
|
||||
|
||||
it('has twelve routes defined', () => {
|
||||
expect(routes).toHaveLength(13)
|
||||
it('has fourteen routes defined', () => {
|
||||
expect(routes).toHaveLength(14)
|
||||
})
|
||||
|
||||
describe('overview', () => {
|
||||
@ -64,6 +64,17 @@ describe('router', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('send', () => {
|
||||
it('requires authorization', () => {
|
||||
expect(routes.find((r) => r.path === '/send').meta.requiresAuth).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads the "Send" component', async () => {
|
||||
const component = await routes.find((r) => r.path === '/send').component()
|
||||
expect(component.default.name).toBe('SendOverview')
|
||||
})
|
||||
})
|
||||
|
||||
describe('profile', () => {
|
||||
it('requires authorization', () => {
|
||||
expect(routes.find((r) => r.path === '/profile').meta.requiresAuth).toBeTruthy()
|
||||
|
||||
@ -14,6 +14,13 @@ const routes = [
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/send',
|
||||
component: () => import('../views/Pages/SendOverview.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
component: () => import('../views/Pages/UserProfileOverview.vue'),
|
||||
|
||||
@ -89,33 +89,40 @@ describe('DashboardLayoutGdd', () => {
|
||||
navbar = wrapper.findAll('ul.navbar-nav').at(0)
|
||||
})
|
||||
|
||||
it('has three items in the navbar', () => {
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(3)
|
||||
it('has four items in the navbar', () => {
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(4)
|
||||
})
|
||||
|
||||
it('has first item "send" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(0).text()).toEqual('send')
|
||||
it('has first item "overview" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(0).text()).toEqual('overview')
|
||||
})
|
||||
|
||||
it('has first item "send" linked to overview in navbar', () => {
|
||||
navbar.findAll('ul > a').at(0).trigger('click')
|
||||
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/overview')
|
||||
it('has first item "overview" linked to overview in navbar', () => {
|
||||
expect(navbar.findAll('ul > a > a').at(0).attributes('href')).toBe('/overview')
|
||||
})
|
||||
|
||||
it('has second item "transactions" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(1).text()).toEqual('transactions')
|
||||
it('has second item "send" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(1).text()).toEqual('send')
|
||||
})
|
||||
|
||||
it('has second item "transactions" linked to transactions in navbar', async () => {
|
||||
expect(wrapper.findAll('a').at(3).attributes('href')).toBe('/transactions')
|
||||
it('has second item "send" linked to /send in navbar', () => {
|
||||
expect(wrapper.findAll('ul > a > a').at(1).attributes('href')).toBe('/send')
|
||||
})
|
||||
|
||||
it('has three items in the navbar', () => {
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(3)
|
||||
it('has third item "transactions" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(2).text()).toEqual('transactions')
|
||||
})
|
||||
|
||||
it('has third item "My profile" linked to profile in navbar', async () => {
|
||||
expect(wrapper.findAll('a').at(5).attributes('href')).toBe('/profile')
|
||||
it('has third item "transactions" linked to transactions in navbar', async () => {
|
||||
expect(wrapper.findAll('ul > a > a').at(2).attributes('href')).toBe('/transactions')
|
||||
})
|
||||
|
||||
it('has fourth item "My profile" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(3).text()).toEqual('site.navbar.my-profil')
|
||||
})
|
||||
|
||||
it('has fourth item "My profile" linked to profile in navbar', async () => {
|
||||
expect(wrapper.findAll('ul > a > a').at(3).attributes('href')).toBe('/profile')
|
||||
})
|
||||
|
||||
it('has a link to the members area', () => {
|
||||
|
||||
@ -2,11 +2,16 @@
|
||||
<div>
|
||||
<side-bar @logout="logout" :balance="balance" :pending="pending">
|
||||
<template slot="links">
|
||||
<p></p>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('overview'),
|
||||
path: '/overview',
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('send'),
|
||||
path: '/overview',
|
||||
path: '/send',
|
||||
}"
|
||||
></sidebar-item>
|
||||
<sidebar-item
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import AccountOverview from './AccountOverview'
|
||||
|
||||
const sendMock = jest.fn()
|
||||
sendMock.mockResolvedValue('success')
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
window.scrollTo = jest.fn()
|
||||
@ -11,26 +8,16 @@ window.scrollTo = jest.fn()
|
||||
describe('AccountOverview', () => {
|
||||
let wrapper
|
||||
|
||||
const propsData = {
|
||||
balance: 123.45,
|
||||
transactionCount: 1,
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$n: jest.fn((n) => String(n)),
|
||||
$store: {
|
||||
state: {
|
||||
email: 'sender@example.org',
|
||||
},
|
||||
},
|
||||
$apollo: {
|
||||
mutate: sendMock,
|
||||
},
|
||||
$n: jest.fn(),
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(AccountOverview, { localVue, mocks, propsData })
|
||||
return mount(AccountOverview, {
|
||||
localVue,
|
||||
mocks,
|
||||
})
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
@ -38,97 +25,21 @@ describe('AccountOverview', () => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a status line', () => {
|
||||
expect(wrapper.find('div.gdd-status').exists()).toBeTruthy()
|
||||
it('has a status gdd-status-gdd', () => {
|
||||
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a send field', () => {
|
||||
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
|
||||
it('has a status gdd-status-gdt', () => {
|
||||
expect(wrapper.find('div.gdd-status-gdt').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a transactions table', () => {
|
||||
expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('transaction form', () => {
|
||||
it('steps forward in the dialog', async () => {
|
||||
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
})
|
||||
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm transaction', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setData({
|
||||
currentTransactionStep: 1,
|
||||
transactionData: {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('resets the transaction process when on-reset is emitted', async () => {
|
||||
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
|
||||
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
|
||||
expect(wrapper.vm.transactionData).toEqual({
|
||||
email: '',
|
||||
amount: 0,
|
||||
memo: '',
|
||||
})
|
||||
})
|
||||
|
||||
describe('transaction is confirmed and server response is success', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmation' })
|
||||
.vm.$emit('send-transaction')
|
||||
})
|
||||
|
||||
it('calls the API when send-transaction is emitted', async () => {
|
||||
expect(sendMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update-balance', () => {
|
||||
expect(wrapper.emitted('update-balance')).toBeTruthy()
|
||||
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
|
||||
})
|
||||
|
||||
it('shows the succes page', () => {
|
||||
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('transaction is confirmed and server response is error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
sendMock.mockRejectedValue({ message: 'receiver not found' })
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmation' })
|
||||
.vm.$emit('send-transaction')
|
||||
})
|
||||
|
||||
it('shows the error page', () => {
|
||||
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
|
||||
})
|
||||
|
||||
it('shows recipient not found', () => {
|
||||
expect(wrapper.text()).toContain('transaction.receiverNotFound')
|
||||
})
|
||||
describe('timestamp updates', () => {
|
||||
it('emits update transactions', async () => {
|
||||
expect(wrapper.emitted('update-transactions')).toHaveLength(1)
|
||||
await wrapper.setData({ timestamp: Date.now() })
|
||||
expect(wrapper.emitted('update-transactions')).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,83 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-container fluid>
|
||||
<gdd-status
|
||||
v-if="showContext"
|
||||
:pending="pending"
|
||||
:balance="balance"
|
||||
:gdt-balance="GdtBalance"
|
||||
/>
|
||||
<b-row>
|
||||
<b-col class="col-6">
|
||||
<b-row>
|
||||
<b-col class="col-11 bg-gray text-white p-3">
|
||||
<status
|
||||
class="gdd-status-gdd"
|
||||
:pending="pending"
|
||||
:balance="balance"
|
||||
status-text="GDD"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-col>
|
||||
<b-col class="col-6 text-right">
|
||||
<b-row>
|
||||
<b-col class="bg-white text-gray p-3">
|
||||
<status
|
||||
class="gdd-status-gdt"
|
||||
:pending="pending"
|
||||
:balance="GdtBalance"
|
||||
status-text="GDT"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<br />
|
||||
<gdd-send :currentTransactionStep="currentTransactionStep">
|
||||
<template #transaction-form>
|
||||
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
|
||||
</template>
|
||||
<template #transaction-confirmation>
|
||||
<transaction-confirmation
|
||||
:email="transactionData.email"
|
||||
:amount="transactionData.amount"
|
||||
:memo="transactionData.memo"
|
||||
:loading="loading"
|
||||
@send-transaction="sendTransaction"
|
||||
@on-reset="onReset"
|
||||
></transaction-confirmation>
|
||||
</template>
|
||||
<template #transaction-result>
|
||||
<transaction-result
|
||||
:error="error"
|
||||
:errorResult="errorResult"
|
||||
@on-reset="onReset"
|
||||
></transaction-result>
|
||||
</template>
|
||||
</gdd-send>
|
||||
<hr />
|
||||
<gdd-transaction-list
|
||||
v-if="showContext"
|
||||
:transactions="transactions"
|
||||
:pageSize="5"
|
||||
:timestamp="timestamp"
|
||||
:transaction-count="transactionCount"
|
||||
@update-transactions="updateTransactions"
|
||||
/>
|
||||
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
|
||||
<gdd-transaction-list-footer :count="transactionCount" />
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import GddStatus from './AccountOverview/GddStatus.vue'
|
||||
import GddSend from './AccountOverview/GddSend.vue'
|
||||
import Status from '../../components/Status.vue'
|
||||
import GddTransactionList from './AccountOverview/GddTransactionList.vue'
|
||||
import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter.vue'
|
||||
import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue'
|
||||
import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue'
|
||||
import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue'
|
||||
import { sendCoins } from '../../graphql/mutations.js'
|
||||
|
||||
const EMPTY_TRANSACTION_DATA = {
|
||||
email: '',
|
||||
amount: 0,
|
||||
memo: '',
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Overview',
|
||||
components: {
|
||||
GddStatus,
|
||||
GddSend,
|
||||
Status,
|
||||
GddTransactionList,
|
||||
GddTransactionListFooter,
|
||||
TransactionForm,
|
||||
TransactionConfirmation,
|
||||
TransactionResult,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
transactionData: { ...EMPTY_TRANSACTION_DATA },
|
||||
error: false,
|
||||
errorResult: '',
|
||||
currentTransactionStep: 0,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@ -92,38 +68,7 @@ export default {
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showContext() {
|
||||
return this.currentTransactionStep === 0
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setTransaction(data) {
|
||||
this.transactionData = { ...data }
|
||||
this.currentTransactionStep = 1
|
||||
},
|
||||
async sendTransaction() {
|
||||
this.loading = true
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: sendCoins,
|
||||
variables: this.transactionData,
|
||||
})
|
||||
.then(() => {
|
||||
this.error = false
|
||||
this.$emit('update-balance', this.transactionData.amount)
|
||||
})
|
||||
.catch((err) => {
|
||||
this.errorResult = err.message
|
||||
this.error = true
|
||||
})
|
||||
this.currentTransactionStep = 2
|
||||
this.loading = false
|
||||
},
|
||||
onReset() {
|
||||
this.transactionData = { ...EMPTY_TRANSACTION_DATA }
|
||||
this.currentTransactionStep = 0
|
||||
},
|
||||
updateTransactions(pagination) {
|
||||
this.$emit('update-transactions', pagination)
|
||||
},
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import GddStatus from './GddStatus'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('GddStatus', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$n: jest.fn((n) => n),
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
balance: 1234,
|
||||
GdtBalance: 9876,
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(GddStatus, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('balance is loading', () => {
|
||||
it('it displays em-dash as the ammount of GDD', () => {
|
||||
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('— GDD')
|
||||
})
|
||||
|
||||
it('it displays em-dash as the ammount of GDT', () => {
|
||||
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('— GDT')
|
||||
})
|
||||
})
|
||||
|
||||
describe('balance is loaded', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({
|
||||
pending: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('it displays the ammount of GDD', () => {
|
||||
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('1234 GDD')
|
||||
})
|
||||
|
||||
it('it displays the ammount of GDT', () => {
|
||||
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('9876 GDT')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<div class="gdd-status">
|
||||
<b-row>
|
||||
<b-col class="p-0">
|
||||
<b-card class="p-0" style="background-color: #ebebeba3 !important">
|
||||
{{ pending ? '—' : $n(balance, 'decimal') }} GDD
|
||||
</b-card>
|
||||
</b-col>
|
||||
<b-col class="pr-0">
|
||||
<b-card class="p-0 text-right" style="background-color: #ebebeba3 !important">
|
||||
{{ pending ? '—' : $n(GdtBalance, 'decimal') }} GDT
|
||||
</b-card>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GddStatus',
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
GdtBalance: { type: Number, default: 0 },
|
||||
pending: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -124,7 +124,7 @@
|
||||
:per-page="pageSize"
|
||||
:total-rows="transactionCount"
|
||||
></pagination-buttons>
|
||||
<div v-if="transactions.length === 0" class="mt-4 text-center">
|
||||
<div v-if="transactionCount === 0" class="mt-4 text-center">
|
||||
<span>{{ $t('transaction.nullTransactions') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -198,4 +198,8 @@ export default {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.gdd-transaction-list-item {
|
||||
outline: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -7,41 +7,8 @@ const localVue = global.localVue
|
||||
const apolloMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
listGDTEntries: {
|
||||
count: 4,
|
||||
gdtEntries: [
|
||||
{
|
||||
amount: 100,
|
||||
gdt: 1700,
|
||||
factor: 17,
|
||||
comment: '',
|
||||
date: '2021-05-02T17:20:11+00:00',
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
},
|
||||
{
|
||||
amount: 1810,
|
||||
gdt: 362,
|
||||
factor: 0.2,
|
||||
comment: 'Dezember 20',
|
||||
date: '2020-12-31T12:00:00+00:00',
|
||||
gdtEntryType: GdtEntryType.GLOBAL_MODIFICATOR,
|
||||
},
|
||||
{
|
||||
amount: 100,
|
||||
gdt: 1700,
|
||||
factor: 17,
|
||||
comment: '',
|
||||
date: '2020-05-07T17:00:00+00:00',
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
},
|
||||
{
|
||||
amount: 100,
|
||||
gdt: 110,
|
||||
factor: 22,
|
||||
comment: '',
|
||||
date: '2020-04-10T13:28:00+00:00',
|
||||
gdtEntryType: GdtEntryType.ELOPAGE_PUBLISHER,
|
||||
},
|
||||
],
|
||||
count: 0,
|
||||
gdtEntries: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -51,10 +18,18 @@ const windowScrollToMock = jest.fn()
|
||||
|
||||
window.scrollTo = windowScrollToMock
|
||||
|
||||
describe('GdtTransactionList', () => {
|
||||
const state = {
|
||||
language: 'en',
|
||||
}
|
||||
|
||||
describe('GdtTransactionList ', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$store: {
|
||||
state,
|
||||
commit: jest.fn(),
|
||||
},
|
||||
$i18n: {
|
||||
locale: 'en',
|
||||
},
|
||||
@ -73,15 +48,80 @@ describe('GdtTransactionList', () => {
|
||||
return mount(GdtTransactionList, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
describe('mount - When no transactions are loaded', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the funding button ', () => {
|
||||
expect(wrapper.find('.gdt-funding').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('links to https://gradido.net/en/memberships/ when clicking', async () => {
|
||||
expect(wrapper.find('.gdt-funding').attributes('href')).toBe(
|
||||
'https://gradido.net/' + state.language + '/memberships/',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('mount - When transactions are loaded', () => {
|
||||
beforeEach(() => {
|
||||
apolloMock.mockResolvedValue({
|
||||
data: {
|
||||
listGDTEntries: {
|
||||
count: 4,
|
||||
gdtEntries: [
|
||||
{
|
||||
id: 1,
|
||||
amount: 100,
|
||||
gdt: 1700,
|
||||
factor: 17,
|
||||
comment: '',
|
||||
date: '2021-05-02T17:20:11+00:00',
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
amount: 1810,
|
||||
gdt: 362,
|
||||
factor: 0.2,
|
||||
comment: 'Dezember 20',
|
||||
date: '2020-12-31T12:00:00+00:00',
|
||||
gdtEntryType: GdtEntryType.GLOBAL_MODIFICATOR,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
amount: 100,
|
||||
gdt: 1700,
|
||||
factor: 17,
|
||||
comment: '',
|
||||
date: '2020-05-07T17:00:00+00:00',
|
||||
gdtEntryType: GdtEntryType.FORM,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
amount: 100,
|
||||
gdt: 110,
|
||||
factor: 22,
|
||||
comment: '',
|
||||
date: '2020-04-10T13:28:00+00:00',
|
||||
gdtEntryType: GdtEntryType.ELOPAGE_PUBLISHER,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div.gdt-transaction-list').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('does not render the funding button ', () => {
|
||||
expect(wrapper.find('.gdt-funding').exists()).toBe(false)
|
||||
})
|
||||
|
||||
describe('server returns valid data', () => {
|
||||
it('calls the API', async () => {
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
@ -1,21 +1,17 @@
|
||||
<template>
|
||||
<div class="gdt-transaction-list">
|
||||
<div class="list-group" style="background-color: #fff">
|
||||
<div v-if="transactionGdtCount === 0">
|
||||
<div class="list-group">
|
||||
<div v-if="transactionGdtCount === 0" class="text-center">
|
||||
{{ $t('gdt.no-transactions') }}
|
||||
<hr />
|
||||
<b-button class="gdt-funding" :href="link" target="_blank">
|
||||
{{ $t('gdt.funding') }}
|
||||
</b-button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
v-for="{
|
||||
transactionId,
|
||||
amount,
|
||||
date,
|
||||
comment,
|
||||
gdtEntryType,
|
||||
factor,
|
||||
gdt,
|
||||
} in transactionsGdt"
|
||||
:key="transactionId"
|
||||
v-for="{ id, amount, date, comment, gdtEntryType, factor, gdt } in transactionsGdt"
|
||||
:key="id"
|
||||
>
|
||||
<transaction
|
||||
:amount="amount"
|
||||
@ -24,6 +20,7 @@
|
||||
:gdtEntryType="gdtEntryType"
|
||||
:factor="factor"
|
||||
:gdt="gdt"
|
||||
:id="id"
|
||||
></transaction>
|
||||
</div>
|
||||
</div>
|
||||
@ -52,6 +49,7 @@ export default {
|
||||
transactionGdtCount: { type: Number, default: 0 },
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
link: 'https://gradido.net/' + this.$store.state.language + '/memberships/',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -97,4 +95,8 @@ export default {
|
||||
.nav-tabs .nav-item.show .nav-link {
|
||||
background-color: #f8f9fe38;
|
||||
}
|
||||
|
||||
.gdt-transaction-list-item {
|
||||
outline: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
135
frontend/src/views/Pages/SendOverview.spec.js
Normal file
135
frontend/src/views/Pages/SendOverview.spec.js
Normal file
@ -0,0 +1,135 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import SendOverview from './SendOverview'
|
||||
|
||||
const sendMock = jest.fn()
|
||||
sendMock.mockResolvedValue('success')
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
// window.scrollTo = jest.fn()
|
||||
|
||||
describe('SendOverview', () => {
|
||||
let wrapper
|
||||
|
||||
const propsData = {
|
||||
balance: 123.45,
|
||||
transactionCount: 1,
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$n: jest.fn((n) => String(n)),
|
||||
$store: {
|
||||
state: {
|
||||
email: 'sender@example.org',
|
||||
},
|
||||
},
|
||||
$apollo: {
|
||||
mutate: sendMock,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(SendOverview, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a status GDD line gdd-status-gdd', () => {
|
||||
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a send field', () => {
|
||||
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
// it('has a transactions table', () => {
|
||||
// expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
|
||||
// })
|
||||
|
||||
describe('transaction form', () => {
|
||||
it('steps forward in the dialog', async () => {
|
||||
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
})
|
||||
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm transaction', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setData({
|
||||
currentTransactionStep: 1,
|
||||
transactionData: {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('resets the transaction process when on-reset is emitted', async () => {
|
||||
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
|
||||
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
|
||||
expect(wrapper.vm.transactionData).toEqual({
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
})
|
||||
})
|
||||
|
||||
describe('transaction is confirmed and server response is success', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmation' })
|
||||
.vm.$emit('send-transaction')
|
||||
})
|
||||
|
||||
it('calls the API when send-transaction is emitted', async () => {
|
||||
expect(sendMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'user@example.org',
|
||||
amount: 23.45,
|
||||
memo: 'Make the best of it!',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update-balance', () => {
|
||||
expect(wrapper.emitted('update-balance')).toBeTruthy()
|
||||
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
|
||||
})
|
||||
|
||||
it('shows the succes page', () => {
|
||||
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('transaction is confirmed and server response is error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
sendMock.mockRejectedValue({ message: 'receiver not found' })
|
||||
await wrapper
|
||||
.findComponent({ name: 'TransactionConfirmation' })
|
||||
.vm.$emit('send-transaction')
|
||||
})
|
||||
|
||||
it('shows the error page', () => {
|
||||
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
|
||||
})
|
||||
|
||||
it('shows recipient not found', () => {
|
||||
expect(wrapper.text()).toContain('transaction.receiverNotFound')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
122
frontend/src/views/Pages/SendOverview.vue
Normal file
122
frontend/src/views/Pages/SendOverview.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-container fluid>
|
||||
<b-row>
|
||||
<b-col class="bg-gray text-white text-center p-3">
|
||||
<status
|
||||
class="gdd-status-gdd"
|
||||
v-if="showContext"
|
||||
:pending="pending"
|
||||
:balance="balance"
|
||||
status-text="GDD"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<br />
|
||||
<gdd-send :currentTransactionStep="currentTransactionStep">
|
||||
<template #transaction-form>
|
||||
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
|
||||
</template>
|
||||
<template #transaction-confirmation>
|
||||
<transaction-confirmation
|
||||
:email="transactionData.email"
|
||||
:amount="transactionData.amount"
|
||||
:memo="transactionData.memo"
|
||||
:loading="loading"
|
||||
@send-transaction="sendTransaction"
|
||||
@on-reset="onReset"
|
||||
></transaction-confirmation>
|
||||
</template>
|
||||
<template #transaction-result>
|
||||
<transaction-result
|
||||
:error="error"
|
||||
:errorResult="errorResult"
|
||||
@on-reset="onReset"
|
||||
></transaction-result>
|
||||
</template>
|
||||
</gdd-send>
|
||||
<hr />
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Status from '../../components/Status.vue'
|
||||
import GddSend from './SendOverview/GddSend.vue'
|
||||
|
||||
import TransactionForm from './SendOverview/GddSend/TransactionForm.vue'
|
||||
import TransactionConfirmation from './SendOverview/GddSend/TransactionConfirmation.vue'
|
||||
import TransactionResult from './SendOverview/GddSend/TransactionResult.vue'
|
||||
import { sendCoins } from '../../graphql/mutations.js'
|
||||
|
||||
const EMPTY_TRANSACTION_DATA = {
|
||||
email: '',
|
||||
amount: 0,
|
||||
memo: '',
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'SendOverview',
|
||||
components: {
|
||||
Status,
|
||||
GddSend,
|
||||
|
||||
TransactionForm,
|
||||
TransactionConfirmation,
|
||||
TransactionResult,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
transactionData: { ...EMPTY_TRANSACTION_DATA },
|
||||
error: false,
|
||||
errorResult: '',
|
||||
currentTransactionStep: 0,
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
GdtBalance: { type: Number, default: 0 },
|
||||
transactions: {
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
pending: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showContext() {
|
||||
return this.currentTransactionStep === 0
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setTransaction(data) {
|
||||
this.transactionData = { ...data }
|
||||
this.currentTransactionStep = 1
|
||||
},
|
||||
async sendTransaction() {
|
||||
this.loading = true
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: sendCoins,
|
||||
variables: this.transactionData,
|
||||
})
|
||||
.then(() => {
|
||||
this.error = false
|
||||
this.$emit('update-balance', this.transactionData.amount)
|
||||
})
|
||||
.catch((err) => {
|
||||
this.errorResult = err.message
|
||||
this.error = true
|
||||
})
|
||||
this.currentTransactionStep = 2
|
||||
this.loading = false
|
||||
},
|
||||
onReset() {
|
||||
this.currentTransactionStep = 0
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -62,4 +62,8 @@ export default {
|
||||
background-color: aquamarine;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.nav-tabs > li > a {
|
||||
outline: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1735,13 +1735,6 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/chart.js@^2.7.55":
|
||||
version "2.9.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.30.tgz#34b99897f4f5ef0f74c8fe4ced70ac52b4d752dd"
|
||||
integrity sha512-EgjxUUZFvf6ls3kW2CwyrnSJhgyKxgwrlp/W5G9wqyPEO9iFatO63zAA7L24YqgMxiDjQ+tG7ODU+2yWH91lPg==
|
||||
dependencies:
|
||||
moment "^2.10.2"
|
||||
|
||||
"@types/d3@3.5.38":
|
||||
version "3.5.38"
|
||||
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.38.tgz#76f8f2e9159ae562965b2fa0e6fbee1aa643a1bc"
|
||||
@ -3823,29 +3816,6 @@ chardet@^0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
||||
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
|
||||
|
||||
chart.js@^2.9.3:
|
||||
version "2.9.4"
|
||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
|
||||
integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
|
||||
dependencies:
|
||||
chartjs-color "^2.1.0"
|
||||
moment "^2.10.2"
|
||||
|
||||
chartjs-color-string@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
|
||||
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@^2.1.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
|
||||
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
|
||||
dependencies:
|
||||
chartjs-color-string "^0.6.0"
|
||||
color-convert "^1.9.3"
|
||||
|
||||
check-types@^8.0.3:
|
||||
version "8.0.3"
|
||||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
|
||||
@ -4073,7 +4043,7 @@ collection-visit@^1.0.0:
|
||||
map-visit "^1.0.0"
|
||||
object-visit "^1.0.0"
|
||||
|
||||
color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
|
||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
@ -9620,7 +9590,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
moment@^2.10.2, moment@^2.19.2:
|
||||
moment@^2.19.2:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
@ -13469,13 +13439,6 @@ vue-bootstrap-typeahead@^0.2.6:
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
vue "^2.5.17"
|
||||
|
||||
vue-chartjs@^3.5.0:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-3.5.1.tgz#d25e845708f7744ae51bed9d23a975f5f8fc6529"
|
||||
integrity sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==
|
||||
dependencies:
|
||||
"@types/chart.js" "^2.7.55"
|
||||
|
||||
vue-cli-plugin-i18n@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-1.0.1.tgz#5a3077de5d62c9b4068e486db1fc97fce9fa0072"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user