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.

275 lines
7.9 KiB

import path from 'path'
import webpack from 'webpack'
import MemoeryFS from 'memory-fs'
import VueSSRPlugin from 'vue-ssr-webpack-plugin'
import { createBundleRenderer } from '../../packages/vue-server-renderer'
function createRenderer (file, cb, options) {
const asBundle = !!(options && options.asBundle)
if (options) delete options.asBundle
const config = {
target: 'node',
entry: path.resolve(__dirname, 'fixtures', file),
devtool: asBundle ? '#source-map' : false,
output: {
path: '/',
filename: 'bundle.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [{ test: /\.js$/, loader: 'babel-loader' }]
},
externals: [require.resolve('../../dist/vue.runtime.common.js')],
plugins: asBundle
? [new VueSSRPlugin()]
: []
}
const compiler = webpack(config)
const fs = new MemoeryFS()
compiler.outputFileSystem = fs
compiler.run((err, stats) => {
expect(err).toBeFalsy()
expect(stats.errors).toBeFalsy()
const bundle = asBundle
? JSON.parse(fs.readFileSync('/vue-ssr-bundle.json', 'utf-8'))
: fs.readFileSync('/bundle.js', 'utf-8')
const renderer = createBundleRenderer(bundle, options)
cb(renderer)
})
}
describe('SSR: bundle renderer', () => {
it('renderToString', done => {
createRenderer('app.js', renderer => {
const context = { url: '/test' }
renderer.renderToString(context, (err, res) => {
9 years ago
expect(err).toBeNull()
expect(res).toBe('<div server-rendered="true">/test</div>')
expect(context.msg).toBe('hello')
done()
})
})
})
it('renderToStream', done => {
createRenderer('app.js', renderer => {
const context = { url: '/test' }
const stream = renderer.renderToStream(context)
let res = ''
stream.on('data', chunk => {
res += chunk.toString()
})
stream.on('end', () => {
expect(res).toBe('<div server-rendered="true">/test</div>')
expect(context.msg).toBe('hello')
done()
})
})
})
it('renderToString catch error', done => {
createRenderer('error.js', renderer => {
renderer.renderToString(err => {
expect(err.message).toBe('foo')
done()
})
})
})
it('renderToStream catch error', done => {
createRenderer('error.js', renderer => {
const stream = renderer.renderToStream()
stream.on('error', err => {
expect(err.message).toBe('foo')
done()
})
})
})
it('render with cache (get/set)', done => {
const cache = {}
const get = jasmine.createSpy('get')
const set = jasmine.createSpy('set')
const options = {
cache: {
// async
get: (key, cb) => {
setTimeout(() => {
get(key)
cb(cache[key])
}, 0)
},
set: (key, val) => {
set(key, val)
cache[key] = val
}
}
}
createRenderer('cache.js', renderer => {
const expected = '<div server-rendered="true">/test</div>'
const key = 'app::1'
renderer.renderToString((err, res) => {
expect(err).toBeNull()
expect(res).toBe(expected)
expect(get).toHaveBeenCalledWith(key)
expect(set).toHaveBeenCalledWith(key, expected)
expect(cache[key]).toBe(expected)
renderer.renderToString((err, res) => {
expect(err).toBeNull()
expect(res).toBe(expected)
expect(get.calls.count()).toBe(2)
expect(set.calls.count()).toBe(1)
done()
})
})
}, options)
})
it('render with cache (get/set/has)', done => {
const cache = {}
const has = jasmine.createSpy('has')
const get = jasmine.createSpy('get')
const set = jasmine.createSpy('set')
const options = {
cache: {
// async
has: (key, cb) => {
has(key)
cb(!!cache[key])
},
// sync
get: key => {
get(key)
return cache[key]
},
set: (key, val) => {
set(key, val)
cache[key] = val
}
}
}
createRenderer('cache.js', renderer => {
const expected = '<div server-rendered="true">/test</div>'
const key = 'app::1'
renderer.renderToString((err, res) => {
expect(err).toBeNull()
expect(res).toBe(expected)
expect(has).toHaveBeenCalledWith(key)
expect(get).not.toHaveBeenCalled()
expect(set).toHaveBeenCalledWith(key, expected)
expect(cache[key]).toBe(expected)
renderer.renderToString((err, res) => {
expect(err).toBeNull()
expect(res).toBe(expected)
expect(has.calls.count()).toBe(2)
expect(get.calls.count()).toBe(1)
expect(set.calls.count()).toBe(1)
done()
})
})
}, options)
})
it('renderToString (bundle format with code split)', done => {
createRenderer('split.js', renderer => {
const context = { url: '/test' }
renderer.renderToString(context, (err, res) => {
expect(err).toBeNull()
expect(res).toBe('<div server-rendered="true">/test<div>async</div></div>')
done()
})
}, { asBundle: true })
})
it('renderToStream (bundle format with code split)', done => {
createRenderer('split.js', renderer => {
const context = { url: '/test' }
const stream = renderer.renderToStream(context)
let res = ''
stream.on('data', chunk => {
res += chunk.toString()
})
stream.on('end', () => {
expect(res).toBe('<div server-rendered="true">/test<div>async</div></div>')
done()
})
}, { asBundle: true })
})
it('renderToString catch error (bundle format with source map)', done => {
createRenderer('error.js', renderer => {
renderer.renderToString(err => {
expect(err.stack).toContain('test/ssr/fixtures/error.js:1:6')
expect(err.message).toBe('foo')
done()
})
}, { asBundle: true })
})
it('renderToString catch error (bundle format with source map)', done => {
createRenderer('error.js', renderer => {
const stream = renderer.renderToStream()
stream.on('error', err => {
expect(err.stack).toContain('test/ssr/fixtures/error.js:1:6')
expect(err.message).toBe('foo')
done()
})
}, { asBundle: true })
})
it('renderToString with template', done => {
createRenderer('app.js', 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 server-rendered="true">/test</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
expect(context.msg).toBe('hello')
done()
})
}, {
template: `<html><head></head><body><!--vue-ssr-outlet--></body></html>`
})
})
it('renderToStream with template', done => {
createRenderer('app.js', 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 server-rendered="true">/test</div>` +
`<script>window.__INITIAL_STATE__={"a":1}</script>` +
`</body></html>`
)
expect(context.msg).toBe('hello')
done()
})
}, {
template: `<html><head></head><body><!--vue-ssr-outlet--></body></html>`
})
})
})