ssr.string.spec.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import Vue from '../../dist/vue.common.js'
  2. import { compileToFunctions } from '../../dist/compiler.common.js'
  3. import createRenderer from '../../dist/server-renderer.js'
  4. const { renderToString } = createRenderer()
  5. // TODO: test custom server-side directives
  6. describe('SSR: renderToString', () => {
  7. it('static attributes', () => {
  8. expect(renderVmWithOptions({
  9. template: '<div id="foo" bar="123"></div>'
  10. })).toContain('<div id="foo" bar="123" server-rendered="true"></div>')
  11. })
  12. it('unary tags', () => {
  13. expect(renderVmWithOptions({
  14. template: '<input value="123">'
  15. })).toContain('<input value="123" server-rendered="true">')
  16. })
  17. it('dynamic attributes', () => {
  18. expect(renderVmWithOptions({
  19. template: '<div qux="quux" :id="foo" :bar="baz"></div>',
  20. data: {
  21. foo: 'hi',
  22. baz: 123
  23. }
  24. })).toContain('<div qux="quux" id="hi" bar="123" server-rendered="true"></div>')
  25. })
  26. it('static class', () => {
  27. expect(renderVmWithOptions({
  28. template: '<div class="foo bar"></div>'
  29. })).toContain('<div server-rendered="true" class="foo bar"></div>')
  30. })
  31. it('dynamic class', () => {
  32. expect(renderVmWithOptions({
  33. template: '<div class="foo bar" :class="[a, { qux: hasQux, quux: hasQuux }]"></div>',
  34. data: {
  35. a: 'baz',
  36. hasQux: true,
  37. hasQuux: false
  38. }
  39. })).toContain('<div server-rendered="true" class="foo bar baz qux"></div>')
  40. })
  41. it('dynamic style', () => {
  42. expect(renderVmWithOptions({
  43. template: '<div style="background-color:black" :style="{ fontSize: fontSize + \'px\', color: color }"></div>',
  44. data: {
  45. fontSize: 14,
  46. color: 'red'
  47. }
  48. })).toContain(
  49. '<div server-rendered="true" style="font-size:14px;color:red;background-color:black"></div>'
  50. )
  51. })
  52. it('text interpolation', () => {
  53. expect(renderVmWithOptions({
  54. template: '<div>{{ foo }} side {{ bar }}</div>',
  55. data: {
  56. foo: 'server',
  57. bar: 'rendering'
  58. }
  59. })).toContain('<div server-rendered="true">server side rendering</div>')
  60. })
  61. it('child component (hoc)', () => {
  62. expect(renderVmWithOptions({
  63. template: '<child class="foo" :msg="msg"></child>',
  64. data: {
  65. msg: 'hello'
  66. },
  67. components: {
  68. child: {
  69. props: ['msg'],
  70. data () {
  71. return { name: 'bar' }
  72. },
  73. render () {
  74. const h = this.$createElement
  75. return h('div', { class: ['bar'] }, [`${this.msg} ${this.name}`])
  76. }
  77. }
  78. }
  79. })).toContain('<div server-rendered="true" class="foo bar">hello bar</div>')
  80. })
  81. it('has correct lifecycle during render', () => {
  82. let lifecycleCount = 1
  83. expect(renderVmWithOptions({
  84. template: '<div><span>{{ val }}</span><test></test></div>',
  85. data: {
  86. val: 'hi'
  87. },
  88. init () {
  89. expect(lifecycleCount++).toBe(1)
  90. },
  91. created () {
  92. this.val = 'hello'
  93. expect(this.val).toBe('hello')
  94. expect(lifecycleCount++).toBe(2)
  95. },
  96. components: {
  97. test: {
  98. init () {
  99. expect(lifecycleCount++).toBe(3)
  100. },
  101. created () {
  102. expect(lifecycleCount++).toBe(4)
  103. },
  104. render () {
  105. expect(lifecycleCount++).toBeGreaterThan(4)
  106. return this.$createElement('span', { class: ['b'] }, 'testAsync')
  107. }
  108. }
  109. }
  110. })).toContain(
  111. '<div server-rendered="true">' +
  112. '<span>hello</span>' +
  113. '<span class="b">testAsync</span>' +
  114. '</div>'
  115. )
  116. })
  117. it('renders asynchronous component', () => {
  118. expect(renderVmWithOptions({
  119. template: `
  120. <div>
  121. <test-async></test-async>
  122. </div>
  123. `,
  124. components: {
  125. testAsync (resolve) {
  126. return resolve({
  127. render () {
  128. return this.$createElement('span', { class: ['b'] }, 'testAsync')
  129. }
  130. })
  131. }
  132. }
  133. })).toContain('<div server-rendered="true"><span class="b">testAsync</span></div>')
  134. })
  135. it('renders asynchronous component (hoc)', () => {
  136. expect(renderVmWithOptions({
  137. template: '<test-async></test-async>',
  138. components: {
  139. testAsync (resolve) {
  140. return resolve({
  141. render () {
  142. return this.$createElement('span', { class: ['b'] }, 'testAsync')
  143. }
  144. })
  145. }
  146. }
  147. })).toContain('<span server-rendered="true" class="b">testAsync</span>')
  148. })
  149. it('renders nested asynchronous component', () => {
  150. expect(renderVmWithOptions({
  151. template: `
  152. <div>
  153. <test-async></test-async>
  154. </div>
  155. `,
  156. components: {
  157. testAsync (resolve) {
  158. const options = compileToFunctions(`
  159. <span class="b">
  160. <test-sub-async></test-sub-async>
  161. </span>
  162. `, { preserveWhitespace: false })
  163. options.components = {
  164. testSubAsync (resolve) {
  165. return resolve({
  166. render () {
  167. return this.$createElement('div', { class: ['c'] }, 'testSubAsync')
  168. }
  169. })
  170. }
  171. }
  172. return resolve(options)
  173. }
  174. }
  175. })).toContain(
  176. '<div server-rendered="true"><span class="b"><div class="c">testSubAsync</div></span></div>'
  177. )
  178. })
  179. it('everything together', () => {
  180. expect(renderVmWithOptions({
  181. template: `
  182. <div>
  183. <p class="hi">yoyo</p>
  184. <div id="ho" :class="{ red: isRed }"></div>
  185. <span>{{ test }}</span>
  186. <input :value="test">
  187. <test></test>
  188. <test-async></test-async>
  189. </div>
  190. `,
  191. data: {
  192. test: 'hi',
  193. isRed: true
  194. },
  195. components: {
  196. test: {
  197. render () {
  198. return this.$createElement('div', { class: ['a'] }, 'test')
  199. }
  200. },
  201. testAsync (resolve) {
  202. return resolve({
  203. render () {
  204. return this.$createElement('span', { class: ['b'] }, 'testAsync')
  205. }
  206. })
  207. }
  208. }
  209. })).toContain(
  210. '<div server-rendered="true">' +
  211. '<p class="hi">yoyo</p>' +
  212. '<div id="ho" class="red"></div>' +
  213. '<span>hi</span>' +
  214. '<input value="hi">' +
  215. '<div class="a">test</div>' +
  216. '<span class="b">testAsync</span>' +
  217. '</div>'
  218. )
  219. })
  220. })
  221. function renderVmWithOptions (options) {
  222. const res = compileToFunctions(options.template, {
  223. preserveWhitespace: false
  224. })
  225. Object.assign(options, res)
  226. delete options.template
  227. return renderToString(new Vue(options))
  228. }