mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-12 01:12:24 +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
|
||||
- run:
|
||||
name: E2E SSR Tests
|
||||
command: yarn test:e2e-ssr
|
||||
command: yarn build && yarn test:e2e-ssr
|
||||
- persist_to_workspace:
|
||||
root: ~/project
|
||||
paths:
|
||||
|
||||
@@ -40,7 +40,7 @@ function rollupConfig({
|
||||
delimiters: ['', ''],
|
||||
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) {
|
||||
let hasAsyncCallback = false
|
||||
|
||||
for (const tag of tags) {
|
||||
tags.forEach((tag) => {
|
||||
if (!tag[tagIDKeyName] || !tag.callback) {
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
hasAsyncCallback = true
|
||||
addCallback(`${type}[data-${tagIDKeyName}="${tag[tagIDKeyName]}"]`, tag.callback)
|
||||
}
|
||||
})
|
||||
|
||||
if (!autoAddListeners || !hasAsyncCallback) {
|
||||
return hasAsyncCallback
|
||||
@@ -60,7 +60,7 @@ export function addListeners () {
|
||||
}
|
||||
|
||||
export function applyCallbacks (matchElement) {
|
||||
for (const [query, callback] of callbacks) {
|
||||
callbacks.forEach(([query, callback]) => {
|
||||
const selector = `${query}[onload="this.__vm_l=1"]`
|
||||
|
||||
let elements = []
|
||||
@@ -72,13 +72,13 @@ export function applyCallbacks (matchElement) {
|
||||
elements = [matchElement]
|
||||
}
|
||||
|
||||
for (const element of elements) {
|
||||
elements.forEach((element) => {
|
||||
/* __vm_cb: whether the load callback has been called
|
||||
* __vm_l: set by onload attribute, whether the element was loaded
|
||||
* __vm_ev: whether the event listener was added or not
|
||||
*/
|
||||
if (element.__vm_cb) {
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
const onload = () => {
|
||||
@@ -105,7 +105,7 @@ export function applyCallbacks (matchElement) {
|
||||
*/
|
||||
if (element.__vm_l) {
|
||||
onload()
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
if (!element.__vm_ev) {
|
||||
@@ -113,6 +113,6 @@ export function applyCallbacks (matchElement) {
|
||||
|
||||
element.addEventListener('load', onload)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ export default function updateClientMetaInfo (appId, options = {}, newInfo) {
|
||||
|
||||
// add load callbacks if the
|
||||
let addLoadListeners = false
|
||||
for (const type of tagsSupportingOnload) {
|
||||
tagsSupportingOnload.forEach((type) => {
|
||||
if (newInfo[type] && addCallbacks(options, type, newInfo[type])) {
|
||||
addLoadListeners = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (addLoadListeners) {
|
||||
addListeners()
|
||||
|
||||
+72
-75
@@ -13,7 +13,9 @@ import { queryElements, getElementsKey } from '../../utils/elements.js'
|
||||
export default function updateTag (appId, options = {}, type, tags, head, body) {
|
||||
const { attribute, tagIDKeyName } = options
|
||||
|
||||
const dataAttributes = [tagIDKeyName, ...commonDataAttributes]
|
||||
const dataAttributes = commonDataAttributes.slice()
|
||||
dataAttributes.push(tagIDKeyName)
|
||||
|
||||
const newElements = []
|
||||
|
||||
const queryOptions = { appId, attribute, type, tagIDKeyName }
|
||||
@@ -36,103 +38,98 @@ export default function updateTag (appId, options = {}, type, tags, head, body)
|
||||
})
|
||||
}
|
||||
|
||||
if (tags.length) {
|
||||
for (const tag of tags) {
|
||||
if (tag.skip) {
|
||||
tags.forEach((tag) => {
|
||||
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
|
||||
}
|
||||
|
||||
const newElement = document.createElement(type)
|
||||
newElement.setAttribute(attribute, appId)
|
||||
|
||||
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)
|
||||
if (attr === 'innerHTML') {
|
||||
newElement.innerHTML = tag.innerHTML
|
||||
continue
|
||||
}
|
||||
|
||||
const oldElements = currentElements[getElementsKey(tag)]
|
||||
|
||||
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
|
||||
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 === '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)
|
||||
}
|
||||
}
|
||||
|
||||
let oldElements = []
|
||||
for (const current of Object.values(currentElements)) {
|
||||
oldElements = [
|
||||
...oldElements,
|
||||
...current
|
||||
]
|
||||
const oldElements = currentElements[getElementsKey(tag)]
|
||||
|
||||
// Remove a duplicate tag from domTagstoRemove, so it isn't cleared.
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
const oldElements = []
|
||||
for (const type in currentElements) {
|
||||
Array.prototype.push.apply(oldElements, currentElements[type])
|
||||
}
|
||||
|
||||
// remove old elements
|
||||
for (const element of oldElements) {
|
||||
oldElements.forEach((element) => {
|
||||
element.parentNode.removeChild(element)
|
||||
}
|
||||
})
|
||||
|
||||
// insert new elements
|
||||
for (const element of newElements) {
|
||||
newElements.forEach((element) => {
|
||||
if (element.hasAttribute('data-body')) {
|
||||
body.appendChild(element)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
if (element.hasAttribute('data-pbody')) {
|
||||
body.insertBefore(element, body.firstChild)
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
head.appendChild(element)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
oldTags: oldElements,
|
||||
|
||||
@@ -19,9 +19,9 @@ export const clientSequences = [
|
||||
]
|
||||
|
||||
// sanitizes potentially dangerous characters
|
||||
export function escape (info, options, escapeOptions) {
|
||||
export function escape (info, options, escapeOptions, escapeKeys) {
|
||||
const { tagIDKeyName } = options
|
||||
const { doEscape = v => v, escapeKeys } = escapeOptions
|
||||
const { doEscape = v => v } = escapeOptions
|
||||
const escaped = {}
|
||||
|
||||
for (const key in info) {
|
||||
@@ -56,13 +56,13 @@ export function escape (info, options, escapeOptions) {
|
||||
} else if (isArray(value)) {
|
||||
escaped[key] = value.map((v) => {
|
||||
if (isPureObject(v)) {
|
||||
return escape(v, options, { ...escapeOptions, escapeKeys: true })
|
||||
return escape(v, options, escapeOptions, true)
|
||||
}
|
||||
|
||||
return doEscape(v)
|
||||
})
|
||||
} else if (isPureObject(value)) {
|
||||
escaped[key] = escape(value, options, { ...escapeOptions, escapeKeys: true })
|
||||
escaped[key] = escape(value, options, escapeOptions, true)
|
||||
} else {
|
||||
escaped[key] = value
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,13 +4,13 @@ const _global = hasGlobalWindow ? window : global
|
||||
|
||||
const console = (_global.console = _global.console || {})
|
||||
|
||||
export function warn (...args) {
|
||||
export function warn (str) {
|
||||
/* istanbul ignore next */
|
||||
if (!console || !console.warn) {
|
||||
return
|
||||
}
|
||||
|
||||
console.warn(...args)
|
||||
console.warn(str)
|
||||
}
|
||||
|
||||
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 VueMeta from '../../../src/browser'
|
||||
import VueMeta from 'vue-meta'
|
||||
import App from './App.vue'
|
||||
import createRouter from './router'
|
||||
|
||||
|
||||
Vendored
+8
-4
@@ -1,10 +1,14 @@
|
||||
import Vue from 'vue'
|
||||
import VueMeta from '../../../src'
|
||||
import { _import, getVueMetaPath } from '../../utils/build'
|
||||
import App from './App.vue'
|
||||
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 VueLoaderPlugin from 'vue-loader/lib/plugin'
|
||||
import { createRenderer } from 'vue-server-renderer'
|
||||
import stdEnv from 'std-env'
|
||||
|
||||
const renderer = createRenderer()
|
||||
|
||||
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) {
|
||||
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.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 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`, '')
|
||||
|
||||
const app = await renderer.renderToString(vueApp)
|
||||
// !!! run inject after renderToString !!!
|
||||
|
||||
const metaInfo = vueApp.$meta().inject()
|
||||
|
||||
const appFile = path.resolve(webpackStats.outputPath, 'index.html')
|
||||
@@ -97,7 +113,7 @@ export function createWebpackConfig (config = {}) {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
exclude: /(node_modules|dist)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
@@ -144,7 +160,8 @@ export function createWebpackConfig (config = {}) {
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue': 'vue/dist/vue.esm.js'
|
||||
'vue': 'vue/dist/vue.esm.js',
|
||||
'vue-meta': getVueMetaPath(true)
|
||||
}
|
||||
},
|
||||
...config
|
||||
|
||||
Reference in New Issue
Block a user