feat(webapp): group invite after login (#8518)

This commit is contained in:
Moriz Wahl 2025-05-09 21:19:07 +02:00 committed by GitHub
parent 9300fbd5fc
commit da8af6ff42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 169 additions and 12 deletions

View File

@ -30,7 +30,7 @@
<template v-if="registrationType !== 'no-public-registration'" #footer>
<ds-space margin-bottom="xxx-small" margin-top="small" centered>
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
<nuxt-link :to="loginLink">{{ $t('site.back-to-login') }}</nuxt-link>
</ds-space>
</template>
</component-slider>
@ -163,6 +163,10 @@ export default {
}
return {
loginLink: {
name: 'login',
query: this.$route.query,
},
links,
metadata,
sliderData: {

View File

@ -1,14 +1,20 @@
import Vuex from 'vuex'
import { mount } from '@vue/test-utils'
import login from './login.vue'
import LoginForm from '~/components/LoginForm/LoginForm.vue'
const localVue = global.localVue
const stubs = {
'client-only': true,
'nuxt-link': true,
'router-link': true,
}
const routerPushMock = jest.fn()
const routerReplaceMock = jest.fn()
const i18nSetMock = jest.fn()
describe('Login.vue', () => {
let store
let mocks
@ -22,6 +28,14 @@ describe('Login.vue', () => {
$t: jest.fn(),
$i18n: {
locale: () => 'en',
set: i18nSetMock,
},
$route: {
query: {},
},
$router: {
replace: routerReplaceMock,
push: routerPushMock,
},
}
asyncData = false
@ -73,5 +87,51 @@ describe('Login.vue', () => {
wrapper = await Wrapper()
expect(redirect).toHaveBeenCalledWith('/')
})
describe('handle succcess', () => {
beforeEach(async () => {
asyncData = true
tosVersion = '0.0.4'
})
describe('with route query to invite code', () => {
beforeEach(async () => {
mocks.$route.query = {
inviteCode: 'ABCDEF',
}
wrapper = await Wrapper()
wrapper.findComponent(LoginForm).vm.$emit('success')
})
it('calls i18n.set', () => {
expect(i18nSetMock).toBeCalledWith('en')
})
it('call router push to registration page', () => {
expect(routerPushMock).toBeCalledWith({
name: 'registration',
query: {
inviteCode: 'ABCDEF',
},
})
})
})
describe('without route query to invite code', () => {
beforeEach(async () => {
mocks.$route.query = {}
wrapper = await Wrapper()
wrapper.findComponent(LoginForm).vm.$emit('success')
})
it('calls i18n.set', () => {
expect(i18nSetMock).toBeCalledWith('en')
})
it('call router push to registration page', () => {
expect(routerReplaceMock).toBeCalledWith('/')
})
})
})
})
})

View File

@ -32,7 +32,14 @@ export default {
this.$i18n.set(this.user.locale || 'en')
try {
await this.$router.replace(this.$route.query.path || '/')
if (this.$route.query.inviteCode) {
this.$router.push({
name: 'registration',
query: this.$route.query,
})
} else {
await this.$router.replace(this.$route.query.path || '/')
}
} catch (err) {
// throw new Error(`Problem handling something: ${err}.`);
// TODO this is causing trouble - most likely due to double redirect on terms&conditions

View File

@ -20,7 +20,7 @@ const app = {
apolloProvider: {
defaultClient: {
query: queryMock,
mutation: mutationMock,
mutate: mutationMock,
},
},
}
@ -387,8 +387,7 @@ describe('Registration', () => {
})
})
// no idea why this is not working
describe.skip('route contains group invite code to public group', () => {
describe('route contains group invite code to public group', () => {
beforeEach(async () => {
jest.clearAllMocks()
queryMock.mockResolvedValue({
@ -433,6 +432,96 @@ describe('Registration', () => {
})
})
})
describe('route contains group invite code to closed group', () => {
beforeEach(async () => {
jest.clearAllMocks()
queryMock.mockResolvedValue({
data: {
validateInviteCode: {
invitedTo: {
id: 'closed-group',
slug: 'closed-group',
groupType: 'closed',
},
},
},
})
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 index', () => {
expect(redirect).toHaveBeenCalledWith('/')
})
it('redeems the code', () => {
expect(mutationMock).toBeCalledWith({
mutation: redeemInviteCodeMutation,
variables: {
code: 'ABCDEF',
},
})
})
})
describe('route contains group invite code to public group, but redeem throws', () => {
beforeEach(async () => {
jest.clearAllMocks()
queryMock.mockResolvedValue({
data: {
validateInviteCode: {
invitedTo: {
id: 'public-group',
slug: 'public-group',
groupType: 'public',
},
},
},
})
mutationMock.mockRejectedValue({
error: 'Aua!',
})
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('redeems the code', () => {
expect(mutationMock).toBeCalledWith({
mutation: redeemInviteCodeMutation,
variables: {
code: 'ABCDEF',
},
})
})
})
})
// copied from webapp/components/Registration/Signup.spec.js as testing template

View File

@ -36,7 +36,7 @@ export default {
}
},
async asyncData({ store, route, app, redirect }) {
// http://localhost:3000/registration?method=invite-code&inviteCode=T9TWMJ
// http://localhost:3000/registration?method=invite-code&inviteCode=PEY8FN
if (store.getters['auth/isLoggedIn']) {
const {
query: { inviteCode: code },
@ -62,18 +62,15 @@ export default {
})
if (mutationResult.data.redeemInviteCode && group.groupType === 'public') {
redirect(`/groups/${group.id}/${group.slug}`)
} else {
redirect('/')
return
}
} else {
redirect('/')
}
} catch (_err) {
redirect('/')
return
}
} else {
redirect('/')
}
redirect('/')
}
},
computed: {