ssrElement.spec.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import { getCompiledString } from './utils'
  2. import { compile } from '../src'
  3. describe('ssr: element', () => {
  4. test('basic elements', () => {
  5. expect(getCompiledString(`<div></div>`)).toMatchInlineSnapshot(
  6. `"\`<div></div>\`"`,
  7. )
  8. expect(getCompiledString(`<div/>`)).toMatchInlineSnapshot(
  9. `"\`<div></div>\`"`,
  10. )
  11. })
  12. test('nested elements', () => {
  13. expect(
  14. getCompiledString(`<div><span></span><span></span></div>`),
  15. ).toMatchInlineSnapshot(`"\`<div><span></span><span></span></div>\`"`)
  16. })
  17. test('void element', () => {
  18. expect(getCompiledString(`<input>`)).toMatchInlineSnapshot(`"\`<input>\`"`)
  19. })
  20. describe('children override', () => {
  21. test('v-html', () => {
  22. expect(getCompiledString(`<div v-html="foo"/>`)).toMatchInlineSnapshot(`
  23. "\`<div>\${
  24. (_ctx.foo) ?? ''
  25. }</div>\`"
  26. `)
  27. })
  28. test('v-text', () => {
  29. expect(getCompiledString(`<div v-text="foo"/>`)).toMatchInlineSnapshot(`
  30. "\`<div>\${
  31. _ssrInterpolate(_ctx.foo)
  32. }</div>\`"
  33. `)
  34. })
  35. test('<textarea> with dynamic value', () => {
  36. expect(getCompiledString(`<textarea :value="foo"/>`))
  37. .toMatchInlineSnapshot(`
  38. "\`<textarea>\${
  39. _ssrInterpolate(_ctx.foo)
  40. }</textarea>\`"
  41. `)
  42. })
  43. test('<textarea> with static value', () => {
  44. expect(
  45. getCompiledString(`<textarea value="fo&gt;o"/>`),
  46. ).toMatchInlineSnapshot(`"\`<textarea>fo&gt;o</textarea>\`"`)
  47. })
  48. test('<textarea> with dynamic v-bind', () => {
  49. expect(compile(`<textarea v-bind="obj">fallback</textarea>`).code)
  50. .toMatchInlineSnapshot(`
  51. "const { mergeProps: _mergeProps } = require("vue")
  52. const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer")
  53. return function ssrRender(_ctx, _push, _parent, _attrs) {
  54. let _temp0
  55. _push(\`<textarea\${
  56. _ssrRenderAttrs(_temp0 = _mergeProps(_ctx.obj, _attrs), "textarea")
  57. }>\${
  58. _ssrInterpolate(("value" in _temp0) ? _temp0.value : "fallback")
  59. }</textarea>\`)
  60. }"
  61. `)
  62. })
  63. test('multiple _ssrInterpolate at parent and child import dependency once', () => {
  64. expect(
  65. compile(`<div>{{ hello }}<textarea v-bind="a"></textarea></div>`).code,
  66. ).toMatchInlineSnapshot(`
  67. "const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require("vue/server-renderer")
  68. return function ssrRender(_ctx, _push, _parent, _attrs) {
  69. let _temp0
  70. _push(\`<div\${
  71. _ssrRenderAttrs(_attrs)
  72. }>\${
  73. _ssrInterpolate(_ctx.hello)
  74. }<textarea\${
  75. _ssrRenderAttrs(_temp0 = _ctx.a, "textarea")
  76. }>\${
  77. _ssrInterpolate(("value" in _temp0) ? _temp0.value : "")
  78. }</textarea></div>\`)
  79. }"
  80. `)
  81. })
  82. test('should pass tag to custom elements w/ dynamic v-bind', () => {
  83. expect(
  84. compile(`<my-foo v-bind="obj"></my-foo>`, {
  85. isCustomElement: () => true,
  86. }).code,
  87. ).toMatchInlineSnapshot(`
  88. "const { mergeProps: _mergeProps } = require("vue")
  89. const { ssrRenderAttrs: _ssrRenderAttrs } = require("vue/server-renderer")
  90. return function ssrRender(_ctx, _push, _parent, _attrs) {
  91. _push(\`<my-foo\${_ssrRenderAttrs(_mergeProps(_ctx.obj, _attrs), "my-foo")}></my-foo>\`)
  92. }"
  93. `)
  94. })
  95. })
  96. describe('attrs', () => {
  97. test('static attrs', () => {
  98. expect(
  99. getCompiledString(`<div id="foo" class="bar"></div>`),
  100. ).toMatchInlineSnapshot(`"\`<div id="foo" class="bar"></div>\`"`)
  101. })
  102. test('ignore static key/ref', () => {
  103. expect(
  104. getCompiledString(`<div key="1" ref="el"></div>`),
  105. ).toMatchInlineSnapshot(`"\`<div></div>\`"`)
  106. })
  107. test('ignore v-bind key/ref', () => {
  108. expect(
  109. getCompiledString(`<div :key="1" :ref="el"></div>`),
  110. ).toMatchInlineSnapshot(`"\`<div></div>\`"`)
  111. })
  112. test('v-bind:class', () => {
  113. expect(getCompiledString(`<div id="foo" :class="bar"></div>`))
  114. .toMatchInlineSnapshot(`
  115. "\`<div id="foo" class="\${
  116. _ssrRenderClass(_ctx.bar)
  117. }"></div>\`"
  118. `)
  119. })
  120. test('static class + v-bind:class', () => {
  121. expect(getCompiledString(`<div class="foo" :class="bar"></div>`))
  122. .toMatchInlineSnapshot(`
  123. "\`<div class="\${
  124. _ssrRenderClass([_ctx.bar, "foo"])
  125. }"></div>\`"
  126. `)
  127. })
  128. test('v-bind:class + static class', () => {
  129. expect(getCompiledString(`<div :class="bar" class="foo"></div>`))
  130. .toMatchInlineSnapshot(`
  131. "\`<div class="\${
  132. _ssrRenderClass([_ctx.bar, "foo"])
  133. }"></div>\`"
  134. `)
  135. })
  136. test('v-bind:style', () => {
  137. expect(getCompiledString(`<div id="foo" :style="bar"></div>`))
  138. .toMatchInlineSnapshot(`
  139. "\`<div id="foo" style="\${
  140. _ssrRenderStyle(_ctx.bar)
  141. }"></div>\`"
  142. `)
  143. })
  144. test('static style + v-bind:style', () => {
  145. expect(getCompiledString(`<div style="color:red;" :style="bar"></div>`))
  146. .toMatchInlineSnapshot(`
  147. "\`<div style="\${
  148. _ssrRenderStyle([{"color":"red"}, _ctx.bar])
  149. }"></div>\`"
  150. `)
  151. })
  152. test('v-bind:arg (boolean)', () => {
  153. expect(getCompiledString(`<input type="checkbox" :checked="checked">`))
  154. .toMatchInlineSnapshot(`
  155. "\`<input type="checkbox"\${
  156. (_ssrIncludeBooleanAttr(_ctx.checked)) ? " checked" : ""
  157. }>\`"
  158. `)
  159. })
  160. test('v-bind:arg (non-boolean)', () => {
  161. expect(getCompiledString(`<div :id="id" class="bar"></div>`))
  162. .toMatchInlineSnapshot(`
  163. "\`<div\${
  164. _ssrRenderAttr("id", _ctx.id)
  165. } class="bar"></div>\`"
  166. `)
  167. })
  168. test('v-bind:[arg]', () => {
  169. expect(getCompiledString(`<div v-bind:[key]="value"></div>`))
  170. .toMatchInlineSnapshot(`
  171. "\`<div\${
  172. _ssrRenderAttrs({ [_ctx.key || ""]: _ctx.value })
  173. }></div>\`"
  174. `)
  175. expect(getCompiledString(`<div class="foo" v-bind:[key]="value"></div>`))
  176. .toMatchInlineSnapshot(`
  177. "\`<div\${
  178. _ssrRenderAttrs({
  179. class: "foo",
  180. [_ctx.key || ""]: _ctx.value
  181. })
  182. }></div>\`"
  183. `)
  184. expect(getCompiledString(`<div :id="id" v-bind:[key]="value"></div>`))
  185. .toMatchInlineSnapshot(`
  186. "\`<div\${
  187. _ssrRenderAttrs({
  188. id: _ctx.id,
  189. [_ctx.key || ""]: _ctx.value
  190. })
  191. }></div>\`"
  192. `)
  193. })
  194. test('v-bind="obj"', () => {
  195. expect(getCompiledString(`<div v-bind="obj"></div>`))
  196. .toMatchInlineSnapshot(`
  197. "\`<div\${
  198. _ssrRenderAttrs(_ctx.obj)
  199. }></div>\`"
  200. `)
  201. expect(getCompiledString(`<div class="foo" v-bind="obj"></div>`))
  202. .toMatchInlineSnapshot(`
  203. "\`<div\${
  204. _ssrRenderAttrs(_mergeProps({ class: "foo" }, _ctx.obj))
  205. }></div>\`"
  206. `)
  207. expect(getCompiledString(`<div :id="id" v-bind="obj"></div>`))
  208. .toMatchInlineSnapshot(`
  209. "\`<div\${
  210. _ssrRenderAttrs(_mergeProps({ id: _ctx.id }, _ctx.obj))
  211. }></div>\`"
  212. `)
  213. // dynamic key + v-bind="object"
  214. expect(getCompiledString(`<div :[key]="id" v-bind="obj"></div>`))
  215. .toMatchInlineSnapshot(`
  216. "\`<div\${
  217. _ssrRenderAttrs(_mergeProps({ [_ctx.key || ""]: _ctx.id }, _ctx.obj))
  218. }></div>\`"
  219. `)
  220. // should merge class and :class
  221. expect(getCompiledString(`<div class="a" :class="b" v-bind="obj"></div>`))
  222. .toMatchInlineSnapshot(`
  223. "\`<div\${
  224. _ssrRenderAttrs(_mergeProps({
  225. class: ["a", _ctx.b]
  226. }, _ctx.obj))
  227. }></div>\`"
  228. `)
  229. // should merge style and :style
  230. expect(
  231. getCompiledString(
  232. `<div style="color:red;" :style="b" v-bind="obj"></div>`,
  233. ),
  234. ).toMatchInlineSnapshot(`
  235. "\`<div\${
  236. _ssrRenderAttrs(_mergeProps({
  237. style: [{"color":"red"}, _ctx.b]
  238. }, _ctx.obj))
  239. }></div>\`"
  240. `)
  241. })
  242. test('should ignore v-on', () => {
  243. expect(
  244. getCompiledString(`<div id="foo" @click="bar"/>`),
  245. ).toMatchInlineSnapshot(`"\`<div id="foo"></div>\`"`)
  246. expect(
  247. getCompiledString(`<div id="foo" v-on="bar"/>`),
  248. ).toMatchInlineSnapshot(`"\`<div id="foo"></div>\`"`)
  249. expect(getCompiledString(`<div v-bind="foo" v-on="bar"/>`))
  250. .toMatchInlineSnapshot(`
  251. "\`<div\${
  252. _ssrRenderAttrs(_ctx.foo)
  253. }></div>\`"
  254. `)
  255. })
  256. })
  257. describe('custom directives', () => {
  258. // #8112 should respect textContent / innerHTML from directive getSSRProps
  259. // if the element has no children
  260. test('custom dir without children', () => {
  261. expect(getCompiledString(`<div v-xxx:x.y="z" />`)).toMatchInlineSnapshot(`
  262. "\`<div\${
  263. _ssrRenderAttrs(_temp0 = _ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.z, "x", { y: true }))
  264. }>\${
  265. ("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
  266. }</div>\`"
  267. `)
  268. })
  269. test('custom dir with children', () => {
  270. expect(getCompiledString(`<div v-xxx:x.y="z">hello</div>`))
  271. .toMatchInlineSnapshot(`
  272. "\`<div\${
  273. _ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.z, "x", { y: true }))
  274. }>hello</div>\`"
  275. `)
  276. })
  277. test('custom dir with normal attrs', () => {
  278. expect(getCompiledString(`<div class="foo" v-xxx />`))
  279. .toMatchInlineSnapshot(`
  280. "\`<div\${
  281. _ssrRenderAttrs(_temp0 = _mergeProps({ class: "foo" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
  282. }>\${
  283. ("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
  284. }</div>\`"
  285. `)
  286. })
  287. test('custom dir with v-bind', () => {
  288. expect(getCompiledString(`<div :title="foo" :class="bar" v-xxx />`))
  289. .toMatchInlineSnapshot(`
  290. "\`<div\${
  291. _ssrRenderAttrs(_temp0 = _mergeProps({
  292. title: _ctx.foo,
  293. class: _ctx.bar
  294. }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
  295. }>\${
  296. ("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
  297. }</div>\`"
  298. `)
  299. })
  300. test('custom dir with v-text', () => {
  301. expect(getCompiledString(`<div v-xxx v-text="foo" />`))
  302. .toMatchInlineSnapshot(`
  303. "\`<div\${
  304. _ssrRenderAttrs(_ssrGetDirectiveProps(_ctx, _directive_xxx))
  305. }>\${
  306. _ssrInterpolate(_ctx.foo)
  307. }</div>\`"
  308. `)
  309. })
  310. test('custom dir with v-text and normal attrs', () => {
  311. expect(getCompiledString(`<div class="test" v-xxx v-text="foo" />`))
  312. .toMatchInlineSnapshot(`
  313. "\`<div\${
  314. _ssrRenderAttrs(_mergeProps({ class: "test" }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
  315. }>\${
  316. _ssrInterpolate(_ctx.foo)
  317. }</div>\`"
  318. `)
  319. })
  320. test('mulptiple custom dirs with v-text', () => {
  321. expect(getCompiledString(`<div v-xxx v-yyy v-text="foo" />`))
  322. .toMatchInlineSnapshot(`
  323. "\`<div\${
  324. _ssrRenderAttrs(_mergeProps(_ssrGetDirectiveProps(_ctx, _directive_xxx), _ssrGetDirectiveProps(_ctx, _directive_yyy)))
  325. }>\${
  326. _ssrInterpolate(_ctx.foo)
  327. }</div>\`"
  328. `)
  329. })
  330. test('custom dir with object v-bind', () => {
  331. expect(getCompiledString(`<div v-bind="x" v-xxx />`))
  332. .toMatchInlineSnapshot(`
  333. "\`<div\${
  334. _ssrRenderAttrs(_temp0 = _mergeProps(_ctx.x, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
  335. }>\${
  336. ("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
  337. }</div>\`"
  338. `)
  339. })
  340. test('custom dir with object v-bind + normal bindings', () => {
  341. expect(
  342. getCompiledString(`<div v-bind="x" class="foo" v-xxx title="bar" />`),
  343. ).toMatchInlineSnapshot(`
  344. "\`<div\${
  345. _ssrRenderAttrs(_temp0 = _mergeProps(_ctx.x, {
  346. class: "foo",
  347. title: "bar"
  348. }, _ssrGetDirectiveProps(_ctx, _directive_xxx)))
  349. }>\${
  350. ("textContent" in _temp0) ? _ssrInterpolate(_temp0.textContent) : _temp0.innerHTML ?? ''
  351. }</div>\`"
  352. `)
  353. })
  354. })
  355. })