Merge branch '531-Newsletter-subscribe-status-change-in-user-profile' into apollo-clicktipp-connector

This commit is contained in:
elweyn 2021-09-16 12:18:06 +02:00
commit 123f45c853
14 changed files with 279 additions and 41 deletions

View File

@ -113,12 +113,6 @@ describe('SideBar', () => {
expect(wrapper.emitted('logout')).toEqual([[]])
})
})
describe('language-switch', () => {
it('has a language-switch button', () => {
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
})
})
})
})
})

View File

@ -64,23 +64,18 @@
</a>
</li>
</ul>
<div class="mt-5 ml-4">
<language-switch />
</div>
</div>
</div>
</nav>
</template>
<script>
import NavbarToggleButton from '@/components/NavbarToggleButton'
import LanguageSwitch from '@/components/LanguageSwitch.vue'
import VueQrcode from 'vue-qrcode'
export default {
name: 'sidebar',
components: {
NavbarToggleButton,
LanguageSwitch,
VueQrcode,
},
props: {

View File

@ -0,0 +1,13 @@
import gql from 'graphql-tag'
export const subscribeNewsletter = gql`
mutation($email: String!, $language: String!) {
subscribeNewsletter(email: $email, language: $language)
}
`
export const unsubscribeNewsletter = gql`
mutation($email: String!, $language: String!) {
unsubscribeNewsletter(email: $email, language: $language)
}
`

View File

@ -41,7 +41,13 @@
"fromCommunity":"Aus der Gemeinschaft",
"toCommunity":"An die Gemeinschaft",
"noDecay": "Keine Vergänglichkeit"
},
},
"setting": {
"newsletter": "Newsletter",
"changeNewsletter": "Newsletter Status ändern",
"newsletterTrue": "Du bist im Newslettersystem eingetraten.",
"newsletterFalse": "Du bist aus Newslettersystem ausgetragen."
},
"form": {
"cancel": "Abbrechen",
"reset": "Zurücksetzen",

View File

@ -42,6 +42,12 @@
"toCommunity":"To the community",
"noDecay": "No Decay"
},
"setting": {
"newsletter": "Newsletter",
"changeNewsletter": "Newsletter status change",
"newsletterTrue": "You are subscribed to newsletter system.",
"newsletterFalse": "You are unsubscribed from newsletter system."
},
"form": {
"cancel":"Cancel",
"reset": "Reset",

View File

@ -26,6 +26,9 @@ export const mutations = {
token: (state, token) => {
state.token = token
},
newsletterState: (state, newsletterState) => {
state.newsletterState = newsletterState
},
}
export const actions = {
@ -36,6 +39,7 @@ export const actions = {
commit('firstName', data.firstName)
commit('lastName', data.lastName)
commit('description', data.description)
commit('newsletterState', data.klickTipp.newsletterState)
},
logout: ({ commit, state }) => {
commit('token', null)
@ -44,6 +48,7 @@ export const actions = {
commit('firstName', '')
commit('lastName', '')
commit('description', '')
commit('newsletterState', null)
localStorage.clear()
},
}
@ -62,6 +67,7 @@ export const store = new Vuex.Store({
username: '',
description: '',
token: null,
newsletterState: null,
},
getters: {},
// Syncronous mutation of the state

View File

@ -1,6 +1,15 @@
import { mutations, actions } from './store'
const { language, email, token, username, firstName, lastName, description } = mutations
const {
language,
email,
token,
username,
firstName,
lastName,
description,
newsletterState,
} = mutations
const { login, logout } = actions
describe('Vuex store', () => {
@ -60,6 +69,14 @@ describe('Vuex store', () => {
expect(state.description).toEqual('Nickelbrille')
})
})
describe('newsletterState', () => {
it('sets the state of newsletterState', () => {
const state = { newsletterState: null }
newsletterState(state, true)
expect(state.newsletterState).toEqual(true)
})
})
})
describe('actions', () => {
@ -73,11 +90,14 @@ describe('Vuex store', () => {
firstName: 'Peter',
lastName: 'Lustig',
description: 'Nickelbrille',
klickTipp: {
newsletterState: true,
},
}
it('calls seven commits', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenCalledTimes(6)
expect(commit).toHaveBeenCalledTimes(7)
})
it('commits email', () => {
@ -109,6 +129,11 @@ describe('Vuex store', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille')
})
it('commits newsletterState', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', true)
})
})
describe('logout', () => {
@ -117,7 +142,7 @@ describe('Vuex store', () => {
it('calls six commits', () => {
logout({ commit, state })
expect(commit).toHaveBeenCalledTimes(6)
expect(commit).toHaveBeenCalledTimes(7)
})
it('commits token', () => {
@ -150,6 +175,11 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(6, 'description', '')
})
it('commits newsletterState', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', null)
})
// how to get this working?
it.skip('calls localStorage.clear()', () => {
const clearStorageMock = jest.fn()

View File

@ -117,10 +117,6 @@ describe('DashboardLayoutGdd', () => {
)
})
it('has a locale switch', () => {
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
})
it('has a logout button', () => {
expect(wrapper.findAll('ul').at(3).text()).toBe('logout')
})

View File

@ -1,7 +1,11 @@
<template>
<b-card class="bg-transparent">
<div class="w-100 text-center">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
<vue-qrcode
v-if="$store.state.email"
:value="$store.state.email"
type="image/png"
></vue-qrcode>
</div>
<div class="card-profile-stats d-flex justify-content-center mt-md-5">

View File

@ -12,38 +12,42 @@
</b-row>
</div>
<b-container>
<div>
<b-form @keyup.prevent="loadSubmitButton">
<b-row class="mb-3">
<b-col class="col-12 col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
<small>{{ $t('form.firstname') }}</small>
<b-col class="col-12">
<small>
<b>{{ $t('form.firstname') }}</b>
</small>
</b-col>
<b-col v-if="showUserData" class="h2 col-sm-10 col-md-9">
<b-col v-if="showUserData" class="col-12">
{{ form.firstName }}
</b-col>
<b-col v-else class="col-md-9 col-sm-10">
<b-col v-else class="col-12">
<b-input type="text" v-model="form.firstName"></b-input>
</b-col>
</b-row>
<b-row class="mb-3">
<b-col class="col-12 col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
<small>{{ $t('form.lastname') }}</small>
<b-col class="col-12">
<small>
<b>{{ $t('form.lastname') }}</b>
</small>
</b-col>
<b-col v-if="showUserData" class="h2 col-sm-10 col-md-9">
<b-col v-if="showUserData" class="col-12">
{{ form.lastName }}
</b-col>
<b-col v-else class="col-md-9 col-sm-10">
<b-col v-else class="col-12">
<b-input type="text" v-model="form.lastName"></b-input>
</b-col>
</b-row>
<b-row class="mb-3" v-show="false">
<b-col class="col-12 col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
<b-col class="col-12">
<small>{{ $t('form.description') }}</small>
</b-col>
<b-col v-if="showUserData" class="col-sm-10 col-md-9">
<b-col v-if="showUserData" class="col-12">
{{ form.description }}
</b-col>
<b-col v-else class="col-sm-10 col-md-9">
<b-col v-else class="col-12">
<b-textarea rows="3" max-rows="6" v-model="form.description"></b-textarea>
</b-col>
</b-row>
@ -64,7 +68,7 @@
</b-col>
</b-row>
</b-form>
</b-container>
</div>
</b-card>
</template>
<script>

View File

@ -18,10 +18,12 @@
<div v-if="showLanguage">
<b-row class="mb-3">
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
<small>{{ $t('language') }}</small>
<b-col class="col-12">
<small>
<b>{{ $t('language') }}</b>
</small>
</b-col>
<b-col class="h2 col-md-9 col-sm-10">{{ $store.state.language }}</b-col>
<b-col class="col-12">{{ $store.state.language }}</b-col>
</b-row>
</div>
@ -29,18 +31,26 @@
<div>
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
<b-row class="mb-2">
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
<small>{{ $t('language') }}</small>
<b-col class="col-12">
<small>
<b>{{ $t('language') }}</b>
</small>
</b-col>
<b-col class="col-md-9 col-sm-10">
<b-col class="col-12">
<language-switch-select @update-language="updateLanguage" :language="language" />
</b-col>
</b-row>
<b-row class="text-right">
<b-col>
<div class="text-right">
<b-button type="submit" variant="primary" class="mt-4">
<div class="text-right" ref="submitButton">
<b-button
:variant="loading ? 'default' : 'success'"
@click="onSubmit"
type="submit"
class="mt-4"
:disabled="loading"
>
{{ $t('form.save') }}
</b-button>
</div>
@ -62,15 +72,22 @@ export default {
return {
showLanguage: true,
language: '',
loading: true,
}
},
methods: {
updateLanguage(e) {
this.language = e
if (this.language !== this.$store.state.language) {
this.loading = false
} else {
this.loading = true
}
},
cancelEdit() {
this.showLanguage = true
},
async onSubmit() {
this.$apollo
.query({

View File

@ -0,0 +1,99 @@
import { mount } from '@vue/test-utils'
import UserCardNewsletter from './UserCard_Newsletter'
import { unsubscribeNewsletter } from '../../../graphql/mutations'
const localVue = global.localVue
const mockAPIcall = jest.fn()
const toastErrorMock = jest.fn()
const toastSuccessMock = jest.fn()
const storeCommitMock = jest.fn()
const newsletterStateMock = jest.fn().mockReturnValue(true)
describe('UserCard_Newsletter', () => {
let wrapper
const mocks = {
$t: jest.fn((t) => t),
$store: {
state: {
language: 'de',
email: 'peter@lustig.de',
newsletterState: newsletterStateMock,
},
commit: storeCommitMock,
},
$toasted: {
success: toastSuccessMock,
error: toastErrorMock,
},
$apollo: {
mutate: mockAPIcall,
},
}
const Wrapper = () => {
return mount(UserCardNewsletter, { localVue, mocks })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the component', () => {
expect(wrapper.find('div#formusernewsletter').exists()).toBeTruthy()
})
it('has an edit BFormCheckbox switch', () => {
expect(wrapper.find('.Test-BFormCheckbox').exists()).toBeTruthy()
})
describe('unsubscribe with sucess', () => {
beforeEach(() => {
mockAPIcall.mockResolvedValue({
data: {
unsubscribeNewsletter: true,
},
})
wrapper.find('input').trigger('change')
})
it('calls the unsubscribe mutation', () => {
expect(mockAPIcall).toBeCalledWith({
mutation: unsubscribeNewsletter,
variables: {
email: 'peter@lustig.de',
language: 'de',
},
})
})
it('updates the store', () => {
expect(storeCommitMock).toBeCalledWith('newsletterState', false)
})
it('toasts a success message', () => {
expect(toastSuccessMock).toBeCalledWith('setting.newsletterFalse')
})
})
describe('unsubscribe with server error', () => {
beforeEach(() => {
mockAPIcall.mockRejectedValue({
message: 'Ouch',
})
wrapper.find('input').trigger('change')
})
it('resets the newsletterState', () => {
expect(wrapper.vm.newsletterState).toBeTruthy()
})
it('toasts an error message', () => {
expect(toastErrorMock).toBeCalledWith('Ouch')
})
})
})
})

View File

@ -0,0 +1,64 @@
<template>
<b-card
id="formusernewsletter"
class="bg-transparent"
style="background-color: #ebebeba3 !important"
>
<div>
<b-row class="mb-3">
<b-col class="mb-2 col-12">
<small>
<b>{{ $t('setting.newsletter') }}</b>
</small>
</b-col>
<b-col class="col-12">
<b-form-checkbox
class="Test-BFormCheckbox"
v-model="newsletterState"
name="check-button"
switch
@change="onSubmit"
>
{{ newsletterState ? $t('setting.newsletterTrue') : $t('setting.newsletterFalse') }}
</b-form-checkbox>
</b-col>
</b-row>
</div>
</b-card>
</template>
<script>
import { subscribeNewsletter, unsubscribeNewsletter } from '../../../graphql/mutations'
export default {
name: 'FormUserNewsletter',
data() {
return {
newsletterState: this.$store.state.newsletterState,
}
},
methods: {
async onSubmit() {
this.$apollo
.mutate({
mutation: this.newsletterState ? subscribeNewsletter : unsubscribeNewsletter,
variables: {
email: this.$store.state.email,
language: this.$store.state.language,
},
})
.then(() => {
this.$store.commit('newsletterState', this.newsletterState)
this.$toasted.success(
this.newsletterState
? this.$t('setting.newsletterTrue')
: this.$t('setting.newsletterFalse'),
)
})
.catch((error) => {
this.newsletterState = this.$store.state.newsletterState
this.$toasted.error(error.message)
})
},
},
}
</script>

View File

@ -6,6 +6,8 @@
<form-user-passwort />
<hr />
<form-user-language />
<hr />
<form-user-newsletter />
</div>
</template>
<script>
@ -13,6 +15,7 @@ import UserCard from './UserProfile/UserCard.vue'
import FormUserData from './UserProfile/UserCard_FormUserData.vue'
import FormUserPasswort from './UserProfile/UserCard_FormUserPasswort.vue'
import FormUserLanguage from './UserProfile/UserCard_Language.vue'
import FormUserNewsletter from './UserProfile/UserCard_Newsletter.vue'
export default {
components: {
@ -20,6 +23,7 @@ export default {
FormUserData,
FormUserPasswort,
FormUserLanguage,
FormUserNewsletter,
},
props: {
balance: { type: Number, default: 0 },