describe("sse extension", function() { function mockEventSource() { var listeners = {}; var wasClosed = false; var url; var mockEventSource = { removeEventListener: function(name, l) { listeners[name] = listeners[name].filter(function(elt, idx, arr) { if (arr[idx] === l) { return false; } return true; }) }, addEventListener: function(message, l) { if (!listeners[message]) { listeners[message] = []; } listeners[message].push(l) }, sendEvent: function(eventName, data) { var eventListeners = listeners[eventName]; if (eventListeners) { eventListeners.forEach(function(listener) { var event = htmx._("makeEvent")(eventName); event.data = data; listener(event); }) } }, close: function() { wasClosed = true; }, wasClosed: function() { return wasClosed; }, connect: function(url) { this.url = url } }; return mockEventSource; } beforeEach(function() { this.server = makeServer(); var eventSource = mockEventSource(); this.eventSource = eventSource; clearWorkArea(); htmx.createEventSource = function(url) { eventSource.connect(url); 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('
' + '
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 updated"); }) it('does not trigger events not on descendents', 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, hx-trigger', function() { this.server.respondWith("GET", "/test", "Clicked!"); var div = make('
' + '
div1
' + '
'); div.click(); this.server.respond(); this.eventSource.wasClosed().should.equal(true) }) it('is closed after removal, hx-swap', function() { this.server.respondWith("GET", "/test", "Clicked!"); var div = make('
' + '
div1
' + '
'); div.click(); this.server.respond(); this.eventSource.wasClosed().should.equal(true) }) it('is closed after removal with no close and activity, hx-trigger', function() { var div = make('
' + '
div1
' + '
'); div.parentElement.removeChild(div); this.eventSource.sendEvent("e1") this.eventSource.wasClosed().should.equal(true) }) // sse and hx-trigger handlers are distinct it('is closed after removal with no close and activity, sse-swap', function() { var div = make('
' + '
div1
' + '
'); 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") }) it('swaps swapped in content', function() { var div = make('
\n' + '
\n' + '
\n' ) this.eventSource.sendEvent("e1", '
') this.eventSource.sendEvent("e2", 'Event 2') byId("d2").innerText.should.equal("Event 2") }) it('works in a child of an hx-ext="sse" element', function() { var div = make('
\n' + '
div1
\n' + '
\n' ) this.eventSource.url = "/event_stream" }) it('only adds sseEventSource to elements with sse-connect', function() { var div = make('
\n' + '
\n' + '
'); (byId('d1')["htmx-internal-data"].sseEventSource == undefined).should.be.true // Even when content is swapped in this.eventSource.sendEvent("e1", '
'); (byId('d2')["htmx-internal-data"].sseEventSource == undefined).should.be.true }) it('initializes connections in swapped content', function() { this.server.respondWith("GET", "/d1", '
div2
'); this.server.respondWith("GET", "/d2", "div2 updated"); var div = make('
'); div.click(); this.server.respond(); this.eventSource.sendEvent("e2"); this.server.respond(); byId("d2").innerHTML.should.equal("div2 updated"); }) it('creates an eventsource on elements with sse-connect', function() { var div = make('
'); (byId("d1")['htmx-internal-data'].sseEventSource == undefined).should.be.false; }) });