describe('Core htmx AJAX headers', function() { const chai = window.chai beforeEach(function() { this.server = makeServer() clearWorkArea() }) afterEach(function() { this.server.restore() clearWorkArea() }) it('should include the HX-Request header', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Request'].should.be.equal('true') xhr.respond(200, {}, '') }) var div = make('
') div.click() this.server.respond() }) it('should include the HX-Trigger header', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Trigger'].should.equal('d1') xhr.respond(200, {}, '') }) var div = make('
') div.click() this.server.respond() }) it('should include the HX-Trigger-Name header', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Trigger-Name'].should.equal('n1') xhr.respond(200, {}, '') }) var div = make('') div.click() this.server.respond() }) it('should include the HX-Target header', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Target'].should.equal('d1') xhr.respond(200, {}, '') }) var div = make('
') div.click() this.server.respond() }) it('should handle simple string HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle dot path HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo.bar' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo.bar', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle simple string HX-Trigger response header in different case properly', function() { this.server.respondWith('GET', '/test', [200, { 'hx-trigger': 'foo' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle a namespaced HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'namespace:foo' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('namespace:foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle basic JSON HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': '{"foo":null}' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo', function(evt) { invokedEvent = true should.equal(null, evt.detail.value) evt.detail.elt.should.equal(div) }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle JSON with array arg HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': '{"foo":[1, 2, 3]}' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo', function(evt) { invokedEvent = true evt.detail.elt.should.equal(div) evt.detail.value.should.deep.equal([1, 2, 3]) }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle JSON with object arg HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': '{"foo":{"a":1, "b":2}}' }, '']) var div = make('
') var invokedEvent = false div.addEventListener('foo', function(evt) { invokedEvent = true evt.detail.elt.should.equal(div) evt.detail.a.should.equal(1) evt.detail.b.should.equal(2) }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should handle JSON with target array arg HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': '{"foo":{"target":"#testdiv"}}' }, '']) var div = make('
') var testdiv = make('
') var invokedEvent = false testdiv.addEventListener('foo', function(evt) { invokedEvent = true evt.detail.elt.should.equal(testdiv) }) div.click() this.server.respond() invokedEvent.should.equal(true) }) it('should survive malformed JSON in HX-Trigger response header', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': '{not: valid}' }, '']) var div = make('
') div.click() this.server.respond() }) it('should handle simple string HX-Trigger response header properly w/ outerHTML swap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo' }, '']) var div = make('
') var invokedEvent = false var handler = htmx.on('foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) htmx.off('foo', handler) }) it('should handle simple comma separated list HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo, bar' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false div.addEventListener('foo', function(evt) { invokedEventFoo = true }) div.addEventListener('bar', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) }) it('should handle simple comma separated list without space HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo,bar' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false div.addEventListener('foo', function(evt) { invokedEventFoo = true }) div.addEventListener('bar', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) }) it('should handle dot path in comma separated list HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'foo.bar,bar.baz' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false div.addEventListener('foo.bar', function(evt) { invokedEventFoo = true }) div.addEventListener('bar.baz', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) }) it('should handle a namespaced comma separated list HX-Trigger response header properly', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger': 'namespace:foo,bar' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false div.addEventListener('namespace:foo', function(evt) { invokedEventFoo = true }) div.addEventListener('bar', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) }) it('should handle HX-Retarget', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Retarget': '#d2' }, 'Result']) var div1 = make('
') var div2 = make('
') div1.click() this.server.respond() div1.innerHTML.should.equal('') div2.innerHTML.should.equal('Result') }) it('should handle HX-Retarget override back to this', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Retarget': 'this' }, 'Result']) var div1 = make('
') var div2 = make('
') div1.click() this.server.respond() div1.innerHTML.should.equal('Result') div2.innerHTML.should.equal('') }) it('should handle HX-Reswap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Reswap': 'innerHTML' }, 'Result']) var div1 = make('
') div1.click() this.server.respond() div1.innerHTML.should.equal('Result') }) it('should handle HX-Reselect', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Reselect': '#d2' }, "
foo
bar
"]) var div = make('
') div.click() this.server.respond() div.innerHTML.should.equal('
bar
') }) it('should handle HX-Reselect unset', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Reselect': 'unset' }, 'bar']) var div = make('
') div.click() this.server.respond() div.innerHTML.should.equal('bar') }) it('should handle simple string HX-Trigger-After-Swap response header properly w/ outerHTML swap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger-After-Swap': 'foo' }, '']) var div = make('
') var invokedEvent = false var handler = htmx.on('foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) htmx.off('foo', handler) }) it('should handle simple comma separated list HX-Trigger-After-Swap response header properly w/ outerHTML swap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger-After-Swap': 'foo, bar' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false var handlerFoo = htmx.on('foo', function(evt) { invokedEventFoo = true }) var handlerBar = htmx.on('bar', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) htmx.off('foo', handlerFoo) htmx.off('bar', handlerBar) }) it('should handle simple string HX-Trigger-After-Settle response header properly w/ outerHTML swap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger-After-Settle': 'foo' }, '']) var div = make('
') var invokedEvent = false var handler = htmx.on('foo', function(evt) { invokedEvent = true }) div.click() this.server.respond() invokedEvent.should.equal(true) htmx.off('foo', handler) }) it('should handle simple comma separated list HX-Trigger-After-Settle response header properly w/ outerHTML swap', function() { this.server.respondWith('GET', '/test', [200, { 'HX-Trigger-After-Settle': 'foo, bar' }, '']) var div = make('
') var invokedEventFoo = false var invokedEventBar = false var handlerFoo = htmx.on('foo', function(evt) { invokedEventFoo = true }) var handlerBar = htmx.on('bar', function(evt) { invokedEventBar = true }) div.click() this.server.respond() invokedEventFoo.should.equal(true) invokedEventBar.should.equal(true) htmx.off('foo', handlerFoo) htmx.off('bar', handlerBar) }) it('should change body content on HX-Location', function(done) { this.server.respondWith('GET', '/test', [200, { 'HX-Location': '{"path":"/test2", "target":"#work-area"}' }, '']) this.server.respondWith('GET', '/test2', [200, {}, '
Yay! Welcome
']) var div = make('
') div.click() this.server.respond() this.server.respond() setTimeout(function() { getWorkArea().innerHTML.should.equal('
Yay! Welcome
') done() }, 30) }) it('should refresh page on HX-Refresh', function() { var refresh = false htmx.location = { reload: function() { refresh = true } } this.server.respondWith('GET', '/test', [200, { 'HX-Refresh': 'true' }, '']) var div = make('
') div.click() this.server.respond() refresh.should.equal(true) htmx.location = window.location }) it('should update location.href on HX-Redirect', function() { htmx.location = { href: window.location.href } this.server.respondWith('GET', '/test', [200, { 'HX-Redirect': 'https://htmx.org/headers/hx-redirect/' }, '']) var div = make('
') div.click() this.server.respond() htmx.location.href.should.equal('https://htmx.org/headers/hx-redirect/') htmx.location = window.location }) it('request to restore history should include the HX-Request header when historyRestoreAsHxRequest true', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Request'].should.be.equal('true') xhr.respond(200, {}, '') }) htmx._('loadHistoryFromServer')('/test') this.server.respond() }) it('request to restore history should not include the HX-Request header when historyRestoreAsHxRequest false', function() { htmx.config.historyRestoreAsHxRequest = false this.server.respondWith('GET', '/test', function(xhr) { should.equal(xhr.requestHeaders['HX-Request'], undefined) xhr.respond(200, {}, '') }) htmx._('loadHistoryFromServer')('/test') this.server.respond() htmx.config.historyRestoreAsHxRequest = true }) it('request history from server with error status code throws error event', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-Request'].should.be.equal('true') xhr.respond(404, {}, '') }) var invokedEvent = false var handler = htmx.on('htmx:historyCacheMissLoadError', function(evt) { invokedEvent = true }) htmx._('loadHistoryFromServer')('/test') this.server.respond() invokedEvent.should.equal(true) htmx.off('htmx:historyCacheMissLoadError', handler) }) it('request to restore history should include the HX-History-Restore-Request header', function() { this.server.respondWith('GET', '/test', function(xhr) { xhr.requestHeaders['HX-History-Restore-Request'].should.be.equal('true') xhr.respond(200, {}, '') }) htmx._('loadHistoryFromServer')('/test') this.server.respond() }) it('request to restore history should include the HX-Current-URL header', function() { this.server.respondWith('GET', '/test', function(xhr) { chai.assert(typeof xhr.requestHeaders['HX-Current-URL'] !== 'undefined', 'HX-Current-URL should not be undefined') xhr.respond(200, {}, '') }) htmx._('loadHistoryFromServer')('/test') this.server.respond() }) })