123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- import { beVisible, haveText, html, test } from '../utils'
- test('element side effects are cleaned up after the elements are removed',
- html`
- <div x-data="{ foo: 1, bar: 1 }">
- <button @click="bar++">bar</button>
- <a href="#" @click.prevent="$refs.span.remove()">remove</a>
- <span x-text="(() => { foo = foo + 1; return bar })" x-ref="span"></span>
- <h1 x-text="foo"></h1>
- <h2 x-text="bar"></h2>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText('2'))
- get('h2').should(haveText('1'))
- get('button').click()
- get('h1').should(haveText('3'))
- get('h2').should(haveText('2'))
- get('a').click()
- get('button').click()
- get('h1').should(haveText('3'))
- get('h2').should(haveText('3'))
- }
- )
- test('nested element side effects are cleaned up after the parent is removed',
- html`
- <div x-data="{ foo: 1, bar: 1 }">
- <button @click="bar++">bar</button>
- <a href="#" @click.prevent="$refs.article.remove()">remove</a>
- <article x-ref="article">
- <span x-text="(() => { foo = foo + 1; return bar })"></span>
- </article>
- <h1 x-text="foo"></h1>
- <h2 x-text="bar"></h2>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText('2'))
- get('h2').should(haveText('1'))
- get('button').click()
- get('h1').should(haveText('3'))
- get('h2').should(haveText('2'))
- get('a').click()
- get('button').click()
- get('h1').should(haveText('3'))
- get('h2').should(haveText('3'))
- }
- )
- test('element magic-based side effects are cleaned up after the element is removed',
- html`
- <div x-data="{ foo: 1, bar: 1 }">
- <button @click="foo++">foo</button>
- <a href="#" @click.prevent="$refs.span.remove()">remove</a>
- <span x-init="$watch('foo', () => bar++)" x-ref="span"></span>
- <h1 x-text="foo"></h1>
- <h2 x-text="bar"></h2>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText('1'))
- get('h2').should(haveText('1'))
- get('button').click()
- get('h1').should(haveText('2'))
- get('h2').should(haveText('2'))
- get('a').click()
- get('button').click()
- get('h1').should(haveText('3'))
- get('h2').should(haveText('2'))
- }
- )
- test('can mutate directive value',
- html`
- <div x-data="{ foo: 'bar', bar: 'baz' }">
- <button @click="$refs.target.setAttribute('x-text', 'bar')">change text</button>
- <span x-text="foo" x-ref="target"></span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('bar'))
- get('button').click()
- get('span').should(haveText('baz'))
- }
- )
- test('can add new directive',
- html`
- <div x-data="{ foo: 'bar' }">
- <button @click="$refs.target.setAttribute('x-text', 'foo')">change text</button>
- <span x-ref="target"></span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText(''))
- get('button').click()
- get('span').should(haveText('bar'))
- }
- )
- test('can pause and queue mutations for later resuming/flushing',
- html`
- <div x-data="{ foo: 1 }">
- <button x-on:click="setTimeout(() => foo++)" x-ref="btn">foo</button>
- <h1 x-text="foo"></h1>
- <a href="#" @click="$refs.btn.removeAttribute('x-on:click')" id="remove">remove</a>
- <a href="#" @click="$refs.btn.setAttribute('x-on:click', 'foo++')" id="add">add</a>
- <a href="#" @click="Alpine.deferMutations()" id="defer">add</a>
- <a href="#" @click="Alpine.flushAndStopDeferringMutations()" id="flush">add</a>
- </div>
- `,
- ({ get }) => {
- get('h1').should(haveText('1'))
- get('button').click()
- get('h1').should(haveText('2'))
- get('#remove').click()
- get('button').click()
- get('h1').should(haveText('2'))
- get('#defer').click()
- get('#add').click()
- get('button').click()
- get('h1').should(haveText('2'))
- get('#flush').click()
- get('button').click()
- get('h1').should(haveText('3'))
- }
- )
- test('does not initialise components twice when contained in multiple mutations',
- html`
- <div x-data="{
- foo: 0,
- bar: 0,
- test() {
- container = document.createElement('div')
- this.$root.appendChild(container)
- alpineElement = document.createElement('span')
- alpineElement.setAttribute('x-data', '{init() {this.bar++}}')
- alpineElement.setAttribute('x-init', 'foo++')
- container.appendChild(alpineElement)
- }
- }">
- <span id="one" x-text="foo"></span>
- <span id="two" x-text="bar"></span>
- <button @click="test">Test</button>
- </div>
- `,
- ({ get }) => {
- get('span#one').should(haveText('0'))
- get('span#two').should(haveText('0'))
- get('button').click()
- get('span#one').should(haveText('1'))
- get('span#two').should(haveText('1'))
- }
- )
- test('directives keep working when node is moved into a different one',
- html`
- <div x-data="{
- foo: 0,
- mutate() {
- let button = document.getElementById('one')
- button.remove()
- let container = document.createElement('p')
- container.appendChild(button)
- this.$root.appendChild(container)
- }
- }">
- <button id="one" @click="foo++">increment</button>
- <button id="two" @click="mutate()">Mutate</button>
- <span x-text="foo"></span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('0'))
- get('button#one').click()
- get('span').should(haveText('1'))
- get('button#two').click()
- get('p').should(beVisible())
- get('button#one').click()
- get('span').should(haveText('2'))
- }
- )
- test('no side effects when directives are added to an element that is removed afterwards',
- html`
- <div x-data="{
- foo: 0,
- mutate() {
- let span = document.createElement('span')
- span.setAttribute('x-on:keydown.a.window', 'foo = foo+1')
- let container = document.getElementById('container')
- container.appendChild(span)
- container.remove()
- }
- }">
- <button @click="mutate()">Mutate</button>
- <p id="container"></p>
- <input type="text">
- <span x-text="foo"></span>
- </div>
- `,
- ({ get }) => {
- get('span').should(haveText('0'))
- get('button').click()
- get('input').type('a')
- get('span').should(haveText('0'))
- }
- )
- test(
- "previously initialized elements are not reinitialized on being moved",
- html`
- <script>
- let count = 0;
- document.addEventListener('alpine:init', () => {
- Alpine.directive('test', el => {
- if (count++ > 3) return;
- el.textContent = count;
- let wrapper = document.createElement('div');
- wrapper.setAttribute('class', 'bg-blue-300 p-8');
- el.parentNode.replaceChild(wrapper, el);
- wrapper.appendChild(el);
- });
- });
- </script>
- <div x-data>
- <div class="bg-red-300 w-32 h-32" x-test></div>
- </div>
- `,
- ({ get }) => {
- get("[x-test]").should(haveText("1"));
- }
- );
- test(
- "previously initialized elements are not cleaned up on being moved",
- html`
- <script>
- let count = 0;
- document.addEventListener("alpine:init", () => {
- Alpine.directive("test", (el, _, { cleanup }) => {
- el.textContent = "Initialized";
- cleanup(() => {
- el.textContent = "Cleaned up";
- });
- Alpine.nextTick(() => {
- el.parentNode.replaceChildren(el);
- })
- });
- });
- </script>
- <div x-data>
- <div class="bg-red-300 w-32 h-32" x-test></div>
- </div>
- `,
- ({ get }) => {
- get("[x-test]").should(haveText("Initialized"));
- }
- );
|