describe('bootstrap unit tests', function() { it("Test that fragment parsing works as expected", function() { var result = htmx.__makeFragment("foo"); var temp = document.createElement('div'); temp.appendChild(result.fragment.cloneNode(true)); assert.equal("foo", temp.textContent.trim()) // Test that template partials are preserved in fragment result = htmx.__makeFragment(``); temp = document.createElement('div'); temp.appendChild(result.fragment.cloneNode(true)); assert.include(temp.innerHTML, 'template') }) it("__makeFragment handles multiple partials", function() { var result = htmx.__makeFragment(`
Main content
`); var temp = document.createElement('div'); temp.appendChild(result.fragment.cloneNode(true)); assert.include(temp.innerHTML, "Main content") assert.include(temp.innerHTML, "template") }) it("__makeFragment extracts title from HTML", function() { var result = htmx.__makeFragment(` Test TitleContent `); assert.equal("Test Title", result.title) }) it("__makeFragment handles body tag response", function() { var result = htmx.__makeFragment(`
Content
`); var temp = document.createElement('div'); temp.appendChild(result.fragment.cloneNode(true)); assert.include(temp.innerHTML, "Content") }) it("__makeFragment handles fragment response", function() { var result = htmx.__makeFragment(`
Fragment
More`); var temp = document.createElement('div'); temp.appendChild(result.fragment.cloneNode(true)); assert.include(temp.innerHTML, "Fragment") assert.include(temp.innerHTML, "More") }) it("__attributeValue returns direct attribute value", function() { const div = createDisconnectedHTML('
'); const result = htmx.__attributeValue(div, 'hx-get', 'default'); assert.equal(result, '/test'); }) it("__attributeValue returns inherited attribute from element", function() { const div = createDisconnectedHTML('
'); const result = htmx.__attributeValue(div, 'hx-get', 'default'); assert.equal(result, '/inherited'); }) it("__attributeValue prefers direct attribute over inherited", function() { const div = createDisconnectedHTML('
'); const result = htmx.__attributeValue(div, 'hx-get', 'default'); assert.equal(result, '/direct'); }) it("__attributeValue finds inherited attribute on parent", function() { const parent = createDisconnectedHTML('
'); const child = parent.firstElementChild; const result = htmx.__attributeValue(child, 'hx-get', 'default'); assert.equal(result, '/parent'); }) it("__attributeValue returns default when attribute not found", function() { const div = createDisconnectedHTML('
'); const result = htmx.__attributeValue(div, 'hx-get', 'default'); assert.equal(result, 'default'); }) it("__parseTriggerSpecs parses simple event", function() { const result = htmx.__parseTriggerSpecs('click'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); }) it("__parseTriggerSpecs parses event with option", function() { const result = htmx.__parseTriggerSpecs('click delay:500'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); assert.equal(result[0].delay, '500'); }) it("__parseTriggerSpecs parses event with multiple options", function() { const result = htmx.__parseTriggerSpecs('click delay:500 throttle:100'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); assert.equal(result[0].delay, '500'); assert.equal(result[0].throttle, '100'); }) it("__parseTriggerSpecs parses event with boolean opts", function() { const result = htmx.__parseTriggerSpecs('click once changed'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); assert.equal(result[0].once, true); assert.equal(result[0].changed, true); }) it("__parseTriggerSpecs parses event with options and boolean opts", function() { const result = htmx.__parseTriggerSpecs('click delay:1s once changed'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); assert.equal(result[0].delay, '1s'); assert.equal(result[0].once, true); assert.equal(result[0].changed, true); }) it("__parseTriggerSpecs parses multiple events", function() { const result = htmx.__parseTriggerSpecs('click, submit'); assert.equal(result.length, 2); assert.equal(result[0].name, 'click'); assert.equal(result[1].name, 'submit'); }) it("__parseTriggerSpecs parses multiple events with options", function() { const result = htmx.__parseTriggerSpecs('click delay:500, keyup changed'); assert.equal(result.length, 2); assert.equal(result[0].name, 'click'); assert.equal(result[0].delay, '500'); assert.equal(result[1].name, 'keyup'); assert.equal(result[1].changed, true); }) it("__parseTriggerSpecs parses event filter", function() { const result = htmx.__parseTriggerSpecs('click[ctrlKey]'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click[ctrlKey]'); }) it("__parseTriggerSpecs parses event filter with spaces", function() { const result = htmx.__parseTriggerSpecs('click[target.value == "test"]'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click[target.value == "test"]'); }) it("__parseTriggerSpecs parses event with from option", function() { const result = htmx.__parseTriggerSpecs('click from:body'); assert.equal(result.length, 1); assert.equal(result[0].name, 'click'); assert.equal(result[0].from, 'body'); }) it("__parseTriggerSpecs throws on unterminated filter", function() { assert.throws(() => { htmx.__parseTriggerSpecs('click[ctrlKey'); }, /unterminated/); }) it("__parseTriggerSpecs handles complex real-world spec", function() { const result = htmx.__parseTriggerSpecs('keyup[target.value.length > 3] changed delay:500ms from:input'); assert.equal(result.length, 1); assert.equal(result[0].name, 'keyup[target.value.length > 3]'); assert.equal(result[0].changed, true); assert.equal(result[0].delay, '500ms'); assert.equal(result[0].from, 'input'); }) it("__parseTriggerSpecs handles complex real-world spec w string and preserves spaces in string", function() { const result = htmx.__parseTriggerSpecs('keyup[target.value == "hello world"] changed delay:500ms from:input'); assert.equal(result.length, 1); assert.equal(result[0].name, 'keyup[target.value == "hello world"]'); assert.equal(result[0].changed, true); assert.equal(result[0].delay, '500ms'); assert.equal(result[0].from, 'input'); }) it("public API surface remains stable", function() { // This test ensures the public API doesn't accidentally change const expectedPublicMethods = [ 'ajax', 'find', 'findAll', 'forEvent', 'on', 'onLoad', 'parseInterval', 'process', 'swap', 'takeClass', 'timeout', 'registerExtension', 'trigger', ].sort(); const expectedPublicProperties = [ 'config' ].sort(); // Get own properties (like config, eventSource) const ownProperties = Object.keys(htmx).filter(name => !name.startsWith("_") && typeof htmx[name] !== 'function').sort(); // Get methods from the prototype const proto = Object.getPrototypeOf(htmx); const protoMethods = Object.getOwnPropertyNames(proto) .filter(name => !name.startsWith("_") && name !== 'constructor' && typeof htmx[name] === 'function') .sort(); // Check methods assert.deepEqual(protoMethods, expectedPublicMethods, 'Public methods have changed. Expected: ' + JSON.stringify(expectedPublicMethods) + ', Got: ' + JSON.stringify(protoMethods)); // Check properties assert.deepEqual(ownProperties, expectedPublicProperties, 'Public properties have changed. Expected: ' + JSON.stringify(expectedPublicProperties) + ', Got: ' + JSON.stringify(ownProperties)); }) })