model.spec.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import Alpine from 'alpinejs'
  2. import { wait, fireEvent } from '@testing-library/dom'
  3. global.MutationObserver = class {
  4. observe() {}
  5. }
  6. test('x-model has value binding when initialized', async () => {
  7. document.body.innerHTML = `
  8. <div x-data="{ foo: 'bar' }">
  9. <input x-model="foo"></input>
  10. </div>
  11. `
  12. Alpine.start()
  13. expect(document.querySelector('input').value).toEqual('bar')
  14. })
  15. test('x-model updates value when updated via input event', async () => {
  16. document.body.innerHTML = `
  17. <div x-data="{ foo: 'bar' }">
  18. <input x-model="foo"></input>
  19. </div>
  20. `
  21. Alpine.start()
  22. fireEvent.input(document.querySelector('input'), { target: { value: 'baz' }})
  23. await wait(() => { expect(document.querySelector('input').value).toEqual('baz') })
  24. })
  25. test('x-model reflects data changed elsewhere', async () => {
  26. document.body.innerHTML = `
  27. <div x-data="{ foo: 'bar' }">
  28. <input x-model="foo"></input>
  29. <button x-on:click="foo = 'baz'"></button>
  30. </div>
  31. `
  32. Alpine.start()
  33. document.querySelector('button').click()
  34. await wait(() => { expect(document.querySelector('input').value).toEqual('baz') })
  35. })
  36. test('x-model casts value to number if number modifier is present', async () => {
  37. document.body.innerHTML = `
  38. <div x-data="{ foo: null }">
  39. <input type="number" x-model.number="foo"></input>
  40. </div>
  41. `
  42. Alpine.start()
  43. fireEvent.input(document.querySelector('input'), { target: { value: '123' }})
  44. await wait(() => { expect(document.querySelector('[x-data]').__x.$data.foo).toEqual(123) })
  45. })
  46. test('x-model trims value if trim modifier is present', async () => {
  47. document.body.innerHTML = `
  48. <div x-data="{ foo: '' }">
  49. <input x-model.trim="foo"></input>
  50. <span x-text="foo"></span>
  51. </div>
  52. `
  53. Alpine.start()
  54. fireEvent.input(document.querySelector('input'), { target: { value: 'bar ' }})
  55. await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
  56. })
  57. test('x-model updates value when updated via changed event when lazy modifier is present', async () => {
  58. document.body.innerHTML = `
  59. <div x-data="{ foo: 'bar' }">
  60. <input x-model.lazy="foo"></input>
  61. </div>
  62. `
  63. Alpine.start()
  64. fireEvent.change(document.querySelector('input'), { target: { value: 'baz' }})
  65. await wait(() => { expect(document.querySelector('input').value).toEqual('baz') })
  66. })
  67. test('x-model binds checkbox value', async () => {
  68. document.body.innerHTML = `
  69. <div x-data="{ foo: true }">
  70. <input type="checkbox" x-model="foo"></input>
  71. <span x-bind:bar="foo"></span>
  72. </div>
  73. `
  74. Alpine.start()
  75. expect(document.querySelector('input').checked).toEqual(true)
  76. expect(document.querySelector('span').getAttribute('bar')).toEqual("true")
  77. fireEvent.change(document.querySelector('input'), { target: { checked: false }})
  78. await wait(() => { expect(document.querySelector('span').getAttribute('bar')).toEqual("false") })
  79. })
  80. test('x-model binds checkbox value to array', async () => {
  81. document.body.innerHTML = `
  82. <div x-data="{ foo: ['bar'] }">
  83. <input type="checkbox" x-model="foo" value="bar"></input>
  84. <input type="checkbox" x-model="foo" value="baz"></input>
  85. <span x-bind:bar="foo"></span>
  86. </div>
  87. `
  88. Alpine.start()
  89. expect(document.querySelectorAll('input')[0].checked).toEqual(true)
  90. expect(document.querySelectorAll('input')[1].checked).toEqual(false)
  91. expect(document.querySelector('span').getAttribute('bar')).toEqual("bar")
  92. fireEvent.change(document.querySelectorAll('input')['1'], { target: { checked: true }})
  93. await wait(() => {
  94. expect(document.querySelectorAll('input')[0].checked).toEqual(true)
  95. expect(document.querySelectorAll('input')[1].checked).toEqual(true)
  96. expect(document.querySelector('span').getAttribute('bar')).toEqual("bar,baz")
  97. })
  98. })
  99. test('x-model binds radio value', async () => {
  100. document.body.innerHTML = `
  101. <div x-data="{ foo: 'bar' }">
  102. <input type="radio" x-model="foo" value="bar"></input>
  103. <input type="radio" x-model="foo" value="baz"></input>
  104. <span x-bind:bar="foo"></span>
  105. </div>
  106. `
  107. Alpine.start()
  108. expect(document.querySelectorAll('input')[0].checked).toEqual(true)
  109. expect(document.querySelectorAll('input')[1].checked).toEqual(false)
  110. expect(document.querySelector('span').getAttribute('bar')).toEqual('bar')
  111. fireEvent.change(document.querySelectorAll('input')[1], { target: { checked: true }})
  112. await wait(() => {
  113. expect(document.querySelectorAll('input')[0].checked).toEqual(false)
  114. expect(document.querySelectorAll('input')[1].checked).toEqual(true)
  115. expect(document.querySelector('span').getAttribute('bar')).toEqual('baz')
  116. })
  117. })
  118. test('x-model binds select dropdown', async () => {
  119. document.body.innerHTML = `
  120. <div x-data="{ foo: 'bar' }">
  121. <select x-model="foo">
  122. <option disabled value="">Please select one</option>
  123. <option>bar</option>
  124. <option>baz</option>
  125. </select>
  126. <span x-text="foo"></span>
  127. </div>
  128. `
  129. Alpine.start()
  130. expect(document.querySelectorAll('option')[0].selected).toEqual(false)
  131. expect(document.querySelectorAll('option')[1].selected).toEqual(true)
  132. expect(document.querySelectorAll('option')[2].selected).toEqual(false)
  133. expect(document.querySelector('span').innerText).toEqual('bar')
  134. fireEvent.change(document.querySelector('select'), { target: { value: 'baz' }});
  135. await wait(() => {
  136. expect(document.querySelectorAll('option')[0].selected).toEqual(false)
  137. expect(document.querySelectorAll('option')[1].selected).toEqual(false)
  138. expect(document.querySelectorAll('option')[2].selected).toEqual(true)
  139. expect(document.querySelector('span').innerText).toEqual('baz')
  140. })
  141. })
  142. test('x-model binds multiple select dropdown', async () => {
  143. document.body.innerHTML = `
  144. <div x-data="{ foo: ['bar'] }">
  145. <select x-model="foo" multiple>
  146. <option disabled value="">Please select one</option>
  147. <option>bar</option>
  148. <option>baz</option>
  149. </select>
  150. <span x-text="foo"></span>
  151. </div>
  152. `
  153. Alpine.start()
  154. expect(document.querySelectorAll('option')[0].selected).toEqual(false)
  155. expect(document.querySelectorAll('option')[1].selected).toEqual(true)
  156. expect(document.querySelectorAll('option')[2].selected).toEqual(false)
  157. expect(document.querySelector('span').innerText).toEqual(['bar'])
  158. document.querySelectorAll('option')[2].selected = true
  159. fireEvent.change(document.querySelector('select'));
  160. await wait(() => {
  161. expect(document.querySelectorAll('option')[0].selected).toEqual(false)
  162. expect(document.querySelectorAll('option')[1].selected).toEqual(true)
  163. expect(document.querySelectorAll('option')[2].selected).toEqual(true)
  164. expect(document.querySelector('span').innerText).toEqual(['bar', 'baz'])
  165. })
  166. })
  167. test('x-model binds nested keys', async () => {
  168. document.body.innerHTML = `
  169. <div x-data="{ some: { nested: { key: 'foo' } } }">
  170. <input type="text" x-model="some.nested.key">
  171. <span x-text="some.nested.key"></span>
  172. </div>
  173. `
  174. Alpine.start()
  175. expect(document.querySelector('input').value).toEqual('foo')
  176. expect(document.querySelector('span').innerText).toEqual('foo')
  177. fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }})
  178. await wait(() => {
  179. expect(document.querySelector('input').value).toEqual('bar')
  180. expect(document.querySelector('span').innerText).toEqual('bar')
  181. })
  182. })