patchProps.spec.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { patchProp } from '../src/patchProp'
  2. import { render, h } from '../src'
  3. import { mockWarn } from '@vue/shared'
  4. describe('runtime-dom: props patching', () => {
  5. mockWarn()
  6. test('basic', () => {
  7. const el = document.createElement('div')
  8. patchProp(el, 'id', null, 'foo')
  9. expect(el.id).toBe('foo')
  10. // prop with string value should be set to empty string on null values
  11. patchProp(el, 'id', null, null)
  12. expect(el.id).toBe('')
  13. })
  14. test('value', () => {
  15. const el = document.createElement('input')
  16. patchProp(el, 'value', null, 'foo')
  17. expect(el.value).toBe('foo')
  18. patchProp(el, 'value', null, null)
  19. expect(el.value).toBe('')
  20. const obj = {}
  21. patchProp(el, 'value', null, obj)
  22. expect(el.value).toBe(obj.toString())
  23. expect((el as any)._value).toBe(obj)
  24. })
  25. test('boolean prop', () => {
  26. const el = document.createElement('select')
  27. patchProp(el, 'multiple', null, '')
  28. expect(el.multiple).toBe(true)
  29. patchProp(el, 'multiple', null, null)
  30. expect(el.multiple).toBe(false)
  31. })
  32. test('innerHTML unmount prev children', () => {
  33. const fn = jest.fn()
  34. const comp = {
  35. render: () => 'foo',
  36. unmounted: fn
  37. }
  38. const root = document.createElement('div')
  39. render(h('div', null, [h(comp)]), root)
  40. expect(root.innerHTML).toBe(`<div>foo</div>`)
  41. render(h('div', { innerHTML: 'bar' }), root)
  42. expect(root.innerHTML).toBe(`<div>bar</div>`)
  43. expect(fn).toHaveBeenCalled()
  44. })
  45. // #954
  46. test('(svg) innerHTML unmount prev children', () => {
  47. const fn = jest.fn()
  48. const comp = {
  49. render: () => 'foo',
  50. unmounted: fn
  51. }
  52. const root = document.createElement('div')
  53. render(h('div', null, [h(comp)]), root)
  54. expect(root.innerHTML).toBe(`<div>foo</div>`)
  55. render(h('svg', { innerHTML: '<g></g>' }), root)
  56. expect(root.innerHTML).toBe(`<svg><g></g></svg>`)
  57. expect(fn).toHaveBeenCalled()
  58. })
  59. test('textContent unmount prev children', () => {
  60. const fn = jest.fn()
  61. const comp = {
  62. render: () => 'foo',
  63. unmounted: fn
  64. }
  65. const root = document.createElement('div')
  66. render(h('div', null, [h(comp)]), root)
  67. expect(root.innerHTML).toBe(`<div>foo</div>`)
  68. render(h('div', { textContent: 'bar' }), root)
  69. expect(root.innerHTML).toBe(`<div>bar</div>`)
  70. expect(fn).toHaveBeenCalled()
  71. })
  72. // #1049
  73. test('set value as-is for non string-value props', () => {
  74. const el = document.createElement('video')
  75. // jsdom doesn't really support video playback. srcObject in a real browser
  76. // should default to `null`, but in jsdom it's `undefined`.
  77. // anyway, here we just want to make sure Vue doesn't set non-string props
  78. // to an empty string on nullish values - it should reset to its default
  79. // value.
  80. const intiialValue = el.srcObject
  81. const fakeObject = {}
  82. patchProp(el, 'srcObject', null, fakeObject)
  83. expect(el.srcObject).not.toBe(fakeObject)
  84. patchProp(el, 'srcObject', null, null)
  85. expect(el.srcObject).toBe(intiialValue)
  86. })
  87. test('catch and warn prop set TypeError', () => {
  88. const el = document.createElement('div')
  89. Object.defineProperty(el, 'someProp', {
  90. set() {
  91. throw new TypeError('Invalid type')
  92. }
  93. })
  94. patchProp(el, 'someProp', null, 'foo')
  95. expect(`Failed setting prop "someProp" on <div>`).toHaveBeenWarnedLast()
  96. })
  97. })