UNPKG

alpinejs

Version:
423 lines (325 loc) 11.3 kB
import Alpine from 'alpinejs' import { fireEvent, wait } from '@testing-library/dom' const timeout = ms => new Promise(resolve => setTimeout(resolve, ms)) test('auto-detect new components at the top level', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <section></section> ` Alpine.start() document.querySelector('section').innerHTML = ` <div x-data="{ foo: '' }"> <input x-model="foo"> <span x-text="foo"></span> </div> ` runObservers[0]([ { target: document.querySelector('section'), type: 'childList', addedNodes: [ document.querySelector('div') ], } ]) fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }}) await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') }) }) test('auto-detect nested new components at the top level', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <section></section> ` Alpine.start() document.querySelector('section').innerHTML = ` <article> <div x-data="{ foo: '' }"> <input x-model="foo"> <span x-text="foo"></span> </div> </article> ` runObservers[0]([ { target: document.querySelector('section'), type: 'childList', addedNodes: [ document.querySelector('article') ], } ]) fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }}) await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') }) }) test('auto-detect new components and dont lose state of existing ones', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <div id="A" x-data="{ foo: '' }"> <input x-model="foo"> <span x-text="foo"></span> <div id="B"></div> </div> ` Alpine.start() fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }}) await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') }) document.querySelector('#B').innerHTML = ` <div x-data="{foo: 'baz'}"> <input x-model="foo"> <span x-text="foo"></span> </div> ` runObservers[0]([ { target: document.querySelector('#A'), type: 'childList', addedNodes: [ document.querySelector('#B div') ], } ]) await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') expect(document.querySelector('#B span').textContent).toEqual('baz') }) }) test('auto-detect new components that are wrapped in non-new component tags', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <div id="A" x-data="{ foo: '' }"> <input x-model="foo"> <span x-text="foo"></span> <div id="B"></div> </div> ` Alpine.start() fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }}) await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') }) document.querySelector('#B').innerHTML = ` <section> <div x-data="{foo: 'baz'}"> <input x-model="foo"> <span x-text="foo"></span> </div> </section> ` runObservers[0]([ { target: document.querySelector('#A'), type: 'childList', addedNodes: [ document.querySelector('#B section') ], } ]) await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') expect(document.querySelector('#B span').textContent).toEqual('baz') }) }) test('auto-initialize new elements added to a component', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <div x-data="{ count: 0 }"> <span x-text="count"></span> <div id="target"> </div> </div> ` Alpine.start() expect(document.querySelector('span').textContent).toEqual('0') document.querySelector('#target').innerHTML = ` <span x-text="count"></span> <button x-on:click="count++"></button> ` runObservers[0]([ { target: document.querySelector('#target'), addedNodes: [ document.querySelector('#target span'), document.querySelector('#target button'), ] } ]) await wait(() => { expect(document.querySelector('#target span').textContent).toEqual('0') }) document.querySelector('button').click() await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') }) await wait(() => { expect(document.querySelector('#target span').textContent).toEqual('1') }) }) test('Alpine mutations don\'t trigger (like x-if and x-for) MutationObserver', async () => { var runObservers = [] var evaluations = 0 global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } window.bob = () => { evaluations++ return 'lob' } document.body.innerHTML = ` <div x-data="{ foo: 'bar' }" id="component"> <template x-if="foo === 'baz'"> <span x-text="bob()"></span> </template> <button @click="foo = 'baz'"></button> </div> ` Alpine.start() document.querySelector('button').click() // Wait out the rendering tick. await new Promise(resolve => setTimeout(resolve, 1)) // Run both queud mutations. runObservers[0]([ { target: document.querySelector('#component'), addedNodes: [ document.querySelector('#component span'), ] } ]) runObservers[1]([ { target: document.querySelector('#component'), addedNodes: [ document.querySelector('#component span'), ] } ]) expect(evaluations).toEqual(2) }) test('auto-detect x-data property changes at run-time', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <div x-data="{ count: 0 }"> <span x-text="count"></span> </div> ` Alpine.start() expect(document.querySelector('span').textContent).toEqual('0') document.querySelector('div').setAttribute('x-data', '{ count: 1 }') runObservers[0]([ { addedNodes: [], type: 'attributes', attributeName: 'x-data', target: document.querySelector('div') } ]) await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') }) }) test('can use $el when changing x-data property at run-time', async () => { var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } document.body.innerHTML = ` <div x-data="{ count: '0' }" data-count="1"> <span x-text="count"></span> </div> ` Alpine.start() expect(document.querySelector('span').textContent).toEqual('0') document.querySelector('div').setAttribute('x-data', '{ count: $el.dataset.count }') runObservers[0]([ { addedNodes: [], type: 'attributes', attributeName: 'x-data', target: document.querySelector('div') } ]) await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') }) }) test('nested components only get registered once on initialization', async () => { global.MutationObserver = class { constructor(callback) {} observe() {} } var initCount = 0 window.registerInit = function () { initCount = initCount + 1 } document.body.innerHTML = ` <div x-data x-init="registerInit()"> <div x-data x-init="registerInit()"></div> </div> ` Alpine.start() expect(initCount).toEqual(2) }) test('can clone an existing component to a new element', async () => { global.MutationObserver = class { constructor(callback) {} observe() {} } document.body.innerHTML = ` <h1 x-data="{ foo: 'bar' }"></h1> <div id="insert-component-here"></div> ` Alpine.start() document.querySelector('#insert-component-here').innerHTML = ` <h2 x-data="{ foo: 'baz' }"> <span x-text="foo"></span> </h2> ` Alpine.clone(document.querySelector('h1').__x, document.querySelector('h2')) expect(document.querySelector('span').textContent).toEqual('bar') }) test('x-attributes are matched exactly', async () => { document.body.innerHTML = ` <div x-data="{ showElement: false }"> <div id="el1" x-show="showElement" /> <div id="el2" xxx-show="showElement" /> <div id="el3" x-showabc="showElement" /> </div> ` Alpine.start() expect(document.getElementById('el1').style.display).toEqual('none') expect(document.getElementById('el2').style.display).not.toEqual('none') await wait(() => { expect(document.getElementById('el3').style.display).not.toEqual('none') }) }) test('a mutation from another part of the HTML doesnt prevent a different alpine component from initializing', async () => { document.body.innerHTML = ` <div x-data x-init="registerInit()"> </div> ` var initCount = 0 window.registerInit = function () { initCount = initCount + 1 } var runObservers = [] global.MutationObserver = class { constructor(callback) { runObservers.push(callback) } observe() {} } Alpine.start() await wait(() => { expect(initCount).toEqual(1) }) document.querySelector('div').innerHTML = ` <h1 x-data x-init="registerInit()"></h1> ` let h2 = document.createElement('h2') document.querySelector('div').parentElement.appendChild(h2) await timeout(5) runObservers[0]([ { target: document.querySelector('h2'), type: 'attributes', addedNodes: [], }, { target: document.querySelector('div'), type: 'childList', addedNodes: [ document.querySelector('h1') ], } ]) await wait(() => { expect(initCount).toEqual(2) }) })