From f39b2e2d39efd89a6c48c926ac7307762e85761b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 14 Jun 2021 21:05:09 +0200 Subject: [PATCH 1/3] Validate Change Username --- frontend/src/apis/loginAPI.js | 5 +- frontend/src/locales/de.json | 6 +- frontend/src/locales/en.json | 6 +- frontend/src/main.js | 17 ++++ .../UserProfile/UserCard_FormUsername.vue | 86 +++++++++++-------- 5 files changed, 80 insertions(+), 40 deletions(-) diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js index 3453bd296..d9d0b2b9c 100644 --- a/frontend/src/apis/loginAPI.js +++ b/frontend/src/apis/loginAPI.js @@ -15,7 +15,7 @@ const apiGet = async (url) => { if (result.status !== 200) { throw new Error('HTTP Status Error ' + result.status) } - if (result.data.state !== 'success') { + if (!['success', 'warning'].includes(result.data.state)) { throw new Error(result.data.msg) } return { success: true, result } @@ -139,6 +139,9 @@ const loginAPI = { } return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload) }, + checkUsername: async (username, groupId = 1) => { + return apiGet(CONFIG.LOGIN_API_URL + `checkUsername?username=${username}&group_id=${groupId}`) + }, } export default loginAPI diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 6536a143b..8554996bb 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -57,9 +57,11 @@ "send_transaction_error":"Leider konnte die Transaktion nicht ausgeführt werden!", "validation": { "gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein", - "is-not": "Du kannst dir selbst keine Gradidos überweisen" + "is-not": "Du kannst dir selbst keine Gradidos überweisen", + "usernmae-unique": "Der Username ist bereits vergeben.", + "usernmae-regex": "Der Username muss mit einem Buchstaben beginnen auf den mindestens zwei alfanumerische Zeichen folgen müssen." }, - "change_username_info": "Das ändern des Usernamens bedarf mehrerer Schritte." + "change_username_info": "Einmal gespeichert, kann der Username ncht mehr geändert werden!" }, "error": { "error":"Fehler" diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index f611e4b91..7b97c2240 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -57,9 +57,11 @@ "send_transaction_error":"Unfortunately, the transaction could not be executed!", "validation": { "gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits", - "is-not": "You cannot send Gradidos to yourself" + "is-not": "You cannot send Gradidos to yourself", + "usernmae-unique": "The username is already taken.", + "usernmae-regex": "The username must start with a letter, followed by at least two alphanumeric characters." }, - "change_username_info": "Changing the username requires several steps." + "change_username_info": "Once saved, the username cannot be changed again!" }, "error": { "error":"Error" diff --git a/frontend/src/main.js b/frontend/src/main.js index 160ff73c7..b5e109fbf 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -9,6 +9,8 @@ import { required, email, min, max, is_not } from 'vee-validate/dist/rules' // store import { store } from './store/store' +import loginAPI from './apis/loginAPI' + // router setup import router from './routes/router' @@ -64,6 +66,21 @@ extend('gddSendAmount', { }, }) +extend('gddUsernameUnique', { + async validate(value) { + const result = await loginAPI.checkUsername(value) + return result.result.data.state === 'success' + }, + message: (_, values) => i18n.t('form.validation.usernmae-unique', values), +}) + +extend('gddUsernameRgex', { + validate(value) { + return !!value.match(/^[a-zA-Z][-_a-zA-Z0-9]{2,}$/) + }, + message: (_, values) => i18n.t('form.validation.usernmae-regex', values), +}) + // eslint-disable-next-line camelcase extend('is_not', { // eslint-disable-next-line camelcase diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue index 2b3b451fe..21fa01aad 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue @@ -1,20 +1,19 @@ @@ -66,7 +73,7 @@ export default { name: 'FormUsername', data() { return { - editUsername: true, + showUsername: true, username: this.$store.state.username, form: { username: this.$store.state.username, @@ -74,6 +81,10 @@ export default { } }, methods: { + cancelEdit() { + this.username = this.$store.state.username + this.showUsername = true + }, async onSubmit() { const result = await loginAPI.changeUsernameProfile( this.$store.state.sessionId, @@ -82,8 +93,9 @@ export default { ) if (result.success) { this.$store.commit('username', this.form.username) - this.editUserdata = this.editUsername = !this.editUsername - alert('Dein Username wurde geändert.') + this.username = this.form.username + this.showUsername = true + this.$toast.success(this.$t('site.profil.user-data.change-success')) } else { alert(result.result.message) } @@ -91,4 +103,8 @@ export default { }, } - + From 2ed08841a1f65cbaaf81a640674437ea3773d353 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 14 Jun 2021 22:56:57 +0200 Subject: [PATCH 2/3] test user card form username --- .../UserProfile/UserCard_FormUsername.spec.js | 121 ++++++++++++++++++ .../UserProfile/UserCard_FormUsername.vue | 7 +- 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js new file mode 100644 index 000000000..b1d705952 --- /dev/null +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.spec.js @@ -0,0 +1,121 @@ +import { mount } from '@vue/test-utils' +import { extend } from 'vee-validate' +import UserCardFormUsername from './UserCard_FormUsername' +import loginAPI from '../../../apis/loginAPI' +import flushPromises from 'flush-promises' + +jest.mock('../../../apis/loginAPI') + +extend('gddUsernameRgex', { + validate(value) { + return true + }, +}) + +extend('gddUsernameUnique', { + validate(value) { + return true + }, +}) + +const localVue = global.localVue + +const mockAPIcall = jest.fn((args) => { + return { success: true } +}) + +loginAPI.changeUsernameProfile = mockAPIcall + +describe('UserCard_FormUsername', () => { + let wrapper + + const mocks = { + $t: jest.fn((t) => t), + $store: { + state: { + sessionId: 1, + email: 'user@example.org', + username: '', + }, + commit: jest.fn(), + }, + $toast: { + success: jest.fn(), + }, + } + + const Wrapper = () => { + return mount(UserCardFormUsername, { localVue, mocks }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the component', () => { + expect(wrapper.find('div#formusername').exists()).toBeTruthy() + }) + + describe('username in store is empty', () => { + it('renders an empty username', () => { + expect(wrapper.find('div.display-username').text()).toEqual('@') + }) + + it('has an edit icon', () => { + expect(wrapper.find('svg.bi-pencil').exists()).toBeTruthy() + }) + + describe('edit username', () => { + beforeEach(async () => { + await wrapper.find('svg.bi-pencil').trigger('click') + }) + + it('shows an input field for the username', () => { + expect(wrapper.find('input[placeholder="Username"]').exists()).toBeTruthy() + }) + + it('shows an cancel icon', () => { + expect(wrapper.find('svg.bi-x-circle').exists()).toBeTruthy() + }) + + it('closes the input when cancel icon is clicked', async () => { + wrapper.find('svg.bi-x-circle').trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.find('input[placeholder="Username"]').exists()).toBeFalsy() + }) + + it('does not change the username when cancel is clicked', async () => { + wrapper.find('input[placeholder="Username"]').setValue('username') + wrapper.find('svg.bi-x-circle').trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.find('div.display-username').text()).toEqual('@') + }) + + it('has a submit button', () => { + expect(wrapper.find('button[type="submit"]').exists()).toBeTruthy() + }) + + describe('successfull submit', () => { + beforeEach(async () => { + await wrapper.find('input[placeholder="Username"]').setValue('username') + await wrapper.find('form').trigger('submit') + await flushPromises() + }) + + it('calls the loginAPI', () => { + expect(mockAPIcall).toHaveBeenCalledWith(1, 'user@example.org', 'username') + }) + + it('displays the new username', () => { + expect(wrapper.find('div.display-username').text()).toEqual('@username') + }) + + it('has no edit button anymore', () => { + expect(wrapper.find('svg.bi-pencil').exists()).toBeFalsy() + }) + }) + }) + }) + }) +}) diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue index 21fa01aad..e908fd08c 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUsername.vue @@ -26,7 +26,7 @@ {{ $t('form.username') }} - @{{ form.username }} + @{{ username }} @@ -97,7 +97,10 @@ export default { this.showUsername = true this.$toast.success(this.$t('site.profil.user-data.change-success')) } else { - alert(result.result.message) + this.$toast.error(result.result.message) + this.showUsername = true + this.username = this.$store.state.username + this.form.username = this.$store.state.username } }, }, From 5a1e90f3f7ea7d423833327fef90e3ffb92c583d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 14 Jun 2021 23:04:21 +0200 Subject: [PATCH 3/3] coverage frontend to 29& --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6e1bbc8e..fedb64676 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -212,7 +212,7 @@ jobs: report_name: Coverage Frontend type: lcov result_path: ./coverage/lcov.info - min_coverage: 28 + min_coverage: 29 token: ${{ github.token }} ##############################################################################