' +
+ '
' +
+ '
' +
+ '
')
+ var input = byId("d1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('GET does not include closest form by default', function () {
+ this.server.respondWith("GET", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], undefined);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("d1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Single input not included twice when in form', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Two inputs are included twice when they have the same name', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.deep.equal(["test", "test2"]);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
' +
+ '' +
+ '' +
+ '
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Two inputs are included twice when in form when they have the same name', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.deep.equal(["test", "test2"]);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Input not included twice when it explicitly refers to parent form', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Input can be referred to externally', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
');
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('Two inputs can be referred to externally', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ params['i2'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
');
+ make('
');
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('A form can be referred to externally', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ params['i2'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
');
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('By default an input includes itself w/ data-* prefix', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('If the element is not includeable, its descendant inputs are included', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ params['i2'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
');
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ })
+
+ it('The `closest` modifier can be used in the hx-include selector', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ params['i2'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
'+
+ '
');
+ var btn = byId('btn')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ })
+
+ it('The `this` modifier can be used in the hx-include selector', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ params['i2'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
'+
+ '
');
+ var btn = byId('btn')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ })
+
+});
diff --git a/www/static/test/1.9.2/test/attributes/hx-indicator.js b/www/static/test/1.9.2/test/attributes/hx-indicator.js
new file mode 100644
index 000000000..94156f74f
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-indicator.js
@@ -0,0 +1,137 @@
+describe("hx-indicator attribute", function(){
+ beforeEach(function() {
+ this.server = sinon.fakeServer.create();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('Indicator classes are properly put on element with no explicit indicator', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('Indicator classes are properly put on element with explicit indicator', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var a1 = make('
')
+ var a2 = make('
')
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+ a2.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(false);
+ a2.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('Indicator classes are properly put on element with relative indicator', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var a1 = make('
')
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('Indicator classes are properly put on element with explicit indicator w/ data-* prefix', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var a1 = make('
')
+ var a2 = make('
')
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+ a2.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(false);
+ a2.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('allows closest syntax in hx-indicator', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make('
')
+ var btn = byId("b1");
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(false);
+ div.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ div.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('is removed when initiating element is removed from the DOM', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var indicator = make('
Indicator
')
+ var div = make('
')
+ var btn = byId("b1");
+ btn.click();
+ indicator.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ indicator.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('allows this syntax in hx-indicator', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make('
')
+ var btn = byId("b1");
+ btn.click();
+ btn.classList.contains("htmx-request").should.equal(false);
+ div.classList.contains("htmx-request").should.equal(true);
+ this.server.respond();
+ btn.classList.contains("htmx-request").should.equal(false);
+ div.classList.contains("htmx-request").should.equal(false);
+ });
+
+ it('multiple requests with same indicator are handled properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var b1 = make('
')
+ var b2 = make('
')
+ var a1 = make('
')
+
+ b1.click();
+ b1.classList.contains("htmx-request").should.equal(false);
+ b2.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+
+ b2.click();
+ b1.classList.contains("htmx-request").should.equal(false);
+ b2.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+
+ // hack to make sinon process only one response
+ this.server.processRequest(this.server.queue.shift());
+
+ b1.classList.contains("htmx-request").should.equal(false);
+ b2.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(true);
+
+ this.server.respond();
+
+ b1.classList.contains("htmx-request").should.equal(false);
+ b2.classList.contains("htmx-request").should.equal(false);
+ a1.classList.contains("htmx-request").should.equal(false);
+
+ });
+
+
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-on.js b/www/static/test/1.9.2/test/attributes/hx-on.js
new file mode 100644
index 000000000..9a2005b11
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-on.js
@@ -0,0 +1,122 @@
+describe("hx-on attribute", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it("can handle basic events w/ no other attributes", function () {
+ var btn = make("
");
+ btn.click();
+ window.foo.should.equal(true);
+ delete window.foo;
+ });
+
+ it("can modify a parameter via htmx:configRequest", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ var params = parseParams(xhr.requestBody);
+ xhr.respond(200, {}, params.foo);
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("bar");
+ });
+
+ it("can cancel an event via preventDefault for htmx:configRequest", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("Foo");
+ });
+
+ it("can respond to kebab-case events", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ var params = parseParams(xhr.requestBody);
+ xhr.respond(200, {}, params.foo);
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("bar");
+ });
+
+ it("has the this symbol set to the element", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "foo");
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("foo");
+ btn.should.equal(window.elt);
+ delete window.elt;
+ });
+
+ it("can handle multi-line JSON", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "foo");
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("foo");
+ var obj = {foo: true, bar: false};
+ obj.should.deep.equal(window.elt);
+ delete window.elt;
+ });
+
+ it("can handle multiple event handlers in the presence of multi-line JSON", function () {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "foo");
+ });
+ var btn = make("
");
+ btn.click();
+ this.server.respond();
+ btn.innerText.should.equal("foo");
+
+ var obj = {foo: true, bar: false};
+ obj.should.deep.equal(window.elt);
+ delete window.elt;
+
+ window.foo.should.equal(true);
+ delete window.foo;
+ });
+
+ it("de-initializes hx-on content properly", function () {
+ window.tempCount = 0;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
Foo
");
+
+ // get response
+ div.click();
+ this.server.respond();
+
+ // click button
+ byId('foo').click();
+ window.tempCount.should.equal(1);
+
+ // get second response
+ div.click();
+ this.server.respond();
+
+ // click button again
+ byId('foo').click();
+ window.tempCount.should.equal(2);
+
+ delete window.tempCount;
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/attributes/hx-params.js b/www/static/test/1.9.2/test/attributes/hx-params.js
new file mode 100644
index 000000000..b2b19ca25
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-params.js
@@ -0,0 +1,101 @@
+describe("hx-params attribute", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('none excludes all params', function () {
+ this.server.respondWith("POST", "/params", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], undefined);
+ should.equal(params['i2'], undefined);
+ should.equal(params['i3'], undefined);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("Clicked!");
+ });
+
+ it('"*" includes all params', function () {
+ this.server.respondWith("POST", "/params", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], "test");
+ should.equal(params['i2'], "test");
+ should.equal(params['i3'], "test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("Clicked!");
+ });
+
+ it('named includes works', function () {
+ this.server.respondWith("POST", "/params", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], "test");
+ should.equal(params['i2'], undefined);
+ should.equal(params['i3'], "test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("Clicked!");
+ });
+
+ it('named exclude works', function () {
+ this.server.respondWith("POST", "/params", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], undefined);
+ should.equal(params['i2'], "test");
+ should.equal(params['i3'], undefined);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("Clicked!");
+ });
+
+ it('named exclude works w/ data-* prefix', function () {
+ this.server.respondWith("POST", "/params", function (xhr) {
+ var params = getParameters(xhr);
+ should.equal(params['i1'], undefined);
+ should.equal(params['i2'], "test");
+ should.equal(params['i3'], undefined);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("Clicked!");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/attributes/hx-patch.js b/www/static/test/1.9.2/test/attributes/hx-patch.js
new file mode 100644
index 000000000..a3c519847
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-patch.js
@@ -0,0 +1,34 @@
+describe("hx-patch attribute", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('issues a PATCH request', function()
+ {
+ this.server.respondWith("PATCH", "/test", function(xhr){
+ xhr.respond(200, {}, "Patched!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Patched!");
+ });
+
+ it('issues a PATCH request w/ data-* prefix', function()
+ {
+ this.server.respondWith("PATCH", "/test", function(xhr){
+ xhr.respond(200, {}, "Patched!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Patched!");
+ });
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-post.js b/www/static/test/1.9.2/test/attributes/hx-post.js
new file mode 100644
index 000000000..6734e30d2
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-post.js
@@ -0,0 +1,36 @@
+describe("hx-post attribute", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('issues a POST request with proper headers', function()
+ {
+ this.server.respondWith("POST", "/test", function(xhr){
+ should.equal(xhr.requestHeaders['X-HTTP-Method-Override'], undefined);
+ xhr.respond(200, {}, "Posted!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Posted!");
+ });
+
+ it('issues a POST request with proper headers w/ data-* prefix', function()
+ {
+ this.server.respondWith("POST", "/test", function(xhr){
+ should.equal(xhr.requestHeaders['X-HTTP-Method-Override'], undefined);
+ xhr.respond(200, {}, "Posted!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Posted!");
+ });
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-preserve.js b/www/static/test/1.9.2/test/attributes/hx-preserve.js
new file mode 100644
index 000000000..1e1c2368a
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-preserve.js
@@ -0,0 +1,39 @@
+describe("hx-preserve attribute", function () {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('handles basic response properly', function () {
+ this.server.respondWith("GET", "/test", "
New Content
New Content
");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("Old Content");
+ byId("d2").innerHTML.should.equal("New Content");
+ })
+
+ it('handles preserved element that might not be existing', function () {
+ this.server.respondWith("GET", "/test", "
New Content
New Content
");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("New Content");
+ byId("d2").innerHTML.should.equal("New Content");
+ })
+
+ it('preserved element should not be swapped if it lies outside of hx-select', function () {
+ this.server.respondWith("GET", "/test", "
New Content
New Content
");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("Old Content");
+ byId("d2").innerHTML.should.equal("New Content");
+ })
+
+});
+
diff --git a/www/static/test/1.9.2/test/attributes/hx-push-url.js b/www/static/test/1.9.2/test/attributes/hx-push-url.js
new file mode 100644
index 000000000..50b88d806
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-push-url.js
@@ -0,0 +1,223 @@
+describe("hx-push-url attribute", function() {
+
+ var HTMX_HISTORY_CACHE_NAME = "htmx-history-cache";
+
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ localStorage.removeItem(HTMX_HISTORY_CACHE_NAME);
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ localStorage.removeItem(HTMX_HISTORY_CACHE_NAME);
+ });
+
+ it("navigation should push an element into the cache when true", function () {
+ this.server.respondWith("GET", "/test", "second");
+ getWorkArea().innerHTML.should.be.equal("");
+ var div = make('
first
');
+ div.click();
+ this.server.respond();
+ div.click();
+ this.server.respond();
+ getWorkArea().textContent.should.equal("second")
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ console.log(cache);
+ cache[cache.length - 1].url.should.equal("/test");
+ });
+
+ it("navigation should push an element into the cache when string", function () {
+ this.server.respondWith("GET", "/test", "second");
+ getWorkArea().innerHTML.should.be.equal("");
+ var div = make('
first
');
+ div.click();
+ this.server.respond();
+ div.click();
+ this.server.respond();
+ getWorkArea().textContent.should.equal("second")
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(2);
+ cache[1].url.should.equal("/abc123");
+ });
+
+ it("restore should return old value", function () {
+ this.server.respondWith("GET", "/test1", '
test1
');
+ this.server.respondWith("GET", "/test2", '
test2
');
+
+ make('
init
');
+
+ byId("d1").click();
+ this.server.respond();
+ var workArea = getWorkArea();
+ workArea.textContent.should.equal("test1")
+
+ byId("d2").click();
+ this.server.respond();
+ workArea.textContent.should.equal("test2")
+
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+
+ cache.length.should.equal(2);
+ htmx._('restoreHistory')("/test1")
+ getWorkArea().textContent.should.equal("test1")
+ });
+
+ it("history restore should not have htmx support classes in content", function () {
+ this.server.respondWith("GET", "/test1", '
test1
');
+ this.server.respondWith("GET", "/test2", '
test2
');
+
+ make('
init
');
+
+ byId("d1").click();
+ this.server.respond();
+ var workArea = getWorkArea();
+ workArea.textContent.should.equal("test1")
+
+ byId("d2").click();
+ this.server.respond();
+ workArea.textContent.should.equal("test2")
+
+ htmx._('restoreHistory')("/test1")
+ getWorkArea().getElementsByClassName("htmx-request").length.should.equal(0);
+ });
+
+ it("cache should only store 10 entries", function () {
+ var x = 0;
+ this.server.respondWith("GET", /test.*/, function(xhr){
+ x++;
+ xhr.respond(200, {}, '
')
+ });
+ getWorkArea().innerHTML.should.be.equal("");
+ make('
');
+ for (var i = 0; i < 20; i++) { // issue 20 requests
+ byId("d1").click();
+ this.server.respond();
+ }
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(10); // should only be 10 elements
+ });
+
+ it("cache miss should issue another GET", function () {
+ this.server.respondWith("GET", "/test1", '
test1
');
+ this.server.respondWith("GET", "/test2", '
test2
');
+
+ make('
init
');
+
+ byId("d1").click();
+ this.server.respond();
+ var workArea = getWorkArea();
+ workArea.textContent.should.equal("test1")
+
+ byId("d2").click();
+ this.server.respond();
+ workArea.textContent.should.equal("test2")
+
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+
+ cache.length.should.equal(2);
+ localStorage.removeItem(HTMX_HISTORY_CACHE_NAME); // clear cache
+ htmx._('restoreHistory')("/test1")
+ this.server.respond();
+ getWorkArea().textContent.should.equal("test1")
+ });
+
+ it("navigation should push an element into the cache w/ data-* prefix", function () {
+ this.server.respondWith("GET", "/test", "second");
+ getWorkArea().innerHTML.should.be.equal("");
+ var div = make('
first
');
+ div.click();
+ this.server.respond();
+ getWorkArea().textContent.should.equal("second")
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(1);
+ });
+
+ it("deals with malformed JSON in history cache when getting", function () {
+ localStorage.setItem(HTMX_HISTORY_CACHE_NAME, "Invalid JSON");
+ var history = htmx._('getCachedHistory')('url');
+ should.equal(history, null);
+ });
+
+ it("deals with malformed JSON in history cache when saving", function () {
+ localStorage.setItem(HTMX_HISTORY_CACHE_NAME, "Invalid JSON");
+ htmx._('saveToHistoryCache')('url', 'content', 'title', 'scroll');
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(1);
+ });
+
+ it("does not blow out cache when saving a URL twice", function () {
+ htmx._('saveToHistoryCache')('url1', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url2', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url3', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url2', 'content', 'title', 'scroll');
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(3);
+ });
+
+ it("history cache is LRU", function () {
+ htmx._('saveToHistoryCache')('url1', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url2', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url3', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url2', 'content', 'title', 'scroll');
+ htmx._('saveToHistoryCache')('url1', 'content', 'title', 'scroll');
+ var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME));
+ cache.length.should.equal(3);
+ cache[0].url.should.equal("url3");
+ cache[1].url.should.equal("url2");
+ cache[2].url.should.equal("url1");
+ });
+
+ it("htmx:afterSettle is called when replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterSettle", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterSettle", handler);
+ }
+ });
+
+ it("should include parameters on a get", function () {
+ var path = "";
+ var handler = htmx.on("htmx:pushedIntoHistory", function (evt) {
+ path = evt.detail.path;
+ });
+ try {
+ this.server.respondWith("GET", /test.*/, function (xhr) {
+ xhr.respond(200, {}, "second")
+ });
+ var form = make('
');
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("second")
+ path.should.equal("/test?foo=bar")
+ } finally {
+ htmx.off("htmx:pushedIntoHistory", handler);
+ }
+ });
+
+ it("saveToHistoryCache should not throw", function () {
+ var bigContent = "Dummy";
+ for (var i = 0; i < 20; i++) {
+ bigContent += bigContent;
+ }
+ try {
+ localStorage.removeItem("htmx-history-cache");
+ htmx._("saveToHistoryCache")("/dummy", bigContent, "Foo", 0);
+ should.equal(localStorage.getItem("htmx-history-cache"), null);
+ } finally {
+ // clear history cache afterwards
+ localStorage.removeItem("htmx-history-cache");
+ }
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/attributes/hx-put.js b/www/static/test/1.9.2/test/attributes/hx-put.js
new file mode 100644
index 000000000..589ec199d
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-put.js
@@ -0,0 +1,34 @@
+describe("hx-put attribute", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('issues a PUT request', function()
+ {
+ this.server.respondWith("PUT", "/test", function(xhr){
+ xhr.respond(200, {}, "Putted!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Putted!");
+ });
+
+ it('issues a PUT request w/ data-* prefix', function()
+ {
+ this.server.respondWith("PUT", "/test", function(xhr){
+ xhr.respond(200, {}, "Putted!");
+ });
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Putted!");
+ });
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-request.js b/www/static/test/1.9.2/test/attributes/hx-request.js
new file mode 100644
index 000000000..e64f6481f
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-request.js
@@ -0,0 +1,39 @@
+describe("hx-request attribute", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('basic hx-request timeout works', function (done) {
+ var timedOut = false;
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make("
")
+ htmx.on(div, 'htmx:timeout', function(){
+ timedOut = true;
+ })
+ div.click();
+ setTimeout(function(){
+ div.innerHTML.should.equal("");
+ // unfortunately it looks like sinon.js doesn't implement the timeout functionality
+ // timedOut.should.equal(true);
+ done();
+ }, 400)
+ });
+
+ it('hx-request header works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ should.equal(xhr.requestHeaders['HX-Request'], undefined);
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/attributes/hx-select-oob.js b/www/static/test/1.9.2/test/attributes/hx-select-oob.js
new file mode 100644
index 000000000..570811e85
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-select-oob.js
@@ -0,0 +1,54 @@
+describe("hx-select-oob attribute", function () {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('basic hx-select-oob works', function()
+ {
+ this.server.respondWith("GET", "/test", "
foo
bar
");
+ var div = make('
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("
foo
");
+ var div2 = byId('d2');
+ div2.innerHTML.should.equal("bar");
+ });
+
+ it('multiple hx-select-oobs works', function()
+ {
+ this.server.respondWith("GET", "/test", "
foo
bar
bar
");
+ var div = make('
');
+ make('
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("
foo
");
+
+ var div2 = byId('d2');
+ div2.innerHTML.should.equal("bar");
+
+ var div3 = byId('d2');
+ div3.innerHTML.should.equal("bar");
+ });
+
+ it('basic hx-select-oob ignores bad selector', function()
+ {
+ this.server.respondWith("GET", "/test", "
foo
bar
");
+ var div = make('
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("
foo
");
+ var div2 = byId('d2');
+ div2.innerHTML.should.equal("");
+ });
+
+
+});
+
diff --git a/www/static/test/1.9.2/test/attributes/hx-select.js b/www/static/test/1.9.2/test/attributes/hx-select.js
new file mode 100644
index 000000000..c58684308
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-select.js
@@ -0,0 +1,40 @@
+describe("BOOTSTRAP - htmx AJAX Tests", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('properly handles a partial of HTML', 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 a full HTML document', 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 a full HTML document w/ data-* prefix', function()
+ {
+ var i = 1;
+ this.server.respondWith("GET", "/test", "
foo
bar
");
+ var div = make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("
foo
");
+ });
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-sse.js b/www/static/test/1.9.2/test/attributes/hx-sse.js
new file mode 100644
index 000000000..138734e55
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-sse.js
@@ -0,0 +1,140 @@
+describe("hx-sse attribute", function() {
+
+ function mockEventSource() {
+ var listeners = {};
+ var wasClosed = false;
+ var mockEventSource = {
+ removeEventListener: function(name) {
+ delete listeners[name];
+ },
+ addEventListener: function (message, l) {
+ listeners[message] = l;
+ },
+ sendEvent: function (eventName, data) {
+ var listener = listeners[eventName];
+ if (listener) {
+ var event = htmx._("makeEvent")(eventName);
+ event.data = data;
+ listener(event);
+ }
+ },
+ close: function () {
+ wasClosed = true;
+ },
+ wasClosed: function () {
+ return wasClosed;
+ }
+ };
+ return mockEventSource;
+ }
+
+ beforeEach(function () {
+ this.server = makeServer();
+ var eventSource = mockEventSource();
+ this.eventSource = eventSource;
+ clearWorkArea();
+ htmx.createEventSource = function(){ return eventSource };
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('handles basic sse triggering', function () {
+
+ this.server.respondWith("GET", "/d1", "div1 updated");
+ this.server.respondWith("GET", "/d2", "div2 updated");
+
+ var div = make('
' +
+ '
div1
' +
+ '
div2
' +
+ '
');
+
+ this.eventSource.sendEvent("e1");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1 updated");
+ byId("d2").innerHTML.should.equal("div2");
+
+ this.eventSource.sendEvent("e2");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1 updated");
+ byId("d2").innerHTML.should.equal("div2 updated");
+ })
+
+ it('does not trigger events that arent named', function () {
+
+ this.server.respondWith("GET", "/d1", "div1 updated");
+
+ var div = make('
');
+
+ this.eventSource.sendEvent("foo");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1");
+
+ this.eventSource.sendEvent("e2");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1");
+
+ this.eventSource.sendEvent("e1");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1 updated");
+ })
+
+ it('does not trigger events not on decendents', function () {
+
+ this.server.respondWith("GET", "/d1", "div1 updated");
+
+ var div = make('
' +
+ '
div1
');
+
+ this.eventSource.sendEvent("foo");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1");
+
+ this.eventSource.sendEvent("e2");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1");
+
+ this.eventSource.sendEvent("e1");
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("div1");
+ })
+
+ it('is closed after removal', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make('
');
+ div.click();
+ this.server.respond();
+ this.eventSource.wasClosed().should.equal(true)
+ })
+
+ it('is closed after removal with no close and activity', function () {
+ var div = make('
');
+ div.parentElement.removeChild(div);
+ this.eventSource.sendEvent("e1")
+ this.eventSource.wasClosed().should.equal(true)
+ })
+
+ it('swaps content properly on SSE swap', function () {
+ var div = make('
\n' +
+ '
\n' +
+ '
\n' +
+ '
\n');
+ byId("d1").innerText.should.equal("")
+ byId("d2").innerText.should.equal("")
+ this.eventSource.sendEvent("e1", "Event 1")
+ byId("d1").innerText.should.equal("Event 1")
+ byId("d2").innerText.should.equal("")
+ this.eventSource.sendEvent("e2", "Event 2")
+ byId("d1").innerText.should.equal("Event 1")
+ byId("d2").innerText.should.equal("Event 2")
+ })
+
+});
+
diff --git a/www/static/test/1.9.2/test/attributes/hx-swap-oob.js b/www/static/test/1.9.2/test/attributes/hx-swap-oob.js
new file mode 100644
index 000000000..e69c43e68
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-swap-oob.js
@@ -0,0 +1,132 @@
+describe("hx-swap-oob attribute", function () {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('handles basic response properly', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped0
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked");
+ byId("d1").innerHTML.should.equal("Swapped0");
+ })
+
+ it('handles more than one oob swap properly', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped1
Swapped2
");
+ var div = make('
click me
');
+ make('
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked");
+ byId("d1").innerHTML.should.equal("Swapped1");
+ byId("d2").innerHTML.should.equal("Swapped2");
+ })
+
+ it('handles no id match properly', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped2
");
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("Clicked");
+ })
+
+ it('handles basic response properly w/ data-* prefix', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped3
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked");
+ byId("d1").innerHTML.should.equal("Swapped3");
+ })
+
+ it('handles outerHTML response properly', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped4
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ byId("d1").getAttribute("foo").should.equal("bar");
+ div.innerHTML.should.equal("Clicked");
+ byId("d1").innerHTML.should.equal("Swapped4");
+ })
+
+ it('handles innerHTML response properly', function () {
+ this.server.respondWith("GET", "/test", "Clicked
Swapped5
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1").getAttribute("foo"), null);
+ div.innerHTML.should.equal("Clicked");
+ byId("d1").innerHTML.should.equal("Swapped5");
+ })
+
+ it('oob swaps can be nested in content', function () {
+ this.server.respondWith("GET", "/test", "
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1").getAttribute("foo"), null);
+ div.innerHTML.should.equal("
Clicked
");
+ byId("d1").innerHTML.should.equal("Swapped6");
+ })
+
+ it('oob swaps can use selectors to match up', function () {
+ this.server.respondWith("GET", "/test", "
");
+ var div = make('
click me
');
+ make('
');
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1").getAttribute("oob-foo"), "bar");
+ div.innerHTML.should.equal("
Clicked
");
+ byId("d1").innerHTML.should.equal("Swapped7");
+ })
+
+ it('swaps into all targets that match the selector (innerHTML)', function () {
+ this.server.respondWith("GET", "/test", "
Clicked
Swapped8
");
+ var div = make('
click me
');
+ make('
No swap
');
+ make('
Not swapped
');
+ make('
Not swapped
');
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("No swap");
+ byId("d2").innerHTML.should.equal("Swapped8");
+ byId("d3").innerHTML.should.equal("Swapped8");
+ })
+
+ it('swaps into all targets that match the selector (outerHTML)', function () {
+ var oobSwapContent = '
Swapped9
';
+ this.server.respondWith("GET", "/test", "
Clicked
" + oobSwapContent);
+ var div = make('
click me
');
+ make('
');
+ make('
');
+ make('
');
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("
No swap
");
+ byId("d2").innerHTML.should.equal(oobSwapContent);
+ byId("d3").innerHTML.should.equal(oobSwapContent);
+ })
+
+ it('oob swap delete works properly', function()
+ {
+ this.server.respondWith("GET", "/test", '
');
+
+ var div = make('
Foo
')
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1"), null);
+ });
+});
+
diff --git a/www/static/test/1.9.2/test/attributes/hx-swap.js b/www/static/test/1.9.2/test/attributes/hx-swap.js
new file mode 100644
index 000000000..744f73cf4
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-swap.js
@@ -0,0 +1,301 @@
+describe("hx-swap attribute", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('swap innerHTML 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('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('swap 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('swap 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('swap 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('swap 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('properly parses various swap specifications', function(){
+ var swapSpec = htmx._("getSwapSpecification"); // internal function for swap spec
+ swapSpec(make("
")).swapStyle.should.equal("innerHTML")
+ swapSpec(make("
")).swapStyle.should.equal("innerHTML")
+ swapSpec(make("
")).swapDelay.should.equal(0)
+ swapSpec(make("
")).settleDelay.should.equal(0) // set to 0 in tests
+ swapSpec(make("
")).swapDelay.should.equal(10)
+ swapSpec(make("
")).settleDelay.should.equal(10)
+ swapSpec(make("
")).swapDelay.should.equal(10)
+ swapSpec(make("
")).settleDelay.should.equal(11)
+ swapSpec(make("
")).swapDelay.should.equal(10)
+ swapSpec(make("
")).settleDelay.should.equal(11)
+ swapSpec(make("
")).settleDelay.should.equal(11)
+ swapSpec(make("
")).settleDelay.should.equal(11)
+ })
+
+ it('works with a swap delay', function(done) {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ div.innerText.should.equal("");
+ setTimeout(function () {
+ div.innerText.should.equal("Clicked!");
+ done();
+ }, 30);
+ });
+
+ it('works with a settle delay', function(done) {
+ this.server.respondWith("GET", "/test", "
");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ div.classList.contains('foo').should.equal(false);
+ setTimeout(function () {
+ byId('d1').classList.contains('foo').should.equal(true);
+ done();
+ }, 30);
+ });
+
+ it('swap outerHTML properly w/ data-* prefix', 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('swap none works properly', function()
+ {
+ this.server.respondWith("GET", "/test", 'Ooops, swapped');
+
+ var div = make('
Foo
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal('Foo');
+ });
+
+
+ it('swap outerHTML does not trigger htmx:afterSwap on original element', function()
+ {
+ this.server.respondWith("GET", "/test", 'Clicked!');
+ var div = make('
')
+ div.addEventListener("htmx:afterSwap", function(){
+ count++;
+ })
+ div.click();
+ var count = 0;
+ should.equal(byId("d1"), div);
+ this.server.respond();
+ should.equal(byId("d1"), null);
+ count.should.equal(0);
+ });
+ it('swap delete works properly', function()
+ {
+ this.server.respondWith("GET", "/test", 'Oops, deleted!');
+
+ var div = make('
Foo
')
+ div.click();
+ this.server.respond();
+ should.equal(byId("d1"), null);
+ });
+
+ it('in presence of bad swap spec, it uses the default swap strategy', function()
+ {
+ var initialSwapStyle = htmx.config.defaultSwapStyle;
+ htmx.config.defaultSwapStyle = "outerHTML";
+ try {
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ var div = make('
')
+ var b1 = byId("b1");
+ b1.click();
+ this.server.respond();
+ div.innerHTML.should.equal('Clicked!');
+ } finally {
+ htmx.config.defaultSwapStyle = initialSwapStyle;
+ }
+ });
+
+
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-sync.js b/www/static/test/1.9.2/test/attributes/hx-sync.js
new file mode 100644
index 000000000..6f4d0d10d
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-sync.js
@@ -0,0 +1,224 @@
+describe("hx-sync attribute", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('can use drop strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b1.click();
+ b2.click();
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ });
+
+ it('defaults to the drop strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b1.click();
+ b2.click();
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ });
+
+ it('can use replace strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b1.click();
+ b2.click();
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Initial');
+ b2.innerHTML.should.equal('Click 0');
+ });
+
+ it('can use queue all strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ ' ' +
+ '
')
+ var b1 = byId("b1");
+ b1.click();
+
+ var b2 = byId("b2");
+ b2.click();
+
+ var b3 = byId("b3");
+ b3.click();
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ b3.innerHTML.should.equal('Initial');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Click 1');
+ b3.innerHTML.should.equal('Initial');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Click 1');
+ b3.innerHTML.should.equal('Click 2');
+ });
+
+ it('can use queue last strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ ' ' +
+ '
')
+ var b1 = byId("b1");
+ b1.click();
+
+ var b2 = byId("b2");
+ b2.click();
+
+ var b3 = byId("b3");
+ b3.click();
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ b3.innerHTML.should.equal('Initial');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ b3.innerHTML.should.equal('Click 1');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ b3.innerHTML.should.equal('Click 1');
+ });
+
+ it('can use queue first strategy', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ ' ' +
+ '
')
+ var b1 = byId("b1");
+ b1.click();
+
+ var b2 = byId("b2");
+ b2.click();
+
+ var b3 = byId("b3");
+ b3.click();
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Initial');
+ b3.innerHTML.should.equal('Initial');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Click 1');
+ b3.innerHTML.should.equal('Initial');
+
+ this.server.respond();
+ b1.innerHTML.should.equal('Click 0');
+ b2.innerHTML.should.equal('Click 1');
+ b3.innerHTML.should.equal('Initial');
+ });
+
+ it('can use abort strategy to end existing abortable request', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b1.click();
+ b2.click();
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Initial');
+ b2.innerHTML.should.equal('Click 0');
+ });
+
+ it('can use abort strategy to drop abortable request when one is in flight', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b2.click();
+ b1.click();
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Initial');
+ b2.innerHTML.should.equal('Click 0');
+ });
+
+ it('can abort a request programmatically', function()
+ {
+ var count = 0;
+ this.server.respondWith("GET", "/test", function(xhr){
+ xhr.respond(200, {}, "Click " + count++);
+ });
+ make('
' +
+ '
')
+ var b1 = byId("b1");
+ var b2 = byId("b2");
+ b1.click();
+ b2.click();
+
+ htmx.trigger(b1, "htmx:abort");
+
+ this.server.respond();
+ this.server.respond();
+ b1.innerHTML.should.equal('Initial');
+ b2.innerHTML.should.equal('Click 0');
+ });
+
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-target.js b/www/static/test/1.9.2/test/attributes/hx-target.js
new file mode 100644
index 000000000..2d4cd0b63
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-target.js
@@ -0,0 +1,205 @@
+describe("hx-target attribute", function(){
+ beforeEach(function() {
+ this.server = sinon.fakeServer.create();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('targets an adjacent element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var div1 = make('
')
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a parent element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ var btn = byId("b1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `this` element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ var btn = byId("b1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `closest` element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ var btn = byId("b1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `closest` element properly w/ hyperscript syntax', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ var btn = byId("b1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `find` element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ div1.click();
+ this.server.respond();
+ var span1 = byId("s1")
+ var span2 = byId("s2")
+ span1.innerHTML.should.equal("Clicked!");
+ span2.innerHTML.should.equal("");
+ });
+
+ it('targets a `find` element properly w/ hyperscript syntax', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div1 = make('
')
+ div1.click();
+ this.server.respond();
+ var span1 = byId("s1")
+ var span2 = byId("s2")
+ span1.innerHTML.should.equal("Clicked!");
+ span2.innerHTML.should.equal("");
+ });
+
+ it('targets an inner element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var div1 = byId("d1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets an inner element properly w/ hyperscript syntax', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var div1 = byId("d1")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('handles bad target gracefully', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Click Me!");
+ });
+
+
+ it('targets an adjacent element properly w/ data-* prefix', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ var div1 = make('
')
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `next` element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ make('
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
')
+ var btn = byId("b1")
+ var div1 = byId("d1")
+ var div2 = byId("d2")
+ var div3 = byId("d3")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ div2.innerHTML.should.equal("");
+ div3.innerHTML.should.equal("");
+ });
+
+ it('targets a `next` element properly w/ hyperscript syntax', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ make('
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
')
+ var btn = byId("b1")
+ var div1 = byId("d1")
+ var div2 = byId("d2")
+ var div3 = byId("d3")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("Clicked!");
+ div2.innerHTML.should.equal("");
+ div3.innerHTML.should.equal("");
+ });
+
+ it('targets a `previous` element properly', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ make('
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
')
+ var btn = byId("b1")
+ var div1 = byId("d1")
+ var div2 = byId("d2")
+ var div3 = byId("d3")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("");
+ div2.innerHTML.should.equal("");
+ div3.innerHTML.should.equal("Clicked!");
+ });
+
+ it('targets a `previous` element properly w/ hyperscript syntax', function()
+ {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ make('
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
')
+ var btn = byId("b1")
+ var div1 = byId("d1")
+ var div2 = byId("d2")
+ var div3 = byId("d3")
+ btn.click();
+ this.server.respond();
+ div1.innerHTML.should.equal("");
+ div2.innerHTML.should.equal("");
+ div3.innerHTML.should.equal("Clicked!");
+ });
+
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-trigger.js b/www/static/test/1.9.2/test/attributes/hx-trigger.js
new file mode 100644
index 000000000..72086a178
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-trigger.js
@@ -0,0 +1,787 @@
+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('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}],
+ "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}],
+ "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");
+ 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('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('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('
' +
+ '
Requests: 0
' +
+ '
');
+ 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('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 propogation', 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('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('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('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("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);
+ }
+ });
+
+})
diff --git a/www/static/test/1.9.2/test/attributes/hx-vals.js b/www/static/test/1.9.2/test/attributes/hx-vals.js
new file mode 100644
index 000000000..e2728e2c4
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-vals.js
@@ -0,0 +1,255 @@
+describe("hx-vals attribute", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('basic hx-vals works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vals works with braces', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('multiple hx-vals works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['v1'].should.equal("test");
+ params['v2'].should.equal("42");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals can be on parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make("
");
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals can override parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make("
");
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals overrides inputs', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals overrides hx-vars', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vals javascript: works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals works with braces', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('multiple hx-vals works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['v1'].should.equal("test");
+ params['v2'].should.equal("42");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals can be on parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
')
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals can override parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
')
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals overrides inputs', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vals treats objects as JSON', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("{\"i2\":\"test\"}");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make("
")
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vals can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vals with braces can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('multiple hx-vals can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('unsetting hx-vals maintains input values', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make(
+ "
\
+ \
+
"
+ )
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/attributes/hx-vars.js b/www/static/test/1.9.2/test/attributes/hx-vars.js
new file mode 100644
index 000000000..be20caf43
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-vars.js
@@ -0,0 +1,155 @@
+describe("hx-vars attribute", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('basic hx-vars works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vars works with braces', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('multiple hx-vars works', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['v1'].should.equal("test");
+ params['v2'].should.equal("42");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vars can be on parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
')
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vars can override parents', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make('
')
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('hx-vars overrides inputs', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("best");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make('
')
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vars can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('basic hx-vars with braces can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('multiple hx-vars can be unset', function () {
+ this.server.respondWith("POST", "/vars", function (xhr) {
+ var params = getParameters(xhr);
+ params.should.be.empty;
+ xhr.respond(200, {}, "Clicked!")
+ });
+ make(
+ "
"
+ );
+ var div = byId("d1");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+ it('unsetting hx-vars maintains input values', function () {
+ this.server.respondWith("POST", "/include", function (xhr) {
+ var params = getParameters(xhr);
+ params['i1'].should.equal("test");
+ xhr.respond(200, {}, "Clicked!")
+ });
+ var div = make(
+ "
\
+ \
+
"
+ )
+ var input = byId("i1")
+ input.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Clicked!");
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/attributes/hx-ws.js b/www/static/test/1.9.2/test/attributes/hx-ws.js
new file mode 100644
index 000000000..4cd9651af
--- /dev/null
+++ b/www/static/test/1.9.2/test/attributes/hx-ws.js
@@ -0,0 +1,77 @@
+describe("hx-ws attribute", function() {
+
+ function mockWebsocket() {
+ var listener;
+ var lastSent;
+ var wasClosed = false;
+ var mockSocket = {
+ addEventListener : function(message, l) {
+ listener = l;
+ },
+ write : function(content) {
+ return listener({data:content});
+ },
+ send : function(data) {
+ lastSent = data;
+ },
+ getLastSent : function() {
+ return lastSent;
+ },
+ close : function() {
+ wasClosed = true;
+ },
+ wasClosed : function () {
+ return wasClosed;
+ }
+ };
+ return mockSocket;
+ }
+
+ beforeEach(function () {
+ this.server = makeServer();
+ var socket = mockWebsocket();
+ this.socket = socket;
+ clearWorkArea();
+ this.oldCreateWebSocket = htmx.createWebSocket;
+ htmx.createWebSocket = function(){
+ return socket
+ };
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ htmx.createWebSocket = this.oldCreateWebSocket;
+ });
+
+ it('handles a basic call back', function () {
+ var div = make('
');
+ this.socket.write("
replaced
")
+ byId("d1").innerHTML.should.equal("replaced");
+ byId("d2").innerHTML.should.equal("div2");
+ })
+
+ it('handles a basic send', function () {
+ var div = make('
');
+ byId("d1").click();
+ var lastSent = this.socket.getLastSent();
+ var data = JSON.parse(lastSent);
+ data.HEADERS["HX-Request"].should.equal("true");
+ })
+
+ it('is closed after removal', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var div = make('
');
+ div.click();
+ this.server.respond();
+ this.socket.wasClosed().should.equal(true)
+ })
+
+ it('is closed after removal with no close and activity', function () {
+ var div = make('
');
+ div.parentElement.removeChild(div);
+ this.socket.write("
replaced
")
+ this.socket.wasClosed().should.equal(true)
+ })
+
+});
+
diff --git a/www/static/test/1.9.2/test/core/ajax.js b/www/static/test/1.9.2/test/core/ajax.js
new file mode 100644
index 000000000..b6ea11238
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/ajax.js
@@ -0,0 +1,992 @@
+describe("Core htmx AJAX Tests", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ // 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 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 explicity 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();
+ setTimeout(function () {
+ window.globalWasCalled.should.equal(true);
+ delete window.globalWasCalled;
+ done();
+ }, 400);
+ } 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');
+ });
+
+})
diff --git a/www/static/test/1.9.2/test/core/api.js b/www/static/test/1.9.2/test/core/api.js
new file mode 100644
index 000000000..aea3fa247
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/api.js
@@ -0,0 +1,343 @@
+describe("Core htmx API test", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('onLoad is called... onLoad', function(done){
+ // also tests on/off
+ this.server.respondWith("GET", "/test", "
")
+ var helper = htmx.onLoad(function (elt) {
+ elt.setAttribute("foo", "bar");
+ });
+ var server = this.server;
+ setTimeout(function() {
+ try {
+ var div = make("
");
+ div.click();
+ server.respond();
+ byId("d1").getAttribute("foo").should.equal("bar");
+ done();
+ } finally {
+ htmx.off("htmx:load", helper);
+ }
+ }, 10)
+ });
+
+ 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('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('logAll works', function () {
+ var initialLogger = htmx.config.logger
+ try {
+ htmx.logAll();
+ } finally {
+ htmx.config.logger = initialLogger;
+ }
+ });
+
+ 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 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 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('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");
+ })
+
+})
diff --git a/www/static/test/1.9.2/test/core/events.js b/www/static/test/1.9.2/test/core/events.js
new file mode 100644
index 000000000..e9c04256d
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/events.js
@@ -0,0 +1,657 @@
+describe("Core htmx Events", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it("htmx:load fires properly", function () {
+ var called = false;
+ var handler = htmx.on("htmx:load", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("GET", "/test", "");
+ this.server.respondWith("GET", "/test", "
");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:load", handler);
+ }
+ });
+
+ it("htmx:configRequest allows attribute addition", function () {
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ evt.detail.parameters['param'] = "true";
+ });
+ try {
+ var param = null;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ param = getParameters(xhr)['param'];
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ param.should.equal("true");
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("htmx:configRequest is also dispatched in kebab-case", function () {
+ var handler = htmx.on("htmx:config-request", function (evt) {
+ evt.detail.parameters['param'] = "true";
+ });
+ try {
+ var param = null;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ param = getParameters(xhr)['param'];
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ param.should.equal("true");
+ } finally {
+ htmx.off("htmx:config-request", handler);
+ }
+ });
+
+ it("events are only dispatched once if kebab and camel case match", function () {
+ var invoked = 0;
+ var handler = htmx.on("custom", function () {
+ invoked = invoked + 1
+ });
+ try {
+ var div = make("
");
+ htmx.trigger(div, "custom");
+ invoked.should.equal(1);
+ } finally {
+ htmx.off("custom", handler);
+ }
+ });
+
+ it("htmx:configRequest allows attribute removal", function () {
+ var param = "foo";
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ delete evt.detail.parameters['param'];
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ param = getParameters(xhr)['param'];
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(param, undefined);
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("htmx:configRequest allows header tweaking", function () {
+ var header = "foo";
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ evt.detail.headers['X-My-Header'] = "bar";
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ header = xhr.requestHeaders['X-My-Header'];
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(header, "bar");
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("htmx:configRequest on form gives access to submit event", function () {
+ var submitterId;
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ evt.detail.headers['X-Submitter-Id'] = evt.detail.triggeringEvent.submitter.id;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ submitterId = xhr.requestHeaders['X-Submitter-Id']
+ xhr.respond(200, {}, "");
+ });
+ make('
');
+ var btn = byId('b1');
+ btn.click();
+ this.server.respond();
+ should.equal(submitterId, "b1")
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("htmx:afterSwap is called when replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterSwap", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterSwap", handler);
+ }
+ });
+
+ it("htmx:oobBeforeSwap is called before swap", function () {
+ var called = false;
+ var handler = htmx.on("htmx:oobBeforeSwap", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
Baz
");
+ });
+ var oob = make('
Blip
');
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("Baz");
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:oobBeforeSwap", handler);
+ }
+ });
+
+ it("htmx:oobBeforeSwap can abort a swap", function () {
+ var called = false;
+ var handler = htmx.on("htmx:oobBeforeSwap", function (evt) {
+ called = true;
+ evt.preventDefault();
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
Baz
");
+ });
+ var oob = make('
Blip
');
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("Blip");
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:oobBeforeSwap", handler);
+ }
+ });
+
+
+ it("htmx:oobBeforeSwap is not called on an oob miss", function () {
+ var called = false;
+ var handler = htmx.on("htmx:oobBeforeSwap", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
Baz
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, false);
+ } finally {
+ htmx.off("htmx:oobBeforeSwap", handler);
+ }
+ });
+
+ it("htmx:oobAfterSwap is called after swap", function () {
+ var called = false;
+ var handler = htmx.on("htmx:oobAfterSwap", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
Baz
");
+ });
+ var oob = make('
Blip
');
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ byId("d1").innerHTML.should.equal("Baz");
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:oobAfterSwap", handler);
+ }
+ });
+
+ it("htmx:oobAfterSwap is not called on an oob miss", function () {
+ var called = false;
+ var handler = htmx.on("htmx:oobAfterSwap", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
Baz
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, false);
+ } finally {
+ htmx.off("htmx:oobAfterSwap", handler);
+ }
+ });
+
+ it("htmx:afterSettle is called once when replacing outerHTML", function () {
+ var called = 0;
+ var handler = htmx.on("htmx:afterSettle", function (evt) {
+ called++;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, 1);
+ } finally {
+ htmx.off("htmx:afterSettle", handler);
+ }
+ });
+
+ it("htmx:afterSettle is called once when replacing outerHTML with whitespace", function () {
+ var called = 0;
+ var handler = htmx.on("htmx:afterSettle", function (evt) {
+ called++;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
\n");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, 1);
+ } finally {
+ htmx.off("htmx:afterSettle", handler);
+ }
+ });
+
+ it("htmx:afterSettle is called twice when replacing outerHTML with whitespace separated elements", function () {
+ var called = 0;
+ var handler = htmx.on("htmx:afterSettle", function (evt) {
+ called++;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
\n
Foo");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, 2);
+ } finally {
+ htmx.off("htmx:afterSettle", handler);
+ }
+ });
+
+ it("htmx:afterRequest is called after a successful request", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+ it("htmx:afterOnLoad is called after a successful request", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterOnLoad", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterOnLoad", handler);
+ }
+ });
+
+ it("htmx:afterRequest is called after a failed request", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(500, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+ it("htmx:sendError is called after a failed request", function (done) {
+ var called = false;
+ var handler = htmx.on("htmx:sendError", function (evt) {
+ called = true;
+ });
+ this.server.restore(); // turn off server mock so connection doesn't work
+ var div = make("
");
+ div.click();
+ setTimeout(function () {
+ htmx.off("htmx:sendError", handler);
+ should.equal(called, true);
+ done();
+ }, 30);
+ });
+
+ it("htmx:afterRequest is called when replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+ it("htmx:afterOnLoad is called when replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterOnLoad", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterOnLoad", handler);
+ }
+ });
+
+ it("htmx:beforeProcessNode is called when replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:beforeProcessNode", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:beforeProcessNode", handler);
+ }
+ });
+
+ it("htmx:beforeProcessNode allows htmx attribute tweaking", function () {
+ var called = false;
+ var handler = htmx.on("htmx:beforeProcessNode", function (evt) {
+ evt.target.setAttribute("hx-post", "/success")
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/success", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:beforeProcessNode", handler);
+ }
+ });
+
+ it("htmx:afterProcessNode is called after replacing outerHTML", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterProcessNode", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterProcessNode", handler);
+ }
+ });
+
+ it("htmx:afterRequest is called when targeting a parent div", function () {
+ var called = false;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ called = true;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ var button = byId('b1');
+ button.click();
+ this.server.respond();
+ should.equal(called, true);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+ it("adding an error in htmx:configRequest stops the request", function () {
+ try {
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ evt.detail.errors.push("An error");
+ });
+ var request = false;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ request = true;
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(request, false);
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("preventDefault() in htmx:configRequest stops the request", function () {
+ try {
+ var handler = htmx.on("htmx:configRequest", function (evt) {
+ evt.preventDefault();
+ });
+ var request = false;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ request = true;
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(request, false);
+ } finally {
+ htmx.off("htmx:configRequest", handler);
+ }
+ });
+
+ it("preventDefault() in the htmx:beforeRequest event cancels the request", function () {
+ try {
+ var handler = htmx.on("htmx:beforeRequest", function (evt) {
+ evt.preventDefault();
+ });
+ var request = false;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ request = true;
+ xhr.respond(200, {}, "
");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(request, false);
+ } finally {
+ htmx.off("htmx:beforeRequest", handler);
+ }
+ });
+
+ it("preventDefault() in the htmx:beforeOnLoad event cancels the swap", function () {
+ try {
+ var handler = htmx.on("htmx:beforeOnLoad", function (evt) {
+ evt.preventDefault();
+ });
+ var request = false;
+ this.server.respondWith("POST", "/test", function (xhr) {
+ request = true;
+ xhr.respond(200, {}, "Bar");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(request, true);
+ div.innerText.should.equal("Foo");
+ } finally {
+ htmx.off("htmx:beforeOnLoad", handler);
+ }
+ });
+
+ it("htmx:afterRequest event contains 'successful' and 'failed' properties indicating success after successful request", function () {
+ var successful = false;
+ var failed = true;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ successful = evt.detail.successful;
+ failed = evt.detail.failed;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(200, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(successful, true);
+ should.equal(failed, false);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+ it("htmx:afterRequest event contains 'successful' and 'failed' properties indicating failure after failed request", function () {
+ var successful = true;
+ var failed = false;
+ var handler = htmx.on("htmx:afterRequest", function (evt) {
+ successful = evt.detail.successful;
+ failed = evt.detail.failed;
+ });
+ try {
+ this.server.respondWith("POST", "/test", function (xhr) {
+ xhr.respond(500, {}, "");
+ });
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ should.equal(successful, false);
+ should.equal(failed, true);
+ } finally {
+ htmx.off("htmx:afterRequest", handler);
+ }
+ });
+
+
+ it("htmx:confirm can cancel request", function () {
+ var allow = false;
+ var handler = htmx.on("htmx:confirm", function (evt) {
+ evt.preventDefault();
+ if (allow) {
+ evt.detail.issueRequest();
+ }
+ });
+
+ try {
+ this.server.respondWith("GET", "/test", "updated");
+ var div = make("
");
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("");
+ allow = true;
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("updated");
+ } finally {
+ htmx.off("htmx:load", handler);
+ }
+ });
+
+ it("has updated target available when target set via htmx:beforeSwap", function () {
+
+ var targetWasUpdatedInAfterSwapHandler = false;
+
+ var beforeSwapHandler = htmx.on("htmx:beforeSwap", function (evt) {
+ console.log("beforeSwap", evt.detail.target, byId('d2'));
+ evt.detail.target = byId('d2');
+ });
+ var afterSwapHandler = htmx.on("htmx:afterSwap", function (evt) {
+ console.log("afterSwap", evt.detail.target, byId('d2'));
+ targetWasUpdatedInAfterSwapHandler = evt.detail.target === byId('d2');
+ });
+
+ try {
+ this.server.respondWith("GET", "/test", "updated");
+ make("
");
+ var div = byId('d0');
+ div.click();
+ this.server.respond();
+ targetWasUpdatedInAfterSwapHandler.should.equal(true);
+ } finally {
+ htmx.off("htmx:beforeSwap", beforeSwapHandler);
+ htmx.off("htmx:afterSwap", afterSwapHandler);
+ }
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/core/headers.js b/www/static/test/1.9.2/test/core/headers.js
new file mode 100644
index 000000000..e7d1ec258
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/headers.js
@@ -0,0 +1,229 @@
+describe("Core htmx AJAX headers", function () {
+ 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 array 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 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 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-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 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 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 change body content on HX-Location", function () {
+ this.server.respondWith("GET", "/test", [200, {"HX-Location": '{"path":"/test2", "target":"#testdiv"}'}, ""]);
+ this.server.respondWith("GET", "/test2", [200, {}, "
Yay! Welcome
"]);
+ var div = make('
');
+ div.click();
+ this.server.respond();
+ this.server.respond();
+ div.innerHTML.should.equal('
Yay! Welcome
');
+ })
+});
diff --git a/www/static/test/1.9.2/test/core/internals.js b/www/static/test/1.9.2/test/core/internals.js
new file mode 100644
index 000000000..2da08457d
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/internals.js
@@ -0,0 +1,135 @@
+describe("Core htmx internals Tests", function() {
+
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it("makeFragment works with janky stuff", function(){
+ htmx._("makeFragment")("").tagName.should.equal("BODY");
+ htmx._("makeFragment")("").tagName.should.equal("BODY");
+
+ //NB - the tag name should be the *parent* element hosting the HTML since we use the fragment children
+ // for the swap
+ htmx._("makeFragment")("
| ").tagName.should.equal("TR");
+ htmx._("makeFragment")("
").tagName.should.equal("TABLE");
+ htmx._("makeFragment")("
").tagName.should.equal("COLGROUP");
+ htmx._("makeFragment")("
|
").tagName.should.equal("TBODY");
+ })
+
+ it("makeFragment works with template wrapping", function(){
+ htmx.config.useTemplateFragments = true;
+ try {
+ htmx._("makeFragment")("").children.length.should.equal(0);
+ htmx._("makeFragment")("").children.length.should.equal(0);
+
+ var fragment = htmx._("makeFragment")("
| ");
+ fragment.firstElementChild.tagName.should.equal("TD");
+
+ fragment = htmx._("makeFragment")("
");
+ fragment.firstElementChild.tagName.should.equal("THEAD");
+
+ fragment = htmx._("makeFragment")("
");
+ fragment.firstElementChild.tagName.should.equal("COL");
+
+ fragment = htmx._("makeFragment")("
|
");
+ fragment.firstElementChild.tagName.should.equal("TR");
+
+ } finally {
+ htmx.config.useTemplateFragments = false;
+ }
+ })
+
+
+ it("makeFragment works with template wrapping and funky combos", function(){
+ htmx.config.useTemplateFragments = true;
+ try {
+ var fragment = htmx._("makeFragment")("
| ");
+ fragment.children[0].tagName.should.equal("TD");
+ fragment.children[1].tagName.should.equal("DIV");
+ } finally {
+ htmx.config.useTemplateFragments = false;
+ }
+ })
+
+ it("set header works with non-ASCII values", function(){
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "/dummy");
+ htmx._("safelySetHeaderValue")(xhr, "Example", "привет");
+ // unfortunately I can't test the value :/
+ // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
+ })
+
+ it("handles parseInterval correctly", function() {
+ chai.expect(htmx.parseInterval("1ms")).to.be.equal(1);
+ chai.expect(htmx.parseInterval("300ms")).to.be.equal(300);
+ chai.expect(htmx.parseInterval("1s")).to.be.equal(1000)
+ chai.expect(htmx.parseInterval("1.5s")).to.be.equal(1500)
+ chai.expect(htmx.parseInterval("2s")).to.be.equal(2000)
+
+ chai.expect(htmx.parseInterval(null)).to.be.undefined
+ chai.expect(htmx.parseInterval("")).to.be.undefined
+ chai.expect(htmx.parseInterval("undefined")).to.be.undefined
+ chai.expect(htmx.parseInterval("true")).to.be.undefined
+ chai.expect(htmx.parseInterval("false")).to.be.undefined
+ })
+
+ it("tokenizes correctly", function() {
+ chai.expect(htmx._("tokenizeString")("a,")).to.be.deep.equal(['a', ',']);
+ chai.expect(htmx._("tokenizeString")("aa,")).to.be.deep.equal(['aa', ',']);
+ chai.expect(htmx._("tokenizeString")("aa,aa")).to.be.deep.equal(['aa', ',', 'aa']);
+ chai.expect(htmx._("tokenizeString")("aa.aa")).to.be.deep.equal(['aa', '.', 'aa']);
+ })
+
+ it("tags respond correctly to shouldCancel", function() {
+ var anchorThatShouldCancel = make("
");
+ htmx._("shouldCancel")({type:'click'}, anchorThatShouldCancel).should.equal(true);
+
+ var anchorThatShouldCancel = make("
");
+ htmx._("shouldCancel")({type:'click'}, anchorThatShouldCancel).should.equal(true);
+
+ var anchorThatShouldNotCancel = make("
");
+ htmx._("shouldCancel")({type:'click'}, anchorThatShouldNotCancel).should.equal(false);
+
+ var form = make("
");
+ htmx._("shouldCancel")({type:'submit'}, form).should.equal(true);
+
+ var form = make("
");
+ var input = byId("i1");
+ htmx._("shouldCancel")({type:'click'}, input).should.equal(true);
+
+ var form = make("
");
+ var button = byId("b1");
+ htmx._("shouldCancel")({type:'click'}, button).should.equal(true);
+
+ })
+
+ it("unset properly unsets a given attribute", function(){
+ make("
");
+ var div = byId("d1");
+ should.equal(undefined, htmx._("getClosestAttributeValue")(div, "foo"));
+ })
+
+ it("unset properly unsets a given attribute on a parent", function(){
+ make("
");
+ var div = byId("d1");
+ should.equal(undefined, htmx._("getClosestAttributeValue")(div, "foo"));
+ })
+
+ it("unset does not unset a value below it in the hierarchy", function(){
+ make("
");
+ var div = byId("d1");
+ should.equal("2", htmx._("getClosestAttributeValue")(div, "foo"));
+ })
+
+ it("encoding values respects enctype on forms", function(){
+ var form = make("
");
+ var value = htmx._("encodeParamsForBody")(null, form, {});
+ (value instanceof FormData).should.equal(true);
+ })
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/core/parameters.js b/www/static/test/1.9.2/test/core/parameters.js
new file mode 100644
index 000000000..39d569c67
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/parameters.js
@@ -0,0 +1,181 @@
+describe("Core htmx Parameter Handling", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('Input includes value', function () {
+ var input = make('
');
+ var vals = htmx._('getInputValues')(input).values;
+ vals['foo'].should.equal('bar');
+ })
+
+ it('Input includes value on get', function () {
+ var input = make('
');
+ var vals = htmx._('getInputValues')(input, "get").values;
+ vals['foo'].should.equal('bar');
+ })
+
+ it('Input includes form', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var vals = htmx._('getInputValues')(input).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ })
+
+ it('Input doesnt include form on get', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var vals = htmx._('getInputValues')(input, 'get').values;
+ vals['foo'].should.equal('bar');
+ should.equal(vals['do'], undefined);
+ })
+
+ it('non-input includes form', function () {
+ var form = make('
');
+ var div = byId('d1');
+ var vals = htmx._('getInputValues')(div, "post").values;
+ vals['do'].should.equal('rey');
+ })
+
+ it('non-input doesnt include form on get', function () {
+ var form = make('
');
+ var div = byId('d1');
+ var vals = htmx._('getInputValues')(div, "get").values;
+ should.equal(vals['do'], undefined);
+ })
+
+ it('Basic form works on get', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(form, 'get').values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ })
+
+ it('Basic form works on non-get', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(form, 'post').values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ })
+
+ it('Double values are included as array', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(form).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey', 'rey']);
+ })
+
+ it('Double values are included as array in correct order', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(byId("i3")).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey1', 'rey2']);
+ })
+
+ it('Double empty values are included as array in correct order', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(byId("i3")).values;
+ vals['do'].should.deep.equal(['', 'rey', '']);
+ })
+
+ it('hx-include works with form', function () {
+ var form = make('
');
+ var div = make('
');
+ var vals = htmx._('getInputValues')(div).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey', 'rey']);
+ })
+
+ it('hx-include works with input', function () {
+ var form = make('
');
+ var div = make('
');
+ var vals = htmx._('getInputValues')(div).values;
+ vals['foo'].should.equal('bar');
+ should.equal(vals['do'], undefined);
+ })
+
+ it('hx-include works with two inputs', function () {
+ var form = make('
');
+ var div = make('
');
+ var vals = htmx._('getInputValues')(div).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey', 'rey']);
+ })
+
+ it('hx-include works with two inputs, plus form', function () {
+ var form = make('
');
+ var div = make('
');
+ var vals = htmx._('getInputValues')(div).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey', 'rey']);
+ })
+
+ it('correctly URL escapes values', function () {
+ htmx._("urlEncode")({}).should.equal("");
+ htmx._("urlEncode")({"foo": "bar"}).should.equal("foo=bar");
+ htmx._("urlEncode")({"foo": "bar", "do" : "rey"}).should.equal("foo=bar&do=rey");
+ htmx._("urlEncode")({"foo": "bar", "do" : ["rey", "blah"]}).should.equal("foo=bar&do=rey&do=blah");
+ });
+
+ it('form includes last focused button', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var button = byId('b1');
+ button.focus();
+ var vals = htmx._('getInputValues')(form).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ vals['btn'].should.equal('bar');
+ })
+
+ it('form includes last focused submit', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var button = byId('s1');
+ button.focus();
+ var vals = htmx._('getInputValues')(form).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ vals['s1'].should.equal('bar');
+ })
+
+ it('form does not include button when focus is lost', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var button = byId('s1');
+ button.focus();
+ input.focus();
+ var vals = htmx._('getInputValues')(form).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ should.equal(vals['s1'], undefined);
+ })
+
+ it('form does not include button when focus is lost outside of form', function () {
+ var form = make('
');
+ var anchor = make('
');
+ var button = byId('s1');
+ button.focus();
+ anchor.focus();
+ var vals = htmx._('getInputValues')(form).values;
+ vals['foo'].should.equal('bar');
+ vals['do'].should.equal('rey');
+ should.equal(vals['s1'], undefined);
+ })
+
+ it('form includes button name and value if button has nested elements when clicked', function () {
+ var form = make('
');
+ var nestedElt = byId('span1');
+ nestedElt.click();
+ var vals = htmx._('getInputValues')(form).values;
+ vals['do'].should.equal('rey');
+ })
+
+});
+
diff --git a/www/static/test/1.9.2/test/core/perf.js b/www/static/test/1.9.2/test/core/perf.js
new file mode 100644
index 000000000..c461aee17
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/perf.js
@@ -0,0 +1,64 @@
+describe("Core htmx perf Tests", function() {
+
+ var HTMX_HISTORY_CACHE_NAME = "htmx-history-cache";
+
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ localStorage.removeItem(HTMX_HISTORY_CACHE_NAME);
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ localStorage.removeItem(HTMX_HISTORY_CACHE_NAME);
+ });
+
+ function stringRepeat(str, num) {
+ num = Number(num);
+
+ var result = '';
+ while (true) {
+ if (num & 1) { // (1)
+ result += str;
+ }
+ num >>>= 1; // (2)
+ if (num <= 0) break;
+ str += str;
+ }
+
+ return result;
+ }
+
+
+ it("history implementation should be fast", function(){
+ // create an entry with a large content string (256k) and see how fast we can write and read it
+ // to local storage as a single entry
+ var entry = {url: stringRepeat("x", 32), content:stringRepeat("x", 256*1024)}
+ var array = [];
+ for (var i = 0; i < 10; i++) {
+ array.push(entry);
+ }
+ var start = performance.now();
+ var string = JSON.stringify(array);
+ localStorage.setItem(HTMX_HISTORY_CACHE_NAME, string);
+ var reReadString = localStorage.getItem(HTMX_HISTORY_CACHE_NAME);
+ var finalJson = JSON.parse(reReadString);
+ var end = performance.now();
+ var timeInMs = end - start;
+ chai.assert(timeInMs < 300, "Should take less than 300ms on most platforms");
+ })
+
+ it("history snapshot cleaning should be fast", function(){
+ //
+ var workArea = getWorkArea();
+ var html = "
Yay, really large HTML documents are fun!
\n";
+ html = stringRepeat(html, 5 * 1024); // ~350K in size, about the size of CNN's body tag :p
+ workArea.insertAdjacentHTML("beforeend", html)
+ var start = performance.now();
+ htmx._("cleanInnerHtmlForHistory")(workArea);
+ var end = performance.now();
+ var timeInMs = end - start;
+ chai.assert(timeInMs < 50, "Should take less than 50ms on most platforms");
+ })
+
+})
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/core/regressions.js b/www/static/test/1.9.2/test/core/regressions.js
new file mode 100644
index 000000000..5a4785576
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/regressions.js
@@ -0,0 +1,211 @@
+describe("Core htmx Regression Tests", function(){
+
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('SVGs process properly in IE11', function()
+ {
+ var btn = make('
')
+ });
+
+ it ('Handles https://github.com/bigskysoftware/htmx/issues/4 properly', function() {
+ this.server.respondWith("GET", "/index2a.php",
+ "
I came from message oob swap I should be second
" +
+ "
I came from a message2 oob swap I should be third but I am in the wrong spot
" +
+ "I'm page2 content (non-swap) I should be first")
+
+ var h1 = make("" +
+ "
" +
+ "
" +
+ "
" +
+ "
Kutty CLICK ME
")
+ h1.click();
+ this.server.respond();
+ htmx.find("#page2").innerHTML.should.equal("I'm page2 content (non-swap) I should be first")
+ htmx.find("#message").innerHTML.should.equal("I came from message oob swap I should be second")
+ htmx.find("#message2").innerHTML.should.equal("I came from a message2 oob swap I should be third but I am in the wrong spot")
+ });
+
+ it ('Handles https://github.com/bigskysoftware/htmx/issues/33 "empty values" properly', function() {
+ this.server.respondWith("POST", "/htmx.php", function (xhr) {
+ xhr.respond(200, {}, xhr.requestBody);
+ });
+
+ var form = make('
')
+ form.click();
+ this.server.respond();
+ form.innerHTML.should.equal("variable=")
+ });
+
+ it ('name=id doesnt cause an error', function(){
+ this.server.respondWith("GET", "/test", "Foo
")
+ var div = make('
Get It
')
+ div.click();
+ this.server.respond();
+ div.innerText.should.contain("Foo")
+ });
+
+ it ('empty id doesnt cause an error', function(){
+ this.server.respondWith("GET", "/test", "Foo\n
")
+ var div = make('
Get It
')
+ div.click();
+ this.server.respond();
+ div.innerText.should.contain("Foo")
+ });
+
+ it ('id with dot in value doesnt cause an error', function(){
+ this.server.respondWith("GET", "/test", "Foo
");
+ var div = make('
Get It
');
+ div.click();
+ this.server.respond();
+ div.innerText.should.contain("Foo");
+ });
+
+ it ('@ symbol in attributes does not break requests', function(){
+ this.server.respondWith("GET", "/test", "
Foo
");
+ var div = make('
Get It
');
+ div.click();
+ this.server.respond();
+ byId("d1").getAttribute('@foo').should.equal('bar');
+ });
+
+ it ('@ symbol in attributes does not break attribute settling requests', function(){
+ this.server.respondWith("GET", "/test", "
Foo
");
+ var div = make('
');
+ div.click();
+ this.server.respond();
+ byId("d1").getAttribute('@foo').should.equal('bar');
+ });
+
+ it ('selected element with ID does not cause NPE when it disappears', function(){
+ this.server.respondWith("GET", "/test", "
Replaced
");
+ var input = make('
');
+ input.focus();
+ input.click();
+ this.server.respond();
+ byId("d1").innerText.should.equal('Replaced');
+ });
+
+ it('does not submit with a false condition on a form', function() {
+ this.server.respondWith("POST", "/test", "Submitted");
+ var defaultPrevented = false;
+ htmx.on("click", function(evt) {
+ defaultPrevented = evt.defaultPrevented;
+ })
+ var form = make('
');
+ form.click()
+ this.server.respond();
+ defaultPrevented.should.equal(true);
+ })
+
+ it('two elements can listen for the same event on another element', function() {
+ this.server.respondWith("GET", "/test", "triggered");
+
+ make('
' +
+ '
');
+
+
+ var div1 = byId("d1");
+ var div2 = byId("d2");
+
+ document.body.click();
+ this.server.respond();
+
+ div2.innerHTML.should.equal("triggered");
+ div1.innerHTML.should.equal("triggered");
+ })
+
+ it('a form can reset based on the htmx:afterRequest event', function() {
+ this.server.respondWith("POST", "/test", "posted");
+ //htmx.logAll();
+
+ var form = make('
');
+ htmx.trigger(form, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content
+
+ var div1 = byId("d1");
+ var input = byId("i1");
+ input.value = "foo";
+ var submit = byId("s1");
+
+ input.value.should.equal("foo");
+ submit.click();
+ this.server.respond();
+
+ div1.innerHTML.should.equal("posted");
+ input.value.should.equal(""); // form should be reset
+ })
+
+ it('supports image maps', function() {
+ this.server.respondWith("GET", "/test", "triggered");
+
+ make('
' +
+ '
' +
+ '
' +
+ '' +
+ '
' +
+ '
');
+
+ var div1 = byId("d1");
+ var area = document.getElementsByTagName('area')[0];
+
+ area.click();
+ this.server.respond();
+
+ div1.innerHTML.should.equal("triggered");
+ })
+
+ it("supports unset on hx-select", function(){
+ this.server.respondWith("GET", "/test", "Foo
Bar");
+ htmx.logAll();
+ make('
')
+ var btn = byId("b1");
+ btn.click()
+ this.server.respond();
+
+ btn.innerText.should.equal("FooBar");
+ })
+
+
+ it("can trigger swaps from fields that don't support setSelectionRange", function(){
+ const template = '
';
+
+ const response = '
';
+ this.server.respondWith("GET", "/test", response);
+ make(template);
+ var input = byId("id_email");
+ // HTMX only attempts to restore the selection on inputs that have a current selection and are active.
+ // additionally we can't set the selection on email inputs (that's the whole bug) so start as a text input where you can set selection
+ // and replace with an email
+ input.focus();
+ input.selectionStart = 3;
+ input.selectionEnd = 3;
+ input.click();
+ this.server.respond();
+ var input = byId("id_email");
+ input.value.should.equal("supertest@test.com");
+ });
+});
diff --git a/www/static/test/1.9.2/test/core/security.js b/www/static/test/1.9.2/test/core/security.js
new file mode 100644
index 000000000..d4be72162
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/security.js
@@ -0,0 +1,32 @@
+describe("security options", function() {
+
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it("can disable a single elt", function(){
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Initial");
+ })
+
+ it("can disable a parent elt", function(){
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ var div = make('
')
+ var btn = byId("b1");
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Initial");
+ })
+
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/core/tokenizer.js b/www/static/test/1.9.2/test/core/tokenizer.js
new file mode 100644
index 000000000..3071ad9b1
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/tokenizer.js
@@ -0,0 +1,48 @@
+describe("Core htmx tokenizer tests", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ function tokenize(str) {
+ return htmx._("tokenizeString")(str);
+ }
+
+ function tokenizeTest(str, result) {
+ return tokenize(str).should.deep.equal(result);
+ }
+
+ it('tokenizes properly', function()
+ {
+ tokenizeTest("", []);
+ tokenizeTest(" ", [" ", " "]);
+ tokenizeTest("(", ["("]);
+ tokenizeTest("()", ["(", ")"]);
+ tokenizeTest("(,)", ["(", ",", ")"]);
+ tokenizeTest(" ( ) ", [" ", "(", " ", ")", " "]);
+ tokenizeTest(" && ) ", [" ", "&", "&", " ", ")", " "]);
+ tokenizeTest(" && ) 'asdf'", [" ", "&", "&", " ", ")", " ", "'asdf'"]);
+ tokenizeTest(" && ) ',asdf'", [" ", "&", "&", " ", ")", " ", "',asdf'"]);
+ tokenizeTest('",asdf"', ['",asdf"']);
+ tokenizeTest('&& ) ",asdf"', ["&", "&", " ", ")", " ", '",asdf"']);
+ });
+
+ it('generates conditionals property', function()
+ {
+ var tokens = tokenize("[code==4||(code==5&&foo==true)]");
+ var conditional = htmx._("maybeGenerateConditional")(null, tokens);
+ var func = eval(conditional);
+ func({code: 5, foo: true}).should.equal(true);
+ func({code: 5, foo: false}).should.equal(false);
+ func({code: 4, foo: false}).should.equal(true);
+ func({code: 3, foo: true}).should.equal(false);
+ });
+
+
+
+
+})
diff --git a/www/static/test/1.9.2/test/core/validation.js b/www/static/test/1.9.2/test/core/validation.js
new file mode 100644
index 000000000..428a4acce
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/validation.js
@@ -0,0 +1,198 @@
+describe("Core htmx client side validation tests", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('HTML5 required validation error prevents request', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ form.textContent.should.equal("No Request");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("No Request");
+ byId("i1").value = "foo";
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('Novalidate skips form validation', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ form.textContent.should.equal("No Request");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('Validation skipped for indirect form submission', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ form.textContent.should.equal("No Request");
+ byId("button").click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('Formnovalidate skips form validation', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ form.textContent.should.equal("No Request");
+ byId("button").click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('HTML5 pattern validation error prevents request', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ byId("i1").value = "xyz";
+ form.textContent.should.equal("No Request");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("No Request");
+ byId("i1").value = "abc";
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('Custom validation error prevents request', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ byId("i1").setCustomValidity("Nope");
+ form.textContent.should.equal("No Request");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("No Request");
+ byId("i1").setCustomValidity("");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('hyperscript validation error prevents request', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+
+ var form = make('
');
+ htmx.trigger(form, "htmx:load");
+ byId("i1").value = "boo";
+ form.textContent.should.equal("No Request");
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("No Request");
+ byId("i1").value = "foo";
+ form.click();
+ this.server.respond();
+ form.textContent.should.equal("Clicked!");
+ });
+
+ it('calls htmx:validation:failed on failure', function()
+ {
+ var form = make('
');
+ var calledEvent = false;
+ var handler = htmx.on(form, "htmx:validation:failed", function(){
+ calledEvent = true;
+ });
+ try {
+ form.click();
+ this.server.respond();
+ } finally {
+ htmx.off(form, handler);
+ }
+ calledEvent.should.equal(true);
+ });
+
+ it('calls htmx:validation:halted on failure', function()
+ {
+ var form = make('
');
+ var errors = null;
+ var handler = htmx.on(form, "htmx:validation:halted", function(evt){
+ errors = evt.detail.errors;
+ });
+ try {
+ form.click();
+ this.server.respond();
+ } finally {
+ htmx.off(form, handler);
+ }
+ errors.length.should.equal(1);
+ byId("i1").should.equal(errors[0].elt);
+ errors[0].validity.valueMissing.should.equal(true);
+ });
+
+
+ it('hx-validate can prevent a single input from submitting', function()
+ {
+ this.server.respondWith("POST", "/test", "Clicked!");
+ var div = make("
No Request
")
+ var form = make('
');
+ var input = byId("i1");
+
+ div.textContent.should.equal("No Request");
+
+ input.value = "abc";
+ input.click();
+ this.server.respond();
+ div.textContent.should.equal("No Request");
+
+ input.value = "1bc";
+ input.click();
+ this.server.respond();
+ div.textContent.should.equal("No Request");
+
+ input.value = "123";
+ input.click();
+ this.server.respond();
+ div.textContent.should.equal("Clicked!");
+ });
+
+})
diff --git a/www/static/test/1.9.2/test/core/verbs.js b/www/static/test/1.9.2/test/core/verbs.js
new file mode 100644
index 000000000..1d0711c2f
--- /dev/null
+++ b/www/static/test/1.9.2/test/core/verbs.js
@@ -0,0 +1,44 @@
+describe("Core htmx AJAX Verbs", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('handles basic posts properly', function () {
+ this.server.respondWith("POST", "/test", "post");
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("post");
+ })
+
+ it('handles basic put properly', function () {
+ this.server.respondWith("PUT", "/test", "put");
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("put");
+ })
+
+ it('handles basic patch properly', function () {
+ this.server.respondWith("PATCH", "/test", "patch");
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("patch");
+ })
+
+ it('handles basic delete properly', function () {
+ this.server.respondWith("DELETE", "/test", "delete");
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("delete");
+ })
+
+});
+
diff --git a/www/static/test/1.9.2/test/ext/ajax-header.js b/www/static/test/1.9.2/test/ext/ajax-header.js
new file mode 100644
index 000000000..0888ef3b5
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/ajax-header.js
@@ -0,0 +1,21 @@
+describe("ajax-header extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('Sends the X-Requested-With header', function () {
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, xhr.requestHeaders['X-Requested-With'])
+ });
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("XMLHttpRequest");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/bad-extension.js b/www/static/test/1.9.2/test/ext/bad-extension.js
new file mode 100644
index 000000000..fcf6a4384
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/bad-extension.js
@@ -0,0 +1,27 @@
+describe("bad extension", function() {
+ htmx.defineExtension("bad-extension", {
+ onEvent : function(name, evt) {throw "onEvent"},
+ transformResponse : function(text, xhr, elt) {throw "transformRequest"},
+ isInlineSwap : function(swapStyle) {throw "isInlineSwap"},
+ handleSwap : function(swapStyle, target, fragment, settleInfo) {throw "handleSwap"},
+ encodeParameters : function(xhr, parameters, elt) {throw "encodeParmeters"}
+ }
+ )
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('does not blow up rendering', function () {
+ this.server.respondWith("GET", "/test", "clicked!");
+ var div = make('
Click Me!
')
+ div.click();
+ this.server.respond();
+ div.innerHTML.should.equal("clicked!");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/class-tools.js b/www/static/test/1.9.2/test/ext/class-tools.js
new file mode 100644
index 000000000..0c5004b56
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/class-tools.js
@@ -0,0 +1,55 @@
+describe("class-tools extension", function(){
+ beforeEach(function() {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function() {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('adds classes properly', function(done)
+ {
+ var div = make('
Click Me!
')
+ should.equal(div.classList.length, 0);
+ setTimeout(function(){
+ should.equal(div.classList.contains("c1"), true);
+ done();
+ }, 100);
+ });
+
+ it('removes classes properly', function(done)
+ {
+ var div = make('
Click Me!
')
+ should.equal(div.classList.contains("foo"), true);
+ should.equal(div.classList.contains("bar"), true);
+ setTimeout(function(){
+ should.equal(div.classList.contains("foo"), true);
+ should.equal(div.classList.contains("bar"), false);
+ done();
+ }, 100);
+ });
+
+ it('adds classes properly w/ data-* prefix', function(done)
+ {
+ var div = make('
Click Me!
')
+ should.equal(div.classList.length, 0);
+ setTimeout(function(){
+ should.equal(div.classList.contains("c1"), true);
+ done();
+ }, 100);
+ });
+
+ it('extension can be on parent', function(done)
+ {
+ var div = make('
')
+ should.equal(div.classList.length, 0);
+ setTimeout(function(){
+ should.equal(div.classList.contains("c1"), false);
+ should.equal(byId("d1").classList.contains("c1"), true);
+ done();
+ }, 100);
+ });
+
+
+})
diff --git a/www/static/test/1.9.2/test/ext/client-side-templates.js b/www/static/test/1.9.2/test/ext/client-side-templates.js
new file mode 100644
index 000000000..29206d21b
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/client-side-templates.js
@@ -0,0 +1,30 @@
+describe("client-side-templates extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('works on basic mustache template', function () {
+ this.server.respondWith("GET", "/test", '{"foo":"bar"}');
+ var btn = make('
')
+ make('')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("*bar*");
+ });
+
+ it('works on basic handlebars template', function () {
+ this.server.respondWith("GET", "/test", '{"foo":"bar"}');
+ var btn = make('
')
+ Handlebars.partials["hb1"] = Handlebars.compile("*{{foo}}*");
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("*bar*");
+ });
+
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/debug.js b/www/static/test/1.9.2/test/ext/debug.js
new file mode 100644
index 000000000..4eeca295e
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/debug.js
@@ -0,0 +1,19 @@
+describe("debug extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('works on basic request', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Clicked!");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/disable-element.js b/www/static/test/1.9.2/test/ext/disable-element.js
new file mode 100644
index 000000000..cde1ad229
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/disable-element.js
@@ -0,0 +1,62 @@
+describe("disable-element extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('disables the triggering element during htmx request', function () {
+ // GIVEN:
+ // - A button triggering an htmx request with disable-element extension
+ // - The button is enabled
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {})
+ });
+ var btn = make('
')
+ btn.disabled.should.equal(false);
+
+ // WHEN clicking
+ btn.click();
+
+ // THEN it's disabled
+ btn.disabled.should.equal(true);
+
+ // WHEN server response has arrived
+ this.server.respond();
+
+ // THEN it's re-enabled
+ btn.disabled.should.equal(false);
+ });
+
+ it('disables the designated element during htmx request', function () {
+ // GIVEN:
+ // - A button triggering an htmx request with disable-element extension
+ // - Another button that needs to be disabled during the htmx request
+ // - Both buttons are enabled
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {})
+ });
+ var btn = make('
')
+ var btn2 = make('
')
+ btn.disabled.should.equal(false);
+ btn2.disabled.should.equal(false);
+
+ // WHEN clicking
+ btn.click();
+
+ // THEN it's not disabled, but the other one is
+ btn.disabled.should.equal(false);
+ btn2.disabled.should.equal(true);
+
+ // WHEN server response has arrived
+ this.server.respond();
+
+ // THEN both buttons are back enabled
+ btn.disabled.should.equal(false);
+ btn2.disabled.should.equal(false);
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/event-header.js b/www/static/test/1.9.2/test/ext/event-header.js
new file mode 100644
index 000000000..a5a66cf58
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/event-header.js
@@ -0,0 +1,23 @@
+describe("event-header extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('Sends the Triggering-Event header', function () {
+ this.server.respondWith("GET", "/test", function (xhr) {
+ xhr.respond(200, {}, xhr.requestHeaders['Triggering-Event'])
+ });
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ var json = JSON.parse(btn.innerText);
+ json.type.should.equal("click");
+ json.target.should.equal("button");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/extension-swap.js b/www/static/test/1.9.2/test/ext/extension-swap.js
new file mode 100644
index 000000000..0186b5250
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/extension-swap.js
@@ -0,0 +1,60 @@
+describe("default extensions behavior", function() {
+
+ var loadCalls, afterSwapCalls, afterSettleCalls;
+
+ beforeEach(function () {
+ loadCalls = [];
+ this.server = makeServer();
+ clearWorkArea();
+
+ htmx.defineExtension("ext-testswap", {
+ onEvent : function(name, evt) {
+ if (name === "htmx:load") {
+ loadCalls.push(evt.detail.elt);
+ }
+ },
+ handleSwap: function (swapStyle, target, fragment, settleInfo) {
+ // simple outerHTML replacement for tests
+ var parentEl = target.parentElement;
+ parentEl.removeChild(target);
+ return [parentEl.appendChild(fragment)]; // return the newly added element
+ }
+
+ });
+
+ });
+
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ htmx.removeExtension("ext-testswap");
+ });
+
+ it('handleSwap: afterSwap and afterSettle triggered if extension defined on parent', function () {
+ this.server.respondWith("GET", "/test", '
');
+ var div = make('
');
+ var btn = div.firstChild;
+ btn.click()
+ this.server.respond();
+ loadCalls.length.should.equal(1);
+ loadCalls[0].textContent.should.equal('Clicked!'); // the new button is loaded
+ });
+
+ it('handleSwap: new content is handled by htmx', function() {
+ this.server.respondWith("GET", "/test", '
');
+ this.server.respondWith("GET", "/test-inner", 'Loaded!');
+ make('
').querySelector('button').click();
+
+ this.server.respond(); // call /test via button trigger=click
+ var btn = byId('test-ext-testswap');
+ btn.textContent.should.equal('Clicked!');
+ loadCalls.length.should.equal(1);
+ loadCalls[0].textContent.should.equal('Clicked!'); // the new button is loaded
+
+ this.server.respond(); // call /test-inner via span trigger=load
+ btn.textContent.should.equal("Clicked!Loaded!");
+ loadCalls.length.should.equal(2);
+ loadCalls[1].textContent.should.equal('Loaded!'); // the new span is loaded
+ });
+
+});
diff --git a/www/static/test/1.9.2/test/ext/hyperscript.js b/www/static/test/1.9.2/test/ext/hyperscript.js
new file mode 100644
index 000000000..6d49a2dae
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/hyperscript.js
@@ -0,0 +1,64 @@
+describe("hyperscript integration", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('can trigger with a custom event', function () {
+ this.server.respondWith("GET", "/test", "Custom Event Sent!");
+ var btn = make('
')
+ htmx.trigger(btn, "htmx:load"); // have to manually trigger the load event for non-AJAX dynamic content
+ btn.click();
+ this.server.respond();
+ btn.innerHTML.should.equal("Custom Event Sent!");
+ });
+
+ it('can handle htmx driven events', function () {
+ this.server.respondWith("GET", "/test", "Clicked!");
+ var btn = make('
')
+ htmx.trigger(btn, "htmx:load");
+ btn.classList.contains("afterSettle").should.equal(false);
+ btn.click();
+ this.server.respond();
+ btn.classList.contains("afterSettle").should.equal(true);
+ });
+
+ it('can handle htmx error events', function () {
+ this.server.respondWith("GET", "/test", [404, {}, "Bad request"]);
+ var div = make('
')
+ var btn = make('
')
+ htmx.trigger(btn, "htmx:load");
+ btn.click();
+ this.server.respond();
+ div.innerHTML.startsWith("Response Status Error Code 404 from");
+ });
+
+ it('hyperscript in non-htmx annotated nodes is evaluated', function () {
+ this.server.respondWith("GET", "/test", "
");
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ var newDiv = byId("d1");
+ newDiv.click();
+ newDiv.innerText.should.equal("Clicked...");
+ });
+
+ it('hyperscript removal example works', function (done) {
+ this.server.respondWith("GET", "/test", "
To Remove
");
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ var newDiv = byId("d1");
+ newDiv.innerText.should.equal("To Remove")
+ setTimeout(function(){
+ newDiv = byId("d1");
+ should.equal(newDiv, null);
+ done();
+ }, 100);
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/include-vals.js b/www/static/test/1.9.2/test/ext/include-vals.js
new file mode 100644
index 000000000..fc05a067b
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/include-vals.js
@@ -0,0 +1,23 @@
+describe("include-vals extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('Includes values properly', function () {
+ var params = {};
+ this.server.respondWith("POST", "/test", function (xhr) {
+ params = getParameters(xhr);
+ xhr.respond(200, {}, "clicked");
+ });
+ var btn = make('
')
+ btn.click();
+ this.server.respond();
+ params['foo'].should.equal("bar");
+ });
+
+});
\ No newline at end of file
diff --git a/www/static/test/1.9.2/test/ext/json-enc.js b/www/static/test/1.9.2/test/ext/json-enc.js
new file mode 100644
index 000000000..ed805a17c
--- /dev/null
+++ b/www/static/test/1.9.2/test/ext/json-enc.js
@@ -0,0 +1,136 @@
+//
+describe("json-enc extension", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it('handles basic post properly', function () {
+ var jsonResponseBody = JSON.stringify({});
+ this.server.respondWith("POST", "/test", jsonResponseBody);
+ var div = make("
click me
");
+ div.click();
+ this.server.respond();
+ this.server.lastRequest.response.should.equal("{}");
+ })
+
+ it('handles basic put properly', function () {
+ var jsonResponseBody = JSON.stringify({});
+ this.server.respondWith("PUT", "/test", jsonResponseBody);
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ this.server.lastRequest.response.should.equal("{}");
+ })
+
+ it('handles basic patch properly', function () {
+ var jsonResponseBody = JSON.stringify({});
+ this.server.respondWith("PATCH", "/test", jsonResponseBody);
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ this.server.lastRequest.response.should.equal("{}");
+ })
+
+ it('handles basic delete properly', function () {
+ var jsonResponseBody = JSON.stringify({});
+ this.server.respondWith("DELETE", "/test", jsonResponseBody);
+ var div = make('
click me
');
+ div.click();
+ this.server.respond();
+ this.server.lastRequest.response.should.equal("{}");
+ })
+
+ it('handles post with form parameters', function () {
+
+ this.server.respondWith("POST", "/test", function (xhr) {
+ var values = JSON.parse(xhr.requestBody);
+ values.should.have.keys("username","password");
+ values["username"].should.be.equal("joe");
+ values["password"].should.be.equal("123456");
+ var ans = { "passwordok": values["password"] == "123456"};
+ xhr.respond(200, {}, JSON.stringify(ans));
+ });
+
+ var html = make('