diff --git a/.vscode/settings.json b/.vscode/settings.json index 908252f41..e2a727871 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,4 +9,4 @@ ], "editor.formatOnSave": true, "eslint.autoFixOnSave": true -} \ No newline at end of file +} diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 86f0665a0..3bff53ddb 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -19,7 +19,7 @@ afterEach(async () => { describe('CreatePost', () => { const mutation = ` mutation { - CreatePost(title: "I am a title", content: "Some content", language: "en") { + CreatePost(title: "I am a title", content: "Some content") { title content slug diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js new file mode 100644 index 000000000..8d68de7d3 --- /dev/null +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -0,0 +1,137 @@ +import { config, mount, createLocalVue } from '@vue/test-utils' +import ContributionForm from './index.vue' +import Styleguide from '@human-connection/styleguide' + +const localVue = createLocalVue() + +localVue.use(Styleguide) + +config.stubs['no-ssr'] = '' + +describe('ContributionForm.vue', () => { + let wrapper + let postTitleInput + let expectedParams + let deutschOption + let cancelBtn + const postTitle = 'this is a title for a post' + const postContent = 'this is a post' + const computed = { locale: () => 'English' } + const 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', + }, + }, + }) + .mockRejectedValue({ message: 'Not Authorised!' }), + }, + $toast: { + error: jest.fn(), + success: jest.fn(), + }, + $i18n: { + locale: () => 'en', + }, + $router: { + back: jest.fn(), + push: jest.fn(), + }, + } + + describe('mount', () => { + const Wrapper = () => { + return mount(ContributionForm, { mocks, localVue, computed }) + } + + beforeEach(() => { + wrapper = Wrapper() + }) + + describe('CreatePost', () => { + describe('invalid form submission', () => { + it('title required for form submission', async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue('this is a title for a post') + await wrapper.find('form').trigger('submit') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + + it('content required for form submission', async () => { + wrapper.vm.updateEditorContent('this is a post') + await wrapper.find('form').trigger('submit') + expect(mocks.$apollo.mutate).not.toHaveBeenCalled() + }) + }) + + describe('valid form submission', () => { + expectedParams = { + variables: { title: postTitle, content: postContent, language: 'en', id: null }, + } + beforeEach(async () => { + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue('this is a title for a post') + wrapper.vm.updateEditorContent('this is a post') + await wrapper.find('form').trigger('submit') + }) + + it('with title and content', () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) + }) + + it("sends a fallback language based on a user's locale", () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) + }) + + it('supports changing the language', async () => { + expectedParams.variables.language = 'de' + deutschOption = wrapper.findAll('li').at(0) + deutschOption.trigger('click') + await wrapper.find('form').trigger('submit') + expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams)) + }) + + it("pushes the user to the post's page", async () => { + expect(mocks.$router.push).toHaveBeenCalledTimes(1) + }) + + it('shows a success toaster', () => { + expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + }) + }) + + describe('cancel', () => { + it('calls $router.back() when cancel button clicked', () => { + cancelBtn = wrapper.find('.cancel-button') + cancelBtn.trigger('click') + expect(mocks.$router.back).toHaveBeenCalledTimes(1) + }) + }) + + describe('handles errors', () => { + beforeEach(async () => { + wrapper = Wrapper() + postTitleInput = wrapper.find('.ds-input') + postTitleInput.setValue('this is a title for a post') + wrapper.vm.updateEditorContent('this is a post') + // second submission causes mutation to reject + await wrapper.find('form').trigger('submit') + }) + it('shows an error toaster when apollo mutation rejects', async () => { + await wrapper.find('form').trigger('submit') + await mocks.$apollo.mutate + expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') + }) + }) + }) + }) +}) diff --git a/webapp/components/ContributionForm/index.vue b/webapp/components/ContributionForm/index.vue index 3e059d7cd..6320eb368 100644 --- a/webapp/components/ContributionForm/index.vue +++ b/webapp/components/ContributionForm/index.vue @@ -13,7 +13,12 @@ :placeholder="locale" />
- + {{ $t('actions.cancel') }} { this.loading = false - this.$toast.success('Saved!') + this.$toast.success(this.$t('contribution.success')) this.disabled = true const result = res.data[this.id ? 'UpdatePost' : 'CreatePost'] diff --git a/webapp/locales/de.json b/webapp/locales/de.json index f2d1e7415..04b18b6b8 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -300,5 +300,8 @@ "avatar": { "submitted": "Upload erfolgreich" } + }, + "contribution": { + "success": "Gespeichert!" } } diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 093393045..839d3147a 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -299,5 +299,8 @@ "avatar": { "submitted": "Upload successful" } + }, + "contribution": { + "success": "Saved!" } } diff --git a/webapp/pages/profile/_id/_slug.spec.js b/webapp/pages/profile/_id/_slug.spec.js index d79f9b885..790c9c6c0 100644 --- a/webapp/pages/profile/_id/_slug.spec.js +++ b/webapp/pages/profile/_id/_slug.spec.js @@ -20,7 +20,7 @@ describe('ProfileSlug', () => { name: 'It is a post', }, $t: jest.fn(), - // If you mocking router, than don't use VueRouter with lacalVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html + // If you're mocking router, then don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html $router: { history: { push: jest.fn(),