2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-21 06:30:33 +03:00

test: add render tests

fix: webpack dev server

chore: use eslint not prettier

unfeat: remove support for comments (its brokenn in Vue, maybe later)
This commit is contained in:
pimlie
2020-07-26 00:11:28 +02:00
parent 28d3fc1923
commit 27aaf4744a
80 changed files with 2785 additions and 12109 deletions
-181
View File
@@ -1,181 +0,0 @@
import path from 'path'
import fs from 'fs-extra'
import { template } from 'lodash'
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.min.js' : ''}`
)
}
process.server = !browser
return path.resolve(__dirname, '../../src')
}
export function webpackRun(config) {
const compiler = webpack(config)
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
reject(err)
}
resolve(stats.toJson())
})
})
}
export async function buildFixture(fixture, config = {}) {
if (!fixture) {
throw new Error('buildFixture should be called with a fixture name')
}
const fixturePath = path.resolve(__dirname, '..', 'fixtures', fixture)
config.entry = path.resolve(fixturePath, 'client.js')
if (!config.name) {
config.name = path.basename(path.dirname(config.entry))
}
const webpackConfig = createWebpackConfig(config)
// remove old files
await fs.remove(webpackConfig.output.path)
// run webpack
process.env.NODE_ENV = 'test'
const webpackStats = await webpackRun(webpackConfig)
// for test debugging
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 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 })
const assets = webpackStats.assets.filter(
asset => !asset.name.includes('load-test')
)
const headAssets = assets
.filter(asset => asset.name.includes('chunk'))
.reduce((s, asset) => `${s}<script src="./${asset.name}"></script>\n`, '')
const bodyAssets = assets
.filter(asset => !asset.name.includes('chunk'))
.reduce((s, asset) => `${s}<script src="./${asset.name}"></script>\n`, '')
const app = await renderer.renderToString(vueApp)
const metaInfo = vueApp.$meta().inject()
const appFile = path.resolve(webpackStats.outputPath, 'index.html')
const html = compiled({ app, headAssets, bodyAssets, ...metaInfo })
await fs.writeFile(appFile, html)
return {
url: `file://${appFile}`,
appFile,
webpackStats,
html,
metaInfo,
}
}
export function createWebpackConfig(config = {}) {
const publicPath = '.vue-meta'
return {
mode: 'development',
devtool: 'none',
output: {
path: path.join(path.dirname(config.entry), publicPath),
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: `/${publicPath}/`,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|dist)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 'core-js@3',
targets: { ie: 9, safari: '5.1' },
},
],
],
},
},
},
{ test: /\.vue$/, use: 'vue-loader' },
],
},
// Expose __dirname to allow automatically setting basename.
context: __dirname,
node: {
__dirname: true,
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
},
plugins: [
new VueLoaderPlugin(),
new webpack.DefinePlugin({
'process.env': {
// make sure our simple polyfills are enabled
NODE_ENV: '"test"',
},
}),
new CopyWebpackPlugin([
{ from: path.join(path.dirname(config.entry), 'static') },
]),
],
resolve: {
alias: {
vue: 'vue/dist/vue.esm.js',
'vue-meta': getVueMetaPath(true),
},
},
...config,
}
}
-57
View File
@@ -1,57 +0,0 @@
import { JSDOM } from 'jsdom'
import {
mount,
shallowMount,
createWrapper,
createLocalVue,
} from '@vue/test-utils'
import { render, renderToString } from '@vue/server-test-utils'
import { attributeMap } from '../../src/client/updaters/attribute'
import { defaultOptions } from '../../src/shared/constants'
import VueMetaPlugin from '../../src'
export {
mount,
shallowMount,
createWrapper,
render,
renderToString,
VueMetaPlugin,
attributeMap,
}
export function getVue() {
return createLocalVue()
}
export function loadVueMetaPlugin(options, localVue = getVue()) {
localVue.use(VueMetaPlugin, Object.assign({}, defaultOptions, options))
return localVue
}
export const vmTick = vm => {
return new Promise(resolve => {
vm.$nextTick(resolve)
})
}
export const pTick = () => new Promise(resolve => process.nextTick(resolve))
export function createDOM(html = '<!DOCTYPE html>', options = {}) {
const dom = new JSDOM(html, options)
return {
dom,
window: dom.window,
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])
}
-359
View File
@@ -1,359 +0,0 @@
import { defaultOptions } from '../../src/shared/constants'
import { attributeMap } from '../../src/client/updaters/attribute'
const metaInfoData = {
title: {
add: {
data: 'Hello World',
expect: ['<title>Hello World</title>'],
test(side, defaultTest) {
if (side === 'client') {
// client side vue-meta uses document.title and doesnt change the html
return () => {
this.expect[0] = '<title>Hello World</title>'
const spy = jest.spyOn(document, 'title', 'set')
defaultTest()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith('Hello World')
}
} else {
return defaultTest
}
},
},
},
base: {
add: {
data: [{ href: 'href' }],
expect: ['<base data-vue-meta="ssr" href="href">'],
},
change: {
data: [{ href: 'href2' }],
expect: ['<base data-vue-meta="ssr" href="href2">'],
},
remove: {
data: [],
expect: [''],
},
},
meta: {
add: {
data: [{ charset: 'utf-8' }, { property: 'a', content: 'a' }],
expect: [
'<meta data-vue-meta="ssr" charset="utf-8">',
'<meta data-vue-meta="ssr" property="a" content="a">',
],
},
change: {
data: [{ charset: 'utf-16' }, { property: 'a', content: 'b' }],
expect: [
'<meta data-vue-meta="ssr" charset="utf-16">',
'<meta data-vue-meta="ssr" property="a" content="b">',
],
},
// make sure elements that already exists are not unnecessarily updated
duplicate: {
data: [{ charset: 'utf-16' }, { property: 'a', content: 'c' }],
expect: [
'<meta data-vue-meta="ssr" charset="utf-16">',
'<meta data-vue-meta="ssr" property="a" content="c">',
],
test(side, defaultTest) {
if (side === 'client') {
return () => {
const tags = defaultTest()
expect(tags.tagsAdded.meta.length).toBe(1)
// TODO: not sure if we really expect this
expect(tags.tagsRemoved.meta.length).toBe(1)
}
}
},
},
remove: {
data: [],
expect: [''],
},
},
link: {
add: {
data: [{ rel: 'stylesheet', href: 'href' }],
expect: ['<link data-vue-meta="ssr" rel="stylesheet" href="href">'],
},
change: {
data: [{ rel: 'stylesheet', href: 'href', media: 'screen' }],
expect: [
'<link data-vue-meta="ssr" rel="stylesheet" href="href" media="screen">',
],
},
remove: {
data: [],
expect: [''],
},
},
style: {
add: {
data: [{ type: 'text/css', cssText: '.foo { color: red; }' }],
expect: [
'<style data-vue-meta="ssr" type="text/css">.foo { color: red; }</style>',
],
},
change: {
data: [{ type: 'text/css', cssText: '.foo { color: blue; }' }],
expect: [
'<style data-vue-meta="ssr" type="text/css">.foo { color: blue; }</style>',
],
},
remove: {
data: [],
expect: [''],
},
},
script: {
add: {
data: [
{
src: 'src1',
async: false,
defer: true,
[defaultOptions.tagIDKeyName]: 'content',
callback: () => {},
},
{ src: 'src-prepend', async: true, defer: false, pbody: true },
{ src: 'src2', async: false, defer: true, body: true },
{ src: 'src3', async: false, skip: true },
{
type: 'application/ld+json',
json: {
'@context': 'http://schema.org',
'@type': 'Organization',
name: 'MyApp',
url: 'https://www.myurl.com',
logo: 'https://www.myurl.com/images/logo.png',
},
},
],
expect: [
'<script data-vue-meta="ssr" src="src1" defer data-vmid="content" onload="this.__vm_l=1"></script>',
'<script data-vue-meta="ssr" src="src-prepend" async data-pbody="true"></script>',
'<script data-vue-meta="ssr" src="src2" defer data-body="true"></script>',
'<script data-vue-meta="ssr" type="application/ld+json">{"@context":"http://schema.org","@type":"Organization","name":"MyApp","url":"https://www.myurl.com","logo":"https://www.myurl.com/images/logo.png"}</script>',
],
test(side, defaultTest) {
return () => {
if (side === 'client') {
for (const index in this.expect) {
this.expect[index] = this.expect[index].replace(
/(async|defer)/g,
'$1=""'
)
this.expect[index] = this.expect[index].replace(
/ onload="this.__vm_l=1"/,
''
)
}
const tags = defaultTest()
expect(tags.tagsAdded.script[0].parentNode.tagName).toBe('HEAD')
expect(tags.tagsAdded.script[1].parentNode.tagName).toBe('BODY')
expect(tags.tagsAdded.script[2].parentNode.tagName).toBe('BODY')
} else {
// ssr doesnt generate data-body tags
const bodyPrepended = this.expect[1]
const bodyAppended = this.expect[2]
this.expect = [this.expect.shift(), this.expect.pop()]
const tags = defaultTest()
const html = tags.text()
expect(html).not.toContain(bodyPrepended)
expect(html).not.toContain(bodyAppended)
}
}
},
},
// this test only runs for client so we can directly expect wrong boolean attributes
change: {
data: [
{
src: 'src',
async: true,
defer: true,
[defaultOptions.tagIDKeyName]: 'content2',
},
],
expect: [
'<script data-vue-meta="ssr" src="src" async="" defer="" data-vmid="content2"></script>',
],
},
remove: {
data: [],
expect: [''],
},
},
noscript: {
add: {
data: [{ innerHTML: '<p>noscript</p>' }],
expect: ['<noscript data-vue-meta="ssr"><p>noscript</p></noscript>'],
},
change: {
data: [{ innerHTML: '<p>noscript, no really</p>' }],
expect: [
'<noscript data-vue-meta="ssr"><p>noscript, no really</p></noscript>',
],
},
remove: {
data: [],
expect: [''],
},
},
htmlAttrs: {
add: {
data: { foo: 'bar' },
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: {
data: { foo: 'baz' },
expect: ['<html foo="baz">'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({ htmlAttrs: { foo: { ssr: 'baz' } } })
}
},
},
remove: {
data: {},
expect: ['<html>'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({ htmlAttrs: { foo: {} } })
}
},
},
},
headAttrs: {
add: {
data: { foo: 'bar' },
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: {
data: { foo: 'baz' },
expect: ['<head foo="baz">'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({ headAttrs: { foo: { ssr: 'baz' } } })
}
},
},
remove: {
data: {},
expect: ['<head>'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({ headAttrs: { foo: {} } })
}
},
},
},
bodyAttrs: {
add: {
data: { foo: 'bar', fizz: ['fuzz', 'fozz'] },
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: {
data: { foo: 'baz' },
expect: ['<body foo="baz">'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({
bodyAttrs: { foo: { ssr: 'baz' }, fizz: {} },
})
}
},
},
remove: {
data: {},
expect: ['<body>'],
test(side, defaultTest) {
return () => {
defaultTest()
expect(attributeMap).toEqual({ bodyAttrs: { foo: {}, fizz: {} } })
}
},
},
},
}
export default metaInfoData
-4
View File
@@ -1,4 +0,0 @@
process.server = true
jest.useFakeTimers()
jest.setTimeout(30000)