add component tests

This commit is contained in:
Alina Beck 2019-07-16 12:01:10 +02:00
parent 7217bb37a4
commit 05b6c63006
4 changed files with 167 additions and 1614 deletions

View File

@ -101,6 +101,7 @@
"eslint-plugin-promise": "~4.2.1",
"eslint-plugin-standard": "~4.0.0",
"eslint-plugin-vue": "~5.2.3",
"flush-promises": "^1.0.2",
"fuse.js": "^3.4.5",
"jest": "~24.8.0",
"node-sass": "~4.12.0",
@ -111,4 +112,4 @@
"vue-jest": "~3.0.4",
"vue-svg-loader": "~0.12.0"
}
}
}

View File

@ -1,4 +1,5 @@
import { mount, createLocalVue } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import MySocialMedia from './my-social-media.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
@ -12,23 +13,17 @@ localVue.use(Filters)
describe('my-social-media.vue', () => {
let wrapper
let store
let mocks
let getters
let input
let submitBtn
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
const newSocialMediaUrl = 'https://twitter.com/mattwr18'
const faviconUrl = 'https://freeradical.zone/favicon.ico'
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({
data: { CreateSocialMeda: { id: 's1', url: socialMediaUrl } },
}),
mutate: jest.fn(),
},
$toast: {
error: jest.fn(),
@ -43,75 +38,152 @@ describe('my-social-media.vue', () => {
})
describe('mount', () => {
let form, input, submitButton
const Wrapper = () => {
store = new Vuex.Store({
const store = new Vuex.Store({
getters,
})
return mount(MySocialMedia, { store, mocks, localVue })
}
it('renders', () => {
wrapper = Wrapper()
expect(wrapper.contains('div')).toBe(true)
})
describe('given currentUser has a social media account linked', () => {
describe('adding social media link', () => {
beforeEach(() => {
getters = {
'auth/user': () => {
return {
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}
},
}
})
it("displays a link to the currentUser's social media", () => {
wrapper = Wrapper()
const socialMediaLink = wrapper.find('a').attributes().href
expect(socialMediaLink).toBe(socialMediaUrl)
form = wrapper.find('form')
input = wrapper.find('input#addSocialMedia')
submitButton = wrapper.find('button')
})
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({
data: { DeleteSocialMeda: { id: 's1', url: socialMediaUrl } },
}),
},
$toast: {
error: jest.fn(),
success: jest.fn(),
},
}
getters = {
'auth/user': () => {
return {
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}
},
}
it('requires the link to be a valid url', () => {
input.setValue('some value')
form.trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('displays a trash sympol after a social media and allows the user to delete it', () => {
wrapper = Wrapper()
const deleteSelector = wrapper.find({ name: 'delete' })
expect(deleteSelector).toEqual({ selector: 'Component' })
const icon = wrapper.find({ name: 'trash' })
icon.trigger('click')
expect(mocks.$apollo.mutate).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 flushPromises()
expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
})
describe('success', () => {
beforeEach(() => {
mocks.$apollo.mutate.mockResolvedValue({
data: { CreateSocialMeda: { id: 's2', url: newSocialMediaUrl } },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
})
it('sends the new url to the backend', () => {
const expected = expect.objectContaining({
variables: { url: newSocialMediaUrl },
})
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('clears the form', async () => {
await flushPromises()
expect(input.value).toBe(undefined)
expect(submitButton.vm.$attrs.disabled).toBe(true)
})
})
})
describe('currentUser does not have a social media account linked', () => {
it('allows a user to add a social media link', () => {
describe('given existing social media links', () => {
beforeEach(() => {
getters = {
'auth/user': () => ({
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}),
}
wrapper = Wrapper()
wrapper.find('form').trigger('submit')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
form = wrapper.find('form')
})
it('displays the links', () => {
expect(wrapper.find(`img[src="${faviconUrl}"]`).exists()).toBe(true)
expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
expect(wrapper.find('a[name="edit"]').exists()).toBe(true)
expect(wrapper.find('a[name="delete"]').exists()).toBe(true)
})
it('does not accept a duplicate url', () => {
input = wrapper.find('input#addSocialMedia')
input.setValue(socialMediaUrl)
form.trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
describe('editing social media link', () => {
beforeEach(() => {
const editButton = wrapper.find('a[name="edit"]')
editButton.trigger('click')
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', () => {
const expected = expect.objectContaining({
variables: { id: 's1', url: newSocialMediaUrl },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('allows the user to cancel editing', () => {
const cancelButton = wrapper.find('button#cancel')
cancelButton.trigger('click')
expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
})
})
describe('deleting social media link', () => {
beforeEach(() => {
const deleteButton = wrapper.find('a[name="delete"]')
deleteButton.trigger('click')
})
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)
})
})
})
})

View File

@ -16,14 +16,20 @@
<ds-list-item v-for="link in socialMediaLinks" :key="link.id">
<ds-input
v-if="editingLink.id === link.id"
model="socialMediaLink"
id="editSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<template v-else>
<a :href="link.url" target="_blank">
<img :src="link.favicon | proxyApiUrl" alt="Link:" width="16" height="16" />
<img
:src="link.favicon"
alt="Link:"
height="16"
width="16"
/>
{{ link.url }}
</a>
<span class="divider">|</span>
@ -51,7 +57,8 @@
<ds-space margin-top="base">
<ds-input
v-if="!editingLink.id"
model="socialMediaLink"
id="addSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
@ -59,7 +66,12 @@
<ds-button primary :disabled="disabled">
{{ editingLink.id ? $t('actions.save') : $t('settings.social-media.submit') }}
</ds-button>
<ds-button v-if="editingLink.id" ghost @click="handleCancel()">
<ds-button
v-if="editingLink.id"
id="cancel"
ghost
@click="handleCancel()"
>
{{ $t('actions.cancel') }}
</ds-button>
</ds-space>
@ -77,10 +89,10 @@ export default {
data() {
return {
formData: {
socialMediaLink: '',
socialMediaUrl: '',
},
formSchema: {
socialMediaLink: {
socialMediaUrl: {
type: 'url',
message: this.$t('common.validations.url'),
},
@ -94,10 +106,10 @@ export default {
currentUser: 'auth/user',
}),
socialMediaLinks() {
const domainRegex = /^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g
const { socialMedia = [] } = this.currentUser
return socialMedia.map(socialMedia => {
const { id, url } = socialMedia
const [domain] = url.match(/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g) || []
return socialMedia.map(({ id, url }) => {
const [domain] = url.match(domainRegex) || []
const favicon = domain ? `${domain}/favicon.ico` : null
return { id, url, favicon }
})
@ -109,19 +121,19 @@ export default {
}),
handleCancel() {
this.editingLink = {}
this.formData.socialMediaLink = ''
this.formData.socialMediaUrl = ''
this.disabled = true
},
handleEditSocialMedia(link) {
this.editingLink = link
this.formData.socialMediaLink = link.url
this.formData.socialMediaUrl = link.url
this.disabled = false
},
handleInput(data) {
this.disabled = true
},
handleInputValid(data) {
if (data.socialMediaLink.length < 1) {
if (data.socialMediaUrl.length < 1) {
this.disabled = true
} else {
this.disabled = false
@ -160,7 +172,7 @@ export default {
},
async handleSubmitSocialMedia() {
const isEditing = !!this.editingLink.id
const url = this.formData.socialMediaLink
const url = this.formData.socialMediaUrl
const duplicateUrl = this.socialMediaLinks.find(link => link.url === url);
if (duplicateUrl && duplicateUrl.id !== this.editingLink.id) {
@ -175,7 +187,7 @@ export default {
}
}
`
let variables = { url }
const variables = { url }
let successMessage = this.$t('settings.social-media.successAdd')
if (isEditing) {
@ -205,7 +217,7 @@ export default {
})
this.$toast.success(successMessage)
this.formData.socialMediaLink = ''
this.formData.socialMediaUrl = ''
this.disabled = true
this.editingLink = {}

File diff suppressed because it is too large Load Diff