diff --git a/admin/src/components/NavBar.spec.js b/admin/src/components/NavBar.spec.js
index b084dd18b..c88a8817d 100644
--- a/admin/src/components/NavBar.spec.js
+++ b/admin/src/components/NavBar.spec.js
@@ -3,11 +3,15 @@ import NavBar from './NavBar.vue'
const localVue = global.localVue
+const apolloMutateMock = jest.fn()
const storeDispatchMock = jest.fn()
const routerPushMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
+ $apollo: {
+ mutate: apolloMutateMock,
+ },
$store: {
state: {
openCreations: 1,
@@ -69,5 +73,9 @@ describe('NavBar', () => {
it('dispatches logout to store', () => {
expect(storeDispatchMock).toBeCalledWith('logout')
})
+
+ it('has called logout mutation', () => {
+ expect(apolloMutateMock).toBeCalled()
+ })
})
})
diff --git a/admin/src/components/NavBar.vue b/admin/src/components/NavBar.vue
index 9103b56e6..d3d8367db 100644
--- a/admin/src/components/NavBar.vue
+++ b/admin/src/components/NavBar.vue
@@ -28,14 +28,18 @@
diff --git a/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js b/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js
deleted file mode 100644
index b1e09da94..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsModerator from './IsModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 111,
- message: 'asd asda sda sda',
- createdAt: '2022-08-29T12:23:27.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Peter',
- userLastName: 'Lustig',
- userId: 107,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-moderator', () => {
- expect(wrapper.find('div.slot-is-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/frontend/src/components/ContributionMessages/slots/IsModerator.vue b/frontend/src/components/ContributionMessages/slots/IsModerator.vue
deleted file mode 100644
index 343b92d97..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsModerator.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ $t('community.moderator') }}
-
{{ message.message }}
-
-
-
-
diff --git a/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js b/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js
deleted file mode 100644
index 24152ad1e..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsNotModerator.spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { mount } from '@vue/test-utils'
-import IsNotModerator from './IsNotModerator.vue'
-
-const localVue = global.localVue
-
-describe('IsNotModerator', () => {
- let wrapper
-
- const mocks = {
- $t: jest.fn((t) => t),
- $d: jest.fn((d) => d),
- }
-
- const propsData = {
- message: {
- id: 113,
- message: 'asda sdad ad asdasd ',
- createdAt: '2022-08-29T12:25:34.000Z',
- updatedAt: null,
- type: 'DIALOG',
- userFirstName: 'Bibi',
- userLastName: 'Bloxberg',
- userId: 108,
- __typename: 'ContributionMessage',
- },
- }
-
- const Wrapper = () => {
- return mount(IsNotModerator, {
- localVue,
- mocks,
- propsData,
- })
- }
-
- describe('mount', () => {
- beforeEach(() => {
- wrapper = Wrapper()
- })
-
- it('has a DIV .slot-is-not-moderator', () => {
- expect(wrapper.find('div.slot-is-not-moderator').exists()).toBe(true)
- })
-
- it('props.message.default', () => {
- expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
- })
- })
-})
diff --git a/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue b/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue
deleted file mode 100644
index 8efca7270..000000000
--- a/frontend/src/components/ContributionMessages/slots/IsNotModerator.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
{{ message.userFirstName }} {{ message.userLastName }}
-
{{ $d(new Date(message.createdAt), 'short') }}
-
{{ message.message }}
-
-
-
-
-
diff --git a/frontend/src/components/Contributions/ContributionForm.spec.js b/frontend/src/components/Contributions/ContributionForm.spec.js
index cf47577a3..8f35948f9 100644
--- a/frontend/src/components/Contributions/ContributionForm.spec.js
+++ b/frontend/src/components/Contributions/ContributionForm.spec.js
@@ -329,7 +329,8 @@ describe('ContributionForm', () => {
describe('invalid form data', () => {
beforeEach(async () => {
- await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
+ // skip this precondition as long as the datepicker is disabled in the component
+ // await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
await wrapper.find('#contribution-amount').find('input').setValue('200')
})
diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue
index 3a9010ec2..efe80f494 100644
--- a/frontend/src/components/Contributions/ContributionForm.vue
+++ b/frontend/src/components/Contributions/ContributionForm.vue
@@ -25,6 +25,7 @@
reset-value=""
:label-no-date-selected="$t('contribution.noDateSelected')"
required
+ :disabled="this.form.id !== null"
>
@@ -42,7 +43,6 @@
id="contribution-memo"
v-model="form.memo"
rows="3"
- max-rows="6"
:placeholder="$t('contribution.yourActivity')"
required
>
diff --git a/frontend/src/components/GddSend/TransactionForm.vue b/frontend/src/components/GddSend/TransactionForm.vue
index 62361c4d0..69c6b6b45 100644
--- a/frontend/src/components/GddSend/TransactionForm.vue
+++ b/frontend/src/components/GddSend/TransactionForm.vue
@@ -46,7 +46,7 @@
@@ -81,7 +81,11 @@
v-slot="{ errors, valid }"
>
-
+
{{ $t('GDD') }}
@@ -115,7 +119,7 @@
v-slot="{ errors }"
>
-
+
@@ -237,4 +241,7 @@ span.errors {
#input-3:focus {
font-weight: bold;
}
+.border-radius {
+ border-radius: 10px;
+}
diff --git a/frontend/src/components/Inputs/InputPasswordConfirmation.vue b/frontend/src/components/Inputs/InputPasswordConfirmation.vue
index 3209018c3..56d58d9ad 100644
--- a/frontend/src/components/Inputs/InputPasswordConfirmation.vue
+++ b/frontend/src/components/Inputs/InputPasswordConfirmation.vue
@@ -29,6 +29,7 @@
required: true,
samePassword: value.password,
}"
+ id="repeat-new-password-input-field"
:label="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
:immediate="true"
:name="createId(register ? $t('form.passwordRepeat') : $t('form.password_new_repeat'))"
diff --git a/frontend/src/components/LinkInformations/RedeemLoggedOut.vue b/frontend/src/components/LinkInformations/RedeemLoggedOut.vue
index 982bfdf08..6673b3c5b 100644
--- a/frontend/src/components/LinkInformations/RedeemLoggedOut.vue
+++ b/frontend/src/components/LinkInformations/RedeemLoggedOut.vue
@@ -25,9 +25,11 @@
diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js
index 9846784d5..3156c2861 100644
--- a/frontend/src/graphql/mutations.js
+++ b/frontend/src/graphql/mutations.js
@@ -136,3 +136,27 @@ export const createContributionMessage = gql`
}
}
`
+
+export const login = gql`
+ mutation($email: String!, $password: String!, $publisherId: Int) {
+ login(email: $email, password: $password, publisherId: $publisherId) {
+ email
+ firstName
+ lastName
+ language
+ klickTipp {
+ newsletterState
+ }
+ hasElopage
+ publisherId
+ isAdmin
+ creation
+ }
+ }
+`
+
+export const logout = gql`
+ mutation {
+ logout
+ }
+`
diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js
index 07b016d0a..1c910a23e 100644
--- a/frontend/src/graphql/queries.js
+++ b/frontend/src/graphql/queries.js
@@ -1,23 +1,5 @@
import gql from 'graphql-tag'
-export const login = gql`
- query($email: String!, $password: String!, $publisherId: Int) {
- login(email: $email, password: $password, publisherId: $publisherId) {
- email
- firstName
- lastName
- language
- klickTipp {
- newsletterState
- }
- hasElopage
- publisherId
- isAdmin
- creation
- }
- }
-`
-
export const verifyLogin = gql`
query {
verifyLogin {
@@ -36,12 +18,6 @@ export const verifyLogin = gql`
}
`
-export const logout = gql`
- query {
- logout
- }
-`
-
export const transactionsQuery = gql`
query($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
@@ -61,6 +37,7 @@ export const transactionsQuery = gql`
linkedUser {
firstName
lastName
+ email
}
decay {
decay
@@ -68,9 +45,6 @@ export const transactionsQuery = gql`
end
duration
}
- linkedUser {
- email
- }
transactionLinkId
}
}
diff --git a/frontend/src/layouts/AuthLayout.spec.js b/frontend/src/layouts/AuthLayout.spec.js
index 4a1d7fef0..8d9411a71 100644
--- a/frontend/src/layouts/AuthLayout.spec.js
+++ b/frontend/src/layouts/AuthLayout.spec.js
@@ -19,6 +19,7 @@ describe('AuthLayout', () => {
meta: {
requiresAuth: false,
},
+ params: {},
},
}
diff --git a/frontend/src/layouts/DashboardLayout.spec.js b/frontend/src/layouts/DashboardLayout.spec.js
index 398724201..846974781 100644
--- a/frontend/src/layouts/DashboardLayout.spec.js
+++ b/frontend/src/layouts/DashboardLayout.spec.js
@@ -18,6 +18,7 @@ const apolloMock = jest.fn().mockResolvedValue({
logout: 'success',
},
})
+const apolloQueryMock = jest.fn()
describe('DashboardLayout', () => {
let wrapper
@@ -40,7 +41,8 @@ describe('DashboardLayout', () => {
},
},
$apollo: {
- query: apolloMock,
+ mutate: apolloMock,
+ query: apolloQueryMock,
},
$store: {
state: {
@@ -142,7 +144,7 @@ describe('DashboardLayout', () => {
describe('update transactions', () => {
beforeEach(async () => {
- apolloMock.mockResolvedValue({
+ apolloQueryMock.mockResolvedValue({
data: {
transactionList: {
balance: {
@@ -163,7 +165,7 @@ describe('DashboardLayout', () => {
})
it('calls the API', () => {
- expect(apolloMock).toBeCalledWith(
+ expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
currentPage: 2,
@@ -201,7 +203,7 @@ describe('DashboardLayout', () => {
describe('update transactions returns error', () => {
beforeEach(async () => {
- apolloMock.mockRejectedValue({
+ apolloQueryMock.mockRejectedValue({
message: 'Ouch!',
})
await wrapper
diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue
index 8e778ab01..2a103a574 100755
--- a/frontend/src/layouts/DashboardLayout.vue
+++ b/frontend/src/layouts/DashboardLayout.vue
@@ -41,7 +41,8 @@
import Navbar from '@/components/Menu/Navbar.vue'
import Sidebar from '@/components/Menu/Sidebar.vue'
import SessionLogoutTimeout from '@/components/SessionLogoutTimeout.vue'
-import { logout, transactionsQuery } from '@/graphql/queries'
+import { transactionsQuery } from '@/graphql/queries'
+import { logout } from '@/graphql/mutations'
import ContentFooter from '@/components/ContentFooter.vue'
import { FadeTransition } from 'vue2-transitions'
import CONFIG from '@/config'
@@ -75,8 +76,8 @@ export default {
methods: {
async logout() {
this.$apollo
- .query({
- query: logout,
+ .mutate({
+ mutation: logout,
})
.then(() => {
this.$store.dispatch('logout')
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json
index 1ed8af19f..a9aee274d 100644
--- a/frontend/src/locales/de.json
+++ b/frontend/src/locales/de.json
@@ -24,7 +24,8 @@
"moderator": "Moderator",
"moderators": "Moderatoren",
"myContributions": "Meine Beiträge zum Gemeinwohl",
- "openContributionLinks": "öffentliche Beitrags-Linkliste",
+ "noOpenContributionLinkText": "Zur Zeit gibt es keine automatischen Schöpfungen.",
+ "openContributionLinks": "Öffentliche Beitrags-Linkliste",
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
"other-communities": "Weitere Gemeinschaften",
"submitContribution": "Beitrag einreichen",
@@ -33,7 +34,7 @@
"contribution": {
"activity": "Tätigkeit",
"alert": {
- "answerQuestion": "Bitte beantworte die Nachfrage",
+ "answerQuestion": "Bitte beantworte die Rückfrage!",
"communityNoteList": "Hier findest du alle eingereichten und bestätigten Beiträge von allen Mitgliedern aus dieser Gemeinschaft.",
"confirm": "bestätigt",
"in_progress": "Es gibt eine Rückfrage der Moderatoren.",
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 113fa1cb9..d9f69581d 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -24,7 +24,8 @@
"moderator": "Moderator",
"moderators": "Moderators",
"myContributions": "My contributions to the common good",
- "openContributionLinks": "open Contribution links list",
+ "noOpenContributionLinkText": "Currently there are no automatic creations.",
+ "openContributionLinks": "Open contribution-link list",
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
"other-communities": "Other communities",
"submitContribution": "Submit contribution",
diff --git a/frontend/src/mixins/authLinks.js b/frontend/src/mixins/authLinks.js
new file mode 100644
index 000000000..6dedd8612
--- /dev/null
+++ b/frontend/src/mixins/authLinks.js
@@ -0,0 +1,12 @@
+export const authLinks = {
+ computed: {
+ login() {
+ if (this.$route.params.code) return '/login/' + this.$route.params.code
+ return '/login'
+ },
+ register() {
+ if (this.$route.params.code) return '/register/' + this.$route.params.code
+ return '/register'
+ },
+ },
+}
diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue
index c98bfff2d..786307405 100644
--- a/frontend/src/pages/Community.vue
+++ b/frontend/src/pages/Community.vue
@@ -231,8 +231,6 @@ export default {
this.items = listContributions.contributionList
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
this.tabIndex = 1
- } else {
- this.tabIndex = 0
}
})
.catch((err) => {
diff --git a/frontend/src/pages/InfoStatistic.vue b/frontend/src/pages/InfoStatistic.vue
index 1e09f83ed..de4b9e224 100644
--- a/frontend/src/pages/InfoStatistic.vue
+++ b/frontend/src/pages/InfoStatistic.vue
@@ -14,7 +14,7 @@
{{ $t('community.openContributionLinks') }}
-
+
{{
$t('community.openContributionLinkText', {
name: CONFIG.COMMUNITY_NAME,
@@ -22,6 +22,9 @@
})
}}
+
+ {{ $t('community.noOpenContributionLinkText') }}
+
-
{{ item.name }}
diff --git a/frontend/src/pages/Login.spec.js b/frontend/src/pages/Login.spec.js
index 6359d07c6..90e98cd44 100644
--- a/frontend/src/pages/Login.spec.js
+++ b/frontend/src/pages/Login.spec.js
@@ -5,7 +5,7 @@ import Login from './Login'
const localVue = global.localVue
-const apolloQueryMock = jest.fn()
+const apolloMutateMock = jest.fn()
const mockStoreDispach = jest.fn()
const mockStoreCommit = jest.fn()
const mockRouterPush = jest.fn()
@@ -41,7 +41,7 @@ describe('Login', () => {
params: {},
},
$apollo: {
- query: apolloQueryMock,
+ mutate: apolloMutateMock,
},
}
@@ -113,7 +113,7 @@ describe('Login', () => {
await wrapper.find('input[placeholder="Email"]').setValue('user@example.org')
await wrapper.find('input[placeholder="form.password"]').setValue('1234')
await flushPromises()
- apolloQueryMock.mockResolvedValue({
+ apolloMutateMock.mockResolvedValue({
data: {
login: 'token',
},
@@ -123,7 +123,7 @@ describe('Login', () => {
})
it('calls the API with the given data', () => {
- expect(apolloQueryMock).toBeCalledWith(
+ expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
variables: {
email: 'user@example.org',
@@ -175,7 +175,7 @@ describe('Login', () => {
describe('login fails', () => {
const createError = async (errorMessage) => {
- apolloQueryMock.mockRejectedValue({
+ apolloMutateMock.mockRejectedValue({
message: errorMessage,
})
wrapper = Wrapper()
diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue
index 6a3db4e39..0b602f74b 100755
--- a/frontend/src/pages/Login.vue
+++ b/frontend/src/pages/Login.vue
@@ -43,7 +43,7 @@
import InputPassword from '@/components/Inputs/InputPassword'
import InputEmail from '@/components/Inputs/InputEmail'
import Message from '@/components/Message/Message'
-import { login } from '@/graphql/queries'
+import { login } from '@/graphql/mutations'
export default {
name: 'Login',
@@ -71,14 +71,13 @@ export default {
container: this.$refs.submitButton,
})
this.$apollo
- .query({
- query: login,
+ .mutate({
+ mutation: login,
variables: {
email: this.form.email,
password: this.form.password,
publisherId: this.$store.state.publisherId,
},
- fetchPolicy: 'network-only',
})
.then(async (result) => {
const {
diff --git a/frontend/src/pages/TransactionLink.spec.js b/frontend/src/pages/TransactionLink.spec.js
index d5a75aa4a..adbb25226 100644
--- a/frontend/src/pages/TransactionLink.spec.js
+++ b/frontend/src/pages/TransactionLink.spec.js
@@ -43,6 +43,7 @@ const mocks = {
$store: {
state: {
token: null,
+ tokenTime: null,
email: 'bibi@bloxberg.de',
},
},
@@ -68,7 +69,7 @@ describe('TransactionLink', () => {
}
describe('mount', () => {
- beforeEach(() => {
+ beforeAll(() => {
jest.clearAllMocks()
wrapper = Wrapper()
})
@@ -214,112 +215,159 @@ describe('TransactionLink', () => {
})
})
- describe('token in store and own link', () => {
- beforeEach(() => {
+ describe('token in store', () => {
+ beforeAll(() => {
mocks.$store.state.token = 'token'
- apolloQueryMock.mockResolvedValue({
- data: {
- queryTransactionLink: {
- __typename: 'TransactionLink',
- id: 92,
- amount: '22',
- memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
- createdAt: '2022-03-17T16:10:28.000Z',
- validUntil: transactionLinkValidExpireDate(),
- redeemedAt: null,
- deletedAt: null,
- user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
- },
- },
- })
- wrapper = Wrapper()
})
- it('has a RedeemSelfCreator component', () => {
- expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true)
- })
-
- it('has a no redeem text', () => {
- expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).text()).toContain(
- 'gdd_per_link.no-redeem',
- )
- })
-
- it.skip('has a link to transaction page', () => {
- expect(wrapper.find('a[target="/transactions"]').exists()).toBe(true)
- })
- })
-
- describe('valid link', () => {
- beforeEach(() => {
- mocks.$store.state.token = 'token'
- apolloQueryMock.mockResolvedValue({
- data: {
- queryTransactionLink: {
- __typename: 'TransactionLink',
- id: 92,
- amount: '22',
- memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
- createdAt: '2022-03-17T16:10:28.000Z',
- validUntil: transactionLinkValidExpireDate(),
- redeemedAt: null,
- deletedAt: null,
- user: { firstName: 'Peter', publisherId: 0, email: 'peter@listig.de' },
- },
- },
- })
- wrapper = Wrapper()
- })
-
- it('has a RedeemValid component', () => {
- expect(wrapper.findComponent({ name: 'RedeemValid' }).exists()).toBe(true)
- })
-
- it('has a button with redeem text', () => {
- expect(wrapper.findComponent({ name: 'RedeemValid' }).find('button').text()).toBe(
- 'gdd_per_link.redeem',
- )
- })
-
- describe('redeem link with success', () => {
- beforeEach(async () => {
- apolloMutateMock.mockResolvedValue()
- await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
+ describe('sufficient token time in store', () => {
+ beforeAll(() => {
+ mocks.$store.state.tokenTime = Math.floor(Date.now() / 1000) + 20
})
- it('calls the API', () => {
- expect(apolloMutateMock).toBeCalledWith(
- expect.objectContaining({
- mutation: redeemTransactionLink,
- variables: {
- code: 'some-code',
+ describe('own link', () => {
+ beforeAll(() => {
+ apolloQueryMock.mockResolvedValue({
+ data: {
+ queryTransactionLink: {
+ __typename: 'TransactionLink',
+ id: 92,
+ amount: '22',
+ memo:
+ 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
+ createdAt: '2022-03-17T16:10:28.000Z',
+ validUntil: transactionLinkValidExpireDate(),
+ redeemedAt: null,
+ deletedAt: null,
+ user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
+ },
},
- }),
- )
+ })
+ wrapper = Wrapper()
+ })
+
+ it('has a RedeemSelfCreator component', () => {
+ expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true)
+ })
+
+ it('has a no redeem text', () => {
+ expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).text()).toContain(
+ 'gdd_per_link.no-redeem',
+ )
+ })
+
+ it.skip('has a link to transaction page', () => {
+ expect(wrapper.find('a[target="/transactions"]').exists()).toBe(true)
+ })
})
- it('toasts a success message', () => {
- expect(mocks.$t).toBeCalledWith('gdd_per_link.redeem')
- expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ')
- })
+ describe('valid link', () => {
+ beforeAll(() => {
+ apolloQueryMock.mockResolvedValue({
+ data: {
+ queryTransactionLink: {
+ __typename: 'TransactionLink',
+ id: 92,
+ amount: '22',
+ memo:
+ 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
+ createdAt: '2022-03-17T16:10:28.000Z',
+ validUntil: transactionLinkValidExpireDate(),
+ redeemedAt: null,
+ deletedAt: null,
+ user: { firstName: 'Peter', publisherId: 0, email: 'peter@listig.de' },
+ },
+ },
+ })
+ wrapper = Wrapper()
+ })
- it('pushes the route to overview', () => {
- expect(routerPushMock).toBeCalledWith('/overview')
+ it('has a RedeemValid component', () => {
+ expect(wrapper.findComponent({ name: 'RedeemValid' }).exists()).toBe(true)
+ })
+
+ it('has a button with redeem text', () => {
+ expect(wrapper.findComponent({ name: 'RedeemValid' }).find('button').text()).toBe(
+ 'gdd_per_link.redeem',
+ )
+ })
+
+ describe('redeem link with success', () => {
+ beforeAll(async () => {
+ apolloMutateMock.mockResolvedValue()
+ await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
+ })
+
+ it('calls the API', () => {
+ expect(apolloMutateMock).toBeCalledWith(
+ expect.objectContaining({
+ mutation: redeemTransactionLink,
+ variables: {
+ code: 'some-code',
+ },
+ }),
+ )
+ })
+
+ it('toasts a success message', () => {
+ expect(mocks.$t).toBeCalledWith('gdd_per_link.redeem')
+ expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ')
+ })
+
+ it('pushes the route to overview', () => {
+ expect(routerPushMock).toBeCalledWith('/overview')
+ })
+ })
+
+ describe('redeem link with error', () => {
+ beforeAll(async () => {
+ apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' })
+ await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
+ })
+
+ it('toasts an error message', () => {
+ expect(toastErrorSpy).toBeCalledWith('Oh Noo!')
+ })
+
+ it('pushes the route to overview', () => {
+ expect(routerPushMock).toBeCalledWith('/overview')
+ })
+ })
})
})
- describe('redeem link with error', () => {
- beforeEach(async () => {
- apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' })
- await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
+ describe('no sufficient token time in store', () => {
+ beforeAll(() => {
+ mocks.$store.state.tokenTime = 1665125185
+ apolloQueryMock.mockResolvedValue({
+ data: {
+ queryTransactionLink: {
+ __typename: 'TransactionLink',
+ id: 92,
+ amount: '22',
+ memo:
+ 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
+ createdAt: '2022-03-17T16:10:28.000Z',
+ validUntil: transactionLinkValidExpireDate(),
+ redeemedAt: null,
+ deletedAt: null,
+ user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
+ },
+ },
+ })
+ wrapper = Wrapper()
})
- it('toasts an error message', () => {
- expect(toastErrorSpy).toBeCalledWith('Oh Noo!')
+ it('has a RedeemLoggedOut component', () => {
+ expect(wrapper.findComponent({ name: 'RedeemLoggedOut' }).exists()).toBe(true)
})
- it('pushes the route to overview', () => {
- expect(routerPushMock).toBeCalledWith('/overview')
+ it('has a link to register with code', () => {
+ expect(wrapper.find('a[href="/register/some-code"]').exists()).toBe(true)
+ })
+
+ it('has a link to login with code', () => {
+ expect(wrapper.find('a[href="/login/some-code"]').exists()).toBe(true)
})
})
})
diff --git a/frontend/src/pages/TransactionLink.vue b/frontend/src/pages/TransactionLink.vue
index bd1909d7d..c3875d20e 100644
--- a/frontend/src/pages/TransactionLink.vue
+++ b/frontend/src/pages/TransactionLink.vue
@@ -103,6 +103,12 @@ export default {
isContributionLink() {
return this.$route.params.code.search(/^CL-/) === 0
},
+ tokenExpiresInSeconds() {
+ const remainingSecs = Math.floor(
+ (new Date(this.$store.state.tokenTime * 1000).getTime() - new Date().getTime()) / 1000,
+ )
+ return remainingSecs <= 0 ? 0 : remainingSecs
+ },
itemType() {
// link is deleted: at, from
if (this.linkData.deletedAt) {
@@ -130,7 +136,9 @@ export default {
return `TEXT`
}
- if (this.$store.state.token) {
+ if (this.$store.state.token && this.$store.state.tokenTime) {
+ if (this.tokenExpiresInSeconds < 5) return `LOGGED_OUT`
+
// logged in, nicht berechtigt einzulösen, eigener link
if (this.linkData.user && this.$store.state.email === this.linkData.user.email) {
return `SELF_CREATOR`
diff --git a/package.json b/package.json
index d0b45a8c7..f306a1e06 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gradido",
- "version": "1.12.1",
+ "version": "1.13.1",
"description": "Gradido",
"main": "index.js",
"repository": "git@github.com:gradido/gradido.git",