feat(webapp): redirect on registration for invite links (#8517)

* feat(webapp): redirect on registration for invite links
This commit is contained in:
Moriz Wahl 2025-05-09 19:57:27 +02:00 committed by GitHub
parent ff366a4075
commit 9300fbd5fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 163 additions and 2 deletions

View File

@ -0,0 +1,19 @@
import gql from 'graphql-tag'
export const validateInviteCodeQuery = gql`
query ($code: String!) {
validateInviteCode(code: $code) {
invitedTo {
id
slug
groupType
}
}
}
`
export const redeemInviteCodeMutation = gql`
mutation ($code: String!) {
redeemInviteCode(code: $code)
}
`

View File

@ -2,6 +2,7 @@ import Vuex from 'vuex'
import { mount } from '@vue/test-utils'
import Registration from './registration.vue'
import Vue from 'vue'
import { validateInviteCodeQuery, redeemInviteCodeMutation } from '~/graphql/inviteCodes'
const localVue = global.localVue
@ -12,6 +13,18 @@ const stubs = {
'infinite-loading': true,
}
const queryMock = jest.fn()
const mutationMock = jest.fn()
const app = {
apolloProvider: {
defaultClient: {
query: queryMock,
mutation: mutationMock,
},
},
}
describe('Registration', () => {
let wrapper
let Wrapper
@ -20,6 +33,7 @@ describe('Registration', () => {
let store
let redirect
let isLoggedIn
let route
beforeEach(() => {
mocks = {
@ -39,6 +53,9 @@ describe('Registration', () => {
asyncData = false
isLoggedIn = false
redirect = jest.fn()
route = {
query: {},
}
})
describe('mount', () => {
@ -65,6 +82,8 @@ describe('Registration', () => {
const aData = await Registration.asyncData({
store,
redirect,
route,
app,
})
Registration.data = function () {
return { ...data, ...aData }
@ -330,6 +349,92 @@ describe('Registration', () => {
expect(redirect).toHaveBeenCalledWith('/')
})
describe('already logged in', () => {
beforeEach(async () => {
asyncData = true
isLoggedIn = true
})
describe('route contains personal invite code', () => {
beforeEach(async () => {
jest.clearAllMocks()
queryMock.mockResolvedValue({
data: {
validateInviteCode: {
invitedTo: null,
},
},
})
route.query.inviteCode = 'ABCDEF'
wrapper = await Wrapper()
})
it('calls validate invite code', () => {
expect(queryMock).toHaveBeenCalledWith({
query: validateInviteCodeQuery,
variables: {
code: 'ABCDEF',
},
})
})
it('redirects to index', () => {
expect(redirect).toHaveBeenCalledWith('/')
})
it('does not redeem the link', () => {
expect(mutationMock).not.toBeCalled()
})
})
// no idea why this is not working
describe.skip('route contains group invite code to public group', () => {
beforeEach(async () => {
jest.clearAllMocks()
queryMock.mockResolvedValue({
data: {
validateInviteCode: {
invitedTo: {
id: 'public-group',
slug: 'public-group',
groupType: 'public',
},
},
},
})
mutationMock.mockResolvedValue({
data: {
redeemInviteCode: true,
},
})
route.query.inviteCode = 'ABCDEF'
wrapper = await Wrapper()
})
it('calls validate invite code', () => {
expect(queryMock).toHaveBeenCalledWith({
query: validateInviteCodeQuery,
variables: {
code: 'ABCDEF',
},
})
})
it('redirects to group', () => {
expect(redirect).toHaveBeenCalledWith('/groups/public-group/public-group')
})
it('redeems the code', () => {
expect(mutationMock).toBeCalledWith({
mutation: redeemInviteCodeMutation,
variables: {
code: 'ABCDEF',
},
})
})
})
})
// copied from webapp/components/Registration/Signup.spec.js as testing template
// describe('with invitation code', () => {
// let action

View File

@ -11,6 +11,7 @@
<script>
import registrationConstants from '~/constants/registrationBranded.js'
import RegistrationSlider from '~/components/Registration/RegistrationSlider'
import { validateInviteCodeQuery, redeemInviteCodeMutation } from '~/graphql/inviteCodes'
export default {
layout: registrationConstants.LAYOUT,
@ -34,9 +35,45 @@ export default {
inviteRegistration: this.$env.INVITE_REGISTRATION === true, // for 'false' in .env INVITE_REGISTRATION is of type undefined and not(!) boolean false, because of internal handling
}
},
asyncData({ store, redirect }) {
async asyncData({ store, route, app, redirect }) {
// http://localhost:3000/registration?method=invite-code&inviteCode=T9TWMJ
if (store.getters['auth/isLoggedIn']) {
redirect('/')
const {
query: { inviteCode: code },
} = route
if (code) {
const {
apolloProvider: { defaultClient: client },
} = app
try {
const result = await client.query({
query: validateInviteCodeQuery,
variables: { code },
})
const {
data: {
validateInviteCode: { invitedTo: group },
},
} = result
if (group) {
const mutationResult = await client.mutate({
mutation: redeemInviteCodeMutation,
variables: { code },
})
if (mutationResult.data.redeemInviteCode && group.groupType === 'public') {
redirect(`/groups/${group.id}/${group.slug}`)
} else {
redirect('/')
}
} else {
redirect('/')
}
} catch (_err) {
redirect('/')
}
} else {
redirect('/')
}
}
},
computed: {