mutation.spec.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { beVisible, haveText, html, test } from '../utils'
  2. test('element side effects are cleaned up after the elements are removed',
  3. html`
  4. <div x-data="{ foo: 1, bar: 1 }">
  5. <button @click="bar++">bar</button>
  6. <a href="#" @click.prevent="$refs.span.remove()">remove</a>
  7. <span x-text="(() => { foo = foo + 1; return bar })" x-ref="span"></span>
  8. <h1 x-text="foo"></h1>
  9. <h2 x-text="bar"></h2>
  10. </div>
  11. `,
  12. ({ get }) => {
  13. get('h1').should(haveText('2'))
  14. get('h2').should(haveText('1'))
  15. get('button').click()
  16. get('h1').should(haveText('3'))
  17. get('h2').should(haveText('2'))
  18. get('a').click()
  19. get('button').click()
  20. get('h1').should(haveText('3'))
  21. get('h2').should(haveText('3'))
  22. }
  23. )
  24. test('nested element side effects are cleaned up after the parent is removed',
  25. html`
  26. <div x-data="{ foo: 1, bar: 1 }">
  27. <button @click="bar++">bar</button>
  28. <a href="#" @click.prevent="$refs.article.remove()">remove</a>
  29. <article x-ref="article">
  30. <span x-text="(() => { foo = foo + 1; return bar })"></span>
  31. </article>
  32. <h1 x-text="foo"></h1>
  33. <h2 x-text="bar"></h2>
  34. </div>
  35. `,
  36. ({ get }) => {
  37. get('h1').should(haveText('2'))
  38. get('h2').should(haveText('1'))
  39. get('button').click()
  40. get('h1').should(haveText('3'))
  41. get('h2').should(haveText('2'))
  42. get('a').click()
  43. get('button').click()
  44. get('h1').should(haveText('3'))
  45. get('h2').should(haveText('3'))
  46. }
  47. )
  48. test('can mutate directive value',
  49. html`
  50. <div x-data="{ foo: 'bar', bar: 'baz' }">
  51. <button @click="$refs.target.setAttribute('x-text', 'bar')">change text</button>
  52. <span x-text="foo" x-ref="target"></span>
  53. </div>
  54. `,
  55. ({ get }) => {
  56. get('span').should(haveText('bar'))
  57. get('button').click()
  58. get('span').should(haveText('baz'))
  59. }
  60. )
  61. test('can add new directive',
  62. html`
  63. <div x-data="{ foo: 'bar' }">
  64. <button @click="$refs.target.setAttribute('x-text', 'foo')">change text</button>
  65. <span x-ref="target"></span>
  66. </div>
  67. `,
  68. ({ get }) => {
  69. get('span').should(haveText(''))
  70. get('button').click()
  71. get('span').should(haveText('bar'))
  72. }
  73. )
  74. test('can pause and queue mutations for later resuming/flushing',
  75. html`
  76. <div x-data="{ foo: 1 }">
  77. <button x-on:click="setTimeout(() => foo++)" x-ref="btn">foo</button>
  78. <h1 x-text="foo"></h1>
  79. <a href="#" @click="$refs.btn.removeAttribute('x-on:click')" id="remove">remove</a>
  80. <a href="#" @click="$refs.btn.setAttribute('x-on:click', 'foo++')" id="add">add</a>
  81. <a href="#" @click="Alpine.deferMutations()" id="defer">add</a>
  82. <a href="#" @click="Alpine.flushAndStopDeferringMutations()" id="flush">add</a>
  83. </div>
  84. `,
  85. ({ get }) => {
  86. get('h1').should(haveText('1'))
  87. get('button').click()
  88. get('h1').should(haveText('2'))
  89. get('#remove').click()
  90. get('button').click()
  91. get('h1').should(haveText('2'))
  92. get('#defer').click()
  93. get('#add').click()
  94. get('button').click()
  95. get('h1').should(haveText('2'))
  96. get('#flush').click()
  97. get('button').click()
  98. get('h1').should(haveText('3'))
  99. }
  100. )
  101. test('does not initialise components twice when contained in multiple mutations',
  102. html`
  103. <div x-data="{
  104. foo: 0,
  105. bar: 0,
  106. test() {
  107. container = document.createElement('div')
  108. this.$root.appendChild(container)
  109. alpineElement = document.createElement('span')
  110. alpineElement.setAttribute('x-data', '{init() {this.bar++}}')
  111. alpineElement.setAttribute('x-init', 'foo++')
  112. container.appendChild(alpineElement)
  113. }
  114. }">
  115. <span id="one" x-text="foo"></span>
  116. <span id="two" x-text="bar"></span>
  117. <button @click="test">Test</button>
  118. </div>
  119. `,
  120. ({ get }) => {
  121. get('span#one').should(haveText('0'))
  122. get('span#two').should(haveText('0'))
  123. get('button').click()
  124. get('span#one').should(haveText('1'))
  125. get('span#two').should(haveText('1'))
  126. }
  127. )
  128. test('directives keep working when node is moved into a different one',
  129. html`
  130. <div x-data="{
  131. foo: 0,
  132. mutate() {
  133. let button = document.getElementById('one')
  134. button.remove()
  135. let container = document.createElement('p')
  136. container.appendChild(button)
  137. this.$root.appendChild(container)
  138. }
  139. }">
  140. <button id="one" @click="foo++">increment</button>
  141. <button id="two" @click="mutate()">Mutate</button>
  142. <span x-text="foo"></span>
  143. </div>
  144. `,
  145. ({ get }) => {
  146. get('span').should(haveText('0'))
  147. get('button#one').click()
  148. get('span').should(haveText('1'))
  149. get('button#two').click()
  150. get('p').should(beVisible())
  151. get('button#one').click()
  152. get('span').should(haveText('2'))
  153. }
  154. )
  155. test('no side effects when directives are added to an element that is removed afterwards',
  156. html`
  157. <div x-data="{
  158. foo: 0,
  159. mutate() {
  160. let span = document.createElement('span')
  161. span.setAttribute('x-on:keydown.a.window', 'foo = foo+1')
  162. let container = document.getElementById('container')
  163. container.appendChild(span)
  164. container.remove()
  165. }
  166. }">
  167. <button @click="mutate()">Mutate</button>
  168. <p id="container"></p>
  169. <input type="text">
  170. <span x-text="foo"></span>
  171. </div>
  172. `,
  173. ({ get }) => {
  174. get('span').should(haveText('0'))
  175. get('button').click()
  176. get('input').type('a')
  177. get('span').should(haveText('0'))
  178. }
  179. )