diff --git a/admin/src/mixins/toaster.js b/admin/src/mixins/toaster.js index 9f79b91e8..2db632a81 100644 --- a/admin/src/mixins/toaster.js +++ b/admin/src/mixins/toaster.js @@ -15,7 +15,7 @@ export const toasters = { toast(message, options) { // for unit tests, check that replace is present if (message.replace) message = message.replace(/^GraphQL error: /, '') - this.$bvToast.toast(message, { + this.$root.$bvToast.toast(message, { autoHideDelay: 5000, appendToast: true, solid: true, diff --git a/frontend/public/index.html b/frontend/public/index.html index a15b46916..b9abe3876 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -52,10 +52,6 @@ -
- -
- diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.spec.js b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js new file mode 100644 index 000000000..b533c6aa1 --- /dev/null +++ b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js @@ -0,0 +1,126 @@ +import { mount } from '@vue/test-utils' +import CollapseLinksList from './CollapseLinksList' + +const localVue = global.localVue + +const mocks = { + $i18n: { + locale: 'en', + }, + $tc: jest.fn((tc) => tc), + $t: jest.fn((t) => t), +} + +const propsData = { + transactionLinks: [ + { + amount: '5', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 87, + memo: 'Eene meene Siegerpreis, vor mir steht ein Schokoeis. Hex-hex!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '6', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 86, + memo: 'Eene meene buntes Laub, auf dem Schrank da liegt kein Staub.', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + ], + transactionLinkCount: 3, + value: 1, + pending: false, + pageSize: 5, +} + +describe('CollapseLinksList', () => { + let wrapper + + const Wrapper = () => { + return mount(CollapseLinksList, { localVue, mocks, propsData }) + } + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the component div.collapse-links-list', () => { + expect(wrapper.find('div.collapse-links-list').exists()).toBeTruthy() + }) + + describe('load more links', () => { + beforeEach(async () => { + await wrapper.find('button.test-button-load-more').trigger('click') + }) + + it('emits input', () => { + expect(wrapper.emitted('input')).toEqual([[2]]) + }) + }) + + describe('reset transaction link list', () => { + beforeEach(async () => { + await wrapper + .findComponent({ name: 'TransactionLink' }) + .vm.$emit('reset-transaction-link-list') + }) + + it('emits input ', () => { + expect(wrapper.emitted('input')).toEqual([[0]]) + }) + }) + + describe('button text', () => { + describe('one more link to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + }) + }) + + it('renders text in singular', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 0) + }) + }) + + describe('less than pageSize links to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + transactionLinkCount: 6, + }) + }) + + it('renders text in plural and shows the correct count of links', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 1, { n: 4 }) + }) + }) + + describe('more than pageSize links to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + transactionLinkCount: 16, + }) + }) + + it('renders text in plural with page size links to load', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 2, { n: 5 }) + }) + }) + }) + }) +}) diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.vue b/frontend/src/components/DecayInformations/CollapseLinksList.vue index 3c6bab053..0c1db5f0a 100644 --- a/frontend/src/components/DecayInformations/CollapseLinksList.vue +++ b/frontend/src/components/DecayInformations/CollapseLinksList.vue @@ -1,14 +1,66 @@ diff --git a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue index 7377b6fa8..6abacd2b8 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue @@ -13,8 +13,8 @@
- {{ (Number(balance) - Number(decay.decay)) | GDD }} - {{ decay.decay | GDD }} {{ $t('math.equal') }} + {{ (Number(balance) - Number(decay)) | GDD }} + {{ decay | GDD }} {{ $t('math.equal') }} {{ balance | GDD }}
@@ -27,9 +27,11 @@ export default { props: { balance: { type: String, + required: true, }, decay: { - type: Object, + type: String, + required: true, }, }, } diff --git a/frontend/src/components/DecayInformations/DecayInformation-Short.vue b/frontend/src/components/DecayInformations/DecayInformation-Short.vue index 1cd0a2d09..ba1f16d72 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-Short.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-Short.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/TransactionRows/DateRow.vue b/frontend/src/components/TransactionRows/DateRow.vue index 20e80ec2b..0c72907fe 100644 --- a/frontend/src/components/TransactionRows/DateRow.vue +++ b/frontend/src/components/TransactionRows/DateRow.vue @@ -2,11 +2,11 @@
-
{{ $t('form.date') }}
+
{{ diffNow ? $t('gdd_per_link.expired') : $t('form.date') }}
- {{ $d(new Date(balanceDate), 'long') }} + {{ dateString }}
@@ -16,10 +16,22 @@ export default { name: 'DateRow', props: { - balanceDate: { + date: { type: String, required: true, }, + diffNow: { + type: Boolean, + required: false, + default: false, + }, + }, + computed: { + dateString() { + return this.diffNow + ? this.$moment(this.date).locale(this.$i18n.locale).fromNow() + : this.$d(new Date(this.date), 'long') + }, }, } diff --git a/frontend/src/components/TransactionRows/DecayRow.vue b/frontend/src/components/TransactionRows/DecayRow.vue index 92d79ef4b..f853d9f93 100644 --- a/frontend/src/components/TransactionRows/DecayRow.vue +++ b/frontend/src/components/TransactionRows/DecayRow.vue @@ -23,7 +23,7 @@ export default { }, props: { decay: { - type: Object, + type: String, required: false, }, }, diff --git a/frontend/src/components/TransactionRows/MemoRow.vue b/frontend/src/components/TransactionRows/MemoRow.vue index ef79fb38e..26b421bf9 100644 --- a/frontend/src/components/TransactionRows/MemoRow.vue +++ b/frontend/src/components/TransactionRows/MemoRow.vue @@ -16,7 +16,7 @@ export default { props: { memo: { type: String, - required: false, + required: true, }, }, } diff --git a/frontend/src/components/Transactions/TransactionCreation.vue b/frontend/src/components/Transactions/TransactionCreation.vue index 43178e5f2..bb131d39a 100644 --- a/frontend/src/components/Transactions/TransactionCreation.vue +++ b/frontend/src/components/Transactions/TransactionCreation.vue @@ -18,10 +18,10 @@ - + - +
diff --git a/frontend/src/components/Transactions/TransactionDecay.vue b/frontend/src/components/Transactions/TransactionDecay.vue index 4038e782f..2f235502a 100644 --- a/frontend/src/components/Transactions/TransactionDecay.vue +++ b/frontend/src/components/Transactions/TransactionDecay.vue @@ -20,8 +20,8 @@ - - + + diff --git a/frontend/src/components/Transactions/TransactionLink.vue b/frontend/src/components/Transactions/TransactionLink.vue deleted file mode 100644 index 5c261adbf..000000000 --- a/frontend/src/components/Transactions/TransactionLink.vue +++ /dev/null @@ -1,70 +0,0 @@ - - diff --git a/frontend/src/components/Transactions/TransactionLinksSummary.spec.js b/frontend/src/components/Transactions/TransactionLinksSummary.spec.js new file mode 100644 index 000000000..60517a843 --- /dev/null +++ b/frontend/src/components/Transactions/TransactionLinksSummary.spec.js @@ -0,0 +1,230 @@ +import { mount } from '@vue/test-utils' +import TransactionLinksSummary from './TransactionLinksSummary' +import { listTransactionLinks } from '@/graphql/queries' +import { toastErrorSpy } from '@test/testSetup' + +const localVue = global.localVue + +const apolloQueryMock = jest.fn() + +const mocks = { + $i18n: { + locale: 'en', + }, + $t: jest.fn((t) => t), + $tc: jest.fn((tc) => tc), + $apollo: { + query: apolloQueryMock, + }, +} + +const propsData = { + amount: '123', + decay: { + decay: '-0.2038314055482643084', + start: '2022-02-25T07:29:26.000Z', + end: '2022-02-28T13:55:47.000Z', + duration: 282381, + }, + transactionLinkCount: 4, +} + +describe('TransactionLinksSummary', () => { + let wrapper + + const Wrapper = () => { + return mount(TransactionLinksSummary, { localVue, mocks, propsData }) + } + + describe('mount', () => { + beforeEach(() => { + apolloQueryMock.mockResolvedValue({ + data: { + listTransactionLinks: [ + { + amount: '75', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 86, + memo: + 'Hokuspokus Haselnuss, Vogelbein und Fliegenfuß, damit der Trick gelingen muss!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '85', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 107, + memo: 'Mäusespeck und Katzenbuckel, Tricks und Tracks und Zauberkugel!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '95', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 92, + memo: + 'Abrakadabra 1,2,3, die Sonne kommt herbei. Schweinepups und Spuckebrei, der Regen ist vorbei.', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '150', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 16, + memo: + 'Abrakadabra 1,2,3 was verschwunden ist komme herbei.Wieseldreck und Schweinemist, zaubern das ist keine List.', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + ], + }, + }) + + wrapper = Wrapper() + }) + + it('renders the component transaction-slot-link', () => { + expect(wrapper.find('div.transaction-slot-link').exists()).toBe(true) + }) + + it('has a component CollapseLinksList', () => { + expect(wrapper.findComponent({ name: 'CollapseLinksList' }).exists()).toBe(true) + }) + + it('calls the API to get the list transaction links', () => { + expect(apolloQueryMock).toBeCalledWith({ + query: listTransactionLinks, + variables: { + currentPage: 1, + }, + fetchPolicy: 'network-only', + }) + }) + + it('has four transactionLinks', () => { + expect(wrapper.vm.transactionLinks).toHaveLength(4) + }) + + describe('reset transaction links', () => { + beforeEach(async () => { + jest.clearAllMocks() + await wrapper.setData({ + currentPage: 0, + pending: false, + pageSize: 5, + }) + }) + + it('reloads transaction links', () => { + expect(apolloQueryMock).toBeCalledWith({ + query: listTransactionLinks, + variables: { + currentPage: 1, + }, + fetchPolicy: 'network-only', + }) + }) + + it('emits update transactions', () => { + expect(wrapper.emitted('update-transactions')).toBeTruthy() + }) + + it('has four transaction links in list', () => { + expect(wrapper.vm.transactionLinks).toHaveLength(4) + }) + }) + + describe('load more transaction links', () => { + beforeEach(async () => { + jest.clearAllMocks() + apolloQueryMock.mockResolvedValue({ + data: { + listTransactionLinks: [ + { + amount: '76', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 87, + memo: + 'Hat jemand die Nummer von der Hexe aus Schneewittchen? Ich bräuchte mal ein paar Äpfel.', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '86', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 108, + memo: + 'Die Windfahn´ krächzt am Dach, Der Uhu im Geklüfte; Was wispert wie ein Ach Verhallend in die Lüfte?', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '96', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 93, + memo: + 'Verschlafen kräht der Hahn, Ein Blitz noch, und ein trüber, Umwölbter Tag bricht an – Walpurgisnacht vorüber!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '150', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 17, + memo: 'Eene meene Flaschenschrank, fertig ist der Hexentrank!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + ], + }, + }) + await wrapper.setData({ + currentPage: 2, + pending: false, + pageSize: 5, + }) + }) + + it('has eight transactionLinks', () => { + expect(wrapper.vm.transactionLinks).toHaveLength(8) + }) + + it('loads more transaction links', () => { + expect(apolloQueryMock).toBeCalledWith({ + query: listTransactionLinks, + variables: { + currentPage: 2, + }, + fetchPolicy: 'network-only', + }) + }) + }) + + describe('loads transaction links with error', () => { + beforeEach(() => { + apolloQueryMock.mockRejectedValue({ message: 'OUCH!' }) + wrapper = Wrapper() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('OUCH!') + }) + }) + }) +}) diff --git a/frontend/src/components/Transactions/TransactionLinksSummary.vue b/frontend/src/components/Transactions/TransactionLinksSummary.vue new file mode 100644 index 000000000..3f80bfd18 --- /dev/null +++ b/frontend/src/components/Transactions/TransactionLinksSummary.vue @@ -0,0 +1,117 @@ + + diff --git a/frontend/src/components/Transactions/TransactionReceive.vue b/frontend/src/components/Transactions/TransactionReceive.vue index e9dc23cdb..eade4a30c 100644 --- a/frontend/src/components/Transactions/TransactionReceive.vue +++ b/frontend/src/components/Transactions/TransactionReceive.vue @@ -19,10 +19,10 @@ - + - + diff --git a/frontend/src/components/Transactions/TransactionSend.vue b/frontend/src/components/Transactions/TransactionSend.vue index 18112f8e1..8183a5734 100644 --- a/frontend/src/components/Transactions/TransactionSend.vue +++ b/frontend/src/components/Transactions/TransactionSend.vue @@ -19,10 +19,10 @@ - + - + diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index d4bf8c1da..89721f02b 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -69,3 +69,9 @@ export const createTransactionLink = gql` } } ` + +export const deleteTransactionLink = gql` + mutation($id: Float!) { + deleteTransactionLink(id: $id) + } +` diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 5887d585a..141dc8651 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -143,3 +143,18 @@ export const queryTransactionLink = gql` } } ` + +export const listTransactionLinks = gql` + query($currentPage: Int = 1, $pageSize: Int = 5) { + listTransactionLinks(currentPage: $currentPage, pageSize: $pageSize) { + id + amount + holdAvailableAmount + memo + code + createdAt + validUntil + redeemedAt + } + } +` diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 1790af454..a20d92935 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -27,6 +27,7 @@ "send": "Gesendet" } }, + "delete": "Löschen", "em-dash": "—", "error": { "empty-transactionlist": "Es gab einen Fehler mit der Übermittlung der Anzahl deiner Transaktionen.", @@ -96,6 +97,9 @@ "copy": "kopieren", "created": "Der Link wurde erstellt!", "decay-14-day": "Vergänglichkeit für 14 Tage", + "delete-the-link": "Den Link löschen?", + "deleted": "Der Link wurde gelöscht!", + "expired": "Abgelaufen", "header": "Gradidos versenden per Link", "link-copied": "Link wurde in die Zwischenablage kopiert", "links_count": "Aktive Links", @@ -120,9 +124,7 @@ "recruited-member": "Eingeladenes Mitglied" }, "language": "Sprache", - "links-list": { - "header": "Liste deiner aktiven Links" - }, + "link-load": "den letzten Link nachladen | die letzten {n} Links nachladen | weitere {n} Links nachladen", "login": "Anmeldung", "math": { "aprox": "~", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index d39aea683..378a4dceb 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -27,6 +27,7 @@ "send": "Sent" } }, + "delete": "Delete", "em-dash": "—", "error": { "empty-transactionlist": "There was an error with the transmission of the number of your transactions.", @@ -96,6 +97,9 @@ "copy": "copy", "created": "Link was created!", "decay-14-day": "Decay for 14 days", + "delete-the-link": "Delete the link?", + "deleted": "The link was deleted!", + "expired": "Expired", "header": "Send Gradidos via link", "link-copied": "Link copied to clipboard", "links_count": "Active links", @@ -120,9 +124,7 @@ "recruited-member": "Invited member" }, "language": "Language", - "links-list": { - "header": "List of your active links" - }, + "link-load": "Load the last link | Load the last {n} links | Load more {n} links", "login": "Login", "math": { "aprox": "~", diff --git a/frontend/src/mixins/toaster.js b/frontend/src/mixins/toaster.js index 68fd78ff9..f6e61e5be 100644 --- a/frontend/src/mixins/toaster.js +++ b/frontend/src/mixins/toaster.js @@ -14,7 +14,7 @@ export const toasters = { }, toast(message, options) { if (message.replace) message = message.replace(/^GraphQL error: /, '') - this.$bvToast.toast(message, { + this.$root.$bvToast.toast(message, { autoHideDelay: 5000, appendToast: true, solid: true,