From d3cc49d37ba260f9a285c078c57e673a32a76732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 3 Nov 2021 13:04:45 +0100 Subject: [PATCH] Implement MySomethingList for social media, use list item slot --- .../MySomethingList/MySomethingList.spec.js | 187 +++++++++++++ .../MySomethingList/MySomethingList.vue | 261 ++++++++++++++++++ .../SocialMedia/SocialMediaListItem.spec.js | 37 +++ .../SocialMedia/SocialMediaListItem.vue | 18 ++ webapp/pages/settings/my-social-media.spec.js | 230 +++++++-------- webapp/pages/settings/my-social-media.vue | 237 +--------------- 6 files changed, 628 insertions(+), 342 deletions(-) create mode 100644 webapp/components/_new/features/MySomethingList/MySomethingList.spec.js create mode 100644 webapp/components/_new/features/MySomethingList/MySomethingList.vue create mode 100644 webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js create mode 100644 webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue 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..7be290b3e --- /dev/null +++ b/webapp/components/_new/features/MySomethingList/MySomethingList.spec.js @@ -0,0 +1,187 @@ +import { mount } from '@vue/test-utils' +import flushPromises from 'flush-promises' +import MySomethingList from './MySomethingList.vue' +import Vuex from 'vuex' +import Vue from 'vue' + +const localVue = global.localVue + +describe('MySomethingList.vue', () => { + let wrapper + let mocks + let getters + const socialMediaUrl = 'https://freeradical.zone/@mattwr18' + const newSocialMediaUrl = 'https://twitter.com/mattwr18' + + beforeEach(() => { + mocks = { + $t: jest.fn(), + $apollo: { + mutate: jest.fn(), + }, + $toast: { + error: jest.fn(), + success: jest.fn(), + }, + } + getters = { + 'auth/user': () => { + return {} + }, + } + }) + + describe('mount', () => { + let form, input, slots, submitButton + const Wrapper = () => { + const store = new Vuex.Store({ + getters, + }) + slots = { 'list-item': '
' } + return mount(MySomethingList, { store, mocks, localVue, slots }) + } + + describe('adding social media link', () => { + beforeEach(() => { + wrapper = Wrapper() + form = wrapper.find('form') + input = wrapper.find('input#addSocialMedia') + submitButton = wrapper.find('button') + }) + + it('requires the link to be a valid url', async () => { + input.setValue('some value') + form.trigger('submit') + await Vue.nextTick() + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('displays an error message when not saved successfully', async () => { + mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + input.setValue(newSocialMediaUrl) + form.trigger('submit') + await Vue.nextTick() + await flushPromises() + expect(mocks.$toast.error).toHaveBeenCalledTimes(1) + }) + + describe('success', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockResolvedValue({ + data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } }, + }) + input.setValue(newSocialMediaUrl) + form.trigger('submit') + await Vue.nextTick() + }) + + it('sends the new url to the backend', () => { + const expected = expect.objectContaining({ + variables: { url: newSocialMediaUrl }, + }) + + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + }) + + it('displays a success message', async () => { + await flushPromises() + expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + }) + + it('clears the form', async () => { + await flushPromises() + expect(input.value).toBe(undefined) + expect(submitButton.vm.$attrs.disabled).toBe(true) + }) + }) + }) + + describe('given existing social media links', () => { + beforeEach(() => { + getters = { + 'auth/user': () => ({ + socialMedia: [{ id: 's1', url: socialMediaUrl }], + }), + } + + wrapper = Wrapper() + form = wrapper.find('form') + }) + + 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) + }) + }) + + it('does not accept a duplicate url', async () => { + wrapper.find('input#addSocialMedia').setValue(socialMediaUrl) + form.trigger('submit') + await Vue.nextTick() + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + describe('editing social media link', () => { + beforeEach(async () => { + const editButton = wrapper.find('.base-button[data-test="edit-button"]') + editButton.trigger('click') + await Vue.nextTick() + 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 }, + }) + input.setValue(newSocialMediaUrl) + form.trigger('submit') + 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', () => { + beforeEach(async () => { + const deleteButton = wrapper.find('.base-button[data-test="delete-button"]') + deleteButton.trigger('click') + await Vue.nextTick() + }) + + it('sends the link id to the backend', () => { + const expected = expect.objectContaining({ + variables: { id: 's1' }, + }) + + expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + }) + + it('displays a success message', async () => { + await flushPromises() + expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + }) + }) + }) + }) +}) diff --git a/webapp/components/_new/features/MySomethingList/MySomethingList.vue b/webapp/components/_new/features/MySomethingList/MySomethingList.vue new file mode 100644 index 000000000..ef374066a --- /dev/null +++ b/webapp/components/_new/features/MySomethingList/MySomethingList.vue @@ -0,0 +1,261 @@ + + + + + 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..fca686dae --- /dev/null +++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.spec.js @@ -0,0 +1,37 @@ +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 = { link: { id: 's1', url: socialMediaUrl, favicon: faviconUrl } } + + wrapper = Wrapper() + }) + + describe('for each link 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..0f807fdcd --- /dev/null +++ b/webapp/components/_new/features/SocialMedia/SocialMediaListItem.vue @@ -0,0 +1,18 @@ + + + diff --git a/webapp/pages/settings/my-social-media.spec.js b/webapp/pages/settings/my-social-media.spec.js index 1aadbb750..13b507049 100644 --- a/webapp/pages/settings/my-social-media.spec.js +++ b/webapp/pages/settings/my-social-media.spec.js @@ -1,5 +1,5 @@ import { mount } from '@vue/test-utils' -import flushPromises from 'flush-promises' +// import flushPromises from 'flush-promises' import MySocialMedia from './my-social-media.vue' import Vuex from 'vuex' import Vue from 'vue' @@ -11,19 +11,19 @@ describe('my-social-media.vue', () => { let mocks let getters const socialMediaUrl = 'https://freeradical.zone/@mattwr18' - const newSocialMediaUrl = 'https://twitter.com/mattwr18' + // const newSocialMediaUrl = 'https://twitter.com/mattwr18' const faviconUrl = 'https://freeradical.zone/favicon.ico' beforeEach(() => { mocks = { $t: jest.fn(), - $apollo: { - mutate: jest.fn(), - }, - $toast: { - error: jest.fn(), - success: jest.fn(), - }, + // $apollo: { + // mutate: jest.fn(), + // }, + // $toast: { + // error: jest.fn(), + // success: jest.fn(), + // }, } getters = { 'auth/user': () => { @@ -33,7 +33,7 @@ describe('my-social-media.vue', () => { }) describe('mount', () => { - let form, input, submitButton + // let form, input, submitButton const Wrapper = () => { const store = new Vuex.Store({ getters, @@ -41,60 +41,60 @@ describe('my-social-media.vue', () => { return mount(MySocialMedia, { store, mocks, localVue }) } - describe('adding social media link', () => { - beforeEach(() => { - wrapper = Wrapper() - form = wrapper.find('form') - input = wrapper.find('input#addSocialMedia') - submitButton = wrapper.find('button') - }) + // describe('adding social media link', () => { + // beforeEach(() => { + // wrapper = Wrapper() + // form = wrapper.find('form') + // input = wrapper.find('input#addSocialMedia') + // submitButton = wrapper.find('button') + // }) - it('requires the link to be a valid url', async () => { - input.setValue('some value') - form.trigger('submit') - await Vue.nextTick() - expect(mocks.$apollo.mutate).not.toHaveBeenCalled() - }) + // it('requires the link to be a valid url', async () => { + // input.setValue('some value') + // form.trigger('submit') + // await Vue.nextTick() + // expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + // }) - it('displays an error message when not saved successfully', async () => { - mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) - input.setValue(newSocialMediaUrl) - form.trigger('submit') - await Vue.nextTick() - await flushPromises() - expect(mocks.$toast.error).toHaveBeenCalledTimes(1) - }) + // it('displays an error message when not saved successfully', async () => { + // mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + // input.setValue(newSocialMediaUrl) + // form.trigger('submit') + // await Vue.nextTick() + // await flushPromises() + // expect(mocks.$toast.error).toHaveBeenCalledTimes(1) + // }) - describe('success', () => { - beforeEach(async () => { - mocks.$apollo.mutate.mockResolvedValue({ - data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } }, - }) - input.setValue(newSocialMediaUrl) - form.trigger('submit') - await Vue.nextTick() - }) + // describe('success', () => { + // beforeEach(async () => { + // mocks.$apollo.mutate.mockResolvedValue({ + // data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } }, + // }) + // input.setValue(newSocialMediaUrl) + // form.trigger('submit') + // await Vue.nextTick() + // }) - it('sends the new url to the backend', () => { - const expected = expect.objectContaining({ - variables: { url: newSocialMediaUrl }, - }) + // it('sends the new url to the backend', () => { + // const expected = expect.objectContaining({ + // variables: { url: newSocialMediaUrl }, + // }) - expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) - }) + // expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + // }) - it('displays a success message', async () => { - await flushPromises() - expect(mocks.$toast.success).toHaveBeenCalledTimes(1) - }) + // it('displays a success message', async () => { + // await flushPromises() + // expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + // }) - it('clears the form', async () => { - await flushPromises() - expect(input.value).toBe(undefined) - expect(submitButton.vm.$attrs.disabled).toBe(true) - }) - }) - }) + // it('clears the form', async () => { + // await flushPromises() + // expect(input.value).toBe(undefined) + // expect(submitButton.vm.$attrs.disabled).toBe(true) + // }) + // }) + // }) describe('given existing social media links', () => { beforeEach(() => { @@ -105,7 +105,7 @@ describe('my-social-media.vue', () => { } wrapper = Wrapper() - form = wrapper.find('form') + // form = wrapper.find('form') }) describe('for each link it', () => { @@ -117,75 +117,75 @@ describe('my-social-media.vue', () => { 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 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('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() - expect(mocks.$apollo.mutate).not.toHaveBeenCalled() - }) + // it('does not accept a duplicate url', async () => { + // wrapper.find('input#addSocialMedia').setValue(socialMediaUrl) + // form.trigger('submit') + // await Vue.nextTick() + // expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + // }) - describe('editing social media link', () => { - beforeEach(async () => { - const editButton = wrapper.find('.base-button[data-test="edit-button"]') - editButton.trigger('click') - await Vue.nextTick() - input = wrapper.find('input#editSocialMedia') - }) + // describe('editing social media link', () => { + // beforeEach(async () => { + // const editButton = wrapper.find('.base-button[data-test="edit-button"]') + // editButton.trigger('click') + // await Vue.nextTick() + // input = wrapper.find('input#editSocialMedia') + // }) - it('disables adding new links while editing', () => { - const addInput = wrapper.find('input#addSocialMedia') + // it('disables adding new links while editing', () => { + // const addInput = wrapper.find('input#addSocialMedia') - expect(addInput.exists()).toBe(false) - }) + // expect(addInput.exists()).toBe(false) + // }) - it('sends the new url to the backend', async () => { - const expected = expect.objectContaining({ - variables: { id: 's1', url: newSocialMediaUrl }, - }) - input.setValue(newSocialMediaUrl) - form.trigger('submit') - await Vue.nextTick() - expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) - }) + // it('sends the new url to the backend', async () => { + // const expected = expect.objectContaining({ + // variables: { id: 's1', url: newSocialMediaUrl }, + // }) + // input.setValue(newSocialMediaUrl) + // form.trigger('submit') + // 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) - }) - }) + // 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', () => { - beforeEach(async () => { - const deleteButton = wrapper.find('.base-button[data-test="delete-button"]') - deleteButton.trigger('click') - await Vue.nextTick() - }) + // describe('deleting social media link', () => { + // beforeEach(async () => { + // const deleteButton = wrapper.find('.base-button[data-test="delete-button"]') + // deleteButton.trigger('click') + // await Vue.nextTick() + // }) - it('sends the link id to the backend', () => { - const expected = expect.objectContaining({ - variables: { id: 's1' }, - }) + // it('sends the link id to the backend', () => { + // const expected = expect.objectContaining({ + // variables: { id: 's1' }, + // }) - expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) - expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) - }) + // expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) + // expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) + // }) - it('displays a success message', async () => { - await flushPromises() - expect(mocks.$toast.success).toHaveBeenCalledTimes(1) - }) - }) + // it('displays a success message', async () => { + // await flushPromises() + // expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + // }) + // }) }) }) }) diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index 5753fc405..57220ce17 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -1,236 +1,19 @@ - -