mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #2254 from gradido/2069-verify-token-before-redeeming-a-link
fix: 🐛 Verify Token Before Redeeming A Link
This commit is contained in:
commit
1b4c013077
@ -43,6 +43,7 @@ const mocks = {
|
|||||||
$store: {
|
$store: {
|
||||||
state: {
|
state: {
|
||||||
token: null,
|
token: null,
|
||||||
|
tokenTime: null,
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -68,7 +69,7 @@ describe('TransactionLink', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
beforeEach(() => {
|
beforeAll(() => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
@ -214,112 +215,159 @@ describe('TransactionLink', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('token in store and own link', () => {
|
describe('token in store', () => {
|
||||||
beforeEach(() => {
|
beforeAll(() => {
|
||||||
mocks.$store.state.token = 'token'
|
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', () => {
|
describe('sufficient token time in store', () => {
|
||||||
expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true)
|
beforeAll(() => {
|
||||||
})
|
mocks.$store.state.tokenTime = Math.floor(Date.now() / 1000) + 20
|
||||||
|
|
||||||
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')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the API', () => {
|
describe('own link', () => {
|
||||||
expect(apolloMutateMock).toBeCalledWith(
|
beforeAll(() => {
|
||||||
expect.objectContaining({
|
apolloQueryMock.mockResolvedValue({
|
||||||
mutation: redeemTransactionLink,
|
data: {
|
||||||
variables: {
|
queryTransactionLink: {
|
||||||
code: 'some-code',
|
__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', () => {
|
describe('valid link', () => {
|
||||||
expect(mocks.$t).toBeCalledWith('gdd_per_link.redeem')
|
beforeAll(() => {
|
||||||
expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ')
|
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', () => {
|
it('has a RedeemValid component', () => {
|
||||||
expect(routerPushMock).toBeCalledWith('/overview')
|
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', () => {
|
describe('no sufficient token time in store', () => {
|
||||||
beforeEach(async () => {
|
beforeAll(() => {
|
||||||
apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' })
|
mocks.$store.state.tokenTime = 1665125185
|
||||||
await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
|
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', () => {
|
it('has a RedeemLoggedOut component', () => {
|
||||||
expect(toastErrorSpy).toBeCalledWith('Oh Noo!')
|
expect(wrapper.findComponent({ name: 'RedeemLoggedOut' }).exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('pushes the route to overview', () => {
|
it('has a link to register with code', () => {
|
||||||
expect(routerPushMock).toBeCalledWith('/overview')
|
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -103,6 +103,12 @@ export default {
|
|||||||
isContributionLink() {
|
isContributionLink() {
|
||||||
return this.$route.params.code.search(/^CL-/) === 0
|
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() {
|
itemType() {
|
||||||
// link is deleted: at, from
|
// link is deleted: at, from
|
||||||
if (this.linkData.deletedAt) {
|
if (this.linkData.deletedAt) {
|
||||||
@ -130,7 +136,9 @@ export default {
|
|||||||
return `TEXT`
|
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
|
// logged in, nicht berechtigt einzulösen, eigener link
|
||||||
if (this.linkData.user && this.$store.state.email === this.linkData.user.email) {
|
if (this.linkData.user && this.$store.state.email === this.linkData.user.email) {
|
||||||
return `SELF_CREATOR`
|
return `SELF_CREATOR`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user