");
+ 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 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 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 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 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);
+ });
+
+
+})
diff --git a/www/test/0.0.8/test/core/events.js b/www/test/0.0.8/test/core/events.js
new file mode 100644
index 00000000..c7db8fa1
--- /dev/null
+++ b/www/test/0.0.8/test/core/events.js
@@ -0,0 +1,122 @@
+describe("Core htmx Events", function() {
+ beforeEach(function () {
+ this.server = makeServer();
+ clearWorkArea();
+ });
+ afterEach(function () {
+ this.server.restore();
+ clearWorkArea();
+ });
+
+ it("load.htmx 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("configRequest.htmx 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("configRequest.htmx 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("configRequest.htmx 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("afterSwap.htmx 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("afterSettle.htmx 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);
+ }
+ });
+
+});
+
diff --git a/www/test/0.0.8/test/core/headers.js b/www/test/0.0.8/test/core/headers.js
new file mode 100644
index 00000000..ab59f992
--- /dev/null
+++ b/www/test/0.0.8/test/core/headers.js
@@ -0,0 +1,118 @@
+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 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();
+ })
+
+});
diff --git a/www/test/0.0.8/test/core/internals.js b/www/test/0.0.8/test/core/internals.js
new file mode 100644
index 00000000..9e6f139e
--- /dev/null
+++ b/www/test/0.0.8/test/core/internals.js
@@ -0,0 +1,23 @@
+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");
+ })
+
+});
\ No newline at end of file
diff --git a/www/test/0.0.8/test/core/parameters.js b/www/test/0.0.8/test/core/parameters.js
new file mode 100644
index 00000000..f6b71a35
--- /dev/null
+++ b/www/test/0.0.8/test/core/parameters.js
@@ -0,0 +1,121 @@
+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);
+ vals['foo'].should.equal('bar');
+ })
+
+ it('Input includes value on get', function () {
+ var input = make('
');
+ var vals = htmx._('getInputValues')(input, "get");
+ vals['foo'].should.equal('bar');
+ })
+
+ it('Input includes form', function () {
+ var form = make('
');
+ var input = byId('i1');
+ var vals = htmx._('getInputValues')(input);
+ 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');
+ 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");
+ 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");
+ should.equal(vals['do'], undefined);
+ })
+
+ it('Basic form works on get', function () {
+ var form = make('
');
+ var vals = htmx._('getInputValues')(form, 'get');
+ 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');
+ 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);
+ 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"));
+ vals['foo'].should.equal('bar');
+ vals['do'].should.deep.equal(['rey1', 'rey2']);
+ })
+
+ it('hx-include works with form', function () {
+ var form = make('
');
+ var div = make('
');
+ var vals = htmx._('getInputValues')(div);
+ 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);
+ 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);
+ 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);
+ 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");
+ });
+
+});
+
diff --git a/www/test/0.0.8/test/core/perf.js b/www/test/0.0.8/test/core/perf.js
new file mode 100644
index 00000000..6fdbbbea
--- /dev/null
+++ b/www/test/0.0.8/test/core/perf.js
@@ -0,0 +1,70 @@
+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("DOM processing should be fast", function(){
+ this.server.respondWith("GET", "/test", "Clicked!");
+
+ // 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 str = stringRepeat("
", 30) + stringRepeat("
\n", 1000) + stringRepeat("
", 30);
+ var start = performance.now();
+ var stuff = make(str);
+ var end = performance.now();
+ var timeInMs = end - start;
+
+ // make sure the DOM actually processed
+ var firstBtn = stuff.querySelector("button");
+ firstBtn.click();
+ this.server.respond();
+ firstBtn.innerHTML.should.equal("Clicked!");
+
+ chai.assert(timeInMs < 100, "Should take less than 100ms on most platforms, took: " + timeInMs + "ms");
+ })
+
+ 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");
+ })
+
+})
\ No newline at end of file
diff --git a/www/test/0.0.8/test/core/regressions.js b/www/test/0.0.8/test/core/regressions.js
new file mode 100644
index 00000000..bc819029
--- /dev/null
+++ b/www/test/0.0.8/test/core/regressions.js
@@ -0,0 +1,68 @@
+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")
+ });
+
+})
diff --git a/www/test/0.0.8/test/core/verbs.js b/www/test/0.0.8/test/core/verbs.js
new file mode 100644
index 00000000..1d0711c2
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/ajax-header.js b/www/test/0.0.8/test/ext/ajax-header.js
new file mode 100644
index 00000000..0888ef3b
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/bad-extension.js b/www/test/0.0.8/test/ext/bad-extension.js
new file mode 100644
index 00000000..fcf6a438
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/class-tools.js b/www/test/0.0.8/test/ext/class-tools.js
new file mode 100644
index 00000000..0c5004b5
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/client-side-templates.js b/www/test/0.0.8/test/ext/client-side-templates.js
new file mode 100644
index 00000000..29206d21
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/debug.js b/www/test/0.0.8/test/ext/debug.js
new file mode 100644
index 00000000..4eeca295
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/extension-swap.js b/www/test/0.0.8/test/ext/extension-swap.js
new file mode 100644
index 00000000..0186b525
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/hyperscript.js b/www/test/0.0.8/test/ext/hyperscript.js
new file mode 100644
index 00000000..07b93bfd
--- /dev/null
+++ b/www/test/0.0.8/test/ext/hyperscript.js
@@ -0,0 +1,37 @@
+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('
')
+ 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('
')
+ 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('
')
+ btn.click();
+ this.server.respond();
+ div.innerHTML.should.equal("Response Status Error Code 404 from /test");
+ });
+
+});
\ No newline at end of file
diff --git a/www/test/0.0.8/test/ext/include-vals.js b/www/test/0.0.8/test/ext/include-vals.js
new file mode 100644
index 00000000..fc05a067
--- /dev/null
+++ b/www/test/0.0.8/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/test/0.0.8/test/ext/json-enc.js b/www/test/0.0.8/test/ext/json-enc.js
new file mode 100644
index 00000000..ed805a17
--- /dev/null
+++ b/www/test/0.0.8/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('