mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-21 05:30:35 +03:00
test: add e2e tests
fix: boolean attributes client side
This commit is contained in:
committed by
Alexander Lichter
parent
a853ce3de7
commit
05b8891110
@@ -0,0 +1,163 @@
|
||||
import _getMetaInfo from '../../src/shared/getMetaInfo'
|
||||
import { mount, loadVueMetaPlugin, vmTick } from '../utils'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
import GoodbyeWorld from '../components/goodbye-world.vue'
|
||||
import HelloWorld from '../components/hello-world.vue'
|
||||
import KeepAlive from '../components/keep-alive.vue'
|
||||
import Changed from '../components/changed.vue'
|
||||
|
||||
const getMetaInfo = component => _getMetaInfo(defaultOptions, component)
|
||||
|
||||
jest.mock('../../src/shared/window', () => ({
|
||||
hasGlobalWindow: false
|
||||
}))
|
||||
|
||||
describe('client', () => {
|
||||
let Vue
|
||||
let html
|
||||
|
||||
beforeAll(() => {
|
||||
Vue = loadVueMetaPlugin()
|
||||
|
||||
// force using timers, jest cant mock rAF
|
||||
delete window.requestAnimationFrame
|
||||
delete window.cancelAnimationFrame
|
||||
|
||||
html = document.createElement('html')
|
||||
document._getElementsByTagName = document.getElementsByTagName
|
||||
jest.spyOn(document, 'getElementsByTagName').mockImplementation((tag) => {
|
||||
if (tag === 'html') {
|
||||
return [html]
|
||||
}
|
||||
|
||||
return document._getElementsByTagName(tag)
|
||||
})
|
||||
})
|
||||
|
||||
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>')
|
||||
})
|
||||
|
||||
test('doesnt update when ssr attribute is set', () => {
|
||||
html.setAttribute(defaultOptions.ssrAttribute, 'true')
|
||||
const wrapper = mount(HelloWorld, { localVue: Vue })
|
||||
|
||||
const { tags } = wrapper.vm.$meta().refresh()
|
||||
expect(tags).toBe(false)
|
||||
})
|
||||
|
||||
test('changed function is called', async () => {
|
||||
let context
|
||||
const changed = jest.fn(function () {
|
||||
context = this
|
||||
})
|
||||
|
||||
const wrapper = mount(Changed, { localVue: Vue, propsData: { changed } })
|
||||
|
||||
await vmTick(wrapper.vm)
|
||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
||||
// TODO: does changed need to run on initialization?
|
||||
expect(changed).toHaveBeenCalledTimes(1)
|
||||
|
||||
wrapper.setData({ childVisible: true })
|
||||
jest.runAllTimers()
|
||||
|
||||
expect(changed).toHaveBeenCalledTimes(2)
|
||||
expect(context._uid).toBe(wrapper.vm._uid)
|
||||
})
|
||||
|
||||
test('afterNavigation function is called', () => {
|
||||
const Vue = loadVueMetaPlugin(false, { refreshOnceOnNavigation: true })
|
||||
const afterNavigation = jest.fn()
|
||||
const component = Vue.component('nav-component', {
|
||||
render: h => h('div'),
|
||||
metaInfo: { afterNavigation }
|
||||
})
|
||||
|
||||
const guards = {}
|
||||
const wrapper = mount(component, {
|
||||
localVue: Vue,
|
||||
mocks: {
|
||||
$router: {
|
||||
beforeEach(fn) {
|
||||
guards.before = fn
|
||||
},
|
||||
afterEach(fn) {
|
||||
guards.after = fn
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(guards.before).toBeDefined()
|
||||
expect(guards.after).toBeDefined()
|
||||
|
||||
guards.before(null, null, () => {})
|
||||
expect(wrapper.vm.$root._vueMeta.paused).toBe(true)
|
||||
|
||||
guards.after()
|
||||
expect(afterNavigation).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,74 @@
|
||||
import _getMetaInfo from '../../src/shared/getMetaInfo'
|
||||
import { loadVueMetaPlugin } from '../utils'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
const getMetaInfo = (component, escapeSequences) => _getMetaInfo(defaultOptions, component, escapeSequences)
|
||||
|
||||
describe('escaping', () => {
|
||||
let Vue
|
||||
|
||||
beforeAll(() => (Vue = loadVueMetaPlugin()))
|
||||
|
||||
test('special chars are escaped unless disabled', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
htmlAttrs: { key: 1 },
|
||||
title: 'Hello & Goodbye',
|
||||
script: [{ innerHTML: 'Hello & Goodbye' }],
|
||||
__dangerouslyDisableSanitizers: ['script']
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component, [[/&/g, '&']])).toEqual({
|
||||
title: 'Hello & Goodbye',
|
||||
titleChunk: 'Hello & Goodbye',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {
|
||||
key: 1
|
||||
},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [{ innerHTML: 'Hello & Goodbye' }],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: ['script'],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('special chars are escaped unless disabled by vmid', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
script: [
|
||||
{ vmid: 'yescape', innerHTML: 'Hello & Goodbye' },
|
||||
{ vmid: 'noscape', innerHTML: 'Hello & Goodbye' }
|
||||
],
|
||||
__dangerouslyDisableSanitizersByTagID: { noscape: ['innerHTML'] }
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component, [[/&/g, '&']])).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [
|
||||
{ innerHTML: 'Hello & Goodbye', vmid: 'yescape' },
|
||||
{ innerHTML: 'Hello & Goodbye', vmid: 'noscape' }
|
||||
],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: { noscape: ['innerHTML'] }
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import _generateServerInjector from '../../src/server/generateServerInjector'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
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(`${action} a tag`, () => {
|
||||
expect.hasAssertions()
|
||||
testFn()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,127 @@
|
||||
import getComponentOption from '../../src/shared/getComponentOption'
|
||||
import inMetaInfoBranch from '../../src/shared/inMetaInfoBranch'
|
||||
import { mount, getVue, loadVueMetaPlugin } from '../utils'
|
||||
|
||||
describe('getComponentOption', () => {
|
||||
let Vue
|
||||
|
||||
beforeAll(() => (Vue = getVue()))
|
||||
|
||||
test('returns an empty object when no matching options are found', () => {
|
||||
const component = new Vue()
|
||||
const mergedOption = getComponentOption({ component, keyName: 'noop' })
|
||||
expect(mergedOption).toEqual({})
|
||||
})
|
||||
|
||||
test('fetches the given option from the given component', () => {
|
||||
const component = new Vue({ someOption: { foo: 'bar' } })
|
||||
const mergedOption = getComponentOption({ component, keyName: 'someOption' })
|
||||
expect(mergedOption.foo).toBeDefined()
|
||||
expect(mergedOption.foo).toEqual('bar')
|
||||
})
|
||||
|
||||
test('calls a function option, injecting the component as context', () => {
|
||||
const component = new Vue({
|
||||
name: 'Foobar',
|
||||
someFunc() {
|
||||
return { opt: this.$options.name }
|
||||
}
|
||||
})
|
||||
const mergedOption = getComponentOption({ component, keyName: 'someFunc' })
|
||||
// TODO: Should this be foobar or Foobar
|
||||
expect(mergedOption.opt).toBeDefined()
|
||||
expect(mergedOption.opt).toEqual('Foobar')
|
||||
})
|
||||
|
||||
test('fetches deeply nested component options and merges them', () => {
|
||||
const localVue = loadVueMetaPlugin(true, { keyName: 'foo' })
|
||||
localVue.component('merge-child', { render: h => h('div'), foo: { bar: 'baz' } })
|
||||
|
||||
const component = localVue.component('parent', {
|
||||
foo: { fizz: 'buzz' },
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
const wrapper = mount(component, { localVue })
|
||||
|
||||
const mergedOption = getComponentOption({ component: wrapper.vm, keyName: 'foo' })
|
||||
expect(mergedOption).toEqual({ bar: 'baz', fizz: 'buzz' })
|
||||
})
|
||||
|
||||
/* this undocumented functionality has been removed
|
||||
test('allows for a custom array merge strategy', () => {
|
||||
const localVue = loadVueMetaPlugin(false, { keyName: 'foo' })
|
||||
localVue.component('array-child', {
|
||||
render: h => h('div'),
|
||||
foo: {
|
||||
meta: [
|
||||
{ name: 'flower', content: 'rose' }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = localVue.component('parent', {
|
||||
foo: {
|
||||
meta: [
|
||||
{ name: 'flower', content: 'tulip' }
|
||||
]
|
||||
},
|
||||
render: h => h('div', null, [h('array-child')])
|
||||
})
|
||||
|
||||
const wrapper = mount(component, { localVue })
|
||||
|
||||
const mergedOption = getComponentOption({
|
||||
component: wrapper.vm,
|
||||
keyName: 'foo',
|
||||
arrayMerge(target, source) {
|
||||
return target.concat(source)
|
||||
}
|
||||
})
|
||||
|
||||
expect(mergedOption).toEqual({ meta: [
|
||||
{ name: 'flower', content: 'tulip' },
|
||||
{ name: 'flower', content: 'rose' }
|
||||
] })
|
||||
}) */
|
||||
|
||||
test('only traverses branches with metaInfo components', () => {
|
||||
const localVue = loadVueMetaPlugin(false, { keyName: 'foo' })
|
||||
|
||||
localVue.component('meta-child', {
|
||||
foo: { bar: 'baz' },
|
||||
render(h) {
|
||||
return h('div', this.$slots.default)
|
||||
}
|
||||
})
|
||||
|
||||
localVue.component('nometa-child', {
|
||||
render(h) {
|
||||
return h('div', this.$slots.default)
|
||||
}
|
||||
})
|
||||
|
||||
const component = localVue.component('parent', {
|
||||
render: h => h('div', null, [
|
||||
h('meta-child', null, [ h('nometa-child') ]),
|
||||
h('nometa-child', null, [ h('meta-child') ]),
|
||||
h('nometa-child')
|
||||
])
|
||||
})
|
||||
|
||||
const wrapper = mount(component, { localVue })
|
||||
|
||||
const mergedOption = getComponentOption({ component: wrapper.vm, keyName: 'foo' })
|
||||
|
||||
expect(mergedOption).toEqual({ bar: 'baz' })
|
||||
expect(wrapper.vm.$children[0]._vueMeta).toBe(true)
|
||||
expect(wrapper.vm.$children[1]._vueMeta).toBe(false)
|
||||
expect(wrapper.vm.$children[2]._vueMeta).toBeUndefined()
|
||||
|
||||
expect(inMetaInfoBranch(wrapper.vm.$children[0])).toBe(true)
|
||||
expect(inMetaInfoBranch(wrapper.vm.$children[0].$children[0])).toBe(false)
|
||||
expect(inMetaInfoBranch(wrapper.vm.$children[1])).toBe(true)
|
||||
expect(inMetaInfoBranch(wrapper.vm.$children[1].$children[0])).toBe(true)
|
||||
expect(inMetaInfoBranch(wrapper.vm.$children[2])).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,823 @@
|
||||
import _getMetaInfo from '../../src/shared/getMetaInfo'
|
||||
import { loadVueMetaPlugin } from '../utils'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
const getMetaInfo = component => _getMetaInfo(defaultOptions, component)
|
||||
|
||||
describe('getMetaInfo', () => {
|
||||
let Vue
|
||||
|
||||
beforeAll(() => (Vue = loadVueMetaPlugin()))
|
||||
|
||||
test('returns appropriate defaults when no meta info is found', () => {
|
||||
const component = new Vue()
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: '',
|
||||
titleChunk: '',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('returns metaInfo when used in component', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
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: [
|
||||
{
|
||||
vmid: 'a',
|
||||
property: 'a',
|
||||
content: 'a'
|
||||
},
|
||||
{
|
||||
vmid: 'a',
|
||||
property: 'a',
|
||||
content: 'b'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'a',
|
||||
property: 'a',
|
||||
content: 'a'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses string titleTemplates', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
titleTemplate: '%s World',
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello World',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s World',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses function titleTemplates', () => {
|
||||
const titleTemplate = chunk => `${chunk} Function World`
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
titleTemplate,
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello Function World',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate,
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('has the proper `this` binding when using function titleTemplates', () => {
|
||||
const titleTemplate = function (chunk) {
|
||||
return `${chunk} ${this.helloWorldText}`
|
||||
}
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
titleTemplate,
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
helloWorldText: 'Function World'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello Function World',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate,
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{ charset: 'utf-8' }
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses string meta templates', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: '%s - My page'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses function meta templates', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses content only if template is not defined', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses content only if template is null', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: null
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses content only if template is false', () => {
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: false
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses meta templates with one-level-deep nested children content', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title!'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title! - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses meta templates with one-level-deep nested children template', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - SHOULD NEVER HAPPEN`
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses meta templates with one-level-deep nested children template and content', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title!',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - SHOULD NEVER HAPPEN`
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title! - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('properly uses meta templates with one-level-deep nested children when parent has no template', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title!',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title'
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'An important title! - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('no errors when metaInfo returns nothing', () => {
|
||||
const component = new Vue({
|
||||
metaInfo() {},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: '',
|
||||
titleChunk: '',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('child can indicate its content should be ignored', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: undefined,
|
||||
bodyAttrs: {
|
||||
class: undefined
|
||||
},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
content: undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
bodyAttrs: {
|
||||
class: 'class'
|
||||
},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hello',
|
||||
titleChunk: 'Hello',
|
||||
titleTemplate: '%s',
|
||||
bodyAttrs: {
|
||||
class: 'class'
|
||||
},
|
||||
headAttrs: {},
|
||||
htmlAttrs: {},
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title - My page'
|
||||
}
|
||||
],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('child can indicate to remove parent vmids', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
title: 'Hi',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
content: null
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
meta: [
|
||||
{
|
||||
vmid: 'og:title',
|
||||
property: 'og:title',
|
||||
content: 'Test title',
|
||||
template: chunk => `${chunk} - My page`
|
||||
}
|
||||
]
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: 'Hi',
|
||||
titleChunk: 'Hi',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('attribute values can be an array', () => {
|
||||
Vue.component('merge-child', {
|
||||
render: h => h('div'),
|
||||
metaInfo: {
|
||||
bodyAttrs: {
|
||||
class: ['foo']
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const component = new Vue({
|
||||
metaInfo: {
|
||||
bodyAttrs: {
|
||||
class: ['bar']
|
||||
}
|
||||
},
|
||||
el: document.createElement('div'),
|
||||
render: h => h('div', null, [h('merge-child')])
|
||||
})
|
||||
|
||||
expect(getMetaInfo(component)).toEqual({
|
||||
title: '',
|
||||
titleChunk: '',
|
||||
titleTemplate: '%s',
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {
|
||||
class: ['bar', 'foo']
|
||||
},
|
||||
meta: [],
|
||||
base: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,112 @@
|
||||
import triggerUpdate from '../../src/client/triggerUpdate'
|
||||
import batchUpdate from '../../src/client/batchUpdate'
|
||||
import { mount, vmTick, VueMetaBrowserPlugin, loadVueMetaPlugin } from '../utils'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
jest.mock('../../src/client/triggerUpdate')
|
||||
jest.mock('../../src/client/batchUpdate')
|
||||
jest.mock('../../package.json', () => ({
|
||||
version: 'test-version'
|
||||
}))
|
||||
|
||||
describe('plugin', () => {
|
||||
let Vue
|
||||
|
||||
beforeEach(() => jest.clearAllMocks())
|
||||
beforeAll(() => (Vue = loadVueMetaPlugin(true)))
|
||||
|
||||
test('is loaded', () => {
|
||||
const instance = new Vue()
|
||||
expect(instance.$meta).toEqual(expect.any(Function))
|
||||
|
||||
expect(instance.$meta().inject).toEqual(expect.any(Function))
|
||||
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||
|
||||
expect(instance.$meta().inject()).toBeUndefined()
|
||||
expect(instance.$meta().refresh()).toBeDefined()
|
||||
|
||||
const options = instance.$meta().getOptions()
|
||||
expect(options).toBeDefined()
|
||||
expect(options.keyName).toBe(defaultOptions.keyName)
|
||||
})
|
||||
|
||||
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(VueMetaBrowserPlugin.version).toBe('test-version')
|
||||
})
|
||||
|
||||
test('updates can be paused and resumed', async () => {
|
||||
const _triggerUpdate = jest.requireActual('../../src/client/triggerUpdate').default
|
||||
const _batchUpdate = jest.requireActual('../../src/client/batchUpdate').default
|
||||
|
||||
const triggerUpdateSpy = triggerUpdate.mockImplementation(_triggerUpdate)
|
||||
const batchUpdateSpy = batchUpdate.mockImplementation(_batchUpdate)
|
||||
|
||||
const Component = Vue.component('test-component', {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.title
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
template: '<div>Test</div>'
|
||||
})
|
||||
|
||||
let title = 'first title'
|
||||
const wrapper = mount(Component, {
|
||||
localVue: Vue,
|
||||
propsData: {
|
||||
title
|
||||
}
|
||||
})
|
||||
|
||||
// no batchUpdate on initialization
|
||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(false)
|
||||
expect(wrapper.vm.$root._vueMeta.paused).toBeFalsy()
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
||||
jest.clearAllMocks()
|
||||
await vmTick(wrapper.vm)
|
||||
|
||||
title = 'second title'
|
||||
wrapper.setProps({ title })
|
||||
|
||||
// batchUpdate on normal update
|
||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
||||
expect(wrapper.vm.$root._vueMeta.paused).toBeFalsy()
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
jest.clearAllMocks()
|
||||
|
||||
wrapper.vm.$meta().pause()
|
||||
title = 'third title'
|
||||
wrapper.setProps({ title })
|
||||
|
||||
// no batchUpdate when paused
|
||||
expect(wrapper.vm.$root._vueMeta.initialized).toBe(true)
|
||||
expect(wrapper.vm.$root._vueMeta.paused).toBe(true)
|
||||
expect(triggerUpdateSpy).toHaveBeenCalledTimes(1)
|
||||
expect(batchUpdateSpy).not.toHaveBeenCalled()
|
||||
jest.clearAllMocks()
|
||||
|
||||
const { metaInfo } = wrapper.vm.$meta().resume()
|
||||
expect(metaInfo.title).toBe(title)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,84 @@
|
||||
import { mount, VueMetaServerPlugin, loadVueMetaPlugin } from '../utils'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
jest.mock('../../package.json', () => ({
|
||||
version: 'test-version'
|
||||
}))
|
||||
|
||||
describe('plugin', () => {
|
||||
let Vue
|
||||
|
||||
beforeEach(() => jest.clearAllMocks())
|
||||
beforeAll(() => (Vue = loadVueMetaPlugin()))
|
||||
|
||||
test('is loaded', () => {
|
||||
const instance = new Vue()
|
||||
expect(instance.$meta).toEqual(expect.any(Function))
|
||||
|
||||
expect(instance.$meta().inject).toEqual(expect.any(Function))
|
||||
expect(instance.$meta().refresh).toEqual(expect.any(Function))
|
||||
expect(instance.$meta().getOptions).toEqual(expect.any(Function))
|
||||
|
||||
expect(instance.$meta().inject()).toBeDefined()
|
||||
expect(instance.$meta().refresh()).toBeDefined()
|
||||
|
||||
const options = instance.$meta().getOptions()
|
||||
expect(options).toBeDefined()
|
||||
expect(options.keyName).toBe(defaultOptions.keyName)
|
||||
})
|
||||
|
||||
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(VueMetaServerPlugin.version).toBe('test-version')
|
||||
})
|
||||
|
||||
test('prints deprecation warning once when using _hasMetaInfo', () => {
|
||||
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
|
||||
const Component = Vue.component('test-component', {
|
||||
template: '<div>Test</div>',
|
||||
[defaultOptions.keyName]: {
|
||||
title: 'Hello World'
|
||||
}
|
||||
})
|
||||
|
||||
Vue.config.devtools = true
|
||||
const { vm } = mount(Component, { localVue: Vue })
|
||||
|
||||
expect(vm._hasMetaInfo).toBe(true)
|
||||
expect(warn).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(vm._hasMetaInfo).toBe(true)
|
||||
expect(warn).toHaveBeenCalledTimes(1)
|
||||
warn.mockRestore()
|
||||
})
|
||||
|
||||
test('can use hasMetaInfo export', () => {
|
||||
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
|
||||
const Component = Vue.component('test-component', {
|
||||
template: '<div>Test</div>',
|
||||
[defaultOptions.keyName]: {
|
||||
title: 'Hello World'
|
||||
}
|
||||
})
|
||||
|
||||
const { vm } = mount(Component, { localVue: Vue })
|
||||
|
||||
expect(VueMetaServerPlugin.hasMetaInfo(vm)).toBe(true)
|
||||
expect(warn).not.toHaveBeenCalled()
|
||||
|
||||
warn.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
import { ensureIsArray } from '../../src/shared/ensure'
|
||||
import setOptions from '../../src/shared/options'
|
||||
import { hasGlobalWindowFn } from '../../src/shared/window'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
describe('shared', () => {
|
||||
test('ensureIsArray ensures var is array', () => {
|
||||
let a = { p: 1 }
|
||||
expect(ensureIsArray(a)).toEqual([])
|
||||
|
||||
a = 1
|
||||
expect(ensureIsArray(a)).toEqual([])
|
||||
|
||||
a = [1]
|
||||
expect(ensureIsArray(a)).toBe(a)
|
||||
})
|
||||
|
||||
test('ensureIsArray ensures obj prop is array', () => {
|
||||
const a = { p: 1 }
|
||||
expect(ensureIsArray(a, 'p')).toEqual({ p: [] })
|
||||
})
|
||||
|
||||
test('no error when window is not defined', () => {
|
||||
expect(hasGlobalWindowFn()).toBe(false)
|
||||
})
|
||||
|
||||
test('can use setOptions', () => {
|
||||
const keyName = 'MY KEY'
|
||||
let options = { keyName }
|
||||
options = setOptions(options)
|
||||
|
||||
expect(options.keyName).toBe(keyName)
|
||||
expect(options.contentKeyName).toBeDefined()
|
||||
expect(options.contentKeyName).toBe(defaultOptions.contentKeyName)
|
||||
})
|
||||
|
||||
test('setOptions warns when afterNavigation not fn', () => {
|
||||
const warn = jest.spyOn(console, 'warn').mockImplementation(noop)
|
||||
|
||||
let options = { afterNavigation: true }
|
||||
options = setOptions(options)
|
||||
|
||||
expect(warn).toHaveBeenCalledTimes(1)
|
||||
expect(options.afterNavigation).toBeUndefined()
|
||||
|
||||
warn.mockRestore()
|
||||
})
|
||||
test('setOptions sets refreshOnceOnNavigation when afterNavigation defined', () => {
|
||||
const warn = jest.spyOn(console, 'warn').mockImplementation(noop)
|
||||
|
||||
let options = { afterNavigation: noop }
|
||||
options = setOptions(options)
|
||||
|
||||
expect(warn).not.toHaveBeenCalled()
|
||||
expect(options.refreshOnceOnNavigation).toBe(true)
|
||||
|
||||
warn.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,97 @@
|
||||
import _updateClientMetaInfo from '../../src/client/updateClientMetaInfo'
|
||||
import { defaultOptions } from '../utils/constants'
|
||||
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(`${action} a tag`, () => {
|
||||
expect.hasAssertions()
|
||||
testFn()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user