patchProps.spec.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { patchProp } from '../src/patchProp'
  2. import { render, h } from '../src'
  3. describe('runtime-dom: props patching', () => {
  4. test('basic', () => {
  5. const el = document.createElement('div')
  6. patchProp(el, 'id', null, 'foo')
  7. expect(el.id).toBe('foo')
  8. // prop with string value should be set to empty string on null values
  9. patchProp(el, 'id', null, null)
  10. expect(el.id).toBe('')
  11. })
  12. test('value', () => {
  13. const el = document.createElement('input')
  14. patchProp(el, 'value', null, 'foo')
  15. expect(el.value).toBe('foo')
  16. patchProp(el, 'value', null, null)
  17. expect(el.value).toBe('')
  18. const obj = {}
  19. patchProp(el, 'value', null, obj)
  20. expect(el.value).toBe(obj.toString())
  21. expect((el as any)._value).toBe(obj)
  22. })
  23. test('boolean prop', () => {
  24. const el = document.createElement('select')
  25. patchProp(el, 'multiple', null, '')
  26. expect(el.multiple).toBe(true)
  27. patchProp(el, 'multiple', null, null)
  28. expect(el.multiple).toBe(false)
  29. })
  30. test('innerHTML unmount prev children', () => {
  31. const fn = jest.fn()
  32. const comp = {
  33. render: () => 'foo',
  34. unmounted: fn
  35. }
  36. const root = document.createElement('div')
  37. render(h('div', null, [h(comp)]), root)
  38. expect(root.innerHTML).toBe(`<div>foo</div>`)
  39. render(h('div', { innerHTML: 'bar' }), root)
  40. expect(root.innerHTML).toBe(`<div>bar</div>`)
  41. expect(fn).toHaveBeenCalled()
  42. })
  43. // #954
  44. test('(svg) innerHTML unmount prev children', () => {
  45. const fn = jest.fn()
  46. const comp = {
  47. render: () => 'foo',
  48. unmounted: fn
  49. }
  50. const root = document.createElement('div')
  51. render(h('div', null, [h(comp)]), root)
  52. expect(root.innerHTML).toBe(`<div>foo</div>`)
  53. render(h('svg', { innerHTML: '<g></g>' }), root)
  54. expect(root.innerHTML).toBe(`<svg><g></g></svg>`)
  55. expect(fn).toHaveBeenCalled()
  56. })
  57. test('textContent unmount prev children', () => {
  58. const fn = jest.fn()
  59. const comp = {
  60. render: () => 'foo',
  61. unmounted: fn
  62. }
  63. const root = document.createElement('div')
  64. render(h('div', null, [h(comp)]), root)
  65. expect(root.innerHTML).toBe(`<div>foo</div>`)
  66. render(h('div', { textContent: 'bar' }), root)
  67. expect(root.innerHTML).toBe(`<div>bar</div>`)
  68. expect(fn).toHaveBeenCalled()
  69. })
  70. // #1049
  71. test('set value as-is for non string-value props', () => {
  72. const el = document.createElement('video')
  73. // jsdom doesn't really support video playback. srcObject in a real browser
  74. // should default to `null`, but in jsdom it's `undefined`.
  75. // anyway, here we just want to make sure Vue doesn't set non-string props
  76. // to an empty string on nullish values - it should reset to its default
  77. // value.
  78. const initialValue = el.srcObject
  79. const fakeObject = {}
  80. patchProp(el, 'srcObject', null, fakeObject)
  81. expect(el.srcObject).not.toBe(fakeObject)
  82. patchProp(el, 'srcObject', null, null)
  83. expect(el.srcObject).toBe(initialValue)
  84. })
  85. test('catch and warn prop set TypeError', () => {
  86. const el = document.createElement('div')
  87. Object.defineProperty(el, 'someProp', {
  88. set() {
  89. throw new TypeError('Invalid type')
  90. }
  91. })
  92. patchProp(el, 'someProp', null, 'foo')
  93. expect(`Failed setting prop "someProp" on <div>`).toHaveBeenWarnedLast()
  94. })
  95. // #1576
  96. test('remove attribute when value is falsy', () => {
  97. const el = document.createElement('div')
  98. patchProp(el, 'id', null, '')
  99. expect(el.hasAttribute('id')).toBe(true)
  100. patchProp(el, 'id', null, null)
  101. expect(el.hasAttribute('id')).toBe(false)
  102. patchProp(el, 'id', null, '')
  103. expect(el.hasAttribute('id')).toBe(true)
  104. patchProp(el, 'id', null, undefined)
  105. expect(el.hasAttribute('id')).toBe(false)
  106. patchProp(el, 'id', null, '')
  107. expect(el.hasAttribute('id')).toBe(true)
  108. // #2677
  109. const img = document.createElement('img')
  110. patchProp(img, 'width', null, '')
  111. expect(el.hasAttribute('width')).toBe(false)
  112. patchProp(img, 'width', null, 0)
  113. expect(img.hasAttribute('width')).toBe(true)
  114. patchProp(img, 'width', null, null)
  115. expect(img.hasAttribute('width')).toBe(false)
  116. patchProp(img, 'width', null, 0)
  117. expect(img.hasAttribute('width')).toBe(true)
  118. patchProp(img, 'width', null, undefined)
  119. expect(img.hasAttribute('width')).toBe(false)
  120. patchProp(img, 'width', null, 0)
  121. expect(img.hasAttribute('width')).toBe(true)
  122. })
  123. test('form attribute', () => {
  124. const el = document.createElement('input')
  125. patchProp(el, 'form', null, 'foo')
  126. // non existant element
  127. expect(el.form).toBe(null)
  128. expect(el.getAttribute('form')).toBe('foo')
  129. // remove attribute
  130. patchProp(el, 'form', 'foo', null)
  131. expect(el.getAttribute('form')).toBe(null)
  132. })
  133. })