diff --git a/backend/package.json b/backend/package.json index c5b3467d6..3cc4936c8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -127,4 +127,4 @@ "prettier": "~1.18.2", "supertest": "~4.0.2" } -} +} \ No newline at end of file diff --git a/backend/yarn.lock b/backend/yarn.lock index 5ef22df84..1059095d7 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1583,32 +1583,6 @@ apollo-server-caching@0.5.0: dependencies: lru-cache "^5.0.0" -apollo-server-core@2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.7.2.tgz#4acd9f4d0d235bef0e596e2a821326dfc07ae7b2" - integrity sha512-Dv6ZMMf8Y+ovkj1ioMtcYvjbcsSMqnZblbPPzOWo29vvKEjMXAL1OTSL1WBYxGA/WSBSCTnxAzipn71XZkYoCw== - dependencies: - "@apollographql/apollo-tools" "^0.4.0" - "@apollographql/graphql-playground-html" "1.6.24" - "@types/ws" "^6.0.0" - apollo-cache-control "0.8.1" - apollo-datasource "0.6.1" - apollo-engine-reporting "1.4.2" - apollo-server-caching "0.5.0" - apollo-server-env "2.4.1" - apollo-server-errors "2.3.1" - apollo-server-plugin-base "0.6.1" - apollo-server-types "0.2.1" - apollo-tracing "0.8.1" - fast-json-stable-stringify "^2.0.0" - graphql-extensions "0.8.2" - graphql-tag "^2.9.2" - graphql-tools "^4.0.0" - graphql-upload "^8.0.2" - sha.js "^2.4.11" - subscriptions-transport-ws "^0.9.11" - ws "^6.0.0" - apollo-server-core@2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.8.0.tgz#0bfba3d5eb557c6ffa68ad60e77f69e2634e211d" diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js index e303216f1..3f9384d27 100644 --- a/webapp/components/ContributionForm/ContributionForm.spec.js +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -26,9 +26,22 @@ describe('ContributionForm.vue', () => { let mocks let propsData const postTitle = 'this is a title for a post' + const postTitleTooShort = 'xx' + let postTitleTooLong = '' + for (let i = 0; i < 65; i++) { + postTitleTooLong += 'x' + } const postContent = 'this is a post' + const postContentTooShort = 'xx' + let postContentTooLong = '' + for (let i = 0; i < 2001; i++) { + postContentTooLong += 'x' + } const imageUpload = { - file: { filename: 'avataar.svg', previewElement: '' }, + file: { + filename: 'avataar.svg', + previewElement: '', + }, url: 'someUrlToImage', } const image = '/uploads/1562010976466-avataaars' @@ -36,22 +49,17 @@ describe('ContributionForm.vue', () => { mocks = { $t: jest.fn(), $apollo: { - mutate: jest - .fn() - .mockResolvedValueOnce({ - data: { - CreatePost: { - title: postTitle, - slug: 'this-is-a-title-for-a-post', - content: postContent, - contentExcerpt: postContent, - language: 'en', - }, + mutate: jest.fn().mockResolvedValueOnce({ + data: { + CreatePost: { + title: postTitle, + slug: 'this-is-a-title-for-a-post', + content: postContent, + contentExcerpt: postContent, + language: 'en', }, - }) - .mockRejectedValue({ - message: 'Not Authorised!', - }), + }, + }), }, $toast: { error: jest.fn(), @@ -115,16 +123,53 @@ describe('ContributionForm.vue', () => { }) describe('invalid form submission', () => { - it('title required for form submission', async () => { - postTitleInput = wrapper.find('.ds-input') - postTitleInput.setValue(postTitle) - await wrapper.find('form').trigger('submit') + it('title and content should not be empty ', async () => { + wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).not.toHaveBeenCalled() }) - it('content required for form submission', async () => { - wrapper.vm.updateEditorContent(postContent) - await wrapper.find('form').trigger('submit') + it('title should not be empty', async () => { + await wrapper.vm.updateEditorContent(postContent) + wrapper.find('.submit-button-for-test').trigger('click') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('title should not be too long', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue(postTitleTooLong) + await wrapper.vm.updateEditorContent(postContent) + wrapper.find('.submit-button-for-test').trigger('click') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('title should not be too short', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue(postTitleTooShort) + await wrapper.vm.updateEditorContent(postContent) + wrapper.find('.submit-button-for-test').trigger('click') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('content should not be empty', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue(postTitle) + await wrapper.find('.submit-button-for-test').trigger('click') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('content should not be too short', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue(postTitle) + await wrapper.vm.updateEditorContent(postContentTooShort) + wrapper.find('.submit-button-for-test').trigger('click') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('content should not be too long', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue(postTitle) + await wrapper.vm.updateEditorContent(postContentTooLong) + wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).not.toHaveBeenCalled() }) }) @@ -145,15 +190,16 @@ describe('ContributionForm.vue', () => { } postTitleInput = wrapper.find('.ds-input') postTitleInput.setValue(postTitle) - wrapper.vm.updateEditorContent(postContent) - await wrapper.find('form').trigger('submit') + await wrapper.vm.updateEditorContent(postContent) }) it('with title and content', () => { + wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) }) it("sends a fallback language based on a user's locale", () => { + wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) @@ -161,7 +207,7 @@ describe('ContributionForm.vue', () => { expectedParams.variables.language = 'de' deutschOption = wrapper.findAll('li').at(0) deutschOption.trigger('click') - await wrapper.find('form').trigger('submit') + wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) @@ -169,22 +215,26 @@ describe('ContributionForm.vue', () => { const categoryIds = ['cat12', 'cat15', 'cat37'] expectedParams.variables.categoryIds = categoryIds wrapper.find(CategoriesSelect).vm.$emit('updateCategories', categoryIds) - await wrapper.find('form').trigger('submit') + await wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) it('supports adding a teaser image', async () => { expectedParams.variables.imageUpload = imageUpload wrapper.find(TeaserImage).vm.$emit('addTeaserImage', imageUpload) - await wrapper.find('form').trigger('submit') + await wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) it("pushes the user to the post's page", async () => { + wrapper.find('.submit-button-for-test').trigger('click') + await mocks.$apollo.mutate expect(mocks.$router.push).toHaveBeenCalledTimes(1) }) - it('shows a success toaster', () => { + it('shows a success toaster', async () => { + wrapper.find('.submit-button-for-test').trigger('click') + await mocks.$apollo.mutate expect(mocks.$toast.success).toHaveBeenCalledTimes(1) }) }) @@ -200,18 +250,19 @@ describe('ContributionForm.vue', () => { describe('handles errors', () => { beforeEach(async () => { jest.useFakeTimers() + mocks.$apollo.mutate = jest.fn().mockRejectedValueOnce({ + message: 'Not Authorised!', + }) wrapper = Wrapper() postTitleInput = wrapper.find('.ds-input') postTitleInput.setValue(postTitle) - wrapper.vm.updateEditorContent(postContent) - // second submission causes mutation to reject - await wrapper.find('form').trigger('submit') + await wrapper.vm.updateEditorContent(postContent) }) it('shows an error toaster when apollo mutation rejects', async () => { - await wrapper.find('form').trigger('submit') + await wrapper.find('.submit-button-for-test').trigger('click') await mocks.$apollo.mutate - expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') + await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') }) }) }) @@ -226,7 +277,12 @@ describe('ContributionForm.vue', () => { content: 'auf Deutsch geschrieben', language: 'de', image, - categories: [{ id: 'cat12', name: 'Democracy & Politics' }], + categories: [ + { + id: 'cat12', + name: 'Democracy & Politics', + }, + ], }, } wrapper = Wrapper() @@ -264,7 +320,7 @@ describe('ContributionForm.vue', () => { postTitleInput = wrapper.find('.ds-input') postTitleInput.setValue(postTitle) wrapper.vm.updateEditorContent(postContent) - await wrapper.find('form').trigger('submit') + await wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) @@ -275,7 +331,7 @@ describe('ContributionForm.vue', () => { wrapper.vm.updateEditorContent(postContent) expectedParams.variables.categoryIds = categoryIds wrapper.find(CategoriesSelect).vm.$emit('updateCategories', categoryIds) - await wrapper.find('form').trigger('submit') + await wrapper.find('.submit-button-for-test').trigger('click') expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) }) }) diff --git a/webapp/components/ContributionForm/ContributionForm.vue b/webapp/components/ContributionForm/ContributionForm.vue index 23a1c75de..dca23a882 100644 --- a/webapp/components/ContributionForm/ContributionForm.vue +++ b/webapp/components/ContributionForm/ContributionForm.vue @@ -1,5 +1,5 @@