import Alpine from 'alpinejs'
import { wait, fireEvent } from '@testing-library/dom'
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms))
global.MutationObserver = class {
observe() {}
}
test('data modified in event listener updates affected attribute bindings', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test('nested data modified in event listener updates affected attribute bindings', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test('.passive modifier should disable e.preventDefault()', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(null)
fireEvent.mouseDown(document.querySelector('button'))
await wait(() => {
expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(false)
})
})
test('.stop modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('div').__x.$data.foo).toEqual('bar')
document.querySelector('span').click()
await wait(() => {
expect(document.querySelector('div').__x.$data.foo).toEqual('baz')
})
})
test('.self modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('bar')
document.querySelector('button').click()
await wait(() => {
expect(document.querySelector('span').textContent).toEqual('bar')
})
document.querySelector('#selfTarget').click()
await wait(() => {
expect(document.querySelector('span').textContent).toEqual('baz')
})
})
test('.prevent modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('input').checked).toEqual(false)
document.querySelector('input').click()
expect(document.querySelector('input').checked).toEqual(false)
})
test('.window modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
document.body.click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test('unbind global event handler when element is removed', async () => {
document._callCount = 0
document.body.innerHTML = `
`
Alpine.start()
document.body.click()
document.body.innerHTML = ''
document.body.click()
await new Promise(resolve => setTimeout(resolve, 1))
expect(document._callCount).toEqual(1)
})
test('.document modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
document.body.click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test('.once modifier', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('0')
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('1') })
document.querySelector('button').click()
await timeout(25)
expect(document.querySelector('span').getAttribute('foo')).toEqual('1')
})
test('.once modifier does not remove listener if false is returned', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('0')
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('1') })
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('2') })
await timeout(25)
expect(document.querySelector('span').getAttribute('foo')).toEqual('2')
})
test('keydown modifiers', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('0')
fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('2') })
fireEvent.keyDown(document.querySelector('input'), { key: ' ' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('4') })
fireEvent.keyDown(document.querySelector('input'), { key: 'Spacebar' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('6') })
fireEvent.keyDown(document.querySelector('input'), { key: 'Escape' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('7') })
})
test('keydown combo modifiers', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('0')
fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('0') })
fireEvent.keyDown(document.querySelector('input'), { key: 'Enter', metaKey: true })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
})
test('keydown with specified key and stop modifier only stops for specified key', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('0')
fireEvent.keyDown(document.querySelector('input'), { key: 'Escape' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
await timeout(25)
expect(document.querySelector('span').textContent).toEqual('1')
})
test('click away', async () => {
// Because jsDom doesn't support .offsetHeight and offsetWidth, we have to
// make our own implementation using a specific class added to the class. Ugh.
Object.defineProperties(window.HTMLElement.prototype, {
offsetHeight: {
get: function () { return this.classList.contains('hidden') ? 0 : 1 }
},
offsetWidth: {
get: function () { return this.classList.contains('hidden') ? 0 : 1 }
}
});
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false)
document.querySelector('li').click()
await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
document.querySelector('ul').click()
await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
document.querySelector('#outer').click()
await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(true) })
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('ul').classList.contains('hidden')).toEqual(false) })
})
test('.passive + .away modifier still disables e.preventDefault()', async () => {
// Pretend like all the elements are visible
Object.defineProperties(window.HTMLElement.prototype, {
offsetHeight: {
get: () => 1
},
offsetWidth: {
get: () => 1
}
});
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(null)
fireEvent.mouseDown(document.querySelector('span'))
await wait(() => {
expect(document.querySelector('div').__x.$data.defaultPrevented).toEqual(false)
})
})
test('supports short syntax', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
document.querySelector('button').click()
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test('event with colon', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').getAttribute('foo')).toEqual('bar')
var event = new CustomEvent('my:event');
document.dispatchEvent(event);
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})
test.skip('prevent default action when an event returns false', async () => {
// This test is skipped because in a browser this works, but it won't
// pass in this tests unless we bypass the promise resolving system
// for the result of an event handler expression.
window.confirm = jest.fn().mockReturnValue(false)
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('input').checked).toEqual(false)
document.querySelector('input').click()
expect(document.querySelector('input').checked).toEqual(false)
window.confirm = jest.fn().mockReturnValue(true)
document.querySelector('input').click()
expect(document.querySelector('input').checked).toEqual(true)
})
test('allow method reference to be passed to listeners', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('bar')
document.querySelector('button').click()
await new Promise(resolve => setTimeout(resolve, 1))
expect(document.querySelector('span').textContent).toEqual('baz')
})
test('event instance is passed to method reference', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('bar')
document.querySelector('button').click()
await new Promise(resolve => setTimeout(resolve, 1))
expect(document.querySelector('span').textContent).toEqual('baz')
})
test('autocomplete event does not trigger keydown with modifier callback', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('span').textContent).toEqual('0')
const autocompleteEvent = new Event('keydown')
fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('0') })
fireEvent.keyDown(document.querySelector('input'), { key: '?' })
await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
fireEvent(document.querySelector('input'), autocompleteEvent)
await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
})
test('.camel modifier correctly binds event listener', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('p').textContent).toEqual('bar')
document.querySelector('button').click();
await wait(() => {
expect(document.querySelector('p').textContent).toEqual('bob');
});
})
test('.camel modifier correctly binds event listener with namespace', async () => {
document.body.innerHTML = `
`
Alpine.start()
expect(document.querySelector('p').textContent).toEqual('bar')
document.querySelector('button').click();
await wait(() => {
expect(document.querySelector('p').textContent).toEqual('bob');
});
})