Skip to content

Commit 408850a

Browse files
Additional Code Coverage (#3282)
* Improve loc coverage by removing dead paths caused by bad type checks and add some tests for other paths * removed exception for microsoft/playwright#5894 that was fixed in 2022 with webkit 16.0
1 parent 21dc121 commit 408850a

6 files changed

Lines changed: 91 additions & 63 deletions

File tree

src/htmx.js

Lines changed: 47 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,7 @@ var htmx = (function() {
14681468
oobElement.removeAttribute('data-hx-swap-oob')
14691469

14701470
const targets = querySelectorAllExt(rootNode, selector, false)
1471-
if (targets) {
1471+
if (targets.length) {
14721472
forEach(
14731473
targets,
14741474
function(target) {
@@ -1687,12 +1687,12 @@ var htmx = (function() {
16871687
}
16881688

16891689
/**
1690-
* @param {Node} target
1690+
* @param {Element} target
16911691
* @param {ParentNode} fragment
16921692
* @param {HtmxSettleInfo} settleInfo
16931693
*/
16941694
function swapOuterHTML(target, fragment, settleInfo) {
1695-
if (target instanceof Element && target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
1695+
if (target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
16961696
return swapInnerHTML(target, fragment, settleInfo)
16971697
}
16981698
/** @type {Node} */
@@ -1718,15 +1718,11 @@ var htmx = (function() {
17181718
newElt = newElt.nextSibling
17191719
}
17201720
cleanUpElement(target)
1721-
if (target instanceof Element) {
1722-
target.remove()
1723-
} else {
1724-
target.parentNode.removeChild(target)
1725-
}
1721+
target.remove()
17261722
}
17271723

17281724
/**
1729-
* @param {Node} target
1725+
* @param {Element} target
17301726
* @param {ParentNode} fragment
17311727
* @param {HtmxSettleInfo} settleInfo
17321728
*/
@@ -1735,7 +1731,7 @@ var htmx = (function() {
17351731
}
17361732

17371733
/**
1738-
* @param {Node} target
1734+
* @param {Element} target
17391735
* @param {ParentNode} fragment
17401736
* @param {HtmxSettleInfo} settleInfo
17411737
*/
@@ -1744,7 +1740,7 @@ var htmx = (function() {
17441740
}
17451741

17461742
/**
1747-
* @param {Node} target
1743+
* @param {Element} target
17481744
* @param {ParentNode} fragment
17491745
* @param {HtmxSettleInfo} settleInfo
17501746
*/
@@ -1753,7 +1749,7 @@ var htmx = (function() {
17531749
}
17541750

17551751
/**
1756-
* @param {Node} target
1752+
* @param {Element} target
17571753
* @param {ParentNode} fragment
17581754
* @param {HtmxSettleInfo} settleInfo
17591755
*/
@@ -1762,7 +1758,7 @@ var htmx = (function() {
17621758
}
17631759

17641760
/**
1765-
* @param {Node} target
1761+
* @param {Element} target
17661762
*/
17671763
function swapDelete(target) {
17681764
cleanUpElement(target)
@@ -1773,7 +1769,7 @@ var htmx = (function() {
17731769
}
17741770

17751771
/**
1776-
* @param {Node} target
1772+
* @param {Element} target
17771773
* @param {ParentNode} fragment
17781774
* @param {HtmxSettleInfo} settleInfo
17791775
*/
@@ -1793,7 +1789,7 @@ var htmx = (function() {
17931789
/**
17941790
* @param {HtmxSwapStyle} swapStyle
17951791
* @param {Element} elt
1796-
* @param {Node} target
1792+
* @param {Element} target
17971793
* @param {ParentNode} fragment
17981794
* @param {HtmxSettleInfo} settleInfo
17991795
*/
@@ -1889,16 +1885,12 @@ var htmx = (function() {
18891885
// preserve focus and selection
18901886
const activeElt = document.activeElement
18911887
let selectionInfo = {}
1892-
try {
1893-
selectionInfo = {
1894-
elt: activeElt,
1895-
// @ts-ignore
1896-
start: activeElt ? activeElt.selectionStart : null,
1897-
// @ts-ignore
1898-
end: activeElt ? activeElt.selectionEnd : null
1899-
}
1900-
} catch (e) {
1901-
// safari issue - see https://github.com/microsoft/playwright/issues/5894
1888+
selectionInfo = {
1889+
elt: activeElt,
1890+
// @ts-ignore
1891+
start: activeElt ? activeElt.selectionStart : null,
1892+
// @ts-ignore
1893+
end: activeElt ? activeElt.selectionEnd : null
19021894
}
19031895
const settleInfo = makeSettleInfo(target)
19041896

@@ -2388,14 +2380,10 @@ var htmx = (function() {
23882380

23892381
/**
23902382
* @param {Event} evt
2391-
* @param {Node} node
2383+
* @param {Element} elt
23922384
* @returns {boolean}
23932385
*/
2394-
function shouldCancel(evt, node) {
2395-
const elt = asElement(node)
2396-
if (!elt) {
2397-
return false
2398-
}
2386+
function shouldCancel(evt, elt) {
23992387
if (evt.type === 'submit' || evt.type === 'click') {
24002388
if (elt.tagName === 'FORM') {
24012389
return true
@@ -2445,7 +2433,7 @@ var htmx = (function() {
24452433
}
24462434

24472435
/**
2448-
* @param {Node} elt
2436+
* @param {Element} elt
24492437
* @param {TriggerHandler} handler
24502438
* @param {HtmxNodeInternalData} nodeData
24512439
* @param {HtmxTriggerSpecification} triggerSpec
@@ -2635,7 +2623,7 @@ var htmx = (function() {
26352623
triggerSpecs.forEach(function(triggerSpec) {
26362624
addTriggerHandler(elt, triggerSpec, nodeData, function(node, evt) {
26372625
const elt = asElement(node)
2638-
if (closest(elt, htmx.config.disableSelector)) {
2626+
if (eltIsDisabled(elt)) {
26392627
cleanUpElement(elt)
26402628
return
26412629
}
@@ -2649,12 +2637,12 @@ var htmx = (function() {
26492637

26502638
/**
26512639
* @callback TriggerHandler
2652-
* @param {Node} elt
2640+
* @param {Element} elt
26532641
* @param {Event} [evt]
26542642
*/
26552643

26562644
/**
2657-
* @param {Node} elt
2645+
* @param {Element} elt
26582646
* @param {HtmxTriggerSpecification} triggerSpec
26592647
* @param {HtmxNodeInternalData} nodeData
26602648
* @param {TriggerHandler} handler
@@ -2823,9 +2811,6 @@ var htmx = (function() {
28232811
return
28242812
}
28252813
const form = getRelatedForm(elt)
2826-
if (!form) {
2827-
return
2828-
}
28292814
return getInternalData(form)
28302815
}
28312816

@@ -2902,7 +2887,7 @@ var htmx = (function() {
29022887
* @param {Element|HTMLInputElement} elt
29032888
*/
29042889
function initNode(elt) {
2905-
if (closest(elt, htmx.config.disableSelector)) {
2890+
if (eltIsDisabled(elt)) {
29062891
cleanUpElement(elt)
29072892
return
29082893
}
@@ -2951,7 +2936,7 @@ var htmx = (function() {
29512936
*/
29522937
function processNode(elt) {
29532938
elt = resolveTarget(elt)
2954-
if (closest(elt, htmx.config.disableSelector)) {
2939+
if (eltIsDisabled(elt)) {
29552940
cleanUpElement(elt)
29562941
return
29572942
}
@@ -3402,7 +3387,8 @@ var htmx = (function() {
34023387
return true
34033388
}
34043389

3405-
/** @param {string} name
3390+
/**
3391+
* @param {string} name
34063392
* @param {string|Array|FormDataEntryValue} value
34073393
* @param {FormData} formData */
34083394
function addValueToFormData(name, value, formData) {
@@ -3415,7 +3401,8 @@ var htmx = (function() {
34153401
}
34163402
}
34173403

3418-
/** @param {string} name
3404+
/**
3405+
* @param {string} name
34193406
* @param {string|Array} value
34203407
* @param {FormData} formData */
34213408
function removeValueFromFormData(name, value, formData) {
@@ -3431,6 +3418,22 @@ var htmx = (function() {
34313418
}
34323419
}
34333420

3421+
/**
3422+
* @param {Element} elt
3423+
* @returns {string|Array}
3424+
*/
3425+
function getValueFromInput(elt) {
3426+
if (elt instanceof HTMLSelectElement && elt.multiple) {
3427+
return toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
3428+
}
3429+
// include file inputs
3430+
if (elt instanceof HTMLInputElement && elt.files) {
3431+
return toArray(elt.files)
3432+
}
3433+
// @ts-ignore value will be undefined for non-input elements, which is fine
3434+
return elt.value
3435+
}
3436+
34343437
/**
34353438
* @param {Element[]} processed
34363439
* @param {FormData} formData
@@ -3446,16 +3449,7 @@ var htmx = (function() {
34463449
}
34473450
if (shouldInclude(elt)) {
34483451
const name = getRawAttribute(elt, 'name')
3449-
// @ts-ignore value will be undefined for non-input elements, which is fine
3450-
let value = elt.value
3451-
if (elt instanceof HTMLSelectElement && elt.multiple) {
3452-
value = toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
3453-
}
3454-
// include file inputs
3455-
if (elt instanceof HTMLInputElement && elt.files) {
3456-
value = toArray(elt.files)
3457-
}
3458-
addValueToFormData(name, value, formData)
3452+
addValueToFormData(name, getValueFromInput(elt), formData)
34593453
if (validate) {
34603454
validateElement(elt, errors)
34613455
}
@@ -3466,7 +3460,7 @@ var htmx = (function() {
34663460
// The input has already been processed and added to the values, but the FormData that will be
34673461
// constructed right after on the form, will include it once again. So remove that input's value
34683462
// now to avoid duplicates
3469-
removeValueFromFormData(input.name, input.value, formData)
3463+
removeValueFromFormData(input.name, getValueFromInput(input), formData)
34703464
} else {
34713465
processed.push(input)
34723466
}
@@ -3484,7 +3478,6 @@ var htmx = (function() {
34843478
}
34853479

34863480
/**
3487-
*
34883481
* @param {Element} elt
34893482
* @param {HtmxElementValidationError[]} errors
34903483
*/
@@ -4127,8 +4120,6 @@ var htmx = (function() {
41274120
return function() {
41284121
return formData[name].apply(formData, arguments)
41294122
}
4130-
} else {
4131-
return target[name]
41324123
}
41334124
}
41344125
const array = formData.getAll(name)

test/attributes/hx-include.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,6 @@ describe('hx-include attribute', function() {
205205
})
206206

207207
it('properly handles multiple select input referred to externally and then via a form then it will only be included once', function() {
208-
// this test highlights a edge case that is not currently handled perfectly
209-
// when it runs removeValueFromFormData to remove an input that will be
210-
// included on a form it only removes the input value and not multiple values in array
211208
var values
212209
this.server.respondWith('Post', '/include', function(xhr) {
213210
values = getParameters(xhr)
@@ -234,9 +231,7 @@ describe('hx-include attribute', function() {
234231
byId('m3').selected = true
235232
div.click()
236233
this.server.respond()
237-
values.should.deep.equal({ multiSelect: ['m3', 'm1', 'm3'] })
238-
// the correct response should be:
239-
// values.should.deep.equal({ multiSelect: ['m1', 'm3'] })
234+
values.should.deep.equal({ multiSelect: ['m1', 'm3'] })
240235
})
241236

242237
it('Two inputs can be referred to externally', function() {

test/core/api.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,4 +628,9 @@ describe('Core htmx API test', function() {
628628
onLoadError.should.equal(true)
629629
htmx.off('htmx:onLoadError', handler)
630630
})
631+
632+
it('process api can process non elements fine', function() {
633+
var div = make('<div>textNode</div>')
634+
htmx.process(div.firstChild)
635+
})
631636
})

test/core/parameters.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,33 @@ describe('Core htmx Parameter Handling', function() {
362362
result.innerHTML.should.equal('OK')
363363
})
364364

365+
it('file is not uploaded with blank filename', function() {
366+
this.server.respondWith('POST', '/test', function(xhr) {
367+
should.equal(xhr.requestHeaders['Content-Type'], undefined)
368+
369+
const file = xhr.requestBody.get('file')
370+
should.equal(file, null)
371+
372+
xhr.respond(200, {}, 'OK')
373+
})
374+
375+
const form = make('<form hx-post="/test" hx-target="#result" hx-encoding="multipart/form-data">' +
376+
'<input type="file" name="file">' +
377+
'<button type="submit"></button>' +
378+
'</form>')
379+
const input = form.querySelector('input')
380+
const file = new File(['Test'], '', { type: 'text/plain' })
381+
const dataTransfer = new DataTransfer()
382+
dataTransfer.items.add(file)
383+
input.files = dataTransfer.files
384+
385+
const result = make('<div id="result"></div>')
386+
387+
form.querySelector('button').click()
388+
this.server.respond()
389+
result.innerHTML.should.equal('OK')
390+
})
391+
365392
it('file is correctly uploaded with htmx.ajax', function() {
366393
this.server.respondWith('POST', '/test', function(xhr) {
367394
should.equal(xhr.requestHeaders['Content-Type'], undefined)

test/core/security.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ describe('security options', function() {
2727
btn.innerHTML.should.equal('Initial')
2828
})
2929

30+
it('can disable a child elt', function() {
31+
this.server.respondWith('GET', '/test', 'Clicked!')
32+
33+
var div = make('<div><button id="b1" hx-disable hx-get="/test">Initial</button></div>')
34+
var btn = byId('b1')
35+
btn.click()
36+
this.server.respond()
37+
btn.innerHTML.should.equal('Initial')
38+
})
39+
3040
it('can disable a single elt dynamically', function() {
3141
this.server.respondWith('GET', '/test', 'Clicked!')
3242

web-test-runner.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const config = {
2929
3030
<h2>web-test-runner Test Suite</h2>
3131
32-
<script>${Math.random() < 0.5 ? 'window.onpopstate = function(event) {}' : ''}</script>
32+
<script>${Math.random() < 0.9 ? 'window.onpopstate = function(event) {}' : ''}</script>
3333
3434
<script src="node_modules/chai/chai.js"></script>
3535
<script src="node_modules/chai-dom/chai-dom.js"></script>

0 commit comments

Comments
 (0)