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(),