mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-24 01:00:33 +03:00
test: use build/dist for e2e testing (#421)
* test: use build/dist for e2e testing to prevent errors due to wrong build configs * test: use wrong build conf to test the test * test: exclude dist for babel-loader in test build * chore: optimize more for ie9 prefer not to use language features which needs to be transpiled for ie9. Eg for..of to forEach and no spreads * fix: continue
This commit is contained in:
@@ -76,7 +76,7 @@ jobs:
|
|||||||
- attach-project
|
- attach-project
|
||||||
- run:
|
- run:
|
||||||
name: E2E SSR Tests
|
name: E2E SSR Tests
|
||||||
command: yarn test:e2e-ssr
|
command: yarn build && yarn test:e2e-ssr
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: ~/project
|
root: ~/project
|
||||||
paths:
|
paths:
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function rollupConfig({
|
|||||||
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 = false',
|
'const polyfill = process.env.NODE_ENV === \'test\'': 'const polyfill = true',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -30,14 +30,14 @@ export function addCallback (query, callback) {
|
|||||||
export function addCallbacks ({ tagIDKeyName }, type, tags, autoAddListeners) {
|
export function addCallbacks ({ tagIDKeyName }, type, tags, autoAddListeners) {
|
||||||
let hasAsyncCallback = false
|
let hasAsyncCallback = false
|
||||||
|
|
||||||
for (const tag of tags) {
|
tags.forEach((tag) => {
|
||||||
if (!tag[tagIDKeyName] || !tag.callback) {
|
if (!tag[tagIDKeyName] || !tag.callback) {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAsyncCallback = true
|
hasAsyncCallback = true
|
||||||
addCallback(`${type}[data-${tagIDKeyName}="${tag[tagIDKeyName]}"]`, tag.callback)
|
addCallback(`${type}[data-${tagIDKeyName}="${tag[tagIDKeyName]}"]`, tag.callback)
|
||||||
}
|
})
|
||||||
|
|
||||||
if (!autoAddListeners || !hasAsyncCallback) {
|
if (!autoAddListeners || !hasAsyncCallback) {
|
||||||
return hasAsyncCallback
|
return hasAsyncCallback
|
||||||
@@ -60,7 +60,7 @@ export function addListeners () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function applyCallbacks (matchElement) {
|
export function applyCallbacks (matchElement) {
|
||||||
for (const [query, callback] of callbacks) {
|
callbacks.forEach(([query, callback]) => {
|
||||||
const selector = `${query}[onload="this.__vm_l=1"]`
|
const selector = `${query}[onload="this.__vm_l=1"]`
|
||||||
|
|
||||||
let elements = []
|
let elements = []
|
||||||
@@ -72,13 +72,13 @@ export function applyCallbacks (matchElement) {
|
|||||||
elements = [matchElement]
|
elements = [matchElement]
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const element of elements) {
|
elements.forEach((element) => {
|
||||||
/* __vm_cb: whether the load callback has been called
|
/* __vm_cb: whether the load callback has been called
|
||||||
* __vm_l: set by onload attribute, whether the element was loaded
|
* __vm_l: set by onload attribute, whether the element was loaded
|
||||||
* __vm_ev: whether the event listener was added or not
|
* __vm_ev: whether the event listener was added or not
|
||||||
*/
|
*/
|
||||||
if (element.__vm_cb) {
|
if (element.__vm_cb) {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const onload = () => {
|
const onload = () => {
|
||||||
@@ -105,7 +105,7 @@ export function applyCallbacks (matchElement) {
|
|||||||
*/
|
*/
|
||||||
if (element.__vm_l) {
|
if (element.__vm_l) {
|
||||||
onload()
|
onload()
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!element.__vm_ev) {
|
if (!element.__vm_ev) {
|
||||||
@@ -113,6 +113,6 @@ export function applyCallbacks (matchElement) {
|
|||||||
|
|
||||||
element.addEventListener('load', onload)
|
element.addEventListener('load', onload)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
|||||||
|
|
||||||
// add load callbacks if the
|
// add load callbacks if the
|
||||||
let addLoadListeners = false
|
let addLoadListeners = false
|
||||||
for (const type of tagsSupportingOnload) {
|
tagsSupportingOnload.forEach((type) => {
|
||||||
if (newInfo[type] && addCallbacks(options, type, newInfo[type])) {
|
if (newInfo[type] && addCallbacks(options, type, newInfo[type])) {
|
||||||
addLoadListeners = true
|
addLoadListeners = true
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
if (addLoadListeners) {
|
if (addLoadListeners) {
|
||||||
addListeners()
|
addListeners()
|
||||||
|
|||||||
+72
-75
@@ -13,7 +13,9 @@ import { queryElements, getElementsKey } from '../../utils/elements.js'
|
|||||||
export default function updateTag (appId, options = {}, type, tags, head, body) {
|
export default function updateTag (appId, options = {}, type, tags, head, body) {
|
||||||
const { attribute, tagIDKeyName } = options
|
const { attribute, tagIDKeyName } = options
|
||||||
|
|
||||||
const dataAttributes = [tagIDKeyName, ...commonDataAttributes]
|
const dataAttributes = commonDataAttributes.slice()
|
||||||
|
dataAttributes.push(tagIDKeyName)
|
||||||
|
|
||||||
const newElements = []
|
const newElements = []
|
||||||
|
|
||||||
const queryOptions = { appId, attribute, type, tagIDKeyName }
|
const queryOptions = { appId, attribute, type, tagIDKeyName }
|
||||||
@@ -36,103 +38,98 @@ export default function updateTag (appId, options = {}, type, tags, head, body)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tags.length) {
|
tags.forEach((tag) => {
|
||||||
for (const tag of tags) {
|
if (tag.skip) {
|
||||||
if (tag.skip) {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newElement = document.createElement(type)
|
||||||
|
newElement.setAttribute(attribute, appId)
|
||||||
|
|
||||||
|
for (const attr in tag) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (!tag.hasOwnProperty(attr)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const newElement = document.createElement(type)
|
if (attr === 'innerHTML') {
|
||||||
newElement.setAttribute(attribute, appId)
|
newElement.innerHTML = tag.innerHTML
|
||||||
|
continue
|
||||||
for (const attr in tag) {
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (!tag.hasOwnProperty(attr)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr === 'innerHTML') {
|
|
||||||
newElement.innerHTML = tag.innerHTML
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr === 'json') {
|
|
||||||
newElement.innerHTML = JSON.stringify(tag.json)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr === 'cssText') {
|
|
||||||
if (newElement.styleSheet) {
|
|
||||||
/* istanbul ignore next */
|
|
||||||
newElement.styleSheet.cssText = tag.cssText
|
|
||||||
} else {
|
|
||||||
newElement.appendChild(document.createTextNode(tag.cssText))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attr === 'callback') {
|
|
||||||
newElement.onload = () => tag[attr](newElement)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const _attr = includes(dataAttributes, attr)
|
|
||||||
? `data-${attr}`
|
|
||||||
: attr
|
|
||||||
|
|
||||||
const isBooleanAttribute = includes(booleanHtmlAttributes, attr)
|
|
||||||
if (isBooleanAttribute && !tag[attr]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = isBooleanAttribute ? '' : tag[attr]
|
|
||||||
newElement.setAttribute(_attr, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldElements = currentElements[getElementsKey(tag)]
|
if (attr === 'json') {
|
||||||
|
newElement.innerHTML = JSON.stringify(tag.json)
|
||||||
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
|
continue
|
||||||
let indexToDelete
|
|
||||||
const hasEqualElement = oldElements.some((existingTag, index) => {
|
|
||||||
indexToDelete = index
|
|
||||||
return newElement.isEqualNode(existingTag)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (hasEqualElement && (indexToDelete || indexToDelete === 0)) {
|
|
||||||
oldElements.splice(indexToDelete, 1)
|
|
||||||
} else {
|
|
||||||
newElements.push(newElement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attr === 'cssText') {
|
||||||
|
if (newElement.styleSheet) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
newElement.styleSheet.cssText = tag.cssText
|
||||||
|
} else {
|
||||||
|
newElement.appendChild(document.createTextNode(tag.cssText))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr === 'callback') {
|
||||||
|
newElement.onload = () => tag[attr](newElement)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const _attr = includes(dataAttributes, attr)
|
||||||
|
? `data-${attr}`
|
||||||
|
: attr
|
||||||
|
|
||||||
|
const isBooleanAttribute = includes(booleanHtmlAttributes, attr)
|
||||||
|
if (isBooleanAttribute && !tag[attr]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = isBooleanAttribute ? '' : tag[attr]
|
||||||
|
newElement.setAttribute(_attr, value)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let oldElements = []
|
const oldElements = currentElements[getElementsKey(tag)]
|
||||||
for (const current of Object.values(currentElements)) {
|
|
||||||
oldElements = [
|
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
|
||||||
...oldElements,
|
let indexToDelete
|
||||||
...current
|
const hasEqualElement = oldElements.some((existingTag, index) => {
|
||||||
]
|
indexToDelete = index
|
||||||
|
return newElement.isEqualNode(existingTag)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hasEqualElement && (indexToDelete || indexToDelete === 0)) {
|
||||||
|
oldElements.splice(indexToDelete, 1)
|
||||||
|
} else {
|
||||||
|
newElements.push(newElement)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const oldElements = []
|
||||||
|
for (const type in currentElements) {
|
||||||
|
Array.prototype.push.apply(oldElements, currentElements[type])
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove old elements
|
// remove old elements
|
||||||
for (const element of oldElements) {
|
oldElements.forEach((element) => {
|
||||||
element.parentNode.removeChild(element)
|
element.parentNode.removeChild(element)
|
||||||
}
|
})
|
||||||
|
|
||||||
// insert new elements
|
// insert new elements
|
||||||
for (const element of newElements) {
|
newElements.forEach((element) => {
|
||||||
if (element.hasAttribute('data-body')) {
|
if (element.hasAttribute('data-body')) {
|
||||||
body.appendChild(element)
|
body.appendChild(element)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.hasAttribute('data-pbody')) {
|
if (element.hasAttribute('data-pbody')) {
|
||||||
body.insertBefore(element, body.firstChild)
|
body.insertBefore(element, body.firstChild)
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
head.appendChild(element)
|
head.appendChild(element)
|
||||||
}
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
oldTags: oldElements,
|
oldTags: oldElements,
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ export const clientSequences = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// sanitizes potentially dangerous characters
|
// sanitizes potentially dangerous characters
|
||||||
export function escape (info, options, escapeOptions) {
|
export function escape (info, options, escapeOptions, escapeKeys) {
|
||||||
const { tagIDKeyName } = options
|
const { tagIDKeyName } = options
|
||||||
const { doEscape = v => v, escapeKeys } = escapeOptions
|
const { doEscape = v => v } = escapeOptions
|
||||||
const escaped = {}
|
const escaped = {}
|
||||||
|
|
||||||
for (const key in info) {
|
for (const key in info) {
|
||||||
@@ -56,13 +56,13 @@ export function escape (info, options, escapeOptions) {
|
|||||||
} else if (isArray(value)) {
|
} else if (isArray(value)) {
|
||||||
escaped[key] = value.map((v) => {
|
escaped[key] = value.map((v) => {
|
||||||
if (isPureObject(v)) {
|
if (isPureObject(v)) {
|
||||||
return escape(v, options, { ...escapeOptions, escapeKeys: true })
|
return escape(v, options, escapeOptions, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return doEscape(v)
|
return doEscape(v)
|
||||||
})
|
})
|
||||||
} else if (isPureObject(value)) {
|
} else if (isPureObject(value)) {
|
||||||
escaped[key] = escape(value, options, { ...escapeOptions, escapeKeys: true })
|
escaped[key] = escape(value, options, escapeOptions, true)
|
||||||
} else {
|
} else {
|
||||||
escaped[key] = value
|
escaped[key] = value
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -4,13 +4,13 @@ const _global = hasGlobalWindow ? window : global
|
|||||||
|
|
||||||
const console = (_global.console = _global.console || {})
|
const console = (_global.console = _global.console || {})
|
||||||
|
|
||||||
export function warn (...args) {
|
export function warn (str) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (!console || !console.warn) {
|
if (!console || !console.warn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn(...args)
|
console.warn(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showWarningNotSupported = () => warn('This vue app/component has no vue-meta configuration')
|
export const showWarningNotSupported = () => warn('This vue app/component has no vue-meta configuration')
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueMeta from '../../../src/browser'
|
import VueMeta from 'vue-meta'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import createRouter from './router'
|
import createRouter from './router'
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
-4
@@ -1,10 +1,14 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueMeta from '../../../src'
|
import { _import, getVueMetaPath } from '../../utils/build'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import createRouter from './router'
|
import createRouter from './router'
|
||||||
|
|
||||||
Vue.use(VueMeta)
|
export default async function createServerApp () {
|
||||||
|
const VueMeta = await _import(getVueMetaPath())
|
||||||
|
|
||||||
App.router = createRouter()
|
Vue.use(VueMeta)
|
||||||
|
|
||||||
export default new Vue(App)
|
App.router = createRouter()
|
||||||
|
|
||||||
|
return new Vue(App)
|
||||||
|
}
|
||||||
|
|||||||
+21
-4
@@ -5,11 +5,26 @@ import webpack from 'webpack'
|
|||||||
import CopyWebpackPlugin from 'copy-webpack-plugin'
|
import CopyWebpackPlugin from 'copy-webpack-plugin'
|
||||||
import VueLoaderPlugin from 'vue-loader/lib/plugin'
|
import VueLoaderPlugin from 'vue-loader/lib/plugin'
|
||||||
import { createRenderer } from 'vue-server-renderer'
|
import { createRenderer } from 'vue-server-renderer'
|
||||||
|
import stdEnv from 'std-env'
|
||||||
|
|
||||||
const renderer = createRenderer()
|
const renderer = createRenderer()
|
||||||
|
|
||||||
export { default as getPort } from 'get-port'
|
export { default as getPort } from 'get-port'
|
||||||
|
|
||||||
|
export function _import (moduleName) {
|
||||||
|
return import(moduleName).then(m => m.default || m)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDist = stdEnv.test && stdEnv.ci
|
||||||
|
|
||||||
|
export function getVueMetaPath (browser) {
|
||||||
|
if (useDist) {
|
||||||
|
return path.resolve(__dirname, `../..${browser ? '/dist/vue-meta.js' : ''}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.resolve(__dirname, `../../src${browser ? '/browser' : ''}`)
|
||||||
|
}
|
||||||
|
|
||||||
export function webpackRun (config) {
|
export function webpackRun (config) {
|
||||||
const compiler = webpack(config)
|
const compiler = webpack(config)
|
||||||
|
|
||||||
@@ -48,7 +63,8 @@ export async function buildFixture (fixture, config = {}) {
|
|||||||
webpackStats.errors.forEach(e => console.error(e)) // eslint-disable-line no-console
|
webpackStats.errors.forEach(e => console.error(e)) // eslint-disable-line no-console
|
||||||
webpackStats.warnings.forEach(e => console.warn(e)) // eslint-disable-line no-console
|
webpackStats.warnings.forEach(e => console.warn(e)) // eslint-disable-line no-console
|
||||||
|
|
||||||
const vueApp = await import(path.resolve(fixturePath, 'server')).then(m => m.default || m)
|
const createApp = await _import(path.resolve(fixturePath, 'server'))
|
||||||
|
const vueApp = await createApp()
|
||||||
|
|
||||||
const templateFile = await fs.readFile(path.resolve(fixturePath, '..', 'app.template.html'), { encoding: 'utf8' })
|
const templateFile = await fs.readFile(path.resolve(fixturePath, '..', 'app.template.html'), { encoding: 'utf8' })
|
||||||
const compiled = template(templateFile, { interpolate: /{{([\s\S]+?)}}/g })
|
const compiled = template(templateFile, { interpolate: /{{([\s\S]+?)}}/g })
|
||||||
@@ -64,7 +80,7 @@ export async function buildFixture (fixture, config = {}) {
|
|||||||
.reduce((s, asset) => `${s}<script src="./${asset.name}"></script>\n`, '')
|
.reduce((s, asset) => `${s}<script src="./${asset.name}"></script>\n`, '')
|
||||||
|
|
||||||
const app = await renderer.renderToString(vueApp)
|
const app = await renderer.renderToString(vueApp)
|
||||||
// !!! run inject after renderToString !!!
|
|
||||||
const metaInfo = vueApp.$meta().inject()
|
const metaInfo = vueApp.$meta().inject()
|
||||||
|
|
||||||
const appFile = path.resolve(webpackStats.outputPath, 'index.html')
|
const appFile = path.resolve(webpackStats.outputPath, 'index.html')
|
||||||
@@ -97,7 +113,7 @@ export function createWebpackConfig (config = {}) {
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules/,
|
exclude: /(node_modules|dist)/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
@@ -144,7 +160,8 @@ export function createWebpackConfig (config = {}) {
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'vue': 'vue/dist/vue.esm.js'
|
'vue': 'vue/dist/vue.esm.js',
|
||||||
|
'vue-meta': getVueMetaPath(true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...config
|
...config
|
||||||
|
|||||||
Reference in New Issue
Block a user