text: import { config, mount } from '@vue/test-utils'
  import ContributionForm from './ContributionForm.vue'
  
  import Vue from 'vue'
  import Vuex from 'vuex'
  import PostMutations from '~/graphql/PostMutations.js'
  import CategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
  
  import ImageUploader from '~/components/ImageUploader/ImageUploader'
  import MutationObserver from 'mutation-observer'
  
  global.MutationObserver = MutationObserver
  
  const localVue = global.localVue
  
  config.stubs['client-only'] = '<span><slot /></span>'
  config.stubs['nuxt-link'] = '<span><slot /></span>'
  config.stubs['v-popover'] = '<span><slot /></span>'
  
  const categories = [
    {
      id: 'cat3',
      slug: 'health-wellbeing',
      icon: 'medkit',
    },
    {
      id: 'cat12',
      slug: 'it-internet-data-privacy',
      icon: 'mouse-pointer',
    },
    {
      id: 'cat9',
      slug: 'democracy-politics',
      icon: 'university',
    },
    {
      id: 'cat15',
      slug: 'consumption-sustainability',
      icon: 'shopping-cart',
    },
    {
      id: 'cat4',
      slug: 'environment-nature',
      icon: 'tree',
    },
  ]
  
  describe('ContributionForm.vue', () => {
    let wrapper,
      postTitleInput,
      expectedParams,
      cancelBtn,
      mocks,
      propsData,
      categoryIds,
      englishLanguage,
      deutschLanguage,
      dataPrivacyButton
    const postTitle = 'this is a title for a post'
    const postTitleTooShort = 'xx'
    let postTitleTooLong = ''
    for (let i = 0; i < 101; i++) {
      postTitleTooLong += 'x'
    }
    const postContent = 'this is a post'
    const imageUpload = {
      file: {
        filename: 'avataar.svg',
        previewElement: '',
      },
      url: 'someUrlToImage',
    }
    const image = { sensitive: false, url: '/uploads/1562010976466-avataaars', aspectRatio: 1 }
    beforeEach(() => {
      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',
                categoryIds,
              },
            },
          }),
        },
        $toast: {
          error: jest.fn(),
          success: jest.fn(),
        },
        $i18n: {
          locale: () => 'en',
        },
        $router: {
          back: jest.fn(),
          push: jest.fn(),
        },
      }
      propsData = {}
    })
  
    describe('mount', () => {
      const getters = {
        'editor/placeholder': () => {
          return 'some cool placeholder'
        },
        'auth/isModerator': () => false,
        'auth/user': () => {
          return {
            id: '4711',
            name: 'You yourself',
            slug: 'you-yourself',
          }
        },
      }
      const store = new Vuex.Store({
        getters,
      })
      const Wrapper = () => {
        return mount(ContributionForm, {
          mocks,
          localVue,
          store,
          propsData,
        })
      }
  
      beforeEach(() => {
        wrapper = Wrapper()
      })
  
      describe('CreatePost', () => {
        describe('invalid form submission', () => {
          beforeEach(async () => {
            wrapper.find(CategoriesSelect).setData({ categories })
            postTitleInput = wrapper.find('.ds-input')
            postTitleInput.setValue(postTitle)
            await wrapper.vm.updateEditorContent(postContent)
            englishLanguage = wrapper
              .findAll('li')
              .filter((language) => language.text() === 'English')
            englishLanguage.trigger('click')
            dataPrivacyButton = await wrapper
              .find(CategoriesSelect)
              .find('[data-test="category-buttons-cat12"]')
            dataPrivacyButton.trigger('click')
          })
  
          it('title cannot be empty', async () => {
            postTitleInput.setValue('')
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
  
          it('title cannot be too long', async () => {
            postTitleInput.setValue(postTitleTooLong)
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
  
          it('title cannot be too short', async () => {
            postTitleInput.setValue(postTitleTooShort)
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
  
          it('content cannot be empty', async () => {
            await wrapper.vm.updateEditorContent('')
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
  
          it('has at least one category', async () => {
            dataPrivacyButton = await wrapper
              .find(CategoriesSelect)
              .find('[data-test="category-buttons-cat12"]')
            dataPrivacyButton.trigger('click')
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
  
          it('has no more than three categories', async () => {
            wrapper.vm.formData.categoryIds = ['cat4', 'cat9', 'cat15', 'cat27']
            await Vue.nextTick()
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
          })
        })
  
        describe('valid form submission', () => {
          beforeEach(async () => {
            expectedParams = {
              mutation: PostMutations().CreatePost,
              variables: {
                title: postTitle,
                content: postContent,
                language: 'en',
                id: null,
                categoryIds: ['cat12'],
                image: null,
              },
            }
            postTitleInput = wrapper.find('.ds-input')
            postTitleInput.setValue(postTitle)
            await wrapper.vm.updateEditorContent(postContent)
            wrapper.find(CategoriesSelect).setData({ categories })
            englishLanguage = wrapper
              .findAll('li')
              .filter((language) => language.text() === 'English')
            englishLanguage.trigger('click')
            await Vue.nextTick()
            dataPrivacyButton = await wrapper
              .find(CategoriesSelect)
              .find('[data-test="category-buttons-cat12"]')
            dataPrivacyButton.trigger('click')
            await Vue.nextTick()
          })
  
          it('creates a post with valid title, content, and at least one category', async () => {
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
          })
  
          it('supports changing the language', async () => {
            expectedParams.variables.language = 'de'
            deutschLanguage = wrapper
              .findAll('li')
              .filter((language) => language.text() === 'Deutsch')
            deutschLanguage.trigger('click')
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
          })
  
          it('supports adding a teaser image', async () => {
            expectedParams.variables.image = {
              aspectRatio: null,
              sensitive: false,
              upload: imageUpload,
            }
            const spy = jest
              .spyOn(FileReader.prototype, 'readAsDataURL')
              .mockImplementation(function () {
                this.onload({ target: { result: 'someUrlToImage' } })
              })
            wrapper.find(ImageUploader).vm.$emit('addHeroImage', imageUpload)
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
            expect(spy).toHaveBeenCalledWith(imageUpload)
            spy.mockReset()
          })
  
          it('content is valid with just a link', async () => {
            await wrapper.vm.updateEditorContent(
              '<a href="https://www.youtube.com/watch?v=smoEelV6FUk" target="_blank"></a>',
            )
            wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
          })
  
          it("pushes the user to the post's page", async () => {
            wrapper.find('form').trigger('submit')
            await mocks.$apollo.mutate
            expect(mocks.$router.push).toHaveBeenCalledTimes(1)
          })
  
          it('shows a success toaster', async () => {
            wrapper.find('form').trigger('submit')
            await mocks.$apollo.mutate
            expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
          })
        })
  
        describe('cancel', () => {
          it('calls $router.back() when cancel button clicked', () => {
            cancelBtn = wrapper.find('[data-test="cancel-button"]')
            cancelBtn.trigger('click')
            expect(mocks.$router.back).toHaveBeenCalledTimes(1)
          })
        })
  
        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)
            await wrapper.vm.updateEditorContent(postContent)
            categoryIds = ['cat12']
            wrapper.find(CategoriesSelect).setData({ categories })
            englishLanguage = wrapper
              .findAll('li')
              .filter((language) => language.text() === 'English')
            englishLanguage.trigger('click')
            await Vue.nextTick()
            dataPrivacyButton = await wrapper
              .find(CategoriesSelect)
              .find('[data-test="category-buttons-cat12"]')
            dataPrivacyButton.trigger('click')
            await Vue.nextTick()
          })
  
          it('shows an error toaster when apollo mutation rejects', async () => {
            await wrapper.find('form').trigger('submit')
            await mocks.$apollo.mutate
            await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
          })
        })
      })
  
      describe('UpdatePost', () => {
        beforeEach(() => {
          propsData = {
            contribution: {
              id: 'p1456',
              slug: 'dies-ist-ein-post',
              title: 'dies ist ein Post',
              content: 'auf Deutsch geschrieben',
              language: 'de',
              image,
              categories: [
                {
                  id: 'cat12',
                  name: 'Democracy & Politics',
                },
              ],
            },
          }
          wrapper = Wrapper()
        })
  
        it('sets title equal to contribution title', () => {
          expect(wrapper.vm.formData.title).toEqual(propsData.contribution.title)
        })
  
        it('sets content equal to contribution content', () => {
          expect(wrapper.vm.formData.content).toEqual(propsData.contribution.content)
        })
  
        describe('valid update', () => {
          beforeEach(() => {
            mocks.$apollo.mutate = jest.fn().mockResolvedValueOnce({
              data: {
                UpdatePost: {
                  title: postTitle,
                  slug: 'this-is-a-title-for-a-post',
                  content: postContent,
                  contentExcerpt: postContent,
                  language: 'en',
                  categoryIds,
                },
              },
            })
            wrapper = Wrapper()
            expectedParams = {
              mutation: PostMutations().UpdatePost,
              variables: {
                title: propsData.contribution.title,
                content: propsData.contribution.content,
                language: propsData.contribution.language,
                id: propsData.contribution.id,
                categoryIds: ['cat12'],
                image: {
                  sensitive: false,
                },
              },
            }
          })
  
          it('calls the UpdatePost apollo mutation', async () => {
            expectedParams.variables.content = postContent
            wrapper.vm.updateEditorContent(postContent)
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
          })
  
          it('supports updating categories', async () => {
            expectedParams.variables.categoryIds.push('cat3')
            wrapper.find(CategoriesSelect).setData({ categories })
            await Vue.nextTick()
            const healthWellbeingButton = await wrapper
              .find(CategoriesSelect)
              .find('[data-test="category-buttons-cat3"]')
            healthWellbeingButton.trigger('click')
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
          })
  
          it('supports deleting a teaser image', async () => {
            expectedParams.variables.image = null
            propsData.contribution.image = { url: '/uploads/someimage.png' }
            wrapper = Wrapper()
            wrapper.find('[data-test="delete-button"]').trigger('click')
            await wrapper.find('form').trigger('submit')
            expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
          })
        })
      })
    })
  })
  