import Vue from '../../dist/vue.common.js' import { compileToFunctions } from '../../dist/compiler.js' import createRenderer from '../../dist/server-renderer.js' const { renderToString } = createRenderer() describe('SSR: renderToString', () => { it('static attributes', done => { renderVmWithOptions({ template: '
' }, result => { expect(result).toContain('
') done() }) }) it('unary tags', done => { renderVmWithOptions({ template: '' }, result => { expect(result).toContain('') done() }) }) it('dynamic attributes', done => { renderVmWithOptions({ template: '
', data: { foo: 'hi', baz: 123 } }, result => { expect(result).toContain('
') done() }) }) it('static class', done => { renderVmWithOptions({ template: '
' }, result => { expect(result).toContain('
') done() }) }) it('dynamic class', done => { renderVmWithOptions({ template: '
', data: { a: 'baz', hasQux: true, hasQuux: false } }, result => { expect(result).toContain('
') done() }) }) it('dynamic style', done => { renderVmWithOptions({ template: '
', data: { fontSize: 14, color: 'red' } }, result => { expect(result).toContain( '
' ) done() }) }) it('text interpolation', done => { renderVmWithOptions({ template: '
{{ foo }} side {{ bar }}
', data: { foo: 'server', bar: 'rendering' } }, result => { expect(result).toContain('
server side rendering
') done() }) }) it('child component (hoc)', done => { renderVmWithOptions({ template: '', data: { msg: 'hello' }, components: { child: { props: ['msg'], data () { return { name: 'bar' } }, render () { const h = this.$createElement return h('div', { class: ['bar'] }, [`${this.msg} ${this.name}`]) } } } }, result => { expect(result).toContain('
hello bar
') done() }) }) it('has correct lifecycle during render', done => { let lifecycleCount = 1 renderVmWithOptions({ template: '
{{ val }}
', data: { val: 'hi' }, init () { expect(lifecycleCount++).toBe(1) }, created () { this.val = 'hello' expect(this.val).toBe('hello') expect(lifecycleCount++).toBe(2) }, components: { test: { init () { expect(lifecycleCount++).toBe(3) }, created () { expect(lifecycleCount++).toBe(4) }, render () { expect(lifecycleCount++).toBeGreaterThan(4) return this.$createElement('span', { class: ['b'] }, 'testAsync') } } } }, result => { expect(result).toContain( '
' + 'hello' + 'testAsync' + '
' ) done() }) }) it('renders asynchronous component', done => { renderVmWithOptions({ template: `
`, components: { testAsync (resolve) { resolve({ render () { return this.$createElement('span', { class: ['b'] }, 'testAsync') } }) } } }, result => { expect(result).toContain('
testAsync
') done() }) }) it('renders asynchronous component (hoc)', done => { renderVmWithOptions({ template: '', components: { testAsync (resolve) { resolve({ render () { return this.$createElement('span', { class: ['b'] }, 'testAsync') } }) } } }, result => { expect(result).toContain('testAsync') done() }) }) it('renders nested asynchronous component', done => { renderVmWithOptions({ template: `
`, components: { testAsync (resolve) { const options = compileToFunctions(` `, { preserveWhitespace: false }) options.components = { testSubAsync (resolve) { resolve({ render () { return this.$createElement('div', { class: ['c'] }, 'testSubAsync') } }) } } resolve(options) } } }, result => { expect(result).toContain('
testSubAsync
') done() }) }) it('everything together', done => { renderVmWithOptions({ template: `

yoyo

{{ test }}
`, data: { test: 'hi', isRed: true, imageUrl: 'https://vuejs.org/images/logo.png' }, components: { test: { render () { return this.$createElement('div', { class: ['a'] }, 'test') } }, testAsync (resolve) { resolve({ render () { return this.$createElement('span', { class: ['b'] }, 'testAsync') } }) } } }, result => { expect(result).toContain( '
' + '

yoyo

' + '
' + 'hi' + '' + '' + '
test
' + 'testAsync' + '
' ) done() }) }) it('normal attr', done => { renderVmWithOptions({ template: `
hello hello hello hello hello
` }, result => { expect(result).toContain( '
' + 'hello' + 'hello' + 'hello' + 'hello' + 'hello' + '
' ) done() }) }) it('enumrated attr', done => { renderVmWithOptions({ template: `
hello hello hello hello hello hello
` }, result => { expect(result).toContain( '
' + 'hello' + 'hello' + 'hello' + 'hello' + 'hello' + 'hello' + '
' ) done() }) }) it('boolean attr', done => { renderVmWithOptions({ template: `
hello hello hello hello
` }, result => { expect(result).toContain( '
' + 'hello' + 'hello' + 'hello' + 'hello' + '
' ) done() }) }) it('v-bind object', done => { renderVmWithOptions({ data: { test: { id: 'a', class: 'b', value: 'c' } }, template: '' }, result => { expect(result).toContain('') done() }) }) it('custom directives', done => { const renderer = createRenderer({ directives: { 'class-prefixer': (node, dir) => { if (node.data.class) { node.data.class = `${dir.value}-${node.data.class}` } if (node.data.staticClass) { node.data.staticClass = `${dir.value}-${node.data.staticClass}` } } } }) renderer.renderToString(new Vue({ render () { const h = this.$createElement return h('p', { class: 'class1', staticClass: 'class2', directives: [{ name: 'class-prefixer', value: 'my' }] }, ['hello world']) } }), (err, result) => { expect(err).toBeNull() expect(result).toContain('

hello world

') done() }) }) it('should catch error', done => { renderToString(new Vue({ render () { throw new Error('oops') } }), err => { expect(err instanceof Error).toBe(true) done() }) }) }) function renderVmWithOptions (options, cb) { const res = compileToFunctions(options.template, { preserveWhitespace: false }) Object.assign(options, res) delete options.template renderToString(new Vue(options), (err, res) => { expect(err).toBeNull() cb(res) }) }