mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-23 13:00:33 +03:00
feat: add support for setting attributes from multiple apps
chore: improve build size
This commit is contained in:
@@ -69,6 +69,7 @@ export default function createApp () {
|
|||||||
return {
|
return {
|
||||||
title: 'Boring Title',
|
title: 'Boring Title',
|
||||||
htmlAttrs: { amp: true },
|
htmlAttrs: { amp: true },
|
||||||
|
bodyAttrs: { class: 'main-app' },
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
skip: this.count < 1,
|
skip: this.count < 1,
|
||||||
@@ -116,6 +117,14 @@ export default function createApp () {
|
|||||||
users: process.server ? [] : window.users
|
users: process.server ? [] : window.users
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
const { set, remove } = this.$meta().addApp('client-only')
|
||||||
|
set({
|
||||||
|
bodyAttrs: { class: 'client-only' }
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => remove(), 3000)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadCallback () {
|
loadCallback () {
|
||||||
this.count++
|
this.count++
|
||||||
@@ -140,6 +149,7 @@ export default function createApp () {
|
|||||||
|
|
||||||
const { set } = app.$meta().addApp('custom')
|
const { set } = app.$meta().addApp('custom')
|
||||||
set({
|
set({
|
||||||
|
bodyAttrs: { class: 'custom-app' },
|
||||||
meta: [{ charset: 'utf-8' }]
|
meta: [{ charset: 'utf-8' }]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ const ChildComponent = {
|
|||||||
metaInfo () {
|
metaInfo () {
|
||||||
return {
|
return {
|
||||||
title: `${this.page} - ${this.date && this.date.toTimeString()}`,
|
title: `${this.page} - ${this.date && this.date.toTimeString()}`,
|
||||||
|
bodyAttrs: {
|
||||||
|
class: 'child-component'
|
||||||
|
},
|
||||||
afterNavigation () {
|
afterNavigation () {
|
||||||
metaUpdated = 'yes'
|
metaUpdated = 'yes'
|
||||||
}
|
}
|
||||||
@@ -82,6 +85,9 @@ const app = new Vue(App)
|
|||||||
const { set, remove } = app.$meta().addApp('custom')
|
const { set, remove } = app.$meta().addApp('custom')
|
||||||
|
|
||||||
set({
|
set({
|
||||||
|
bodyAttrs: {
|
||||||
|
class: 'custom-app'
|
||||||
|
},
|
||||||
meta: [
|
meta: [
|
||||||
{ charset: 'utf=8' }
|
{ charset: 'utf=8' }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -43,13 +43,15 @@ function rollupConfig({
|
|||||||
const isBrowserBuild = !config.output || !config.output.format || config.output.format === 'umd' || config.output.file.includes('.browser.')
|
const isBrowserBuild = !config.output || !config.output.format || config.output.format === 'umd' || config.output.file.includes('.browser.')
|
||||||
|
|
||||||
const replaceConfig = {
|
const replaceConfig = {
|
||||||
exclude: 'node_modules/**',
|
exclude: 'node_modules/(?!is-mergeable-object)',
|
||||||
delimiters: ['', ''],
|
delimiters: ['', ''],
|
||||||
values: {
|
values: {
|
||||||
// replaceConfig needs to have some values
|
// replaceConfig needs to have some values
|
||||||
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
|
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
|
||||||
'process.env.VERSION': `"${version}"`,
|
'process.env.VERSION': `"${version}"`,
|
||||||
'process.server' : isBrowserBuild ? 'false' : 'true'
|
'process.server' : isBrowserBuild ? 'false' : 'true',
|
||||||
|
// remove react stuff from is-mergeable-object
|
||||||
|
'|| isReactElement(value)': '|| false'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -60,7 +60,11 @@ export function addListeners () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function applyCallbacks (matchElement) {
|
export function applyCallbacks (matchElement) {
|
||||||
callbacks.forEach(([query, callback]) => {
|
callbacks.forEach((args) => {
|
||||||
|
// do not use destructuring for args, it increases transpiled size
|
||||||
|
// due to var checks while we are guaranteed the structure of the cb
|
||||||
|
const query = args[0]
|
||||||
|
const callback = args[1]
|
||||||
const selector = `${query}[onload="this.__vm_l=1"]`
|
const selector = `${query}[onload="this.__vm_l=1"]`
|
||||||
|
|
||||||
let elements = []
|
let elements = []
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
|||||||
|
|
||||||
if (includes(metaInfoAttributeKeys, type)) {
|
if (includes(metaInfoAttributeKeys, type)) {
|
||||||
const tagName = type.substr(0, 4)
|
const tagName = type.substr(0, 4)
|
||||||
updateAttribute(options, newInfo[type], getTag(tags, tagName))
|
updateAttribute(appId, options, type, newInfo[type], getTag(tags, tagName))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { booleanHtmlAttributes } from '../../shared/constants'
|
import { booleanHtmlAttributes } from '../../shared/constants'
|
||||||
import { toArray, includes } from '../../utils/array'
|
import { includes } from '../../utils/array'
|
||||||
import { isArray } from '../../utils/is-type'
|
|
||||||
|
// keep a local map of attribute values
|
||||||
|
// instead of adding it to the html
|
||||||
|
export const attributeMap = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the document's html tag attributes
|
* Updates the document's html tag attributes
|
||||||
@@ -8,39 +11,62 @@ import { isArray } from '../../utils/is-type'
|
|||||||
* @param {Object} attrs - the new document html attributes
|
* @param {Object} attrs - the new document html attributes
|
||||||
* @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
* @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
||||||
*/
|
*/
|
||||||
export default function updateAttribute ({ attribute } = {}, attrs, tag) {
|
export default function updateAttribute (appId, { attribute } = {}, type, attrs, tag) {
|
||||||
const vueMetaAttrString = tag.getAttribute(attribute)
|
const vueMetaAttrString = tag.getAttribute(attribute)
|
||||||
const vueMetaAttrs = vueMetaAttrString ? vueMetaAttrString.split(',') : []
|
if (vueMetaAttrString) {
|
||||||
const toRemove = toArray(vueMetaAttrs)
|
attributeMap[type] = JSON.parse(decodeURI(vueMetaAttrString))
|
||||||
|
tag.removeAttribute(attribute)
|
||||||
|
}
|
||||||
|
|
||||||
const keepIndexes = []
|
let data = attributeMap[type] || {}
|
||||||
for (const attr in attrs) {
|
|
||||||
if (attrs.hasOwnProperty(attr)) {
|
|
||||||
const value = includes(booleanHtmlAttributes, attr)
|
|
||||||
? ''
|
|
||||||
: isArray(attrs[attr]) ? attrs[attr].join(' ') : attrs[attr]
|
|
||||||
|
|
||||||
tag.setAttribute(attr, value || '')
|
const toUpdate = []
|
||||||
|
|
||||||
if (!includes(vueMetaAttrs, attr)) {
|
// remove attributes from the map
|
||||||
vueMetaAttrs.push(attr)
|
// which have been removed for this appId
|
||||||
|
for (const attr in data) {
|
||||||
|
if (data[attr] && appId in data[attr]) {
|
||||||
|
toUpdate.push(attr)
|
||||||
|
|
||||||
|
if (!attrs[attr]) {
|
||||||
|
delete data[attr][appId]
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter below wont ever check -1
|
|
||||||
keepIndexes.push(toRemove.indexOf(attr))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removedAttributesCount = toRemove
|
for (const attr in attrs) {
|
||||||
.filter((el, index) => !includes(keepIndexes, index))
|
const attrData = data[attr]
|
||||||
.reduce((acc, attr) => {
|
|
||||||
tag.removeAttribute(attr)
|
|
||||||
return acc + 1
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
if (vueMetaAttrs.length === removedAttributesCount) {
|
if (!attrData || attrData[appId] !== attrs[attr]) {
|
||||||
tag.removeAttribute(attribute)
|
toUpdate.push(attr)
|
||||||
} else {
|
|
||||||
tag.setAttribute(attribute, (vueMetaAttrs.sort()).join(','))
|
if (attrs[attr]) {
|
||||||
|
data[attr] = data[attr] || {}
|
||||||
|
data[attr][appId] = attrs[attr]
|
||||||
|
} else {
|
||||||
|
delete data[attr][appId]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const attr of toUpdate) {
|
||||||
|
const attrData = data[attr]
|
||||||
|
|
||||||
|
const attrValues = []
|
||||||
|
for (const appId in attrData) {
|
||||||
|
Array.prototype.push.apply(attrValues, [].concat(attrData[appId]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrValues.length) {
|
||||||
|
const attrValue = includes(booleanHtmlAttributes, attr) && attrValues.some(Boolean)
|
||||||
|
? ''
|
||||||
|
: attrValues.filter(Boolean).join(' ')
|
||||||
|
|
||||||
|
tag.setAttribute(attr, attrValue)
|
||||||
|
} else {
|
||||||
|
tag.removeAttribute(attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeMap[type] = data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,17 +47,30 @@ export default function generateServerInjector (options, metaInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (metaInfoAttributeKeys.includes(type)) {
|
if (metaInfoAttributeKeys.includes(type)) {
|
||||||
let str = attributeGenerator(options, type, serverInjector.data[type], arg)
|
const attributeData = {}
|
||||||
|
|
||||||
if (serverInjector.extraData) {
|
const data = serverInjector.data[type]
|
||||||
for (const appId in serverInjector.extraData) {
|
if (data) {
|
||||||
const data = serverInjector.extraData[appId][type]
|
for (const attr in data) {
|
||||||
const extraStr = attributeGenerator(options, type, data, arg)
|
attributeData[attr] = {
|
||||||
str = `${str}${extraStr}`
|
[options.ssrAppId]: data[attr]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
for (const appId in serverInjector.extraData) {
|
||||||
|
const data = serverInjector.extraData[appId][type]
|
||||||
|
if (data) {
|
||||||
|
for (const attr in data) {
|
||||||
|
attributeData[attr] = {
|
||||||
|
...attributeData[attr],
|
||||||
|
[appId]: data[attr]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributeGenerator(options, type, attributeData, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = tagGenerator(options, type, serverInjector.data[type], arg)
|
let str = tagGenerator(options, type, serverInjector.data[type], arg)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { booleanHtmlAttributes } from '../../shared/constants'
|
import { booleanHtmlAttributes } from '../../shared/constants'
|
||||||
import { isUndefined, isArray } from '../../utils/is-type'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates tag attributes for use on the server.
|
* Generates tag attributes for use on the server.
|
||||||
@@ -10,22 +9,26 @@ import { isUndefined, isArray } from '../../utils/is-type'
|
|||||||
*/
|
*/
|
||||||
export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data, addSrrAttribute) {
|
export default function attributeGenerator ({ attribute, ssrAttribute } = {}, type, data, addSrrAttribute) {
|
||||||
let attributeStr = ''
|
let attributeStr = ''
|
||||||
const watchedAttrs = []
|
|
||||||
|
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
if (data.hasOwnProperty(attr)) {
|
const attrData = data[attr]
|
||||||
watchedAttrs.push(attr)
|
const attrValues = []
|
||||||
|
|
||||||
attributeStr += isUndefined(data[attr]) || booleanHtmlAttributes.includes(attr)
|
for (const appId in attrData) {
|
||||||
? attr
|
attrValues.push(...[].concat(attrData[appId]))
|
||||||
: `${attr}="${isArray(data[attr]) ? data[attr].join(' ') : data[attr]}"`
|
}
|
||||||
|
|
||||||
|
if (attrValues.length) {
|
||||||
|
attributeStr += booleanHtmlAttributes.includes(attr) && attrValues.some(Boolean)
|
||||||
|
? `${attr}`
|
||||||
|
: `${attr}="${attrValues.join(' ')}"`
|
||||||
|
|
||||||
attributeStr += ' '
|
attributeStr += ' '
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attributeStr) {
|
if (attributeStr) {
|
||||||
attributeStr += `${attribute}="${(watchedAttrs.sort()).join(',')}"`
|
attributeStr += `${attribute}="${encodeURI(JSON.stringify(data))}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'htmlAttrs' && addSrrAttribute) {
|
if (type === 'htmlAttrs' && addSrrAttribute) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import updateClientMetaInfo from '../client/updateClientMetaInfo'
|
import updateClientMetaInfo from '../client/updateClientMetaInfo'
|
||||||
import { removeElementsByAppId } from '../utils/elements'
|
import { updateAttribute } from '../client/updaters'
|
||||||
|
import { metaInfoAttributeKeys } from '../shared/constants'
|
||||||
|
import { getTag, removeElementsByAppId } from '../utils/elements'
|
||||||
|
|
||||||
let appsMetaInfo
|
let appsMetaInfo
|
||||||
|
|
||||||
@@ -24,6 +26,12 @@ export function setMetaInfo (vm, appId, options, metaInfo) {
|
|||||||
|
|
||||||
export function removeMetaInfo (vm, appId, options) {
|
export function removeMetaInfo (vm, appId, options) {
|
||||||
if (vm && vm.$el) {
|
if (vm && vm.$el) {
|
||||||
|
const tags = {}
|
||||||
|
for (const type of metaInfoAttributeKeys) {
|
||||||
|
const tagName = type.substr(0, 4)
|
||||||
|
updateAttribute(appId, options, type, {}, getTag(tags, tagName))
|
||||||
|
}
|
||||||
|
|
||||||
return removeElementsByAppId(options, appId)
|
return removeElementsByAppId(options, appId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,19 +57,18 @@ export const defaultOptions = {
|
|||||||
ssrAppId
|
ssrAppId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The metaInfo property keys which are used to disable escaping
|
||||||
|
export const disableOptionKeys = [
|
||||||
|
'__dangerouslyDisableSanitizers',
|
||||||
|
'__dangerouslyDisableSanitizersByTagID'
|
||||||
|
]
|
||||||
|
|
||||||
// List of metaInfo property keys which are configuration options (and dont generate html)
|
// List of metaInfo property keys which are configuration options (and dont generate html)
|
||||||
export const metaInfoOptionKeys = [
|
export const metaInfoOptionKeys = [
|
||||||
'titleChunk',
|
'titleChunk',
|
||||||
'titleTemplate',
|
'titleTemplate',
|
||||||
'changed',
|
'changed',
|
||||||
'__dangerouslyDisableSanitizers',
|
...disableOptionKeys
|
||||||
'__dangerouslyDisableSanitizersByTagID'
|
|
||||||
]
|
|
||||||
|
|
||||||
// The metaInfo property keys which are used to disable escaping
|
|
||||||
export const disableOptionKeys = [
|
|
||||||
'__dangerouslyDisableSanitizers',
|
|
||||||
'__dangerouslyDisableSanitizersByTagID'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
// List of metaInfo property keys which only generates attributes and no tags
|
// List of metaInfo property keys which only generates attributes and no tags
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ export function escape (info, options, escapeOptions, escapeKeys) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let [ disableKey ] = disableOptionKeys
|
// do not use destructuring for disableOptionKeys, it increases transpiled size
|
||||||
|
// due to var checks while we are guaranteed the structure of the cb
|
||||||
|
let disableKey = disableOptionKeys[0]
|
||||||
|
|
||||||
if (escapeOptions[disableKey] && includes(escapeOptions[disableKey], key)) {
|
if (escapeOptions[disableKey] && includes(escapeOptions[disableKey], key)) {
|
||||||
// this info[key] doesnt need to escaped if the option is listed in __dangerouslyDisableSanitizers
|
// this info[key] doesnt need to escaped if the option is listed in __dangerouslyDisableSanitizers
|
||||||
escaped[key] = value
|
escaped[key] = value
|
||||||
@@ -81,8 +84,10 @@ export function escape (info, options, escapeOptions, escapeKeys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function escapeMetaInfo (options, info, escapeSequences = []) {
|
export function escapeMetaInfo (options, info, escapeSequences = []) {
|
||||||
|
// do not use destructuring for seq, it increases transpiled size
|
||||||
|
// due to var checks while we are guaranteed the structure of the cb
|
||||||
const escapeOptions = {
|
const escapeOptions = {
|
||||||
doEscape: value => escapeSequences.reduce((val, [v, r]) => val.replace(v, r), value)
|
doEscape: value => escapeSequences.reduce((val, seq) => val.replace(seq[0], seq[1]), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
disableOptionKeys.forEach((disableKey, index) => {
|
disableOptionKeys.forEach((disableKey, index) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getComponentMetaInfo } from '../../src/shared/getComponentOption'
|
import { getComponentMetaInfo } from '../../src/shared/getComponentOption'
|
||||||
import _getMetaInfo from '../../src/shared/getMetaInfo'
|
import _getMetaInfo from '../../src/shared/getMetaInfo'
|
||||||
import { mount, createWrapper, loadVueMetaPlugin, vmTick } from '../utils'
|
import { mount, createWrapper, loadVueMetaPlugin, vmTick, clearClientAttributeMap } from '../utils'
|
||||||
import { defaultOptions } from '../../src/shared/constants'
|
import { defaultOptions } from '../../src/shared/constants'
|
||||||
|
|
||||||
import GoodbyeWorld from '../components/goodbye-world.vue'
|
import GoodbyeWorld from '../components/goodbye-world.vue'
|
||||||
@@ -226,6 +226,13 @@ describe('client', () => {
|
|||||||
// this component uses a computed prop to simulate a non-synchronous
|
// this component uses a computed prop to simulate a non-synchronous
|
||||||
// metaInfo update like you would have with a Vuex mutation
|
// metaInfo update like you would have with a Vuex mutation
|
||||||
const Component = Vue.extend({
|
const Component = Vue.extend({
|
||||||
|
metaInfo () {
|
||||||
|
return {
|
||||||
|
htmlAttrs: {
|
||||||
|
theme: this.theme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hiddenTheme: 'light'
|
hiddenTheme: 'light'
|
||||||
@@ -239,14 +246,7 @@ describe('client', () => {
|
|||||||
beforeMount () {
|
beforeMount () {
|
||||||
this.hiddenTheme = 'dark'
|
this.hiddenTheme = 'dark'
|
||||||
},
|
},
|
||||||
render: h => h('div'),
|
render: h => h('div')
|
||||||
metaInfo () {
|
|
||||||
return {
|
|
||||||
htmlAttrs: {
|
|
||||||
theme: this.theme
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const vm = new Component().$mount(el)
|
const vm = new Component().$mount(el)
|
||||||
@@ -263,6 +263,8 @@ describe('client', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('changes during hydration initialization trigger an update', async () => {
|
test('changes during hydration initialization trigger an update', async () => {
|
||||||
|
clearClientAttributeMap()
|
||||||
|
|
||||||
html.setAttribute(defaultOptions.ssrAttribute, 'true')
|
html.setAttribute(defaultOptions.ssrAttribute, 'true')
|
||||||
|
|
||||||
const el = document.createElement('div')
|
const el = document.createElement('div')
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe('generators', () => {
|
|||||||
const testInfo = typeTests[action]
|
const testInfo = typeTests[action]
|
||||||
|
|
||||||
// return when no test case available
|
// return when no test case available
|
||||||
if (!testCases[action] && !testInfo.test) {
|
if (!testCases[action]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import _updateClientMetaInfo from '../../src/client/updateClientMetaInfo'
|
|||||||
import { defaultOptions, ssrAppId, ssrAttribute } from '../../src/shared/constants'
|
import { defaultOptions, ssrAppId, ssrAttribute } from '../../src/shared/constants'
|
||||||
import metaInfoData from '../utils/meta-info-data'
|
import metaInfoData from '../utils/meta-info-data'
|
||||||
import * as load from '../../src/client/load'
|
import * as load from '../../src/client/load'
|
||||||
|
import { clearClientAttributeMap } from '../utils'
|
||||||
|
|
||||||
const updateClientMetaInfo = (type, data) => _updateClientMetaInfo(ssrAppId, defaultOptions, { [type]: data })
|
const updateClientMetaInfo = (type, data) => _updateClientMetaInfo(ssrAppId, defaultOptions, { [type]: data })
|
||||||
|
|
||||||
@@ -43,7 +44,6 @@ describe('updaters', () => {
|
|||||||
},
|
},
|
||||||
remove: (tags) => {
|
remove: (tags) => {
|
||||||
// TODO: i'd expect tags.removedTags to be populated
|
// TODO: i'd expect tags.removedTags to be populated
|
||||||
|
|
||||||
typeTests.add.expect.forEach((expected, index) => {
|
typeTests.add.expect.forEach((expected, index) => {
|
||||||
expect(html.outerHTML).not.toContain(expected)
|
expect(html.outerHTML).not.toContain(expected)
|
||||||
})
|
})
|
||||||
@@ -57,6 +57,8 @@ describe('updaters', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe(`${type} type tests`, () => {
|
describe(`${type} type tests`, () => {
|
||||||
|
beforeAll(() => clearClientAttributeMap())
|
||||||
|
|
||||||
Object.keys(typeTests).forEach((action) => {
|
Object.keys(typeTests).forEach((action) => {
|
||||||
const testInfo = typeTests[action]
|
const testInfo = typeTests[action]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { JSDOM } from 'jsdom'
|
import { JSDOM } from 'jsdom'
|
||||||
import { mount, shallowMount, createWrapper, createLocalVue } from '@vue/test-utils'
|
import { mount, shallowMount, createWrapper, createLocalVue } from '@vue/test-utils'
|
||||||
import { renderToString } from '@vue/server-test-utils'
|
import { renderToString } from '@vue/server-test-utils'
|
||||||
|
import { attributeMap } from '../../src/client/updaters/attribute'
|
||||||
import { defaultOptions } from '../../src/shared/constants'
|
import { defaultOptions } from '../../src/shared/constants'
|
||||||
import VueMetaPlugin from '../../src'
|
import VueMetaPlugin from '../../src'
|
||||||
|
|
||||||
@@ -39,3 +40,11 @@ export function createDOM (html = '<!DOCTYPE html>', options = {}) {
|
|||||||
document: dom.window.document
|
document: dom.window.document
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dirty hack to remove data from previous test
|
||||||
|
// this is ok because this code normally only runs on
|
||||||
|
// the client and not during ssr
|
||||||
|
// TODO: findout why jest.resetModules doesnt work for this
|
||||||
|
export function clearClientAttributeMap() {
|
||||||
|
Object.keys(attributeMap).forEach(key => delete attributeMap[key])
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defaultOptions } from '../../src/shared/constants'
|
import { defaultOptions } from '../../src/shared/constants'
|
||||||
|
import { attributeMap } from '../../src/client/updaters/attribute'
|
||||||
|
|
||||||
const metaInfoData = {
|
const metaInfoData = {
|
||||||
title: {
|
title: {
|
||||||
@@ -187,43 +188,127 @@ const metaInfoData = {
|
|||||||
htmlAttrs: {
|
htmlAttrs: {
|
||||||
add: {
|
add: {
|
||||||
data: { foo: 'bar' },
|
data: { foo: 'bar' },
|
||||||
expect: ['<html foo="bar" data-vue-meta="foo">']
|
expect: ['<html foo="bar" data-vue-meta="%7B%22foo%22:%7B%22ssr%22:%22bar%22%7D%7D">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
if (side === 'client') {
|
||||||
|
this.expect[0] = this.expect[0].replace(/ data-vue-meta="[^"]+"/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
if (side === 'client') {
|
||||||
|
expect(attributeMap).toEqual({ htmlAttrs: { foo: { ssr: 'bar' } } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: { foo: 'baz' },
|
data: { foo: 'baz' },
|
||||||
expect: ['<html foo="baz" data-vue-meta="foo">']
|
expect: ['<html foo="baz">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ htmlAttrs: { foo: { ssr: 'baz' } } })
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: {},
|
data: {},
|
||||||
expect: ['<html>']
|
expect: ['<html>'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ htmlAttrs: { foo: {} } })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headAttrs: {
|
headAttrs: {
|
||||||
add: {
|
add: {
|
||||||
data: { foo: 'bar' },
|
data: { foo: 'bar' },
|
||||||
expect: ['<head foo="bar" data-vue-meta="foo">']
|
expect: ['<head foo="bar" data-vue-meta="%7B%22foo%22:%7B%22ssr%22:%22bar%22%7D%7D">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
if (side === 'client') {
|
||||||
|
this.expect[0] = this.expect[0].replace(/ data-vue-meta="[^"]+"/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
if (side === 'client') {
|
||||||
|
expect(attributeMap).toEqual({ headAttrs: { foo: { ssr: 'bar' } } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: { foo: 'baz' },
|
data: { foo: 'baz' },
|
||||||
expect: ['<head foo="baz" data-vue-meta="foo">']
|
expect: ['<head foo="baz">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ headAttrs: { foo: { ssr: 'baz' } } })
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: {},
|
data: {},
|
||||||
expect: ['<head>']
|
expect: ['<head>'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ headAttrs: { foo: {} } })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bodyAttrs: {
|
bodyAttrs: {
|
||||||
add: {
|
add: {
|
||||||
data: { foo: 'bar', fizz: ['fuzz', 'fozz'] },
|
data: { foo: 'bar', fizz: ['fuzz', 'fozz'] },
|
||||||
expect: ['<body foo="bar" fizz="fuzz fozz" data-vue-meta="fizz,foo">']
|
expect: ['<body foo="bar" fizz="fuzz fozz" data-vue-meta="%7B%22foo%22:%7B%22ssr%22:%22bar%22%7D,%22fizz%22:%7B%22ssr%22:%5B%22fuzz%22,%22fozz%22%5D%7D%7D">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
if (side === 'client') {
|
||||||
|
this.expect[0] = this.expect[0].replace(/ data-vue-meta="[^"]+"/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
if (side === 'client') {
|
||||||
|
expect(attributeMap).toEqual({ bodyAttrs: {
|
||||||
|
foo: { ssr: 'bar' },
|
||||||
|
fizz: { ssr: ['fuzz', 'fozz'] }
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
change: {
|
change: {
|
||||||
data: { foo: 'baz' },
|
data: { foo: 'baz' },
|
||||||
expect: ['<body foo="baz" data-vue-meta="fizz,foo">']
|
expect: ['<body foo="baz">'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ bodyAttrs: { foo: { ssr: 'baz' }, fizz: {} } })
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
remove: {
|
remove: {
|
||||||
data: {},
|
data: {},
|
||||||
expect: ['<body>']
|
expect: ['<body>'],
|
||||||
|
test (side, defaultTest) {
|
||||||
|
return () => {
|
||||||
|
defaultTest()
|
||||||
|
|
||||||
|
expect(attributeMap).toEqual({ bodyAttrs: { foo: {}, fizz: {} } })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user