diff --git a/test/core/shadowdom.js b/test/core/shadowdom.js
new file mode 100644
index 00000000..7f3c3c32
--- /dev/null
+++ b/test/core/shadowdom.js
@@ -0,0 +1,1328 @@
+describe("Core HTMX shadow DOM Tests", function() {
+ //Skip these tests if browser doesn't support shadow DOM
+ if(typeof window.ShadowRoot === 'undefined') return;
+
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ var workArea = getWorkArea()
+ if(!workArea.shadowRoot) workArea.attachShadow({ mode: 'open' })
+ workArea.shadowRoot.innerHTML = ""
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ getWorkArea().shadowRoot.innerHTML = ""
+ });
+
+ // Locally redefine the `byId` and `make` functions to use shadow DOM
+ function byId(id) {
+ return getWorkArea().shadowRoot.getElementById(id) || document.getElementById(id)
+ }
+ function make(htmlStr) {
+ htmlStr = htmlStr.trim()
+ var makeFn = function () {
+ var range = document.createRange();
+ var fragment = range.createContextualFragment(htmlStr);
+ var wa = getWorkArea().shadowRoot;
+ var child = null;
+ var children = fragment.children || fragment.childNodes; // IE
+ var appendedChildren = []
+ while(children.length > 0) {
+ child = children[0];
+ wa.appendChild(child);
+ appendedChildren.push(child)
+ }
+ htmx.process(wa);
+ return child; // return last added element
+ };
+ if (getWorkArea()) {
+ return makeFn();
+ } else {
+ ready(makeFn);
+ }
+ }
+
+ // bootstrap test
+ it('issues a GET request on click and swaps content', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ });
+
+ it('processes inner content properly', function()
+ {
+ this.server.respondWith("GET", "/test", 'Click Me');
+ this.server.respondWith("GET", "/test2", "Clicked!");
+
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal('Click Me');
+ var a = div.querySelector('a');
+ a.click();
+ this.server.respond();
+ a.innerHTML.should.equal('Clicked!');
+ });
+
+ it('handles swap outerHTML properly', function()
+ {
+ this.server.respondWith("GET", "/test", 'Click Me');
+ this.server.respondWith("GET", "/test2", "Clicked!");
+
+ var div = make('')
+ div.click();
+ should.equal(byId("d1"), div);
+ this.server.respond();
+ should.equal(byId("d1"), null);
+ byId("a1").click();
+ this.server.respond();
+ byId("a1").innerHTML.should.equal('Clicked!');
+ });
+
+ it('handles beforebegin properly', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, '' + i + '');
+ });
+ this.server.respondWith("GET", "/test2", "*");
+
+ var div = make('*
')
+ var parent = div.parentElement;
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*");
+ removeWhiteSpace(parent.innerText).should.equal("1*");
+
+ byId("a1").click();
+ this.server.respond();
+ removeWhiteSpace(parent.innerText).should.equal("**");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*");
+ removeWhiteSpace(parent.innerText).should.equal("*2*");
+
+ byId("a2").click();
+ this.server.respond();
+ removeWhiteSpace(parent.innerText).should.equal("***");
+ });
+
+ it('handles afterbegin properly', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, "" + i);
+ });
+
+ var div = make('*
')
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("1*");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("21*");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("321*");
+ });
+
+ it('handles afterbegin properly with no initial content', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, "" + i);
+ });
+
+ var div = make('')
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("1");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("21");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("321");
+ });
+
+ it('handles afterend properly', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, '' + i + '');
+ });
+ this.server.respondWith("GET", "/test2", "*");
+
+ var div = make('*
')
+ var parent = div.parentElement;
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*");
+ removeWhiteSpace(parent.innerText).should.equal("*1");
+
+ byId("a1").click();
+ this.server.respond();
+ removeWhiteSpace(parent.innerText).should.equal("**");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*");
+ removeWhiteSpace(parent.innerText).should.equal("*2*");
+
+ byId("a2").click();
+ this.server.respond();
+ removeWhiteSpace(parent.innerText).should.equal("***");
+ });
+
+ it('handles beforeend properly', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, "" + i);
+ });
+
+ var div = make('*
')
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*1");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*12");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("*123");
+ });
+
+ it('handles beforeend properly with no initial content', function()
+ {
+ var i = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ i++;
+ xhr.respond(200, {}, "" + i);
+ });
+
+ var div = make('')
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("1");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("12");
+
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("123");
+ });
+
+ it('handles hx-target properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ var btn = make('');
+ var target = make('Initial');
+ btn.click();
+ target.innerHTML.should.equal("Initial");
+ this.server.respond();
+ target.innerHTML.should.equal("Clicked!");
+ });
+
+ it('handles 204 NO CONTENT responses properly', function()
+ {
+ this.server.respondWith("GET", "/test", [204, {}, "No Content!"]);
+
+ var btn = make('');
+ btn.click();
+ btn.innerHTML.should.equal("Click Me!");
+ this.server.respond();
+ btn.innerHTML.should.equal("Click Me!");
+ });
+
+ it('handles 304 NOT MODIFIED responses properly', function()
+ {
+ this.server.respondWith("GET", "/test-1", [200, {}, "Content for Tab 1"]);
+ this.server.respondWith("GET", "/test-2", [200, {}, "Content for Tab 2"]);
+
+ var target = make('')
+ var btn1 = make('');
+ var btn2 = make('');
+
+ btn1.click();
+ target.innerHTML.should.equal("");
+ this.server.respond();
+ target.innerHTML.should.equal("Content for Tab 1");
+
+ btn2.click();
+ this.server.respond();
+ target.innerHTML.should.equal("Content for Tab 2");
+
+ this.server.respondWith("GET", "/test-1", [304, {}, "Content for Tab 1"]);
+ this.server.respondWith("GET", "/test-2", [304, {}, "Content for Tab 2"]);
+
+ btn1.click();
+ this.server.respond();
+ target.innerHTML.should.equal("Content for Tab 1");
+
+ btn2.click();
+ this.server.respond();
+ target.innerHTML.should.equal("Content for Tab 2");
+ });
+
+ it('handles hx-trigger with non-default value', 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('handles hx-trigger with load event', 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('sets the content type of the request properly', function (done) {
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "done");
+ xhr.overriddenMimeType.should.equal("text/html");
+ done();
+ });
+ var div = make('Click Me!
');
+ div.click();
+ this.server.respond();
+ });
+
+ it('issues two requests when clicked twice before response', function()
+ {
+ var i = 1;
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "click " + i);
+ i++
+ });
+ var div = make('');
+ div.click();
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("click 1");
+ this.server.respond();
+ div.innerHTML.should.equal("click 2");
+ });
+
+ it('issues two requests when clicked three times before response', function()
+ {
+ var i = 1;
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "click " + i);
+ i++
+ });
+ var div = make('');
+ div.click();
+ div.click();
+ div.click();
+ this.server.respondAll();
+ div.innerHTML.should.equal("click 2");
+ });
+
+ it('properly handles hx-select for basic situation', function()
+ {
+ var i = 1;
+ this.server.respondWith("GET", "/test", "foo
bar
");
+ var div = make('');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("foo
");
+ });
+
+ it('properly handles hx-select for full html document situation', function()
+ {
+ this.server.respondWith("GET", "/test", "foo
bar
");
+ var div = make('');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("foo
");
+ });
+
+ it('properly settles attributes on interior elements', function(done)
+ {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1").getAttribute("width"), null);
+ setTimeout(function () {
+ should.equal(byId("d1").getAttribute("width"), "bar");
+ done();
+ }, 20);
+ });
+
+ it('properly settles attributes elements with single quotes in id', function(done)
+ {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1'").getAttribute("width"), null);
+ setTimeout(function () {
+ should.equal(byId("d1'").getAttribute("width"), "bar");
+ done();
+ }, 20);
+ });
+
+ it('properly settles attributes elements with double quotes in id', function(done)
+ {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1\"").getAttribute("width"), null);
+ setTimeout(function () {
+ should.equal(byId("d1\"").getAttribute("width"), "bar");
+ done();
+ }, 20);
+ });
+
+ it('properly handles multiple select input', function()
+ {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ var form = make('');
+
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({});
+
+ byId("m1").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:"m1"});
+
+ byId("m1").selected = true;
+ byId("m3").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:["m1", "m3"]});
+ });
+
+ it('properly handles multiple select input when "multiple" attribute is empty string', function()
+ {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ var form = make('');
+
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({});
+
+ byId("m1").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:"m1"});
+
+ byId("m1").selected = true;
+ byId("m3").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:["m1", "m3"]});
+ });
+
+ it('properly handles two multiple select inputs w/ same name', function()
+ {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ var form = make('');
+
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({});
+
+ byId("m1").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:"m1"});
+
+ byId("m1").selected = true;
+ byId("m3").selected = true;
+ byId("m7").selected = true;
+ byId("m8").selected = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiSelect:["m1", "m3", "m7", "m8"]});
+ });
+
+ it('properly handles multiple email input', function()
+ {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ var form = make('');
+
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiEmail: ''});
+
+ byId("multiEmail").value = 'foo@example.com';
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiEmail:"foo@example.com"});
+
+ byId("multiEmail").value = 'foo@example.com,bar@example.com';
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({multiEmail:"foo@example.com,bar@example.com"});
+ });
+
+ it('properly handles checkbox inputs', function()
+ {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ var form = make('');
+
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({});
+
+ byId("cb1").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:"cb1"});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb2"]});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = true;
+ byId("cb3").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb2", "cb3"]});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = true;
+ byId("cb3").checked = true;
+ byId("cb4").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb2", "cb3"], c2:"cb4"});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = true;
+ byId("cb3").checked = true;
+ byId("cb4").checked = true;
+ byId("cb5").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb2", "cb3"], c2:["cb4", "cb5"]});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = true;
+ byId("cb3").checked = true;
+ byId("cb4").checked = true;
+ byId("cb5").checked = true;
+ byId("cb6").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb2", "cb3"], c2:["cb4", "cb5"], c3:"cb6"});
+
+ byId("cb1").checked = true;
+ byId("cb2").checked = false;
+ byId("cb3").checked = true;
+ byId("cb4").checked = false;
+ byId("cb5").checked = true;
+ byId("cb6").checked = true;
+ form.click();
+ this.server.respond();
+ values.should.deep.equal({c1:["cb1", "cb3"], c2:"cb5", c3:"cb6"});
+
+ });
+
+ it('text nodes dont screw up settling via variable capture', function()
+ {
+ this.server.respondWith("GET", "/test", "fooo");
+ this.server.respondWith("GET", "/test2", "clicked");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ byId("d1").click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("clicked");
+ });
+
+ it('script nodes evaluate', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(true);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('stand alone script nodes evaluate', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(true);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('script nodes can define global functions', function()
+ {
+ try {
+ window.foo = {}
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ foo.bar().should.equal(42);
+ } finally {
+ delete foo;
+ }
+ });
+
+ it('child script nodes evaluate when children', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(true);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('child script nodes evaluate when first child', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(true);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('child script nodes evaluate when not explicitly marked javascript', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(true);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('script nodes do not evaluate when explicitly marked as something other than javascript', function()
+ {
+ var globalWasCalled = false;
+ window.callGlobal = function() {
+ globalWasCalled = true;
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ globalWasCalled.should.equal(false);
+ } finally {
+ delete window.callGlobal;
+ }
+ });
+
+ it('script nodes evaluate after swap', function()
+ {
+ window.callGlobal = function() {
+ console.log("Here...");
+ window.tempVal = byId("d1").innerText
+ }
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ window.tempVal.should.equal("After settle...");
+ } finally {
+ delete window.callGlobal;
+ delete window.tempVal;
+ }
+ });
+
+ it('script node exceptions do not break rendering', function()
+ {
+ this.skip("Rendering does not break, but the exception bubbles up and mocha reports it");
+ this.server.respondWith("GET", "/test", "clicked");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("clicked");
+ console.log(div.innerText);
+ console.log("here");
+ });
+
+ it('allows empty verb values', function()
+ {
+ var path = null;
+ var div = make("");
+ htmx.on(div, "htmx:configRequest", function (evt) {
+ path = evt.detail.path;
+ return false;
+ });
+ div.click();
+ this.server.respond();
+ path.should.not.be.null;
+ });
+
+ it('allows blank verb values', function()
+ {
+ var path = null;
+ var div = make("");
+ htmx.on(div, "htmx:configRequest", function (evt) {
+ path = evt.detail.path;
+ return false;
+ });
+ div.click();
+ this.server.respond();
+ path.should.not.be.null;
+ });
+
+ it('input values are not settle swapped (causes flicker)', function()
+ {
+ this.server.respondWith("GET", "/test", "");
+ var input = make("");
+ input.click();
+ this.server.respond();
+ input = byId('i1');
+ input.value.should.equal('bar');
+ });
+
+ it('autofocus attribute works properly', function()
+ {
+ this.server.respondWith("GET", "/test", "");
+ var input = make("");
+ input.focus();
+ input.click();
+ document.activeElement.should.equal(input);
+ this.server.respond();
+ var input2 = byId('i2');
+ document.activeElement.should.equal(input2);
+ });
+
+ it('autofocus attribute works properly w/ child', function()
+ {
+ this.server.respondWith("GET", "/test", "");
+ var input = make("");
+ input.focus();
+ input.click();
+ document.activeElement.should.equal(input);
+ this.server.respond();
+ var input2 = byId('i2');
+ document.activeElement.should.equal(input2);
+ });
+
+ it('autofocus attribute works properly w/ true value', function()
+ {
+ this.server.respondWith("GET", "/test", "");
+ var input = make("");
+ input.focus();
+ input.click();
+ document.activeElement.should.equal(input);
+ this.server.respond();
+ var input2 = byId('i2');
+ document.activeElement.should.equal(input2);
+ });
+
+ it('multipart/form-data encoding works', function()
+ {
+ this.server.respondWith("POST", "/test", function(xhr){
+ should.equal(xhr.requestHeaders['Content-Type'], undefined);
+ if (xhr.requestBody.get) { //IE 11 does not support
+ xhr.requestBody.get("i1").should.equal('foo');
+ }
+ xhr.respond(200, {}, "body: " + xhr.requestBody);
+ });
+ var form = make("");
+ form.focus();
+ form.click();
+ this.server.respond();
+ });
+
+ it('removed elements do not issue requests', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function (xhr) {
+ count++;
+ xhr.respond(200, {}, "");
+ });
+ var btn = make('')
+ htmx.remove(btn);
+ btn.click();
+ this.server.respond();
+ count.should.equal(0);
+ });
+
+ it('title tags update title', function()
+ {
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "htmx rocks!Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!");
+ window.document.title.should.equal("htmx rocks!");
+ });
+
+ it('svg title tags do not update title', function()
+ {
+ var originalTitle = window.document.title
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!");
+ window.document.title.should.equal(originalTitle);
+ });
+
+ it('first title tag outside svg title tags updates title', function()
+ {
+ var originalTitle = window.document.title
+ var newTitle = originalTitle + "!!!";
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "" + newTitle + "Clicked!x");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!");
+ window.document.title.should.equal(newTitle);
+ });
+
+ it('title update does not URL escape', function()
+ {
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, "</> htmx rocks!Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!");
+ window.document.title.should.equal("> htmx rocks!");
+ });
+
+ it('by default 400 content is not swapped', function()
+ {
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(400, {}, "Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Click Me!");
+ });
+
+ it('400 content can be swapped if configured to do so', function()
+ {
+ var handler = htmx.on("htmx:beforeSwap", function (event) {
+ if (event.detail.xhr.status === 400) {
+ event.detail.shouldSwap = true;
+ }
+ });
+
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(400, {}, "Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!");
+ htmx.off("htmx:beforeSwap", handler);
+ });
+
+ it('400 content can be retargeted if configured to do so', function()
+ {
+ var handler = htmx.on("htmx:beforeSwap", function (event) {
+ if (event.detail.xhr.status === 400) {
+ event.detail.shouldSwap = true;
+ event.detail.target = byId('d1')
+ }
+ });
+
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(400, {}, "Clicked!");
+ });
+ var btn = make('')
+ var div = make('')
+ btn.click();
+ this.server.respond();
+ div.innerText.should.equal("Clicked!");
+ htmx.off("htmx:beforeSwap", handler);
+ });
+
+ it('errors are triggered only on 400+', function()
+ {
+ var errors = 0;
+ var handler = htmx.on("htmx:responseError", function(){
+ errors++;
+ })
+ this.server.respondWith("GET", "/test1", function (xhr) {
+ xhr.respond(204, {}, "Clicked!");
+ });
+ this.server.respondWith("GET", "/test2", function (xhr) {
+ xhr.respond(400, {}, "Clicked!");
+ });
+ var btn1 = make('')
+ var btn2 = make('')
+ btn1.click();
+ btn2.click();
+ this.server.respond();
+ this.server.respond();
+ errors.should.equal(1);
+ htmx.off("htmx:responseError", handler);
+ });
+
+
+ it('content can be modified if configured to do so', function()
+ {
+ var handler = htmx.on("htmx:beforeSwap", function (event) {
+ if (event.detail.xhr.status === 400) {
+ event.detail.shouldSwap = true;
+ event.detail.serverResponse = event.detail.serverResponse + "!!";
+ }
+ });
+
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(400, {}, "Clicked!");
+ });
+ var btn = make('')
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Clicked!!!");
+ htmx.off("htmx:beforeSwap", handler);
+ });
+
+ it('scripts w/ src attribute are properly loaded', function(done)
+ {
+ try {
+ this.server.respondWith("GET", "/test", "");
+ var div = make("");
+ div.click();
+ this.server.respond();
+ byId("setGlobalScript").addEventListener("load", function () {
+ window.globalWasCalled.should.equal(true);
+ delete window.globalWasCalled;
+ done();
+ })
+ } finally {
+ delete window.globalWasCalled;
+ }
+ });
+
+ it('should load tags with colon in their names', function() {
+ this.server.respondWith('GET', '/test', 'Foobar');
+
+ var btn = make('');
+ btn.click();
+ this.server.respond();
+
+ btn.innerHTML.should.equal('Foobar');
+ });
+
+ it('properly handles clicked submit button with a value inside a htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit input with a value inside a htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit button with a value inside a non-htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit input with a value inside a non-htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit button with a value outside a htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('' +
+ '');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit input with a value outside a htmx form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('' +
+ '');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: 'buttonValue'});
+ })
+
+ it('properly handles clicked submit button with a value stacking with regular input', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("btnA").click();
+ this.server.respond();
+ values.should.deep.equal({action: 'A'});
+
+ byId("btnB").click();
+ this.server.respond();
+ values.should.deep.equal({action: ['A', 'B']});
+
+ byId("btnC").click();
+ this.server.respond();
+ values.should.deep.equal({action: ['A', 'C']});
+ })
+
+ it('properly handles clicked submit input with a value stacking with regular input', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('');
+
+ byId("btnA").click();
+ this.server.respond();
+ values.should.deep.equal({action: 'A'});
+
+ byId("btnB").click();
+ this.server.respond();
+ values.should.deep.equal({action: ['A', 'B']});
+
+ byId("btnC").click();
+ this.server.respond();
+ values.should.deep.equal({action: ['A', 'C']});
+ })
+
+ it('properly handles clicked submit button with a value inside a form, referencing another form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('' +
+ '');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: ['inputValue', 'buttonValue']});
+ })
+
+ it('properly handles clicked submit input with a value inside a form, referencing another form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('' +
+ '');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: ['inputValue', 'buttonValue']});
+ })
+
+ it('properly handles inputs external to form', function () {
+ var values;
+ this.server.respondWith("Post", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(204, {}, "");
+ });
+
+ make('' +
+ '' +
+ '' +
+ '');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({t1: 'textValue', b1: ['inputValue', 'buttonValue'], s1: "selectValue"});
+ })
+
+ it('handles form post with button formmethod dialog properly', function () {
+ var values;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(200, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ values.should.deep.equal({ foo: 'bar' });
+ })
+
+ it('handles form get with button formmethod dialog properly', function () {
+ var responded = false;
+ this.server.respondWith("GET", "/test", function (xhr) {
+ responded = true;
+ xhr.respond(200, {}, "");
+ });
+
+ make('');
+
+ byId("submit").click();
+ this.server.respond();
+ responded.should.equal(true);
+ })
+
+ it("can associate submit buttons from outside a form with the current version of the form after swap", function(){
+ const template = '\n' +
+ '';
+
+ var values
+ this.server.respondWith("/test", function (xhr) {
+ values = getParameters(xhr);
+ xhr.respond(200, {}, template);
+ });
+ make(template);
+ const button = byId("outside");
+ button.focus();
+ button.click();
+ this.server.respond();
+ values.should.deep.equal({name: "", outside: ""});
+ button.focus();
+ button.click();
+ this.server.respond();
+ values.should.deep.equal({name: "", outside: ""});
+ })
+})
\ No newline at end of file
diff --git a/test/index.html b/test/index.html
index 6559420a..d02893af 100644
--- a/test/index.html
+++ b/test/index.html
@@ -64,6 +64,7 @@
+