renderAttrs.spec.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import {
  2. renderAttrs,
  3. renderClass,
  4. renderStyle,
  5. renderAttr
  6. } from '../src/helpers/renderAttrs'
  7. import { escapeHtml } from '@vue/shared'
  8. describe('ssr: renderAttrs', () => {
  9. test('ignore reserved props', () => {
  10. expect(
  11. renderAttrs({
  12. key: 1,
  13. ref: () => {},
  14. onClick: () => {}
  15. })
  16. ).toBe('')
  17. })
  18. test('normal attrs', () => {
  19. expect(
  20. renderAttrs({
  21. id: 'foo',
  22. title: 'bar'
  23. })
  24. ).toBe(` id="foo" title="bar"`)
  25. })
  26. test('escape attrs', () => {
  27. expect(
  28. renderAttrs({
  29. id: '"><script'
  30. })
  31. ).toBe(` id="&quot;&gt;&lt;script"`)
  32. })
  33. test('boolean attrs', () => {
  34. expect(
  35. renderAttrs({
  36. checked: true,
  37. multiple: false
  38. })
  39. ).toBe(` checked`) // boolean attr w/ false should be ignored
  40. })
  41. test('ignore falsy values', () => {
  42. expect(
  43. renderAttrs({
  44. foo: false,
  45. title: null,
  46. baz: undefined
  47. })
  48. ).toBe(` foo="false"`) // non boolean should render `false` as is
  49. })
  50. test('props to attrs', () => {
  51. expect(
  52. renderAttrs({
  53. readOnly: true, // simple lower case conversion
  54. htmlFor: 'foobar' // special cases
  55. })
  56. ).toBe(` readonly for="foobar"`)
  57. })
  58. test('preserve name on custom element', () => {
  59. expect(
  60. renderAttrs(
  61. {
  62. fooBar: 'ok'
  63. },
  64. 'my-el'
  65. )
  66. ).toBe(` fooBar="ok"`)
  67. })
  68. })
  69. describe('ssr: renderAttr', () => {
  70. test('basic', () => {
  71. expect(renderAttr('foo', 'bar')).toBe(` foo="bar"`)
  72. })
  73. test('null and undefined', () => {
  74. expect(renderAttr('foo', null)).toBe(``)
  75. expect(renderAttr('foo', undefined)).toBe(``)
  76. })
  77. test('escape', () => {
  78. expect(renderAttr('foo', '<script>')).toBe(
  79. ` foo="${escapeHtml(`<script>`)}"`
  80. )
  81. })
  82. })
  83. describe('ssr: renderClass', () => {
  84. test('via renderProps', () => {
  85. expect(
  86. renderAttrs({
  87. class: ['foo', 'bar']
  88. })
  89. ).toBe(` class="foo bar"`)
  90. })
  91. test('standalone', () => {
  92. expect(renderClass(`foo`)).toBe(`foo`)
  93. expect(renderClass([`foo`, `bar`])).toBe(`foo bar`)
  94. expect(renderClass({ foo: true, bar: false })).toBe(`foo`)
  95. expect(renderClass([{ foo: true, bar: false }, `baz`])).toBe(`foo baz`)
  96. })
  97. test('escape class values', () => {
  98. expect(renderClass(`"><script`)).toBe(`&quot;&gt;&lt;script`)
  99. })
  100. })
  101. describe('ssr: renderStyle', () => {
  102. test('via renderProps', () => {
  103. expect(
  104. renderAttrs({
  105. style: {
  106. color: 'red'
  107. }
  108. })
  109. ).toBe(` style="color:red;"`)
  110. })
  111. test('standalone', () => {
  112. expect(renderStyle(`color:red`)).toBe(`color:red`)
  113. expect(
  114. renderStyle({
  115. color: `red`
  116. })
  117. ).toBe(`color:red;`)
  118. expect(
  119. renderStyle([
  120. { color: `red` },
  121. { fontSize: `15px` } // case conversion
  122. ])
  123. ).toBe(`color:red;font-size:15px;`)
  124. })
  125. test('number handling', () => {
  126. expect(
  127. renderStyle({
  128. fontSize: 15, // should be ignored since font-size requires unit
  129. opacity: 0.5
  130. })
  131. ).toBe(`opacity:0.5;`)
  132. })
  133. test('escape inline CSS', () => {
  134. expect(renderStyle(`"><script`)).toBe(`&quot;&gt;&lt;script`)
  135. expect(
  136. renderStyle({
  137. color: `"><script`
  138. })
  139. ).toBe(`color:&quot;&gt;&lt;script;`)
  140. })
  141. })