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('
Click Me!
'); + 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", "
After settle...
"); + 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, {}, "" + originalTitle + "UPDATE" + "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 + "fooClicked!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('
' + + '' + + 'A' + + 'B' + + 'C' + + '
'); + + 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' + + '\n' + + '\n' + + '
\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 @@ +