2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-18 05:20:34 +03:00

feat: major refactor, cleanup and jest tests

This commit is contained in:
pimlie
2019-02-09 21:45:22 +01:00
parent 9dfb001d4e
commit 5d64d43862
61 changed files with 8598 additions and 822 deletions
+79
View File
@@ -0,0 +1,79 @@
import _getMetaInfo from '../src/shared/getMetaInfo'
import { mount, defaultOptions, loadVueMetaPlugin } from './utils'
import GoodbyeWorld from './fixtures/goodbye-world.vue'
import HelloWorld from './fixtures/hello-world.vue'
import KeepAlive from './fixtures/keep-alive.vue'
const getMetaInfo = component => _getMetaInfo(defaultOptions, component)
describe('client', () => {
let Vue
beforeAll(() => (Vue = loadVueMetaPlugin()))
test('meta-info refreshed on component\'s data change', () => {
const wrapper = mount(HelloWorld, { localVue: Vue })
let metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Hello World')
wrapper.setData({ title: 'Goodbye World' })
metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Goodbye World')
})
test('child meta-info removed when child is toggled', () => {
const wrapper = mount(GoodbyeWorld, { localVue: Vue })
let metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Hello World')
wrapper.setData({ childVisible: false })
metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Goodbye World')
wrapper.setData({ childVisible: true })
metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Hello World')
})
test('child meta-info removed when keep-alive child is toggled', () => {
const wrapper = mount(KeepAlive, { localVue: Vue })
let metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Hello World')
wrapper.setData({ childVisible: false })
metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Alive World')
wrapper.setData({ childVisible: true })
metaInfo = getMetaInfo(wrapper.vm)
expect(metaInfo.title).toEqual('Hello World')
})
test('meta-info is removed when destroyed', () => {
const parentComponent = new Vue({ render: h => h('div') })
const wrapper = mount(HelloWorld, { localVue: Vue, parentComponent })
let metaInfo = getMetaInfo(wrapper.vm.$parent)
expect(metaInfo.title).toEqual('Hello World')
wrapper.destroy()
jest.runAllTimers()
metaInfo = getMetaInfo(wrapper.vm.$parent)
expect(metaInfo.title).toEqual('')
})
test('meta-info can be rendered with inject', () => {
const wrapper = mount(HelloWorld, { localVue: Vue })
const metaInfo = wrapper.vm.$meta().inject()
expect(metaInfo.title.text()).toEqual('<title data-vue-meta="true">Hello World</title>')
})
})
+33
View File
@@ -0,0 +1,33 @@
<template>
<html {{ head.headAttrs.text() }}>
<head></head>
bla
</html>
</template>
<script>
export default {
metaInfo() {
return {
title: this.title
}
},
data() {
return {
title: 'Hello World',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' }
]
}
},
computed: {
head() {
return meta.inject()
}
}
}
</script>
+26
View File
@@ -0,0 +1,26 @@
<template>
<div>
<hello-world v-if="childVisible"></hello-world>
</div>
</template>
<script>
import HelloWorld from './hello-world.vue'
export default {
components: {
HelloWorld
},
metaInfo() {
return {
title: this.title,
}
},
data() {
return {
childVisible: true,
title: 'Goodbye World'
}
}
}
</script>
+24
View File
@@ -0,0 +1,24 @@
<template>
<div>Test</div>
</template>
<script>
export default {
metaInfo() {
return {
title: this.title
}
},
data() {
return {
title: 'Hello World',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' }
]
}
}
}
</script>
+28
View File
@@ -0,0 +1,28 @@
<template>
<div>
<keep-alive>
<hello-world v-if="childVisible"></hello-world>
</keep-alive>
</div>
</template>
<script>
import HelloWorld from './hello-world.vue'
export default {
components: {
HelloWorld
},
metaInfo() {
return {
title: this.title,
}
},
data() {
return {
childVisible: true,
title: 'Alive World'
}
}
}
</script>
+61
View File
@@ -0,0 +1,61 @@
import _generateServerInjector from '../src/server/generateServerInjector'
import { defaultOptions } from './utils'
import metaInfoData from './utils/meta-info-data'
const generateServerInjector = (type, data) => _generateServerInjector(defaultOptions, type, data)
describe('generators', () => {
Object.keys(metaInfoData).forEach((type) => {
const typeTests = metaInfoData[type]
const testCases = {
add: (tags) => {
let html = tags.text()
// ssr only returns the attributes, convert to full tag
if (['htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
html = `<${type.substr(0, 4)} ${html}>`
}
typeTests.add.expect.forEach((expected) => {
expect(html).toContain(expected)
})
}
}
describe(`${type} type tests`, () => {
Object.keys(typeTests).forEach((action) => {
const testInfo = typeTests[action]
// return when no test case available
if (!testCases[action] && !testInfo.test) {
return
}
const defaultTestFn = () => {
const tags = generateServerInjector(type, testInfo.data)
testCases[action](tags)
return tags
}
let testFn
if (testInfo.test) {
testFn = testInfo.test('server', defaultTestFn)
if (testFn === true) {
testFn = defaultTestFn
}
} else {
testFn = defaultTestFn
}
if (testFn && typeof testFn === 'function') {
test.only(`${action} a tag`, () => {
expect.hasAssertions()
testFn()
})
}
})
})
})
})
-76
View File
@@ -1,76 +0,0 @@
import Vue from 'vue'
import getComponentOption from '../src/shared/getComponentOption'
describe('getComponentOption', () => {
const container = document.createElement('div')
let component
afterEach(() => component.$destroy())
it('returns an empty object when no matching options are found', () => {
component = new Vue()
const mergedOption = getComponentOption({ component, option: 'noop' })
expect(mergedOption).to.eql({})
})
it('fetches the given option from the given component', () => {
component = new Vue({ someOption: 'foo' })
const mergedOption = getComponentOption({ component, option: 'someOption' })
expect(mergedOption).to.eql('foo')
})
it('calls a function option, injecting the component as context', () => {
component = new Vue({
name: 'foobar',
someFunc () {
return this.$options.name
}
})
const mergedOption = getComponentOption({ component, option: 'someFunc' })
expect(mergedOption).to.eql('foobar')
})
it('fetches deeply nested component options and merges them', () => {
Vue.component('merge-child', { template: '<div></div>', foo: { bar: 'baz' } })
component = new Vue({
foo: { fizz: 'buzz' },
render: (h) => h('div', null, [h('merge-child')]),
el: container
})
const mergedOption = getComponentOption({ component, option: 'foo', deep: true })
expect(mergedOption).to.eql({ bar: 'baz', fizz: 'buzz' })
})
it('allows for a custom array merge strategy', () => {
Vue.component('array-child', {
template: '<div></div>',
foo: [
{ name: 'flower', content: 'rose' }
]
})
component = new Vue({
render: (h) => h('div', null, [h('array-child')]),
foo: [
{ name: 'flower', content: 'tulip' }
],
el: container
})
const mergedOption = getComponentOption({
component,
option: 'foo',
deep: true,
arrayMerge (target, source) {
return target.concat(source)
}
})
expect(mergedOption).to.eql([
{ name: 'flower', content: 'tulip' },
{ name: 'flower', content: 'rose' }
])
})
})
+76
View File
@@ -0,0 +1,76 @@
import getComponentOption from '../src/shared/getComponentOption'
import { getVue } from './utils'
describe('getComponentOption', () => {
let Vue
beforeAll(() => (Vue = getVue()))
it('returns an empty object when no matching options are found', () => {
const component = new Vue()
const mergedOption = getComponentOption({ component, keyName: 'noop' })
expect(mergedOption).toEqual({})
})
it('fetches the given option from the given component', () => {
const component = new Vue({ someOption: 'foo' })
const mergedOption = getComponentOption({ component, keyName: 'someOption' })
expect(mergedOption).toEqual('foo')
})
it('calls a function option, injecting the component as context', () => {
const component = new Vue({
name: 'Foobar',
someFunc() {
return this.$options.name
}
})
const mergedOption = getComponentOption({ component, keyName: 'someFunc' })
// TODO: Should this be foobar or Foobar
expect(mergedOption).toEqual('Foobar')
})
it('fetches deeply nested component options and merges them', () => {
Vue.component('merge-child', { render: h => h('div'), foo: { bar: 'baz' } })
const component = new Vue({
foo: { fizz: 'buzz' },
el: document.createElement('div'),
render: h => h('div', null, [h('merge-child')])
})
const mergedOption = getComponentOption({ component, keyName: 'foo', deep: true })
expect(mergedOption).toEqual({ bar: 'baz', fizz: 'buzz' })
})
it('allows for a custom array merge strategy', () => {
Vue.component('array-child', {
render: h => h('div'),
foo: [
{ name: 'flower', content: 'rose' }
]
})
const component = new Vue({
foo: [
{ name: 'flower', content: 'tulip' }
],
el: document.createElement('div'),
render: h => h('div', null, [h('array-child')])
})
const mergedOption = getComponentOption({
component,
keyName: 'foo',
deep: true,
arrayMerge(target, source) {
return target.concat(source)
}
})
expect(mergedOption).toEqual([
{ name: 'flower', content: 'tulip' },
{ name: 'flower', content: 'rose' }
])
})
})
@@ -1,35 +1,17 @@
import Vue from 'vue'
import _getMetaInfo from '../src/shared/getMetaInfo'
import {
VUE_META_ATTRIBUTE,
VUE_META_CONTENT_KEY,
VUE_META_KEY_NAME,
VUE_META_SERVER_RENDERED_ATTRIBUTE,
VUE_META_TAG_LIST_ID_KEY_NAME,
VUE_META_TEMPLATE_KEY_NAME
} from '../src/shared/constants'
import { defaultOptions, loadVueMetaPlugin } from './utils'
// set some default options
const defaultOptions = {
keyName: VUE_META_KEY_NAME,
attribute: VUE_META_ATTRIBUTE,
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
contentKeyName: VUE_META_CONTENT_KEY,
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
}
const getMetaInfo = _getMetaInfo(defaultOptions)
const getMetaInfo = component => _getMetaInfo(defaultOptions, component)
describe('getMetaInfo', () => {
// const container = document.createElement('div')
let component
let Vue
afterEach(() => component.$destroy())
beforeAll(() => (Vue = loadVueMetaPlugin()))
it('returns appropriate defaults when no meta info is found', () => {
component = new Vue()
expect(getMetaInfo(component)).to.eql({
test('returns appropriate defaults when no meta info is found', () => {
const component = new Vue()
expect(getMetaInfo(component)).toEqual({
title: '',
titleChunk: '',
titleTemplate: '%s',
@@ -47,8 +29,8 @@ describe('getMetaInfo', () => {
})
})
it('returns metaInfo when used in component', () => {
component = new Vue({
test('returns metaInfo when used in component', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -56,7 +38,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -75,8 +58,37 @@ describe('getMetaInfo', () => {
__dangerouslyDisableSanitizersByTagID: {}
})
})
it('removes duplicate metaInfo in same component', () => {
component = new Vue({
test('converts base tag to array', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
base: { href: 'href' }
}
})
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
htmlAttrs: {},
headAttrs: {},
bodyAttrs: {},
meta: [],
base: [
{ href: 'href' }
],
link: [],
style: [],
script: [],
noscript: [],
__dangerouslyDisableSanitizers: [],
__dangerouslyDisableSanitizersByTagID: {}
})
})
test('removes duplicate metaInfo in same component', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -93,7 +105,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -117,8 +130,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses string titleTemplates', () => {
component = new Vue({
test('properly uses string titleTemplates', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
titleTemplate: '%s World',
@@ -127,7 +140,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello World',
titleChunk: 'Hello',
titleTemplate: '%s World',
@@ -147,10 +161,10 @@ describe('getMetaInfo', () => {
})
})
it('properly uses function titleTemplates', () => {
test('properly uses function titleTemplates', () => {
const titleTemplate = chunk => `${chunk} Function World`
component = new Vue({
const component = new Vue({
metaInfo: {
title: 'Hello',
titleTemplate,
@@ -159,7 +173,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello Function World',
titleChunk: 'Hello',
titleTemplate,
@@ -179,12 +194,12 @@ describe('getMetaInfo', () => {
})
})
it('has the proper `this` binding when using function titleTemplates', () => {
test('has the proper `this` binding when using function titleTemplates', () => {
const titleTemplate = function (chunk) {
return `${chunk} ${this.helloWorldText}`
}
component = new Vue({
const component = new Vue({
metaInfo: {
title: 'Hello',
titleTemplate,
@@ -192,13 +207,14 @@ describe('getMetaInfo', () => {
{ charset: 'utf-8' }
]
},
data () {
data() {
return {
helloWorldText: 'Function World'
}
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello Function World',
titleChunk: 'Hello',
titleTemplate,
@@ -218,8 +234,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses string meta templates', () => {
component = new Vue({
test('properly uses string meta templates', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -232,7 +248,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -256,8 +273,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses function meta templates', () => {
component = new Vue({
test('properly uses function meta templates', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -270,7 +287,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -294,8 +312,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses content only if template is not defined', () => {
component = new Vue({
test('properly uses content only if template is not defined', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -307,7 +325,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -331,8 +350,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses content only if template is null', () => {
component = new Vue({
test('properly uses content only if template is null', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -345,7 +364,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -369,8 +389,8 @@ describe('getMetaInfo', () => {
})
})
it('properly uses content only if template is false', () => {
component = new Vue({
test('properly uses content only if template is false', () => {
const component = new Vue({
metaInfo: {
title: 'Hello',
meta: [
@@ -383,7 +403,8 @@ describe('getMetaInfo', () => {
]
}
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -407,9 +428,9 @@ describe('getMetaInfo', () => {
})
})
it('properly uses meta templates with one-level-deep nested children content', () => {
test('properly uses meta templates with one-level-deep nested children content', () => {
Vue.component('merge-child', {
template: '<div></div>',
render: h => h('div'),
metaInfo: {
title: 'Hello',
meta: [
@@ -422,7 +443,7 @@ describe('getMetaInfo', () => {
}
})
component = new Vue({
const component = new Vue({
metaInfo: {
meta: [
{
@@ -433,11 +454,11 @@ describe('getMetaInfo', () => {
}
]
},
render: (h) => h('div', null, [h('merge-child')]),
el: document.createElement('div')
el: document.createElement('div'),
render: h => h('div', null, [h('merge-child')])
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -463,9 +484,9 @@ describe('getMetaInfo', () => {
// TODO: Still failing :( Child template won't be applied if child has no content as well
it('properly uses meta templates with one-level-deep nested children template', () => {
test('properly uses meta templates with one-level-deep nested children template', () => {
Vue.component('merge-child', {
template: '<div></div>',
render: h => h('div'),
metaInfo: {
title: 'Hello',
meta: [
@@ -478,7 +499,7 @@ describe('getMetaInfo', () => {
}
})
component = new Vue({
const component = new Vue({
metaInfo: {
meta: [
{
@@ -489,11 +510,11 @@ describe('getMetaInfo', () => {
}
]
},
render: (h) => h('div', null, [h('merge-child')]),
el: document.createElement('div')
el: document.createElement('div'),
render: h => h('div', null, [h('merge-child')])
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
@@ -517,9 +538,9 @@ describe('getMetaInfo', () => {
})
})
it('properly uses meta templates with one-level-deep nested children template and content', () => {
test('properly uses meta templates with one-level-deep nested children template and content', () => {
Vue.component('merge-child', {
template: '<div></div>',
render: h => h('div'),
metaInfo: {
title: 'Hello',
meta: [
@@ -533,7 +554,7 @@ describe('getMetaInfo', () => {
}
})
component = new Vue({
const component = new Vue({
metaInfo: {
meta: [
{
@@ -544,11 +565,11 @@ describe('getMetaInfo', () => {
}
]
},
render: (h) => h('div', null, [h('merge-child')]),
el: document.createElement('div')
el: document.createElement('div'),
render: h => h('div', null, [h('merge-child')])
})
expect(getMetaInfo(component)).to.eql({
expect(getMetaInfo(component)).toEqual({
title: 'Hello',
titleChunk: 'Hello',
titleTemplate: '%s',
-4
View File
@@ -1,4 +0,0 @@
const testsContext = require.context('.', true, /\.spec$/)
const srcContext = require.context('../src', true, /\.js$/)
testsContext.keys().forEach(testsContext)
srcContext.keys().forEach(srcContext)
-33
View File
@@ -1,33 +0,0 @@
import Vue from 'vue'
import VueMeta from '../src/shared/plugin'
import {
VUE_META_KEY_NAME,
VUE_META_ATTRIBUTE,
VUE_META_SERVER_RENDERED_ATTRIBUTE,
VUE_META_TAG_LIST_ID_KEY_NAME
} from '../src/shared/constants'
describe('plugin', () => {
Vue.use(VueMeta, {
keyName: VUE_META_KEY_NAME,
attribute: VUE_META_ATTRIBUTE,
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
})
it('adds $meta() to Vue prototype', () => {
const instance = new Vue()
expect(instance.$meta).to.be.a('function')
})
it('components have _hasMetaInfo set to true', () => {
const Component = Vue.component('test-component', {
template: '<div>Test</div>',
[VUE_META_KEY_NAME]: {
title: 'helloworld'
}
})
const vm = new Vue(Component).$mount()
expect(vm._hasMetaInfo).to.equal(true)
})
})
+32
View File
@@ -0,0 +1,32 @@
import { mount, defaultOptions, VueMetaPlugin, loadVueMetaPlugin } from './utils'
jest.mock('../package.json', () => ({
version: 'test-version'
}))
describe('plugin', () => {
let Vue
beforeAll(() => (Vue = loadVueMetaPlugin()))
test('is loaded', () => {
const instance = new Vue()
expect(instance.$meta).toEqual(expect.any(Function))
})
test('component has _hasMetaInfo set to true', () => {
const Component = Vue.component('test-component', {
template: '<div>Test</div>',
[defaultOptions.keyName]: {
title: 'Hello World'
}
})
const { vm } = mount(Component, { localVue: Vue })
expect(vm._hasMetaInfo).toBe(true)
})
test('plugin sets package version', () => {
expect(VueMetaPlugin.version).toBe('test-version')
})
})
+97
View File
@@ -0,0 +1,97 @@
import _updateClientMetaInfo from '../src/client/updateClientMetaInfo'
import { defaultOptions } from './utils'
import metaInfoData from './utils/meta-info-data'
const updateClientMetaInfo = (type, data) => _updateClientMetaInfo(defaultOptions, { [type]: data })
describe('updaters', () => {
let html
beforeAll(() => {
html = document.getElementsByTagName('html')[0]
// remove default meta charset
Array.from(html.getElementsByTagName('meta')).forEach(el => el.parentNode.removeChild(el))
})
Object.keys(metaInfoData).forEach((type) => {
const typeTests = metaInfoData[type]
const testCases = {
add: (tags) => {
typeTests.add.expect.forEach((expected, index) => {
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
expect(tags.addedTags[type][index].outerHTML).toBe(expected)
}
expect(html.outerHTML).toContain(expected)
})
},
change: (tags) => {
typeTests.add.expect.forEach((expected, index) => {
if (!typeTests.change.expect.includes(expected)) {
expect(html.outerHTML).not.toContain(expected)
}
})
typeTests.change.expect.forEach((expected, index) => {
if (!['title', 'htmlAttrs', 'headAttrs', 'bodyAttrs'].includes(type)) {
expect(tags.addedTags[type][index].outerHTML).toBe(expected)
}
expect(html.outerHTML).toContain(expected)
})
},
remove: (tags) => {
// TODO: i'd expect tags.removedTags to be populated
typeTests.add.expect.forEach((expected, index) => {
expect(html.outerHTML).not.toContain(expected)
})
typeTests.change.expect.forEach((expected, index) => {
expect(html.outerHTML).not.toContain(expected)
})
expect(html.outerHTML).not.toContain(`<${type}`)
}
}
describe(`${type} type tests`, () => {
Object.keys(typeTests).forEach((action) => {
const testInfo = typeTests[action]
// return when no test case available
if (!testCases[action] && !testInfo.test) {
return
}
const defaultTestFn = () => {
const tags = updateClientMetaInfo(type, testInfo.data)
if (testCases[action]) {
testCases[action](tags)
}
return tags
}
let testFn
if (testInfo.test) {
testFn = testInfo.test('client', defaultTestFn)
if (testFn === true) {
testFn = defaultTestFn
}
} else {
testFn = defaultTestFn
}
if (testFn && typeof testFn === 'function') {
test.only(`${action} a tag`, () => {
expect.hasAssertions()
testFn()
})
}
})
})
})
})
+37
View File
@@ -0,0 +1,37 @@
import { mount, createLocalVue } from '@vue/test-utils'
import { renderToString } from '@vue/server-test-utils'
import VueMetaPlugin from '../../src'
import {
VUE_META_ATTRIBUTE,
VUE_META_CONTENT_KEY,
VUE_META_KEY_NAME,
VUE_META_SERVER_RENDERED_ATTRIBUTE,
VUE_META_TAG_LIST_ID_KEY_NAME,
VUE_META_TEMPLATE_KEY_NAME
} from '../../src/shared/constants'
export {
mount,
renderToString,
VueMetaPlugin
}
export const defaultOptions = {
keyName: VUE_META_KEY_NAME,
attribute: VUE_META_ATTRIBUTE,
ssrAttribute: VUE_META_SERVER_RENDERED_ATTRIBUTE,
metaTemplateKeyName: VUE_META_TEMPLATE_KEY_NAME,
contentKeyName: VUE_META_CONTENT_KEY,
tagIDKeyName: VUE_META_TAG_LIST_ID_KEY_NAME
}
export function getVue() {
return createLocalVue()
}
export function loadVueMetaPlugin(options, localVue = getVue()) {
localVue.use(VueMetaPlugin, Object.assign({}, defaultOptions, options))
return localVue
}
+216
View File
@@ -0,0 +1,216 @@
import { defaultOptions } from './'
const metaInfoData = {
title: {
add: {
data: 'Hello World',
expect: ['<title data-vue-meta="true">Hello World</title>'],
test(side, defaultTest) {
if (side === 'client') {
// client side vue-meta uses document.title and doesnt change the html
return () => {
this.expect[0] = '<title>Hello World</title>'
const spy = jest.spyOn(document, 'title', 'set')
defaultTest()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith('Hello World')
}
} else {
return defaultTest
}
}
}
},
base: {
add: {
data: [{ href: 'href' }],
expect: ['<base data-vue-meta="true" href="href">']
},
change: {
data: [{ href: 'href2' }],
expect: ['<base data-vue-meta="true" href="href2">']
},
remove: {
data: [],
expect: ['']
}
},
meta: {
add: {
data: [{ charset: 'utf-8' }, { property: 'a', content: 'a' }],
expect: [
'<meta data-vue-meta="true" charset="utf-8">',
'<meta data-vue-meta="true" property="a" content="a">'
]
},
change: {
data: [
{ charset: 'utf-16' },
{ property: 'a', content: 'b' }
],
expect: [
'<meta data-vue-meta="true" charset="utf-16">',
'<meta data-vue-meta="true" property="a" content="b">'
]
},
// make sure elements that already exists are not unnecessarily updated
duplicate: {
data: [
{ charset: 'utf-16' },
{ property: 'a', content: 'c' }
],
expect: [
'<meta data-vue-meta="true" charset="utf-16">',
'<meta data-vue-meta="true" property="a" content="c">'
],
test(side, defaultTest) {
if (side === 'client') {
return () => {
const tags = defaultTest()
expect(tags.addedTags.meta.length).toBe(1)
// TODO: not sure if we really expect this
expect(tags.removedTags.meta.length).toBe(1)
}
}
}
},
remove: {
data: [],
expect: ['']
}
},
link: {
add: {
data: [{ rel: 'stylesheet', href: 'href' }],
expect: ['<link data-vue-meta="true" rel="stylesheet" href="href">']
},
change: {
data: [{ rel: 'stylesheet', href: 'href', media: 'screen' }],
expect: ['<link data-vue-meta="true" rel="stylesheet" href="href" media="screen">']
},
remove: {
data: [],
expect: ['']
}
},
style: {
add: {
data: [{ type: 'text/css', cssText: '.foo { color: red; }' }],
expect: ['<style data-vue-meta="true" type="text/css">.foo { color: red; }</style>']
},
change: {
data: [{ type: 'text/css', cssText: '.foo { color: blue; }' }],
expect: ['<style data-vue-meta="true" type="text/css">.foo { color: blue; }</style>']
},
remove: {
data: [],
expect: ['']
}
},
script: {
add: {
data: [
{ src: 'src', async: true, defer: true, [defaultOptions.tagIDKeyName]: 'content' },
{ src: 'src', async: true, defer: true, body: true }
],
expect: [
'<script data-vue-meta="true" src="src" async="true" defer="true" data-vmid="content"></script>',
'<script data-vue-meta="true" src="src" async="true" defer="true" data-body="true"></script>'
],
test(side, defaultTest) {
return () => {
if (side === 'client') {
const tags = defaultTest()
expect(tags.addedTags.script[0].parentNode.tagName).toBe('HEAD')
expect(tags.addedTags.script[1].parentNode.tagName).toBe('BODY')
} else {
// ssr doesnt generate data-body tags
const bodyScript = this.expect[1]
this.expect = [this.expect[0]]
const tags = defaultTest()
expect(tags.text()).not.toContain(bodyScript)
}
}
}
},
change: {
data: [{ src: 'src', async: true, defer: true, [defaultOptions.tagIDKeyName]: 'content2' }],
expect: ['<script data-vue-meta="true" src="src" async="true" defer="true" data-vmid="content2"></script>']
},
remove: {
data: [],
expect: ['']
}
},
noscript: {
add: {
data: [{ innerHTML: '<p>noscript</p>' }],
expect: ['<noscript data-vue-meta="true"><p>noscript</p></noscript>']
},
change: {
data: [{ innerHTML: '<p>noscript, no really</p>' }],
expect: ['<noscript data-vue-meta="true"><p>noscript, no really</p></noscript>']
},
remove: {
data: [],
expect: ['']
}
},
htmlAttrs: {
add: {
data: { foo: 'bar' },
expect: ['<html foo="bar" data-vue-meta="foo">']
},
change: {
data: { foo: 'baz' },
expect: ['<html foo="baz" data-vue-meta="foo">']
},
remove: {
data: {},
expect: ['<html>']
}
},
headAttrs: {
add: {
data: { foo: 'bar' },
expect: ['<head foo="bar" data-vue-meta="foo">']
},
change: {
data: { foo: 'baz' },
expect: ['<head foo="baz" data-vue-meta="foo">']
},
remove: {
data: {},
expect: ['<head>']
}
},
bodyAttrs: {
add: {
data: { foo: 'bar' },
expect: ['<body foo="bar" data-vue-meta="foo">']
},
change: {
data: { foo: 'baz' },
expect: ['<body foo="baz" data-vue-meta="foo">']
},
remove: {
data: {},
expect: ['<body>']
}
},
empty: {
add: {
data: [{}],
expect: [''],
test: side => side === 'server'
}
}
}
export default metaInfoData
+5
View File
@@ -0,0 +1,5 @@
import jsdom from 'jsdom-global'
jsdom()
jest.useFakeTimers()