describe('hx-trigger attribute', function() {
beforeEach(function() {
this.server = sinon.fakeServer.create()
clearWorkArea()
})
afterEach(function() {
this.server.restore()
clearWorkArea()
})
it('non-default value works', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var form = make('
')
form.click()
form.innerHTML.should.equal('Click Me!')
this.server.respond()
form.innerHTML.should.equal('Clicked!')
})
it('changed modifier works', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input = make('')
var div = make('')
input.click()
this.server.respond()
div.innerHTML.should.equal('')
input.click()
this.server.respond()
div.innerHTML.should.equal('')
input.value = 'bar'
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
})
it('changed modifier works along from clause with single input', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input = make('')
make('')
var div = make('')
input.click()
this.server.respond()
div.innerHTML.should.equal('')
input.click()
this.server.respond()
div.innerHTML.should.equal('')
input.value = 'bar'
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
})
// This test and the next one should be kept in sync.
it('changed modifier works along from clause with two inputs', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input1 = make('')
var input2 = make('')
make('')
var div = make('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('')
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.value = 'bar'
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.value = 'foo'
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
// This test and the previous one should be kept in sync.
it('changed modifier counts each triggerspec separately', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input1 = make('')
var input2 = make('')
make('')
make('')
var div = make('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('')
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.value = 'bar'
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
input2.value = 'foo'
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 4')
})
it('separate changed modifier works along from clause with two inputs', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input1 = make('')
var input2 = make('')
make('')
var div = make('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('')
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.value = 'bar'
input2.click()
this.server.respond()
div.innerHTML.should.equal('')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.value = 'foo'
input1.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input2.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
it('once modifier works', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input = make('')
var div = make('')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.value = 'bar'
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
})
it('once modifier works with multiple triggers', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var input = make('')
var div = make('')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.value = 'bar'
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
input.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
htmx.trigger(input, 'foo')
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
it('polling works', function(complete) {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
if (requests > 5) {
complete()
// cancel polling with a
xhr.respond(286, {}, 'Requests: ' + requests)
} else {
xhr.respond(200, {}, 'Requests: ' + requests)
}
})
this.server.autoRespond = true
this.server.autoRespondAfter = 0
make('')
})
it('non-default value works w/ data-* prefix', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var form = make('')
form.click()
form.innerHTML.should.equal('Click Me!')
this.server.respond()
form.innerHTML.should.equal('Clicked!')
})
it('works with multiple events', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div = make('Requests: 0
')
div.innerHTML.should.equal('Requests: 0')
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
div.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
it('parses spec strings', function() {
var specExamples = {
'': [{ trigger: 'click' }],
'every 1s': [{ trigger: 'every', pollInterval: 1000 }],
'every 0s': [{ trigger: 'every', pollInterval: 0 }],
'every 0ms': [{ trigger: 'every', pollInterval: 0 }],
click: [{ trigger: 'click' }],
customEvent: [{ trigger: 'customEvent' }],
'event changed': [{ trigger: 'event', changed: true }],
'event once': [{ trigger: 'event', once: true }],
'event throttle:1s': [{ trigger: 'event', throttle: 1000 }],
'event throttle:0s': [{ trigger: 'event', throttle: 0 }],
'event throttle:0ms': [{ trigger: 'event', throttle: 0 }],
'event throttle:1s, foo': [{ trigger: 'event', throttle: 1000 }, { trigger: 'foo' }],
'event delay:1s': [{ trigger: 'event', delay: 1000 }],
'event delay:1s, foo': [{ trigger: 'event', delay: 1000 }, { trigger: 'foo' }],
'event delay:0s, foo': [{ trigger: 'event', delay: 0 }, { trigger: 'foo' }],
'event delay:0ms, foo': [{ trigger: 'event', delay: 0 }, { trigger: 'foo' }],
'event changed once delay:1s': [{ trigger: 'event', changed: true, once: true, delay: 1000 }],
'event1,event2': [{ trigger: 'event1' }, { trigger: 'event2' }],
'event1, event2': [{ trigger: 'event1' }, { trigger: 'event2' }],
'event1 once, event2 changed': [{ trigger: 'event1', once: true }, { trigger: 'event2', changed: true }],
'event1,': [{ trigger: 'event1' }],
' ': [{ trigger: 'click' }]
}
for (var specString in specExamples) {
var div = make("")
var spec = htmx._('getTriggerSpecs')(div)
spec.should.deep.equal(specExamples[specString], 'Found : ' + JSON.stringify(spec) + ', expected : ' + JSON.stringify(specExamples[specString]) + ' for spec: ' + specString)
}
})
it('sets default trigger for forms', function() {
var form = make('')
var spec = htmx._('getTriggerSpecs')(form)
spec.should.deep.equal([{ trigger: 'submit' }])
})
it('sets default trigger for form elements', function() {
var form = make('')
var spec = htmx._('getTriggerSpecs')(form)
spec.should.deep.equal([{ trigger: 'change' }])
})
it('filters properly with false filter spec', function() {
this.server.respondWith('GET', '/test', 'Called!')
var form = make('')
form.click()
form.innerHTML.should.equal('Not Called')
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
this.server.respond()
form.innerHTML.should.equal('Not Called')
})
it('filters properly with true filter spec', function() {
this.server.respondWith('GET', '/test', 'Called!')
var form = make('')
form.click()
form.innerHTML.should.equal('Not Called')
var event = htmx._('makeEvent')('evt')
event.foo = true
form.dispatchEvent(event)
this.server.respond()
form.innerHTML.should.equal('Called!')
})
it('filters properly compound filter spec', function() {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
event.foo = true
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
event.bar = true
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
})
it('can refer to target element in condition', function() {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
div.classList.add('doIt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
})
it('can refer to target element in condition w/ equality', function() {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
div.id = 'foo'
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
})
it('negative condition', function() {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
div.classList.add('disabled')
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
div.classList.remove('disabled')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
})
it('global function call works', function() {
window.globalFun = function(evt) {
return evt.bar
}
try {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
event.bar = false
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
event.bar = true
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
} finally {
delete window.globalFun
}
})
it('global property event filter works', function() {
window.foo = {
bar: false
}
try {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
foo.bar = true
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
} finally {
delete window.foo
}
})
it('global variable filter works', function() {
try {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Not Called')
window.foo = true
div.dispatchEvent(event)
this.server.respond()
div.innerHTML.should.equal('Called!')
} finally {
delete window.foo
}
})
it('can filter polling', function(complete) {
this.server.respondWith('GET', '/test', 'Called!')
window.foo = false
var div = make('Not Called
')
var div2 = make('Not Called
')
this.server.autoRespond = true
this.server.autoRespondAfter = 0
setTimeout(function() {
div.innerHTML.should.equal('Not Called')
div2.innerHTML.should.equal('Called!')
delete window.foo
complete()
}, 100)
})
it('bad condition issues error', function() {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
var errorEvent = null
var handler = htmx.on('htmx:eventFilter:error', function(event) {
errorEvent = event
})
try {
var event = htmx._('makeEvent')('evt')
div.dispatchEvent(event)
should.not.equal(null, errorEvent)
should.not.equal(null, errorEvent.detail.source)
console.log(errorEvent.detail.source)
} finally {
htmx.off('htmx:eventFilter:error', handler)
}
})
it('filters properly with true for empty condition', function() {
this.server.respondWith('GET', '/test', 'Called!')
var form = make('')
form.click()
form.innerHTML.should.equal('Not Called')
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
this.server.respond()
form.innerHTML.should.equal('Called!')
})
it('syntax error in condition issues error', function() {
this.server.respondWith('GET', '/test', 'Called!')
var errorEvent = null
var handler = htmx.on('htmx:syntax:error', function(event) {
errorEvent = event
})
var form = make('')
try {
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
should.not.equal(null, errorEvent)
should.not.equal(null, errorEvent.detail.source)
console.log(errorEvent.detail.source)
} finally {
htmx.off('htmx:syntax:error', handler)
}
})
it('filters properly with condition containing square backets', function() {
this.server.respondWith('GET', '/test', 'Called!')
var form = make('')
form.click()
form.innerHTML.should.equal('Not Called')
var event = htmx._('makeEvent')('evt')
event.foo = [true]
form.dispatchEvent(event)
this.server.respond()
form.innerHTML.should.equal('Called!')
})
it('from clause works', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div2 = make('')
var div1 = make('Requests: 0
')
div1.innerHTML.should.equal('Requests: 0')
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Requests: 0')
div2.click()
this.server.respond()
div1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with body selector', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('Requests: 0
')
div1.innerHTML.should.equal('Requests: 0')
document.body.click()
this.server.respond()
div1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with document selector', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('Requests: 0
')
div1.innerHTML.should.equal('Requests: 0')
htmx.trigger(document, 'foo')
this.server.respond()
div1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with window selector', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('Requests: 0
')
div1.innerHTML.should.equal('Requests: 0')
htmx.trigger(window, 'foo')
this.server.respond()
div1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with closest clause', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('')
var a1 = byId('a1')
a1.innerHTML.should.equal('Requests: 0')
div1.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with find clause', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('')
var a1 = byId('a1')
a1.innerHTML.should.equal('Requests: 0')
a1.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with next', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
make('Requests: 0')
var a1 = byId('a1')
a1.innerHTML.should.equal('Requests: 0')
a1.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 1')
})
it('from clause works with previous', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
make('Requests: 0')
var a1 = byId('a1')
a1.innerHTML.should.equal('Requests: 0')
a1.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 1')
})
it('event listeners on other elements are removed when an element is swapped out', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/test2', 'Clicked')
var div1 = make('')
var div2 = byId('d2')
div2.innerHTML.should.equal('Requests: 0')
document.body.click()
this.server.respond()
requests.should.equal(1)
requests.should.equal(1)
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Clicked')
requests.should.equal(2)
document.body.click()
this.server.respond()
requests.should.equal(2)
})
it('multiple triggers with from clauses mixed in work', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div2 = make('')
var div1 = make('Requests: 0
')
div1.innerHTML.should.equal('Requests: 0')
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Requests: 1')
div2.click()
this.server.respond()
div1.innerHTML.should.equal('Requests: 2')
})
it('from clause works with multiple extended selectors', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
make('' +
'' +
'Requests: 0')
var btn = byId('btn')
var a1 = byId('a1')
a1.innerHTML.should.equal('Requests: 0')
btn.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 1')
a1.click()
this.server.respond()
a1.innerHTML.should.equal('Requests: 2')
})
it('event listeners can filter on target', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div1 = make('' +
'
Requests: 0
' +
'
' +
'
' +
'
')
var div1 = byId('d1')
var div2 = byId('d2')
var div3 = byId('d3')
div1.innerHTML.should.equal('Requests: 0')
document.body.click()
this.server.respond()
requests.should.equal(0)
div1.click()
this.server.respond()
requests.should.equal(0)
div2.click()
this.server.respond()
requests.should.equal(0)
div3.click()
this.server.respond()
requests.should.equal(1)
})
it('consume prevents event propagation', function() {
this.server.respondWith('GET', '/foo', 'foo')
this.server.respondWith('GET', '/bar', 'bar')
var div = make("')
byId('d1').click()
this.server.respond()
// should not have been replaced by click
byId('d1').parentElement.should.equal(div)
byId('d1').innerText.should.equal('bar')
})
it('throttle prevents multiple requests from happening', function(done) {
var requests = 0
var server = this.server
server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
server.respond()
div.click()
server.respond()
div.click()
server.respond()
div.click()
server.respond()
// should not have been replaced by click
div.innerText.should.equal('Requests: 1')
setTimeout(function() {
div.click()
server.respond()
div.innerText.should.equal('Requests: 2')
div.click()
server.respond()
div.innerText.should.equal('Requests: 2')
done()
}, 50)
})
it('A throttle of 0 does not prevent multiple requests from happening', function(done) {
var requests = 0
var server = this.server
server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
server.respondWith('GET', '/bar', 'bar')
var div = make(
""
)
div.click()
server.respond()
div.innerText.should.equal('Requests: 1')
div.click()
server.respond()
div.innerText.should.equal('Requests: 2')
div.click()
server.respond()
div.innerText.should.equal('Requests: 3')
div.click()
server.respond()
div.innerText.should.equal('Requests: 4')
done()
})
it('delay delays the request', function(done) {
var requests = 0
var server = this.server
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
this.server.respond()
div.click()
this.server.respond()
div.click()
this.server.respond()
div.click()
this.server.respond()
div.innerText.should.equal('')
setTimeout(function() {
server.respond()
div.innerText.should.equal('Requests: 1')
div.click()
server.respond()
div.innerText.should.equal('Requests: 1')
done()
}, 50)
})
it('A 0 delay does not delay the request', function(done) {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make(
""
)
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 1')
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 2')
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 3')
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 4')
done()
})
it('requests are queued with last one winning by default', function() {
var requests = 0
var server = this.server
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
div.click()
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 1')
this.server.respond()
div.innerText.should.equal('Requests: 2')
this.server.respond()
div.innerText.should.equal('Requests: 2')
})
it('queue:all queues all requests', function() {
var requests = 0
var server = this.server
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
div.click()
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 1')
this.server.respond()
div.innerText.should.equal('Requests: 2')
this.server.respond()
div.innerText.should.equal('Requests: 3')
})
it('queue:first queues first request', function() {
var requests = 0
var server = this.server
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
div.click()
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 1')
this.server.respond()
div.innerText.should.equal('Requests: 2')
this.server.respond()
div.innerText.should.equal('Requests: 2')
})
it('queue:none queues no requests', function() {
var requests = 0
var server = this.server
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
this.server.respondWith('GET', '/bar', 'bar')
var div = make("")
div.click()
div.click()
div.click()
this.server.respond()
div.innerText.should.equal('Requests: 1')
this.server.respond()
div.innerText.should.equal('Requests: 1')
this.server.respond()
div.innerText.should.equal('Requests: 1')
})
it('load event works w/ positive filters', function() {
this.server.respondWith('GET', '/test', 'Loaded!')
var div = make('Load Me!
')
div.innerHTML.should.equal('Load Me!')
this.server.respond()
div.innerHTML.should.equal('Loaded!')
})
it('load event works w/ negative filters', function() {
this.server.respondWith('GET', '/test', 'Loaded!')
var div = make('Load Me!
')
div.innerHTML.should.equal('Load Me!')
this.server.respond()
div.innerHTML.should.equal('Load Me!')
})
it('reveal event works on two elements', function() {
this.server.respondWith('GET', '/test1', 'test 1')
this.server.respondWith('GET', '/test2', 'test 2')
var div = make('')
var div2 = make('')
div.innerHTML.should.equal('')
div2.innerHTML.should.equal('')
htmx.trigger(div, 'revealed')
htmx.trigger(div2, 'revealed')
this.server.respondAll()
div.innerHTML.should.equal('test 1')
div2.innerHTML.should.equal('test 2')
})
it('scrolling triggers revealed event', function(done) {
this.server.respondWith('GET', '/test', 'test')
this.server.autoRespond = true
this.server.autoRespondAfter = 0
var div = make('')
div.innerHTML.should.equal('')
div.scrollIntoView({ block: 'end', behavior: htmx.config.scrollBehavior })
htmx.trigger(document.body, 'scroll')
setTimeout(function() {
div.innerHTML.should.equal('test')
done()
}, 250)
})
if (window.__playwright__binding__) {
it('scrolling triggers intersect event', function(done) {
// test only works reliably with playwright
this.server.respondWith('GET', '/test', 'test')
this.server.autoRespond = true
this.server.autoRespondAfter = 0
var div = make('')
div.innerHTML.should.equal('')
div.scrollIntoView({ block: 'end', behavior: htmx.config.scrollBehavior })
htmx.trigger(document.body, 'scroll')
setTimeout(function() {
div.innerHTML.should.equal('test')
done()
}, 250)
})
}
it('triggering revealed while component not yet inited still works', function(done) {
this.server.respondWith('GET', '/test', 'test')
var div = make('')
var data = div['htmx-internal-data']
delete data.initHash // simulate not inited or revealed yet
div.removeAttribute('data-hx-revealed')
var server1 = this.server
div.innerHTML.should.equal('')
div.scrollIntoView({ block: 'end', behavior: htmx.config.scrollBehavior })
htmx.trigger(document.body, 'scroll')
setTimeout(function() {
server1.autoRespond = true
server1.autoRespondAfter = 0
htmx.process(div) // processing the div should also trigger revealed event now
setTimeout(function() {
div.innerHTML.should.equal('test')
done()
}, 10)
}, 250)
})
it('reveal event works when triggered by window', function() {
this.server.respondWith('GET', '/test1', 'test 1')
var div = make('foo
')
div.innerHTML.should.equal('foo')
this.server.respondAll()
div.innerHTML.should.equal('test 1')
})
it('revealed can be paired w/ other events', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div = make('foo
')
div.innerHTML.should.equal('foo')
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
div.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
it('revealed doesnt cause other events to trigger', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div = make('foo
')
div.innerHTML.should.equal('foo')
this.server.respondAll()
div.innerHTML.should.equal('foo')
})
it('fires the htmx:trigger event when an AJAX attribute is specified', function() {
var param = 'foo'
var handler = htmx.on('htmx:trigger', function(evt) {
param = 'bar'
})
try {
this.server.respondWith('GET', '/test1', 'test 1')
var div = make('')
div.click()
should.equal(param, 'bar')
} finally {
htmx.off('htmx:trigger', handler)
}
})
it('fires the htmx:trigger event when no AJAX attribute is specified', function() {
var param = 'foo'
var handler = htmx.on('htmx:trigger', function(evt) {
param = 'bar'
})
try {
var div = make('')
div.click()
should.equal(param, 'bar')
} finally {
htmx.off('htmx:trigger', handler)
}
})
it('fires the htmx:trigger event for delayed triggers', function(done) {
var param = 'foo'
var handler = htmx.on('htmx:trigger', function(evt) {
param = 'bar'
})
var div = make('')
div.click()
setTimeout(function() {
try {
should.equal(param, 'bar')
done()
} finally {
htmx.off('htmx:trigger', handler)
}
}, 50)
})
it('fires the htmx:trigger event when the trigger is a load', function(done) {
this.server.respondWith(
'GET',
'/test',
'Response
'
)
var div = make('Submit
')
div.click()
this.server.respond()
var response = div.children[0]
response.innerText.should.equal('Response')
setTimeout(function() {
try {
response.innerText.should.equal('Done')
done()
} finally {
}
}, 100)
})
it('filters support "this" reference to the current element', function() {
this.server.respondWith('GET', '/test', 'Called!')
var form = make('')
form.click()
this.server.respond()
form.innerHTML.should.equal('Not Called')
form.classList.add('bar')
form.click()
this.server.respond()
form.innerHTML.should.equal('Called!')
})
it('correctly handles CSS descendant combinators', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var outer = make(`
`)
var inner = byId('inner')
var second = byId('second')
var other = byId('other')
second.innerHTML.should.equal('Unclicked.')
other.innerHTML.should.equal('Unclicked.')
inner.click()
this.server.respond()
second.innerHTML.should.equal('Clicked!')
other.innerHTML.should.equal('Clicked!')
})
it('correctly handles CSS descendant combinators in modifier target', function() {
this.server.respondWith('GET', '/test', 'Called')
make('')
var div = make('Not Called
')
byId('a1').click()
this.server.respond()
div.innerHTML.should.equal('Not Called')
byId('a2').click()
this.server.respond()
div.innerHTML.should.equal('Called')
})
it('correctly handles CSS descendant combinators in modifier root', function() {
this.server.respondWith('GET', '/test', 'Called')
var errorEvent = null
var handler = htmx.on('htmx:syntax:error', function(event) {
errorEvent = event
})
var form = make('Not Called
')
try {
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
should.equal(null, errorEvent)
} finally {
htmx.off('htmx:syntax:error', handler)
}
})
it('correctly handles intersect with modifier threshold', function() {
this.server.respondWith('GET', '/test', 'Called')
var errorEvent = null
var handler = htmx.on('htmx:syntax:error', function(event) {
errorEvent = event
})
var form = make('Not Called
')
try {
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
should.equal(null, errorEvent)
} finally {
htmx.off('htmx:syntax:error', handler)
}
})
it('issues error with invalid trigger spec', function() {
this.server.respondWith('GET', '/test', 'Called')
var errorEvent = null
var handler = htmx.on('htmx:syntax:error', function(event) {
errorEvent = event
})
var form = make('Not Called
')
try {
var event = htmx._('makeEvent')('evt')
form.dispatchEvent(event)
should.not.equal(null, errorEvent)
should.not.equal(null, errorEvent.detail.source)
console.log(errorEvent.detail.source)
} finally {
htmx.off('htmx:syntax:error', handler)
}
})
it('uses trigger specs cache if defined', function() {
var initialCacheConfig = htmx.config.triggerSpecsCache
htmx.config.triggerSpecsCache = {}
var specExamples = {
'every 1s': [{ trigger: 'every', pollInterval: 1000 }],
click: [{ trigger: 'click' }],
customEvent: [{ trigger: 'customEvent' }],
'event changed': [{ trigger: 'event', changed: true }],
'event once': [{ trigger: 'event', once: true }],
'event delay:1s': [{ trigger: 'event', delay: 1000 }],
'event throttle:1s': [{ trigger: 'event', throttle: 1000 }],
'event delay:1s, foo': [{ trigger: 'event', delay: 1000 }, { trigger: 'foo' }],
'event throttle:1s, foo': [{ trigger: 'event', throttle: 1000 }, { trigger: 'foo' }],
'event changed once delay:1s': [{ trigger: 'event', changed: true, once: true, delay: 1000 }],
'event1,event2': [{ trigger: 'event1' }, { trigger: 'event2' }],
'event1, event2': [{ trigger: 'event1' }, { trigger: 'event2' }],
'event1 once, event2 changed': [{ trigger: 'event1', once: true }, { trigger: 'event2', changed: true }]
}
for (var specString in specExamples) {
var div = make("")
var spec = htmx._('getTriggerSpecs')(div)
spec.should.deep.equal(specExamples[specString], 'Found : ' + JSON.stringify(spec) + ', expected : ' + JSON.stringify(specExamples[specString]) + ' for spec: ' + specString)
}
Object.keys(htmx.config.triggerSpecsCache).length.should.greaterThan(0)
Object.keys(htmx.config.triggerSpecsCache).length.should.equal(Object.keys(specExamples).length)
htmx.config.triggerSpecsCache = initialCacheConfig
})
it('correctly reuses trigger specs from the cache if defined', function() {
var initialCacheConfig = htmx.config.triggerSpecsCache
htmx.config.triggerSpecsCache = {}
var triggerStr = 'event changed once delay:1s'
var expectedSpec = [{ trigger: 'event', changed: true, once: true, delay: 1000 }]
var div = make("")
var spec = htmx._('getTriggerSpecs')(div)
spec.should.deep.equal(expectedSpec, 'Found : ' + JSON.stringify(spec) + ', expected : ' + JSON.stringify(expectedSpec) + ' for spec: ' + triggerStr)
spec.push('This should be carried to further identical specs thanks to the cache')
var div2 = make("")
var spec2 = htmx._('getTriggerSpecs')(div2)
spec2.should.deep.equal(spec, 'Found : ' + JSON.stringify(spec) + ', expected : ' + JSON.stringify(spec2) + ' for cached spec: ' + triggerStr)
Object.keys(htmx.config.triggerSpecsCache).length.should.equal(1)
htmx.config.triggerSpecsCache = initialCacheConfig
})
it('handles spaces at the end of trigger specs', function() {
var requests = 0
this.server.respondWith('GET', '/test', function(xhr) {
requests++
xhr.respond(200, {}, 'Requests: ' + requests)
})
var div = make('Requests: 0
')
div.innerHTML.should.equal('Requests: 0')
this.server.respond()
div.innerHTML.should.equal('Requests: 1')
div.click()
this.server.respond()
div.innerHTML.should.equal('Requests: 2')
})
it('Removing polling trigger and processing node removes timeout', function(complete) {
this.server.respondWith('GET', '/test', 'Called!')
var div = make('Not Called
')
div.removeAttribute('hx-trigger')
should.not.equal(div['htmx-internal-data'].timeout, undefined)
htmx.process(div)
should.equal(div['htmx-internal-data'].timeout, undefined)
this.server.autoRespond = true
this.server.autoRespondAfter = 0
setTimeout(function() {
div.innerHTML.should.equal('Not Called')
delete window.foo
complete()
}, 30)
})
})