ssrDirectives.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /**
  2. * @jest-environment node
  3. */
  4. import { renderToString } from '../src/renderToString'
  5. import {
  6. createApp,
  7. h,
  8. withDirectives,
  9. vShow,
  10. vModelText,
  11. vModelRadio,
  12. vModelCheckbox,
  13. resolveDirective
  14. } from 'vue'
  15. import { ssrGetDirectiveProps, ssrRenderAttrs } from '../src'
  16. describe('ssr: directives', () => {
  17. describe('template v-show', () => {
  18. test('basic', async () => {
  19. expect(
  20. await renderToString(
  21. createApp({
  22. template: `<div v-show="true"/>`
  23. })
  24. )
  25. ).toBe(`<div style=""></div>`)
  26. expect(
  27. await renderToString(
  28. createApp({
  29. template: `<div v-show="false"/>`
  30. })
  31. )
  32. ).toBe(`<div style="display:none;"></div>`)
  33. })
  34. test('with static style', async () => {
  35. expect(
  36. await renderToString(
  37. createApp({
  38. template: `<div style="color:red" v-show="false"/>`
  39. })
  40. )
  41. ).toBe(`<div style="color:red;display:none;"></div>`)
  42. })
  43. test('with dynamic style', async () => {
  44. expect(
  45. await renderToString(
  46. createApp({
  47. data: () => ({ style: { color: 'red' } }),
  48. template: `<div :style="style" v-show="false"/>`
  49. })
  50. )
  51. ).toBe(`<div style="color:red;display:none;"></div>`)
  52. })
  53. test('with static + dynamic style', async () => {
  54. expect(
  55. await renderToString(
  56. createApp({
  57. data: () => ({ style: { color: 'red' } }),
  58. template: `<div :style="style" style="font-size:12;" v-show="false"/>`
  59. })
  60. )
  61. ).toBe(`<div style="color:red;font-size:12;display:none;"></div>`)
  62. })
  63. })
  64. describe('template v-model', () => {
  65. test('text', async () => {
  66. expect(
  67. await renderToString(
  68. createApp({
  69. data: () => ({ text: 'hello' }),
  70. template: `<input v-model="text">`
  71. })
  72. )
  73. ).toBe(`<input value="hello">`)
  74. })
  75. test('radio', async () => {
  76. expect(
  77. await renderToString(
  78. createApp({
  79. data: () => ({ selected: 'foo' }),
  80. template: `<input type="radio" value="foo" v-model="selected">`
  81. })
  82. )
  83. ).toBe(`<input type="radio" value="foo" checked>`)
  84. expect(
  85. await renderToString(
  86. createApp({
  87. data: () => ({ selected: 'foo' }),
  88. template: `<input type="radio" value="bar" v-model="selected">`
  89. })
  90. )
  91. ).toBe(`<input type="radio" value="bar">`)
  92. // non-string values
  93. expect(
  94. await renderToString(
  95. createApp({
  96. data: () => ({ selected: 'foo' }),
  97. template: `<input type="radio" :value="{}" v-model="selected">`
  98. })
  99. )
  100. ).toBe(`<input type="radio">`)
  101. })
  102. test('checkbox', async () => {
  103. expect(
  104. await renderToString(
  105. createApp({
  106. data: () => ({ checked: true }),
  107. template: `<input type="checkbox" v-model="checked">`
  108. })
  109. )
  110. ).toBe(`<input type="checkbox" checked>`)
  111. expect(
  112. await renderToString(
  113. createApp({
  114. data: () => ({ checked: false }),
  115. template: `<input type="checkbox" v-model="checked">`
  116. })
  117. )
  118. ).toBe(`<input type="checkbox">`)
  119. expect(
  120. await renderToString(
  121. createApp({
  122. data: () => ({ checked: ['foo'] }),
  123. template: `<input type="checkbox" value="foo" v-model="checked">`
  124. })
  125. )
  126. ).toBe(`<input type="checkbox" value="foo" checked>`)
  127. expect(
  128. await renderToString(
  129. createApp({
  130. data: () => ({ checked: [] }),
  131. template: `<input type="checkbox" value="foo" v-model="checked">`
  132. })
  133. )
  134. ).toBe(`<input type="checkbox" value="foo">`)
  135. })
  136. test('textarea', async () => {
  137. expect(
  138. await renderToString(
  139. createApp({
  140. data: () => ({ foo: 'hello' }),
  141. template: `<textarea v-model="foo"/>`
  142. })
  143. )
  144. ).toBe(`<textarea>hello</textarea>`)
  145. })
  146. test('dynamic type', async () => {
  147. expect(
  148. await renderToString(
  149. createApp({
  150. data: () => ({ type: 'text', model: 'hello' }),
  151. template: `<input :type="type" v-model="model">`
  152. })
  153. )
  154. ).toBe(`<input type="text" value="hello">`)
  155. expect(
  156. await renderToString(
  157. createApp({
  158. data: () => ({ type: 'checkbox', model: true }),
  159. template: `<input :type="type" v-model="model">`
  160. })
  161. )
  162. ).toBe(`<input type="checkbox" checked>`)
  163. expect(
  164. await renderToString(
  165. createApp({
  166. data: () => ({ type: 'checkbox', model: false }),
  167. template: `<input :type="type" v-model="model">`
  168. })
  169. )
  170. ).toBe(`<input type="checkbox">`)
  171. expect(
  172. await renderToString(
  173. createApp({
  174. data: () => ({ type: 'checkbox', model: ['hello'] }),
  175. template: `<input :type="type" value="hello" v-model="model">`
  176. })
  177. )
  178. ).toBe(`<input type="checkbox" value="hello" checked>`)
  179. expect(
  180. await renderToString(
  181. createApp({
  182. data: () => ({ type: 'checkbox', model: [] }),
  183. template: `<input :type="type" value="hello" v-model="model">`
  184. })
  185. )
  186. ).toBe(`<input type="checkbox" value="hello">`)
  187. expect(
  188. await renderToString(
  189. createApp({
  190. data: () => ({ type: 'radio', model: 'hello' }),
  191. template: `<input :type="type" value="hello" v-model="model">`
  192. })
  193. )
  194. ).toBe(`<input type="radio" value="hello" checked>`)
  195. expect(
  196. await renderToString(
  197. createApp({
  198. data: () => ({ type: 'radio', model: 'hello' }),
  199. template: `<input :type="type" value="bar" v-model="model">`
  200. })
  201. )
  202. ).toBe(`<input type="radio" value="bar">`)
  203. })
  204. test('with v-bind', async () => {
  205. expect(
  206. await renderToString(
  207. createApp({
  208. data: () => ({
  209. obj: { type: 'radio', value: 'hello' },
  210. model: 'hello'
  211. }),
  212. template: `<input v-bind="obj" v-model="model">`
  213. })
  214. )
  215. ).toBe(`<input type="radio" value="hello" checked>`)
  216. })
  217. })
  218. describe('vnode v-show', () => {
  219. test('basic', async () => {
  220. expect(
  221. await renderToString(
  222. createApp({
  223. render() {
  224. return withDirectives(h('div'), [[vShow, true]])
  225. }
  226. })
  227. )
  228. ).toBe(`<div></div>`)
  229. expect(
  230. await renderToString(
  231. createApp({
  232. render() {
  233. return withDirectives(h('div'), [[vShow, false]])
  234. }
  235. })
  236. )
  237. ).toBe(`<div style="display:none;"></div>`)
  238. })
  239. test('with merge', async () => {
  240. expect(
  241. await renderToString(
  242. createApp({
  243. render() {
  244. return withDirectives(
  245. h('div', {
  246. style: {
  247. color: 'red'
  248. }
  249. }),
  250. [[vShow, false]]
  251. )
  252. }
  253. })
  254. )
  255. ).toBe(`<div style="color:red;display:none;"></div>`)
  256. })
  257. })
  258. describe('vnode v-model', () => {
  259. test('text', async () => {
  260. expect(
  261. await renderToString(
  262. createApp({
  263. render() {
  264. return withDirectives(h('input'), [[vModelText, 'hello']])
  265. }
  266. })
  267. )
  268. ).toBe(`<input value="hello">`)
  269. })
  270. test('radio', async () => {
  271. expect(
  272. await renderToString(
  273. createApp({
  274. render() {
  275. return withDirectives(
  276. h('input', { type: 'radio', value: 'hello' }),
  277. [[vModelRadio, 'hello']]
  278. )
  279. }
  280. })
  281. )
  282. ).toBe(`<input type="radio" value="hello" checked>`)
  283. expect(
  284. await renderToString(
  285. createApp({
  286. render() {
  287. return withDirectives(
  288. h('input', { type: 'radio', value: 'hello' }),
  289. [[vModelRadio, 'foo']]
  290. )
  291. }
  292. })
  293. )
  294. ).toBe(`<input type="radio" value="hello">`)
  295. })
  296. test('checkbox', async () => {
  297. expect(
  298. await renderToString(
  299. createApp({
  300. render() {
  301. return withDirectives(h('input', { type: 'checkbox' }), [
  302. [vModelCheckbox, true]
  303. ])
  304. }
  305. })
  306. )
  307. ).toBe(`<input type="checkbox" checked>`)
  308. expect(
  309. await renderToString(
  310. createApp({
  311. render() {
  312. return withDirectives(h('input', { type: 'checkbox' }), [
  313. [vModelCheckbox, false]
  314. ])
  315. }
  316. })
  317. )
  318. ).toBe(`<input type="checkbox">`)
  319. expect(
  320. await renderToString(
  321. createApp({
  322. render() {
  323. return withDirectives(
  324. h('input', { type: 'checkbox', value: 'foo' }),
  325. [[vModelCheckbox, ['foo']]]
  326. )
  327. }
  328. })
  329. )
  330. ).toBe(`<input type="checkbox" value="foo" checked>`)
  331. expect(
  332. await renderToString(
  333. createApp({
  334. render() {
  335. return withDirectives(
  336. h('input', { type: 'checkbox', value: 'foo' }),
  337. [[vModelCheckbox, []]]
  338. )
  339. }
  340. })
  341. )
  342. ).toBe(`<input type="checkbox" value="foo">`)
  343. })
  344. })
  345. test('custom directive w/ getSSRProps (vdom)', async () => {
  346. expect(
  347. await renderToString(
  348. createApp({
  349. render() {
  350. return withDirectives(h('div'), [
  351. [
  352. {
  353. getSSRProps({ value }) {
  354. return { id: value }
  355. }
  356. },
  357. 'foo'
  358. ]
  359. ])
  360. }
  361. })
  362. )
  363. ).toBe(`<div id="foo"></div>`)
  364. })
  365. test('custom directive w/ getSSRProps (optimized)', async () => {
  366. expect(
  367. await renderToString(
  368. createApp({
  369. data() {
  370. return {
  371. x: 'foo'
  372. }
  373. },
  374. directives: {
  375. xxx: {
  376. getSSRProps({ value, arg, modifiers }) {
  377. return { id: [value, arg, modifiers.ok].join('-') }
  378. }
  379. }
  380. },
  381. ssrRender(_ctx, _push, _parent, _attrs) {
  382. const _directive_xxx = resolveDirective('xxx')!
  383. _push(
  384. `<div${ssrRenderAttrs(
  385. ssrGetDirectiveProps(_ctx, _directive_xxx, _ctx.x, 'arg', {
  386. ok: true
  387. })
  388. )}></div>`
  389. )
  390. }
  391. })
  392. )
  393. ).toBe(`<div id="foo-arg-true"></div>`)
  394. })
  395. })