You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

231 lines
7.0 KiB

import webpack from 'webpack'
import Vue from '../../dist/vue.runtime.common.js'
import { VueSSRClientPlugin } from 'vue-ssr-webpack-plugin'
import { compileWithWebpack } from './compile-with-webpack'
import { createRenderer } from '../../packages/vue-server-renderer'
import { createRenderer as createBundleRenderer } from './ssr-bundle-render.spec.js'
const defaultTemplate = `<html><head></head><body><!--vue-ssr-outlet--></body></html>`
function generateClientManifest (file, cb) {
compileWithWebpack(file, {
output: {
path: '/',
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new VueSSRClientPlugin()
]
}, fs => {
cb(JSON.parse(fs.readFileSync('/vue-ssr-client-manifest.json', 'utf-8')))
})
}
function createRendererWithManifest (file, options, cb) {
if (typeof options === 'function') {
cb = options
options = null
}
generateClientManifest(file, clientManifest => {
createBundleRenderer(file, Object.assign({
asBundle: true,
template: defaultTemplate,
clientManifest
}, options), cb)
})
}
describe('SSR: template option', () => {
it('renderToString', done => {
const renderer = createRenderer({
template: defaultTemplate
})
const context = {
head: '<meta name="viewport" content="width=device-width">',
styles: '<style>h1 { color: red }</style>',
state: { a: 1 }
}
renderer.renderToString(new Vue({
template: '<div>hi</div>'
}), (err, res) => {
expect(err).toBeNull()
expect(res).toContain(
`<html><head>${context.head}${context.styles}</head><body>` +
`<div data-server-rendered="true">hi</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
done()
}, context)
})
it('renderToStream', done => {
const renderer = createRenderer({
template: defaultTemplate
})
const context = {
head: '<meta name="viewport" content="width=device-width">',
styles: '<style>h1 { color: red }</style>',
state: { a: 1 }
}
const stream = renderer.renderToStream(new Vue({
template: '<div>hi</div>'
}), context)
let res = ''
stream.on('data', chunk => {
res += chunk
})
stream.on('end', () => {
expect(res).toContain(
`<html><head>${context.head}${context.styles}</head><body>` +
`<div data-server-rendered="true">hi</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
done()
})
})
it('bundleRenderer + renderToString', done => {
createBundleRenderer('app.js', {
asBundle: true,
template: defaultTemplate
}, renderer => {
const context = {
head: '<meta name="viewport" content="width=device-width">',
styles: '<style>h1 { color: red }</style>',
state: { a: 1 },
url: '/test'
}
renderer.renderToString(context, (err, res) => {
expect(err).toBeNull()
expect(res).toContain(
`<html><head>${context.head}${context.styles}</head><body>` +
`<div data-server-rendered="true">/test</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
expect(context.msg).toBe('hello')
done()
})
})
})
it('bundleRenderer + renderToStream', done => {
createBundleRenderer('app.js', {
asBundle: true,
template: defaultTemplate
}, renderer => {
const context = {
head: '<meta name="viewport" content="width=device-width">',
styles: '<style>h1 { color: red }</style>',
state: { a: 1 },
url: '/test'
}
const stream = renderer.renderToStream(context)
let res = ''
stream.on('data', chunk => {
res += chunk.toString()
})
stream.on('end', () => {
expect(res).toContain(
`<html><head>${context.head}${context.styles}</head><body>` +
`<div data-server-rendered="true">/test</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
expect(context.msg).toBe('hello')
done()
})
})
})
const expectedHTMLWithManifest = preloadOtherAssets =>
`<html><head>` +
// used chunks should have preload
`<link rel="preload" href="/manifest.js" as="script">` +
`<link rel="preload" href="/main.js" as="script">` +
`<link rel="preload" href="/0.js" as="script">` +
// images and fonts are only preloaded when explicitly asked for
(preloadOtherAssets ? `<link rel="preload" href="/test.png" as="image">` : ``) +
(preloadOtherAssets ? `<link rel="preload" href="/test.woff2" as="font" type="font/woff2" crossorigin>` : ``) +
// unused chunks should have prefetch
`<link rel="prefetch" href="/1.js" as="script">` +
`</head><body>` +
`<div data-server-rendered="true"><div>async test.woff2 test.png</div></div>` +
// manifest chunk should be first
`<script src="/manifest.js"></script>` +
// async chunks should be before main chunk
`<script src="/0.js"></script>` +
`<script src="/main.js"></script>` +
`</body></html>`
createClientManifestAssertions(true)
createClientManifestAssertions(false)
function createClientManifestAssertions (directMode) {
it('bundleRenderer + renderToString + clientManifest', done => {
createRendererWithManifest('split.js', { directMode }, renderer => {
renderer.renderToString({}, (err, res) => {
expect(err).toBeNull()
expect(res).toContain(expectedHTMLWithManifest(false))
done()
})
})
})
it('bundleRenderer + renderToStream + clientManifest + shouldPreload', done => {
createRendererWithManifest('split.js', {
directMode,
shouldPreload: (file, type) => {
if (type === 'image' || type === 'script' || type === 'font') {
return true
}
}
}, renderer => {
const stream = renderer.renderToStream({})
let res = ''
stream.on('data', chunk => {
res += chunk.toString()
})
stream.on('end', () => {
expect(res).toContain(expectedHTMLWithManifest(true))
done()
})
})
})
it('bundleRenderer + renderToString + clientManifest + no template', done => {
createRendererWithManifest('split.js', {
directMode,
template: null
}, renderer => {
const context = {}
renderer.renderToString(context, (err, res) => {
expect(err).toBeNull()
const customOutput =
`<html><head>${
context.renderPreloadLinks() +
context.renderPrefetchLinks()
}</head><body>${
res +
context.renderScripts()
}</body></html>`
expect(customOutput).toContain(expectedHTMLWithManifest(false))
done()
})
})
})
}
})