diff --git a/backend/src/middleware/notifications/notificationsMiddleware.js b/backend/src/middleware/notifications/notificationsMiddleware.js index 2bc53ab7c..5419771ea 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.js +++ b/backend/src/middleware/notifications/notificationsMiddleware.js @@ -41,7 +41,6 @@ const publishNotifications = async (context, promises) => { notifications.forEach((notificationAdded, index) => { pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) if (notificationAdded.to.sendNotificationEmails) { - // Wolle await sendMail( notificationTemplate({ email: notificationsEmailAddresses[index].email, diff --git a/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js b/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js index 9253709f9..ca2e36818 100644 --- a/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js +++ b/cypress/integration/UserProfile.SocialMedia/I_add_a_social_media_link.js @@ -1,9 +1,12 @@ import { When } from "cypress-cucumber-preprocessor/steps"; When('I add a social media link', () => { - cy.get('input#addSocialMedia') - .type('https://freeradical.zone/peter-pan') - .get('button') + cy.get('button') .contains('Add link') .click() + .get('#editSocialMedia') + .type('https://freeradical.zone/peter-pan') + .get('button') + .contains('Save') + .click() }) \ No newline at end of file diff --git a/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js b/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js index 203b97032..93e35bf28 100644 --- a/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js +++ b/cypress/integration/UserProfile.SocialMedia/I_have_added_a_social_media_link.js @@ -2,9 +2,12 @@ import { Given } from "cypress-cucumber-preprocessor/steps"; Given('I have added a social media link', () => { cy.visit('/settings/my-social-media') - .get('input#addSocialMedia') - .type('https://freeradical.zone/peter-pan') .get('button') .contains('Add link') .click() + .get('#editSocialMedia') + .type('https://freeradical.zone/peter-pan') + .get('button') + .contains('Save') + .click() }) \ No newline at end of file diff --git a/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js b/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js new file mode 100644 index 000000000..513207110 --- /dev/null +++ b/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js @@ -0,0 +1,128 @@ +import { mount } from '@vue/test-utils' +import MySomethingList from './MySomethingList.vue' +import Vue from 'vue' + +const localVue = global.localVue + +describe('MySomethingList.vue', () => { + let wrapper + let propsData + let data + let mocks + + beforeEach(() => { + propsData = { + useFormData: { dummy: '' }, + useItems: [{ id: 'id', dummy: 'dummy' }], + namePropertyKey: 'dummy', + callbacks: { edit: jest.fn(), submit: jest.fn(), delete: jest.fn() }, + } + data = () => { + return {} + } + mocks = { + $t: jest.fn(), + $apollo: { + mutate: jest.fn(), + }, + $toast: { + error: jest.fn(), + success: jest.fn(), + }, + } + }) + + describe('mount', () => { + let form, slots + const Wrapper = () => { + slots = { + 'list-item': '
', + 'edit-item': '
', + } + return mount(MySomethingList, { + propsData, + data, + mocks, + localVue, + slots, + }) + } + + describe('given existing item', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + describe('for each item it', () => { + it('displays the item as slot "list-item"', () => { + expect(wrapper.find('.list-item').exists()).toBe(true) + }) + + it('displays the edit button', () => { + expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true) + }) + + it('displays the delete button', () => { + expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true) + }) + }) + + describe('editing item', () => { + beforeEach(async () => { + const editButton = wrapper.find('.base-button[data-test="edit-button"]') + editButton.trigger('click') + await Vue.nextTick() + }) + + it('disables adding items while editing', () => { + const submitButton = wrapper.find('.base-button[data-test="add-save-button"]') + expect(submitButton.text()).not.toContain('settings.social-media.submit') + }) + + it('allows the user to cancel editing', async () => { + expect(wrapper.find('.edit-item').exists()).toBe(true) + const cancelButton = wrapper.find('button#cancel') + cancelButton.trigger('click') + await Vue.nextTick() + expect(wrapper.find('.edit-item').exists()).toBe(false) + }) + }) + + describe('calls callback functions', () => { + it('calls edit', async () => { + const editButton = wrapper.find('.base-button[data-test="edit-button"]') + editButton.trigger('click') + await Vue.nextTick() + const expectedItem = expect.objectContaining({ id: 'id', dummy: 'dummy' }) + expect(propsData.callbacks.edit).toHaveBeenCalledTimes(1) + expect(propsData.callbacks.edit).toHaveBeenCalledWith(expect.any(Object), expectedItem) + }) + + it('calls submit', async () => { + form = wrapper.find('form') + form.trigger('submit') + await Vue.nextTick() + form.trigger('submit') + await Vue.nextTick() + const expectedItem = expect.objectContaining({ id: '' }) + expect(propsData.callbacks.submit).toHaveBeenCalledTimes(1) + expect(propsData.callbacks.submit).toHaveBeenCalledWith( + expect.any(Object), + true, + expectedItem, + { dummy: '' }, + ) + }) + + it('calls delete', async () => { + const deleteButton = wrapper.find('.base-button[data-test="delete-button"]') + deleteButton.trigger('click') + await Vue.nextTick() + const expectedItem = expect.objectContaining({ id: 'id', dummy: 'dummy' }) + expect(propsData.callbacks.delete).toHaveBeenCalledTimes(1) + expect(propsData.callbacks.delete).toHaveBeenCalledWith(expect.any(Object), expectedItem) + }) + }) + }) + }) +}) diff --git a/webapp/components/_new/features/MySomethingList/MySomethingList.vue b/webapp/components/_new/features/MySomethingList/MySomethingList.vue new file mode 100644 index 000000000..fbfcca932 --- /dev/null +++ b/webapp/components/_new/features/MySomethingList/MySomethingList.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js new file mode 100644 index 000000000..c7359d873 --- /dev/null +++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js @@ -0,0 +1,36 @@ +import { shallowMount } from '@vue/test-utils' +import SocialMediaListItem from './SocialMediaListItem.vue' + +describe('SocialMediaListItem.vue', () => { + let wrapper + let propsData + const socialMediaUrl = 'https://freeradical.zone/@mattwr18' + const faviconUrl = 'https://freeradical.zone/favicon.ico' + + beforeEach(() => { + propsData = {} + }) + + describe('shallowMount', () => { + const Wrapper = () => { + return shallowMount(SocialMediaListItem, { propsData }) + } + + describe('given existing social media links', () => { + beforeEach(() => { + propsData = { item: { id: 's1', url: socialMediaUrl, favicon: faviconUrl } } + wrapper = Wrapper() + }) + + describe('for each link item it', () => { + it('displays the favicon', () => { + expect(wrapper.find(`img[src="${faviconUrl}"]`).exists()).toBe(true) + }) + + it('displays the url', () => { + expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true) + }) + }) + }) + }) +}) diff --git a/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue new file mode 100644 index 000000000..75dea7ba8 --- /dev/null +++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue @@ -0,0 +1,18 @@ + + + diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 9816e167a..a98f0a320 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -773,6 +773,8 @@ "name": "Sicherheit" }, "social-media": { + "addNewTitle": "Neuen Link hinzufügen", + "editTitle": "Link \"{name}\" ändern", "name": "Soziale Netzwerke", "placeholder": "Deine Webadresse des Sozialen Netzwerkes", "requireUnique": "Dieser Link existiert bereits", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 11b920056..f6606b933 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -773,6 +773,8 @@ "name": "Security" }, "social-media": { + "addNewTitle": "Add new link", + "editTitle": "Edit link \"{name}\"", "name": "Social media", "placeholder": "Your social media url", "requireUnique": "You added this url already", diff --git a/webapp/pages/settings/my-social-media.spec.js b/webapp/pages/settings/my-social-media.spec.js index 1aadbb750..7136e1e7c 100644 --- a/webapp/pages/settings/my-social-media.spec.js +++ b/webapp/pages/settings/my-social-media.spec.js @@ -33,7 +33,7 @@ describe('my-social-media.vue', () => { }) describe('mount', () => { - let form, input, submitButton + let form, input const Wrapper = () => { const store = new Vuex.Store({ getters, @@ -42,11 +42,12 @@ describe('my-social-media.vue', () => { } describe('adding social media link', () => { - beforeEach(() => { + beforeEach(async () => { wrapper = Wrapper() form = wrapper.find('form') - input = wrapper.find('input#addSocialMedia') - submitButton = wrapper.find('button') + form.trigger('submit') + await Vue.nextTick() + input = wrapper.find('input#editSocialMedia') }) it('requires the link to be a valid url', async () => { @@ -79,7 +80,6 @@ describe('my-social-media.vue', () => { const expected = expect.objectContaining({ variables: { url: newSocialMediaUrl }, }) - expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) @@ -88,10 +88,10 @@ describe('my-social-media.vue', () => { expect(mocks.$toast.success).toHaveBeenCalledTimes(1) }) - it('clears the form', async () => { + it('switches back to list', async () => { await flushPromises() - expect(input.value).toBe(undefined) - expect(submitButton.vm.$attrs.disabled).toBe(true) + const submitButton = wrapper.find('.base-button[data-test="add-save-button"]') + expect(submitButton.text()).not.toContain('settings.social-media.submit') }) }) }) @@ -100,10 +100,9 @@ describe('my-social-media.vue', () => { beforeEach(() => { getters = { 'auth/user': () => ({ - socialMedia: [{ id: 's1', url: socialMediaUrl }], + socialMedia: [{ id: 's1', url: socialMediaUrl, favicon: faviconUrl }], }), } - wrapper = Wrapper() form = wrapper.find('form') }) @@ -116,18 +115,12 @@ describe('my-social-media.vue', () => { it('displays the url', () => { expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true) }) - - it('displays the edit button', () => { - expect(wrapper.find('.base-button[data-test="edit-button"]').exists()).toBe(true) - }) - - it('displays the delete button', () => { - expect(wrapper.find('.base-button[data-test="delete-button"]').exists()).toBe(true) - }) }) it('does not accept a duplicate url', async () => { - wrapper.find('input#addSocialMedia').setValue(socialMediaUrl) + form.trigger('submit') + await Vue.nextTick() + wrapper.find('input#editSocialMedia').setValue(socialMediaUrl) form.trigger('submit') await Vue.nextTick() expect(mocks.$apollo.mutate).not.toHaveBeenCalled() @@ -141,12 +134,6 @@ describe('my-social-media.vue', () => { input = wrapper.find('input#editSocialMedia') }) - it('disables adding new links while editing', () => { - const addInput = wrapper.find('input#addSocialMedia') - - expect(addInput.exists()).toBe(false) - }) - it('sends the new url to the backend', async () => { const expected = expect.objectContaining({ variables: { id: 's1', url: newSocialMediaUrl }, @@ -156,13 +143,6 @@ describe('my-social-media.vue', () => { await Vue.nextTick() expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) - - it('allows the user to cancel editing', async () => { - const cancelButton = wrapper.find('button#cancel') - cancelButton.trigger('click') - await Vue.nextTick() - expect(wrapper.find('input#editSocialMedia').exists()).toBe(false) - }) }) describe('deleting social media link', () => { @@ -176,7 +156,6 @@ describe('my-social-media.vue', () => { const expected = expect.objectContaining({ variables: { id: 's1' }, }) - expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 5753fc405..a985371d6 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -1,97 +1,73 @@ - -