bind.spec.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import Alpine from 'alpinejs'
  2. import { wait } from '@testing-library/dom'
  3. global.MutationObserver = class {
  4. observe() {}
  5. }
  6. test('attribute bindings are set on initialize', async () => {
  7. document.body.innerHTML = `
  8. <div x-data="{ foo: 'bar' }">
  9. <span x-bind:foo="foo"></span>
  10. </div>
  11. `
  12. Alpine.start()
  13. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  14. })
  15. test('class attribute bindings are merged by string syntax', async () => {
  16. document.body.innerHTML = `
  17. <div x-data="{ isOn: false }">
  18. <span class="foo" x-bind:class="isOn ? 'bar': ''"></span>
  19. <button @click="isOn = ! isOn"></button>
  20. </div>
  21. `
  22. Alpine.start()
  23. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  24. expect(document.querySelector('span').classList.contains('bar')).toBeFalsy()
  25. document.querySelector('button').click()
  26. await wait(() => {
  27. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  28. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  29. })
  30. document.querySelector('button').click()
  31. await wait(() => {
  32. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  33. expect(document.querySelector('span').classList.contains('bar')).toBeFalsy()
  34. })
  35. })
  36. test('class attribute bindings are merged by array syntax', async () => {
  37. document.body.innerHTML = `
  38. <div x-data="{ isOn: false }">
  39. <span class="foo" x-bind:class="isOn ? ['bar', 'baz']: ['bar']"></span>
  40. <button @click="isOn = ! isOn"></button>
  41. </div>
  42. `
  43. Alpine.start()
  44. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  45. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  46. expect(document.querySelector('span').classList.contains('baz')).toBeFalsy()
  47. document.querySelector('button').click()
  48. await wait(() => {
  49. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  50. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  51. expect(document.querySelector('span').classList.contains('baz')).toBeTruthy()
  52. })
  53. document.querySelector('button').click()
  54. await wait(() => {
  55. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  56. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  57. expect(document.querySelector('span').classList.contains('baz')).toBeFalsy()
  58. })
  59. })
  60. test('class attribute bindings are removed by object syntax', async () => {
  61. document.body.innerHTML = `
  62. <div x-data="{ isOn: false }">
  63. <span class="foo" x-bind:class="{ 'foo': isOn }"></span>
  64. </div>
  65. `
  66. Alpine.start()
  67. expect(document.querySelector('span').classList.contains('foo')).toBeFalsy()
  68. })
  69. test('class attribute bindings are added by string syntax', async () => {
  70. document.body.innerHTML = `
  71. <div x-data="{ initialClass: 'foo' }">
  72. <span x-bind:class="initialClass"></span>
  73. </div>
  74. `
  75. Alpine.start()
  76. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  77. })
  78. test('class attribute bindings are added by object syntax', async () => {
  79. document.body.innerHTML = `
  80. <div x-data="{ isOn: true }">
  81. <span x-bind:class="{ 'foo': isOn }"></span>
  82. </div>
  83. `
  84. Alpine.start()
  85. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  86. })
  87. test('multiple classes are added by object syntax', async () => {
  88. document.body.innerHTML = `
  89. <div x-data="{ isOn: false }">
  90. <span class="foo bar" x-bind:class="{ 'foo bar': isOn }"></span>
  91. </div>
  92. `
  93. Alpine.start()
  94. expect(document.querySelector('span').classList.contains('foo')).toBeFalsy()
  95. expect(document.querySelector('span').classList.contains('bar')).toBeFalsy()
  96. })
  97. test('multiple classes are removed by object syntax', async () => {
  98. document.body.innerHTML = `
  99. <div x-data="{ isOn: true }">
  100. <span x-bind:class="{ 'foo bar': isOn }"></span>
  101. </div>
  102. `
  103. Alpine.start()
  104. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  105. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  106. })
  107. test('class attribute bindings are added by nested object syntax', async () => {
  108. document.body.innerHTML = `
  109. <div x-data="{ nested: { isOn: true } }">
  110. <span x-bind:class="{ 'foo': nested.isOn }"></span>
  111. </div>
  112. `
  113. Alpine.start()
  114. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy()
  115. })
  116. test('class attribute bindings are added by array syntax', async () => {
  117. document.body.innerHTML = `
  118. <div x-data="{}">
  119. <span class="" x-bind:class="['foo']"></span>
  120. </div>
  121. `
  122. Alpine.start()
  123. expect(document.querySelector('span').classList.contains('foo')).toBeTruthy
  124. })
  125. test('class attribute bindings are synced by string syntax', async () => {
  126. document.body.innerHTML = `
  127. <div x-data="{foo: 'bar baz'}">
  128. <span class="" x-bind:class="foo"></span>
  129. </div>
  130. `
  131. Alpine.start()
  132. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy
  133. expect(document.querySelector('span').classList.contains('baz')).toBeTruthy
  134. })
  135. test('boolean attributes set to false are removed from element', async () => {
  136. document.body.innerHTML = `
  137. <div x-data="{ isSet: false }">
  138. <input x-bind:disabled="isSet"></input>
  139. <input x-bind:checked="isSet"></input>
  140. <input x-bind:required="isSet"></input>
  141. <input x-bind:readonly="isSet"></input>
  142. <input x-bind:hidden="isSet"></input>
  143. </div>
  144. `
  145. Alpine.start()
  146. expect(document.querySelectorAll('input')[0].disabled).toBeFalsy()
  147. expect(document.querySelectorAll('input')[1].checked).toBeFalsy()
  148. expect(document.querySelectorAll('input')[2].required).toBeFalsy()
  149. expect(document.querySelectorAll('input')[3].readOnly).toBeFalsy()
  150. expect(document.querySelectorAll('input')[4].hidden).toBeFalsy()
  151. })
  152. test('boolean attributes set to true are added to element', async () => {
  153. document.body.innerHTML = `
  154. <div x-data="{ isSet: true }">
  155. <input x-bind:disabled="isSet"></input>
  156. <input x-bind:checked="isSet"></input>
  157. <input x-bind:required="isSet"></input>
  158. <input x-bind:readonly="isSet"></input>
  159. </div>
  160. `
  161. Alpine.start()
  162. expect(document.querySelectorAll('input')[0].disabled).toBeTruthy()
  163. expect(document.querySelectorAll('input')[1].checked).toBeTruthy()
  164. expect(document.querySelectorAll('input')[2].required).toBeTruthy()
  165. expect(document.querySelectorAll('input')[3].readOnly).toBeTruthy()
  166. })
  167. test('binding supports short syntax', async () => {
  168. document.body.innerHTML = `
  169. <div x-data="{ foo: 'bar' }">
  170. <span :class="foo"></span>
  171. </div>
  172. `
  173. Alpine.start()
  174. expect(document.querySelector('span').classList.contains('bar')).toBeTruthy()
  175. })