describe('Core htmx API test', function() { beforeEach(function() { this.server = makeServer() clearWorkArea() }) afterEach(function() { this.server.restore() clearWorkArea() }) it('should find properly', function() { var div = make("
") div.should.equal(htmx.find('#d1')) div.should.equal(htmx.find('.c1')) div.should.equal(htmx.find('.c2')) div.should.equal(htmx.find('.c1.c2')) }) it('should find properly from elt', function() { var div = make("
") htmx.find(div, 'a').id.should.equal('a1') }) it('should find all properly', function() { var div = make("
") htmx.findAll('.c1').length.should.equal(3) htmx.findAll('.c2').length.should.equal(2) htmx.findAll('.c3').length.should.equal(1) }) it('should find all properly from elt', function() { var div = make("
") htmx.findAll(div, '.c1').length.should.equal(3) htmx.findAll(div, '.c2').length.should.equal(2) htmx.findAll(div, '.c3').length.should.equal(1) }) it('should find closest element properly', function() { var div = make("
") var a = htmx.find(div, 'a') htmx.closest(a, 'div').should.equal(div) }) it('should remove element properly', function() { var div = make('
') var a = htmx.find(div, 'a') htmx.remove(a) div.innerHTML.should.equal('') }) it('should remove element properly w/ selector', function() { var div = make("
") var a = htmx.find(div, 'a') htmx.remove('#a1') div.innerHTML.should.equal('') }) it('should add class properly', function() { var div = make('
') div.classList.contains('foo').should.equal(false) htmx.addClass(div, 'foo') div.classList.contains('foo').should.equal(true) }) it('should add class properly w/ selector', function() { var div = make("
") div.classList.contains('foo').should.equal(false) htmx.addClass('#div1', 'foo') div.classList.contains('foo').should.equal(true) }) it('should add class properly after delay', function(done) { var div = make('
') div.classList.contains('foo').should.equal(false) htmx.addClass(div, 'foo', 10) div.classList.contains('foo').should.equal(false) setTimeout(function() { div.classList.contains('foo').should.equal(true) done() }, 20) }) it('should remove class properly', function() { var div = make('
') htmx.addClass(div, 'foo') div.classList.contains('foo').should.equal(true) htmx.removeClass(div, 'foo') div.classList.contains('foo').should.equal(false) }) it('should remove class properly w/ selector', function() { var div = make("
") htmx.addClass(div, 'foo') div.classList.contains('foo').should.equal(true) htmx.removeClass('#div1', 'foo') div.classList.contains('foo').should.equal(false) }) it('should add class properly after delay', function(done) { var div = make('
') htmx.addClass(div, 'foo') div.classList.contains('foo').should.equal(true) htmx.removeClass(div, 'foo', 10) div.classList.contains('foo').should.equal(true) setTimeout(function() { div.classList.contains('foo').should.equal(false) done() }, 20) }) it('should toggle class properly', function() { var div = make('
') div.classList.contains('foo').should.equal(false) htmx.toggleClass(div, 'foo') div.classList.contains('foo').should.equal(true) htmx.toggleClass(div, 'foo') div.classList.contains('foo').should.equal(false) }) it('should toggle class properly w/ selector', function() { var div = make("
") div.classList.contains('foo').should.equal(false) htmx.toggleClass('#div1', 'foo') div.classList.contains('foo').should.equal(true) htmx.toggleClass('#div1', 'foo') div.classList.contains('foo').should.equal(false) }) it('should take class properly', function() { var div1 = make('
') var div2 = make('
') var div3 = make('
') div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(false) htmx.takeClass(div1, 'foo') div1.classList.contains('foo').should.equal(true) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(false) htmx.takeClass(div2, 'foo') div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(true) div3.classList.contains('foo').should.equal(false) htmx.takeClass(div3, 'foo') div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(true) }) it('should take class properly w/ selector', function() { var div1 = make("
") var div2 = make("
") var div3 = make("
") div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(false) htmx.takeClass('#div1', 'foo') div1.classList.contains('foo').should.equal(true) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(false) htmx.takeClass('#div2', 'foo') div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(true) div3.classList.contains('foo').should.equal(false) htmx.takeClass('#div3', 'foo') div1.classList.contains('foo').should.equal(false) div2.classList.contains('foo').should.equal(false) div3.classList.contains('foo').should.equal(true) }) it('eval can be suppressed', function() { var calledEvent = false var handler = htmx.on('htmx:evalDisallowedError', function() { calledEvent = true }) try { htmx.config.allowEval = false should.equal(htmx._('tokenizeString'), undefined) } finally { htmx.config.allowEval = true htmx.off('htmx:evalDisallowedError', handler) } calledEvent.should.equal(true) }) it('ajax api works', function() { this.server.respondWith('GET', '/test', 'foo!') var div = make('
') htmx.ajax('GET', '/test', div) this.server.respond() div.innerHTML.should.equal('foo!') }) it('ajax api works by ID', function() { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', '#d1') this.server.respond() div.innerHTML.should.equal('foo!') }) it('ajax api does not fall back to body when target invalid', function() { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', '#d2') this.server.respond() document.body.innerHTML.should.not.equal('foo!') }) it('ajax api fails when target invalid', function(done) { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', '#d2').then( (value) => { }, (reason) => { done() } ) this.server.respond() div.innerHTML.should.equal('') }) it('ajax api fails when target invalid even if source set', function(done) { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', { source: div, target: '#d2' }).then( (value) => { }, (reason) => { done() } ) this.server.respond() div.innerHTML.should.equal('') }) it('ajax api fails when source invalid and no target set', function(done) { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', { source: '#d2' }).then( (value) => { }, (reason) => { done() } ) this.server.respond() div.innerHTML.should.equal('') }) it('ajax api falls back to targeting source if target not set', function() { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") htmx.ajax('GET', '/test', { source: div }) this.server.respond() div.innerHTML.should.equal('foo!') }) // it('ajax api falls back to targeting body if target and source not set', function() { // this.server.respondWith('GET', '/test', 'foo!') // var div = make("
") // const saveBody = document.body.innerHTML // htmx.ajax('GET', '/test', {}) // this.server.respond() // document.body.innerHTML.should.equal('foo!') // document.body.innerHTML = saveBody // }) it('ajax api works with swapSpec', function() { this.server.respondWith('GET', '/test', "

foo!

") var div = make("
") htmx.ajax('GET', '/test', { target: '#target', swap: 'outerHTML' }) this.server.respond() div.innerHTML.should.equal('

foo!

') }) it('ajax api works with select', function() { this.server.respondWith('GET', '/test', "
foo
bar
") var div = make("
") htmx.ajax('GET', '/test', { target: '#target', select: '#d2' }) this.server.respond() div.innerHTML.should.equal('
bar
') }) it('ajax api works with Hx-Select overrides select', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Reselect': '#d2' }, "
foo
bar
"]) var div = make("
") htmx.ajax('GET', '/test', { target: '#target', select: '#d1' }) this.server.respond() div.innerHTML.should.equal('
bar
') }) it('ajax returns a promise', function(done) { // in IE we do not return a promise if (typeof Promise !== 'undefined') { this.server.respondWith('GET', '/test', 'foo!') var div = make("
") var promise = htmx.ajax('GET', '/test', '#d1') this.server.respond() div.innerHTML.should.equal('foo!') promise.then(function() { done() }) } else { done() } }) it('ajax api can pass parameters', function() { this.server.respondWith('POST', '/test', function(xhr) { var params = getParameters(xhr) params.i1.should.equal('test') xhr.respond(200, {}, 'Clicked!') }) var div = make("
") htmx.ajax('POST', '/test', { target: '#d1', values: { i1: 'test' } }) this.server.respond() div.innerHTML.should.equal('Clicked!') }) it('ajax api Content-Type header is application/x-www-form-urlencoded', function() { this.server.respondWith('POST', '/test', function(xhr) { var params = getParameters(xhr) xhr.requestHeaders['Content-Type'].should.equal('application/x-www-form-urlencoded;charset=utf-8') params.i1.should.equal('test') xhr.respond(200, {}, 'Clicked!') }) var div = make("
") htmx.ajax('POST', '/test', { target: '#d1', values: { i1: 'test' } }) this.server.respond() div.innerHTML.should.equal('Clicked!') }) it('ajax api Content-Type header override to application/json', function() { this.server.respondWith('POST', '/test', function(xhr) { var params = getParameters(xhr) xhr.requestHeaders['Content-Type'].should.equal('application/json;charset=utf-8') params.i1.should.equal('test') xhr.respond(200, {}, 'Clicked!') }) var div = make("
") htmx.ajax('POST', '/test', { target: '#d1', swap: 'innerHTML', headers: { 'Content-Type': 'application/json' }, values: { i1: 'test' } }) this.server.respond() div.innerHTML.should.equal('Clicked!') }) it('can re-init with new attributes', function() { this.server.respondWith('PATCH', '/test', 'patch') this.server.respondWith('DELETE', '/test', 'delete') var div = make('
click me
') div.click() this.server.respond() div.innerHTML.should.equal('patch') div.removeAttribute('hx-patch') div.setAttribute('hx-delete', '/test') htmx.process(div) div.click() this.server.respond() div.innerHTML.should.equal('delete') }) it('does not trigger load on re-init of an existing element', function() { this.server.respondWith('GET', '/test', 'test') var div = make('
') this.server.respond() div.innerHTML.should.equal('test') div.setAttribute('hx-swap', 'afterbegin') htmx.process(div) this.server.respond() div.innerHTML.should.equal('test') }) it('onLoad is called... onLoad', function() { // also tests on/off this.server.respondWith('GET', '/test', "
") var helper = htmx.onLoad(function(elt) { elt.setAttribute('foo', 'bar') }) try { var div = make("
") div.click() this.server.respond() byId('d1').getAttribute('foo').should.equal('bar') htmx.off('htmx:load', helper) } catch (error) { // Clean up the event if the test fails, then throw it again htmx.off('htmx:load', helper) throw error } }) it('triggers properly', function() { var div = make('
') var myEventCalled = false var detailStr = '' htmx.on('myEvent', function(evt) { myEventCalled = true detailStr = evt.detail.str }) htmx.trigger(div, 'myEvent', { str: 'foo' }) myEventCalled.should.equal(true) detailStr.should.equal('foo') }) it('triggers properly w/ selector', function() { var div = make("
") var myEventCalled = false var detailStr = '' htmx.on('myEvent', function(evt) { myEventCalled = true detailStr = evt.detail.str }) htmx.trigger('#div1', 'myEvent', { str: 'foo' }) myEventCalled.should.equal(true) detailStr.should.equal('foo') }) it('triggers with no details properly', function() { var div = make('
') var myEventCalled = false htmx.on('myEvent', function(evt) { myEventCalled = true }) htmx.trigger(div, 'myEvent') myEventCalled.should.equal(true) }) it('swaps content properly (basic)', function() { var output = make('') htmx.swap('#output', '
Swapped!
', { swapStyle: 'innerHTML' }) output.innerHTML.should.be.equal('
Swapped!
') }) it('swaps content properly (with select)', function() { var output = make('') htmx.swap('#output', '

Swapped!

', { swapStyle: 'innerHTML' }, { select: '#select-me' }) output.innerHTML.should.be.equal('

Swapped!

') }) it('swaps content properly (with oob)', function() { var output = make('') var oobDiv = make('
') htmx.swap('#output', '
OOB Swapped!
Swapped!
', { swapStyle: 'innerHTML' }) output.innerHTML.should.be.equal('
Swapped!
') oobDiv.innerHTML.should.be.equal('OOB Swapped!') }) it('swaps content properly (with select oob)', function() { var output = make('') var oobDiv = make('
') htmx.swap('#output', '
OOB Swapped!
Swapped!
', { swapStyle: 'innerHTML' }, { selectOOB: '#oob:innerHTML' }) output.innerHTML.should.be.equal('
Swapped!
') oobDiv.innerHTML.should.be.equal('OOB Swapped!') }) it('swap delete works when parent is removed', function() { this.server.respondWith('DELETE', '/test', 'delete') var parent = make('
click me
') var div = htmx.find(parent, '#d1') div.click() div.remove() parent.remove() this.server.respond() parent.children.length.should.equal(0) }) it('swap outerHTML works when parent is removed', function() { this.server.respondWith('GET', '/test', 'delete') var parent = make('
click me
') var div = htmx.find(parent, '#d1') div.click() div.remove() parent.remove() this.server.respond() parent.children.length.should.equal(0) }) })