on.spec.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import Alpine from 'alpinejs'
  2. import { wait, fireEvent } from '@testing-library/dom'
  3. const timeout = ms => new Promise(resolve => setTimeout(resolve, ms))
  4. global.MutationObserver = class {
  5. observe() {}
  6. }
  7. test('data modified in event listener updates effected attribute bindings', async () => {
  8. document.body.innerHTML = `
  9. <div x-data="{ foo: 'bar' }">
  10. <button x-on:click="foo = 'baz'"></button>
  11. <span x-bind:foo="foo"></span>
  12. </div>
  13. `
  14. Alpine.start()
  15. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  16. document.querySelector('button').click()
  17. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
  18. })
  19. test('nested data modified in event listener updates effected attribute bindings', async () => {
  20. document.body.innerHTML = `
  21. <div x-data="{ nested: { foo: 'bar' } }">
  22. <button x-on:click="nested.foo = 'baz'"></button>
  23. <span x-bind:foo="nested.foo"></span>
  24. </div>
  25. `
  26. Alpine.start()
  27. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  28. document.querySelector('button').click()
  29. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
  30. })
  31. test('.stop modifier', async () => {
  32. document.body.innerHTML = `
  33. <div x-data="{ foo: 'bar' }">
  34. <button x-on:click="foo = 'baz'">
  35. <span></span>
  36. </button>
  37. </div>
  38. `
  39. Alpine.start()
  40. expect(document.querySelector('div').__x.$data.foo).toEqual('bar')
  41. document.querySelector('span').click()
  42. await wait(() => {
  43. expect(document.querySelector('div').__x.$data.foo).toEqual('baz')
  44. })
  45. })
  46. test('.prevent modifier', async () => {
  47. document.body.innerHTML = `
  48. <div x-data="{}">
  49. <input type="checkbox" x-on:click.prevent>
  50. </div>
  51. `
  52. Alpine.start()
  53. expect(document.querySelector('input').checked).toEqual(false)
  54. document.querySelector('input').click()
  55. expect(document.querySelector('input').checked).toEqual(false)
  56. })
  57. test('.window modifier', async () => {
  58. document.body.innerHTML = `
  59. <div x-data="{ foo: 'bar' }">
  60. <div x-on:click.window="foo = 'baz'"></div>
  61. <span x-bind:foo="foo"></span>
  62. </div>
  63. `
  64. Alpine.start()
  65. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  66. document.body.click()
  67. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
  68. })
  69. test('.document modifier', async () => {
  70. document.body.innerHTML = `
  71. <div x-data="{ foo: 'bar' }">
  72. <div x-on:click.document="foo = 'baz'"></div>
  73. <span x-bind:foo="foo"></span>
  74. </div>
  75. `
  76. Alpine.start()
  77. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  78. document.body.click()
  79. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
  80. })
  81. test('.once modifier', async () => {
  82. document.body.innerHTML = `
  83. <div x-data="{ count: 0 }">
  84. <button x-on:click.once="count = count+1"></button>
  85. <span x-bind:foo="count"
  86. </div>
  87. `
  88. Alpine.start()
  89. expect(document.querySelector('span').getAttribute('foo')).toEqual('0')
  90. document.querySelector('button').click()
  91. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('1') })
  92. document.querySelector('button').click()
  93. await timeout(25)
  94. expect(document.querySelector('span').getAttribute('foo')).toEqual('1')
  95. })
  96. test('keydown modifiers', async () => {
  97. document.body.innerHTML = `
  98. <div x-data="{ count: 0 }">
  99. <input type="text" x-on:keydown="count++" x-on:keydown.enter="count++">
  100. <span x-text="count"></span>
  101. </div>
  102. `
  103. Alpine.start()
  104. expect(document.querySelector('span').innerText).toEqual(0)
  105. fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
  106. await wait(() => { expect(document.querySelector('span').innerText).toEqual(2) })
  107. fireEvent.keyDown(document.querySelector('input'), { key: 'Escape' })
  108. await wait(() => { expect(document.querySelector('span').innerText).toEqual(3) })
  109. })
  110. test('click away', async () => {
  111. // Because jsDom doesn't support .offsetHeight and offsetWidth, we have to
  112. // make our own implementation using a specific class added to the class. Ugh.
  113. Object.defineProperties(window.HTMLElement.prototype, {
  114. offsetHeight: {
  115. get: function() { return this.classList.contains('hidden') ? 0 : 1 }
  116. },
  117. offsetWidth: {
  118. get: function() { return this.classList.contains('hidden') ? 0 : 1 }
  119. }
  120. });
  121. document.body.innerHTML = `
  122. <div id="outer">
  123. <div x-data="{ isOpen: true }">
  124. <button x-on:click="isOpen = true"></button>
  125. <ul x-bind:class="{ 'hidden': ! isOpen }" x-on:click.away="isOpen = false">
  126. <li>...</li>
  127. </ul>
  128. </div>
  129. </div>
  130. `
  131. Alpine.start()
  132. expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false)
  133. document.querySelector('li').click()
  134. await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
  135. document.querySelector('ul').click()
  136. await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
  137. document.querySelector('#outer').click()
  138. await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(true) })
  139. document.querySelector('button').click()
  140. await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
  141. })
  142. test('supports short syntax', async () => {
  143. document.body.innerHTML = `
  144. <div x-data="{ foo: 'bar' }">
  145. <button @click="foo = 'baz'"></button>
  146. <span x-bind:foo="foo"></span>
  147. </div>
  148. `
  149. Alpine.start()
  150. expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
  151. document.querySelector('button').click()
  152. await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
  153. })