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 https://github.com/microsoft/playwright/issues/5894 that was fixed in 2022 with webkit 16.0
This commit is contained in:
MichaelWest22 2025-04-25 07:54:43 +12:00 committed by GitHub
parent 21dc121fce
commit 408850a08e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 91 additions and 63 deletions

View File

@ -1468,7 +1468,7 @@ var htmx = (function() {
oobElement.removeAttribute('data-hx-swap-oob')
const targets = querySelectorAllExt(rootNode, selector, false)
if (targets) {
if (targets.length) {
forEach(
targets,
function(target) {
@ -1687,12 +1687,12 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
function swapOuterHTML(target, fragment, settleInfo) {
if (target instanceof Element && target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
if (target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
return swapInnerHTML(target, fragment, settleInfo)
}
/** @type {Node} */
@ -1718,15 +1718,11 @@ var htmx = (function() {
newElt = newElt.nextSibling
}
cleanUpElement(target)
if (target instanceof Element) {
target.remove()
} else {
target.parentNode.removeChild(target)
}
target.remove()
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1735,7 +1731,7 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1744,7 +1740,7 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1753,7 +1749,7 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1762,7 +1758,7 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
*/
function swapDelete(target) {
cleanUpElement(target)
@ -1773,7 +1769,7 @@ var htmx = (function() {
}
/**
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1793,7 +1789,7 @@ var htmx = (function() {
/**
* @param {HtmxSwapStyle} swapStyle
* @param {Element} elt
* @param {Node} target
* @param {Element} target
* @param {ParentNode} fragment
* @param {HtmxSettleInfo} settleInfo
*/
@ -1889,16 +1885,12 @@ var htmx = (function() {
// preserve focus and selection
const activeElt = document.activeElement
let selectionInfo = {}
try {
selectionInfo = {
elt: activeElt,
// @ts-ignore
start: activeElt ? activeElt.selectionStart : null,
// @ts-ignore
end: activeElt ? activeElt.selectionEnd : null
}
} catch (e) {
// safari issue - see https://github.com/microsoft/playwright/issues/5894
selectionInfo = {
elt: activeElt,
// @ts-ignore
start: activeElt ? activeElt.selectionStart : null,
// @ts-ignore
end: activeElt ? activeElt.selectionEnd : null
}
const settleInfo = makeSettleInfo(target)
@ -2388,14 +2380,10 @@ var htmx = (function() {
/**
* @param {Event} evt
* @param {Node} node
* @param {Element} elt
* @returns {boolean}
*/
function shouldCancel(evt, node) {
const elt = asElement(node)
if (!elt) {
return false
}
function shouldCancel(evt, elt) {
if (evt.type === 'submit' || evt.type === 'click') {
if (elt.tagName === 'FORM') {
return true
@ -2445,7 +2433,7 @@ var htmx = (function() {
}
/**
* @param {Node} elt
* @param {Element} elt
* @param {TriggerHandler} handler
* @param {HtmxNodeInternalData} nodeData
* @param {HtmxTriggerSpecification} triggerSpec
@ -2635,7 +2623,7 @@ var htmx = (function() {
triggerSpecs.forEach(function(triggerSpec) {
addTriggerHandler(elt, triggerSpec, nodeData, function(node, evt) {
const elt = asElement(node)
if (closest(elt, htmx.config.disableSelector)) {
if (eltIsDisabled(elt)) {
cleanUpElement(elt)
return
}
@ -2649,12 +2637,12 @@ var htmx = (function() {
/**
* @callback TriggerHandler
* @param {Node} elt
* @param {Element} elt
* @param {Event} [evt]
*/
/**
* @param {Node} elt
* @param {Element} elt
* @param {HtmxTriggerSpecification} triggerSpec
* @param {HtmxNodeInternalData} nodeData
* @param {TriggerHandler} handler
@ -2823,9 +2811,6 @@ var htmx = (function() {
return
}
const form = getRelatedForm(elt)
if (!form) {
return
}
return getInternalData(form)
}
@ -2902,7 +2887,7 @@ var htmx = (function() {
* @param {Element|HTMLInputElement} elt
*/
function initNode(elt) {
if (closest(elt, htmx.config.disableSelector)) {
if (eltIsDisabled(elt)) {
cleanUpElement(elt)
return
}
@ -2951,7 +2936,7 @@ var htmx = (function() {
*/
function processNode(elt) {
elt = resolveTarget(elt)
if (closest(elt, htmx.config.disableSelector)) {
if (eltIsDisabled(elt)) {
cleanUpElement(elt)
return
}
@ -3402,7 +3387,8 @@ var htmx = (function() {
return true
}
/** @param {string} name
/**
* @param {string} name
* @param {string|Array|FormDataEntryValue} value
* @param {FormData} formData */
function addValueToFormData(name, value, formData) {
@ -3415,7 +3401,8 @@ var htmx = (function() {
}
}
/** @param {string} name
/**
* @param {string} name
* @param {string|Array} value
* @param {FormData} formData */
function removeValueFromFormData(name, value, formData) {
@ -3431,6 +3418,22 @@ var htmx = (function() {
}
}
/**
* @param {Element} elt
* @returns {string|Array}
*/
function getValueFromInput(elt) {
if (elt instanceof HTMLSelectElement && elt.multiple) {
return toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
}
// include file inputs
if (elt instanceof HTMLInputElement && elt.files) {
return toArray(elt.files)
}
// @ts-ignore value will be undefined for non-input elements, which is fine
return elt.value
}
/**
* @param {Element[]} processed
* @param {FormData} formData
@ -3446,16 +3449,7 @@ var htmx = (function() {
}
if (shouldInclude(elt)) {
const name = getRawAttribute(elt, 'name')
// @ts-ignore value will be undefined for non-input elements, which is fine
let value = elt.value
if (elt instanceof HTMLSelectElement && elt.multiple) {
value = toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
}
// include file inputs
if (elt instanceof HTMLInputElement && elt.files) {
value = toArray(elt.files)
}
addValueToFormData(name, value, formData)
addValueToFormData(name, getValueFromInput(elt), formData)
if (validate) {
validateElement(elt, errors)
}
@ -3466,7 +3460,7 @@ var htmx = (function() {
// The input has already been processed and added to the values, but the FormData that will be
// constructed right after on the form, will include it once again. So remove that input's value
// now to avoid duplicates
removeValueFromFormData(input.name, input.value, formData)
removeValueFromFormData(input.name, getValueFromInput(input), formData)
} else {
processed.push(input)
}
@ -3484,7 +3478,6 @@ var htmx = (function() {
}
/**
*
* @param {Element} elt
* @param {HtmxElementValidationError[]} errors
*/
@ -4127,8 +4120,6 @@ var htmx = (function() {
return function() {
return formData[name].apply(formData, arguments)
}
} else {
return target[name]
}
}
const array = formData.getAll(name)

View File

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

View File

@ -628,4 +628,9 @@ describe('Core htmx API test', function() {
onLoadError.should.equal(true)
htmx.off('htmx:onLoadError', handler)
})
it('process api can process non elements fine', function() {
var div = make('<div>textNode</div>')
htmx.process(div.firstChild)
})
})

View File

@ -362,6 +362,33 @@ describe('Core htmx Parameter Handling', function() {
result.innerHTML.should.equal('OK')
})
it('file is not uploaded with blank filename', function() {
this.server.respondWith('POST', '/test', function(xhr) {
should.equal(xhr.requestHeaders['Content-Type'], undefined)
const file = xhr.requestBody.get('file')
should.equal(file, null)
xhr.respond(200, {}, 'OK')
})
const form = make('<form hx-post="/test" hx-target="#result" hx-encoding="multipart/form-data">' +
'<input type="file" name="file">' +
'<button type="submit"></button>' +
'</form>')
const input = form.querySelector('input')
const file = new File(['Test'], '', { type: 'text/plain' })
const dataTransfer = new DataTransfer()
dataTransfer.items.add(file)
input.files = dataTransfer.files
const result = make('<div id="result"></div>')
form.querySelector('button').click()
this.server.respond()
result.innerHTML.should.equal('OK')
})
it('file is correctly uploaded with htmx.ajax', function() {
this.server.respondWith('POST', '/test', function(xhr) {
should.equal(xhr.requestHeaders['Content-Type'], undefined)

View File

@ -27,6 +27,16 @@ describe('security options', function() {
btn.innerHTML.should.equal('Initial')
})
it('can disable a child elt', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var div = make('<div><button id="b1" hx-disable hx-get="/test">Initial</button></div>')
var btn = byId('b1')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Initial')
})
it('can disable a single elt dynamically', function() {
this.server.respondWith('GET', '/test', 'Clicked!')

View File

@ -29,7 +29,7 @@ const config = {
<h2>web-test-runner Test Suite</h2>
<script>${Math.random() < 0.5 ? 'window.onpopstate = function(event) {}' : ''}</script>
<script>${Math.random() < 0.9 ? 'window.onpopstate = function(event) {}' : ''}</script>
<script src="node_modules/chai/chai.js"></script>
<script src="node_modules/chai-dom/chai-dom.js"></script>