diff --git a/admin/package.json b/admin/package.json index 3406c326a..521f34bfc 100644 --- a/admin/package.json +++ b/admin/package.json @@ -33,6 +33,7 @@ "bootstrap": "4.3.1", "bootstrap-vue": "^2.21.2", "core-js": "^3.6.5", + "date-fns": "^2.29.3", "dotenv-webpack": "^7.0.3", "express": "^4.17.1", "graphql": "^15.6.1", diff --git a/admin/src/components/Fedaration/FederationVisualizeItem.spec.js b/admin/src/components/Fedaration/FederationVisualizeItem.spec.js new file mode 100644 index 000000000..6058cc6f4 --- /dev/null +++ b/admin/src/components/Fedaration/FederationVisualizeItem.spec.js @@ -0,0 +1,183 @@ +import { mount } from '@vue/test-utils' +import FederationVisualizeItem from './FederationVisualizeItem.vue' + +const localVue = global.localVue +const today = new Date() +const createdDate = new Date() +createdDate.setDate(createdDate.getDate() - 3) + +let propsData = { + item: { + id: 7590, + foreign: false, + publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7', + url: 'http://localhost/api/2_0', + lastAnnouncedAt: createdDate, + verifiedAt: today, + lastErrorAt: null, + createdAt: createdDate, + updatedAt: null, + }, +} + +const mocks = { + $i18n: { + locale: 'en', + }, +} + +describe('FederationVisualizeItem', () => { + let wrapper + + const Wrapper = () => { + return mount(FederationVisualizeItem, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the component', () => { + expect(wrapper.find('div.federation-visualize-item').exists()).toBe(true) + }) + + describe('rendering item properties', () => { + it('has the url', () => { + expect(wrapper.find('.row > div:nth-child(2) > div').text()).toBe( + 'http://localhost/api/2_0', + ) + }) + + it('has the public key', () => { + expect(wrapper.find('.row > div:nth-child(2) > small').text()).toContain( + 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7'.substring(0, 26), + ) + }) + + describe('verified item', () => { + it('has the check icon', () => { + expect(wrapper.find('svg.bi-check').exists()).toBe(true) + }) + + it('has the text variant "success"', () => { + expect(wrapper.find('.text-success').exists()).toBe(true) + }) + }) + + describe('not verified item', () => { + beforeEach(() => { + propsData = { + item: { + id: 7590, + foreign: false, + publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7', + url: 'http://localhost/api/2_0', + lastAnnouncedAt: createdDate, + verifiedAt: null, + lastErrorAt: null, + createdAt: createdDate, + updatedAt: null, + }, + } + wrapper = Wrapper() + }) + + it('has the x-circle icon', () => { + expect(wrapper.find('svg.bi-x-circle').exists()).toBe(true) + }) + + it('has the text variant "danger"', () => { + expect(wrapper.find('.text-danger').exists()).toBe(true) + }) + }) + + // describe('with different locales (de, en, fr, es, nl)', () => { + describe('lastAnnouncedAt', () => { + it('computes the time string for different locales (de, en, fr, es, nl)', () => { + wrapper.vm.$i18n.locale = 'de' + wrapper = Wrapper() + expect(wrapper.vm.lastAnnouncedAt).toBe('vor 3 Tagen') + + wrapper.vm.$i18n.locale = 'fr' + wrapper = Wrapper() + expect(wrapper.vm.lastAnnouncedAt).toBe('il y a 3 jours') + + wrapper.vm.$i18n.locale = 'es' + wrapper = Wrapper() + expect(wrapper.vm.lastAnnouncedAt).toBe('hace 3 días') + + wrapper.vm.$i18n.locale = 'nl' + wrapper = Wrapper() + expect(wrapper.vm.lastAnnouncedAt).toBe('3 dagen geleden') + }) + + describe('lastAnnouncedAt == null', () => { + beforeEach(() => { + propsData = { + item: { + id: 7590, + foreign: false, + publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7', + url: 'http://localhost/api/2_0', + lastAnnouncedAt: null, + verifiedAt: null, + lastErrorAt: null, + createdAt: createdDate, + updatedAt: null, + }, + } + wrapper = Wrapper() + }) + + it('computes empty string', async () => { + expect(wrapper.vm.lastAnnouncedAt).toBe('') + }) + }) + }) + + describe('createdAt', () => { + it('computes the time string for different locales (de, en, fr, es, nl)', () => { + wrapper.vm.$i18n.locale = 'de' + wrapper = Wrapper() + expect(wrapper.vm.createdAt).toBe('vor 3 Tagen') + + wrapper.vm.$i18n.locale = 'fr' + wrapper = Wrapper() + expect(wrapper.vm.createdAt).toBe('il y a 3 jours') + + wrapper.vm.$i18n.locale = 'es' + wrapper = Wrapper() + expect(wrapper.vm.createdAt).toBe('hace 3 días') + + wrapper.vm.$i18n.locale = 'nl' + wrapper = Wrapper() + expect(wrapper.vm.createdAt).toBe('3 dagen geleden') + }) + + describe('createdAt == null', () => { + beforeEach(() => { + propsData = { + item: { + id: 7590, + foreign: false, + publicKey: 'eaf6a426b24fd54f8fbae11c17700fc595080ca25159579c63d38dbc64284ba7', + url: 'http://localhost/api/2_0', + lastAnnouncedAt: createdDate, + verifiedAt: null, + lastErrorAt: null, + createdAt: null, + updatedAt: null, + }, + } + wrapper = Wrapper() + }) + + it('computes empty string', async () => { + expect(wrapper.vm.createdAt).toBe('') + }) + }) + }) + }) + }) +}) diff --git a/admin/src/components/Fedaration/FederationVisualizeItem.vue b/admin/src/components/Fedaration/FederationVisualizeItem.vue new file mode 100644 index 000000000..faace7da1 --- /dev/null +++ b/admin/src/components/Fedaration/FederationVisualizeItem.vue @@ -0,0 +1,63 @@ + + diff --git a/admin/src/components/NavBar.spec.js b/admin/src/components/NavBar.spec.js index 1927f258c..6a4a69959 100644 --- a/admin/src/components/NavBar.spec.js +++ b/admin/src/components/NavBar.spec.js @@ -62,8 +62,12 @@ describe('NavBar', () => { ) }) + it('has a link to /federation', () => { + expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/federation') + }) + it('has a link to /statistic', () => { - expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic') + expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe('/statistic') }) }) @@ -72,7 +76,7 @@ describe('NavBar', () => { beforeEach(async () => { delete window.location window.location = '' - await wrapper.findAll('.nav-item').at(4).find('a').trigger('click') + await wrapper.findAll('.nav-item').at(5).find('a').trigger('click') }) afterEach(() => { @@ -97,7 +101,7 @@ describe('NavBar', () => { window.location = { assign: windowLocationMock, } - await wrapper.findAll('.nav-item').at(5).find('a').trigger('click') + await wrapper.findAll('.nav-item').at(6).find('a').trigger('click') }) afterEach(() => { diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue index dae4bba91..2efeda048 100644 --- a/admin/src/components/NavBar.vue +++ b/admin/src/components/NavBar.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/GddTransactionList.spec.js b/frontend/src/components/GddTransactionList.spec.js index 7473c11e0..b1cdf9eb9 100644 --- a/frontend/src/components/GddTransactionList.spec.js +++ b/frontend/src/components/GddTransactionList.spec.js @@ -93,8 +93,9 @@ describe('GddTransactionList', () => { { id: -1, typeId: 'DECAY', - amount: '-0.16778637075575395772595', - balance: '31.59320453982945549519405', + amount: '-0.16', + balance: '31.59', + previousBalance: '31.75', balanceDate: '2022-03-03T08:54:54', memo: '', linkedUser: null, @@ -110,6 +111,7 @@ describe('GddTransactionList', () => { typeId: 'SEND', amount: '1', balance: '31.76099091058520945292', + previousBalance: '30.76', balanceDate: '2022-02-28T13:55:47', memo: 'Um den Kessel schlingt den Reihn, Werft die Eingeweid‘ hinein. Kröte du, die Nacht und Tag Unterm kalten Steine lag,', @@ -129,6 +131,7 @@ describe('GddTransactionList', () => { typeId: 'RECEIVE', amount: '10', balance: '10', + previousBalance: '31.75', balanceDate: '2022-02-23T10:55:30', memo: 'Monatlanges Gift sog ein, In den Topf zuerst hinein… (William Shakespeare, Die Hexen aus Macbeth)', @@ -148,6 +151,7 @@ describe('GddTransactionList', () => { typeId: 'CREATION', amount: '1000', balance: '32.96482231613347376132', + previousBalance: '31.75', balanceDate: '2022-02-25T07:29:26', memo: 'Jammern hilft nichts, sondern ich kann selber meinen Teil dazu beitragen.', linkedUser: { @@ -414,6 +418,7 @@ describe('GddTransactionList', () => { return { amount: '3.14', balanceDate: '2021-04-29T17:26:40+00:00', + previousBalance: '31.75', decay: { decay: '-477.01', start: '2021-05-13T17:46:31.000Z', diff --git a/frontend/src/components/GddTransactionList.vue b/frontend/src/components/GddTransactionList.vue index deed0dedb..092ff6a34 100644 --- a/frontend/src/components/GddTransactionList.vue +++ b/frontend/src/components/GddTransactionList.vue @@ -19,10 +19,7 @@ class="pointer bg-white appBoxShadow gradido-border-radius px-4 pt-2 test-list-group-item" > @@ -34,27 +31,15 @@ class="pointer mb-3 bg-white appBoxShadow gradido-border-radius p-3 test-list-group-item" > diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index b8635f44a..a5357e6d9 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -89,6 +89,8 @@ "decay_introduced": "Die Vergänglichkeit wurde eingeführt am:", "decay_since_last_transaction": "Vergänglichkeit seit der letzten Transaktion", "last_transaction": "Letzte Transaktion", + "new_balance": "Neuer Kontostand", + "old_balance": "Vorheriger Kontostand", "past_time": "Vergangene Zeit", "Starting_block_decay": "Startblock Vergänglichkeit", "total": "Gesamt", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 15449a208..42f22f709 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -89,6 +89,8 @@ "decay_introduced": "Decay was introduced on:", "decay_since_last_transaction": "Decay since the last transaction", "last_transaction": "Last transaction:", + "new_balance": "New balance", + "old_balance": "Previous balance", "past_time": "Time passed", "Starting_block_decay": "Starting Block Decay", "total": "Total", diff --git a/frontend/src/pages/Community.spec.js b/frontend/src/pages/Community.spec.js index 28d742fe9..ab1572d37 100644 --- a/frontend/src/pages/Community.spec.js +++ b/frontend/src/pages/Community.spec.js @@ -72,6 +72,7 @@ describe('Community', () => { messagesCount: 0, deniedAt: null, deniedBy: null, + moderatorId: null, }, { id: 1550, @@ -88,6 +89,7 @@ describe('Community', () => { messagesCount: 0, deniedAt: null, deniedBy: null, + moderatorId: null, }, ], contributionCount: 1, diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index 20e7d3fe6..4388d6f4a 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -196,7 +196,9 @@ export default { methods: { updateTabIndex() { const index = COMMUNITY_TABS.indexOf(this.$route.params.tab) - this.tabIndex = index > -1 ? index : 0 + this.$nextTick(() => { + this.tabIndex = index > -1 ? index : 0 + }) this.closeAllOpenCollapse() }, closeAllOpenCollapse() { diff --git a/frontend/src/pages/Send.spec.js b/frontend/src/pages/Send.spec.js index 0a0d5da71..3aae5a83e 100644 --- a/frontend/src/pages/Send.spec.js +++ b/frontend/src/pages/Send.spec.js @@ -10,6 +10,7 @@ const apolloMutationMock = jest.fn() apolloMutationMock.mockResolvedValue('success') const navigatorClipboardMock = jest.fn() +const routerPushMock = jest.fn() const localVue = global.localVue @@ -38,6 +39,9 @@ describe('Send', () => { $route: { query: {}, }, + $router: { + push: routerPushMock, + }, } const Wrapper = () => { @@ -85,8 +89,8 @@ describe('Send', () => { it('shows the transaction formular again', () => { expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBe(true) }) - // TODO:SKIPED at this point, a check must be made in the components ? - it.skip('restores the previous data in the formular', () => { + + it('restores the previous data in the formular', () => { expect(wrapper.find("input[type='email']").vm.$el.value).toBe('user@example.org') expect(wrapper.find("input[type='text']").vm.$el.value).toBe('23.45') expect(wrapper.find('textarea').vm.$el.value).toBe('Make the best of it!') @@ -107,10 +111,11 @@ describe('Send', () => { expect.objectContaining({ mutation: sendCoins, variables: { - email: 'user@example.org', + identifier: 'user@example.org', amount: 23.45, memo: 'Make the best of it!', selected: SEND_TYPES.send, + userName: '', }, }), ) @@ -162,6 +167,67 @@ describe('Send', () => { }) }) + describe('with gradidoID query', () => { + beforeEach(() => { + mocks.$route.query.gradidoID = 'gradido-ID' + wrapper = Wrapper() + }) + + it('has no email input field', () => { + expect( + wrapper.findComponent({ name: 'TransactionForm' }).find('input[type="email"]').exists(), + ).toBe(false) + }) + + describe('submit form', () => { + beforeEach(async () => { + jest.clearAllMocks() + const transactionForm = wrapper.findComponent({ name: 'TransactionForm' }) + await transactionForm.find('input[type="text"]').setValue('34.56') + await transactionForm.find('textarea').setValue('Make the best of it!') + await transactionForm.find('form').trigger('submit') + await flushPromises() + }) + + it('steps forward in the dialog', () => { + expect(wrapper.findComponent({ name: 'TransactionConfirmationSend' }).exists()).toBe(true) + }) + + describe('confirm transaction', () => { + beforeEach(async () => { + jest.clearAllMocks() + await wrapper + .findComponent({ name: 'TransactionConfirmationSend' }) + .find('button.btn-gradido') + .trigger('click') + }) + + it('calls the API', async () => { + expect(apolloMutationMock).toBeCalledWith( + expect.objectContaining({ + mutation: sendCoins, + variables: { + identifier: 'gradido-ID', + amount: 34.56, + memo: 'Make the best of it!', + selected: SEND_TYPES.send, + userName: '', + }, + }), + ) + }) + + it('resets the gradido ID query in route', () => { + expect(routerPushMock).toBeCalledWith({ + query: { + gradidoID: undefined, + }, + }) + }) + }) + }) + }) + describe('transaction form link', () => { const now = new Date().toISOString() beforeEach(async () => { diff --git a/frontend/src/pages/Send.vue b/frontend/src/pages/Send.vue index 65677ecdd..30ffc06ed 100644 --- a/frontend/src/pages/Send.vue +++ b/frontend/src/pages/Send.vue @@ -11,9 +11,7 @@