TL;DR: The tests for Build a Customer Complaint Form don’t fire the change event on a checkbox when programmatically changing its checked attribute, and it seems to me like they should, since it prevents them from acknowledging a seemingly valid way of fulfilling the user stories. Should I open a GitHub Issue about this, or is this intentional?
Note: This is in freeCodeCamp Support and not in Curriculum Help because I’m not looking for help with my code or wondering what I did wrong; rather, I’m wondering whether the discrepancy between the way I fulfilled the user stories and what the tests accounted for is something worth opening a GitHub issue about.
Hey everyone
![]()
To keep a *very* long story somewhat short, my solution for the Build a Customer Complaint Form lab in the JS course was failing several tests (19, 21, 28, 30, and 33) despite demonstrably fulfilling the written requirements thereof, and I eventually figured out that this is because my code depended on the change event being fired on the #other-complaint checkbox (and something similar for the #other-solution radio buttons group, but I’m trying to keep this brief).
As far as I know (and correct me if I’m wrong), the change event is always fired on a checkbox when it is selected normally by a user (through the mouse or keyboard or whatever). However, the tests for the lab don’t dispatch the event when they programmatically change the checked value of the aforementioned checkbox.
Test 21, for example:
When #other-complaint is checked and the value of #complaint-description is changed to an invalid value, you should set its border color to red.
document.getElementById("other-complaint").checked = true; const field = document.getElementById("complaint-description"); field.value = "Not enough chars" field.dispatchEvent(new Event("input", { bubbles: true })); field.dispatchEvent(new Event("change", { bubbles: true })); assert.equal(field.style.borderColor, "red");
It seems to me that a solution depending on the change event is a valid one, since said event is always fired in a normal user’s interaction with a checkbox. As such, my question is:
Should I open a Github Issue suggesting that the tests be modified to dispatch a change event on the checkbox when its checked value is programmatically changed, or are the tests intentionally designed this way? If they are, why?
Thanks! ![]()
IDK how relevant my code for the lab is, but here it is, just in case:
<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Customer Complaint Form</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Complaint Form</h1>
<form id="form">
<fieldset id="personal-info">
<div>
<label for="full-name">Full Name:</label>
<input
type="text"
id="full-name"
name="full-name"
placeholder="John Doe"
required
/>
</div>
<div>
<label for="email">Email Address:</label>
<input
type="email"
id="email"
name="email"
placeholder="example@domain.com"
required
/>
</div>
</fieldset>
<hr>
<fieldset id="product-info">
<div>
<label for="order-no">Order No:</label>
<input
type="text"
id="order-no"
name="order-no"
placeholder="2024######"
pattern="2024\d{6}"
/>
</div>
<div>
<label for="product-code">Product Code:</label>
<input
type="text"
id="product-code"
name="product-code"
placeholder="XX##-X###-XX#"
pattern="[a-zA-Z]{2}\d{2}-[a-zA-Z]\d{3}-[a-zA-Z]{2}\d"
/>
</div>
<div>
<label for="quantity">Quantity:</label>
<input
type="number"
id="quantity"
name="quantity"
min="1"
>
</div>
</fieldset>
<fieldset id="complaints-group">
<legend>Complaint Reason:</legend>
<div>
<input
type="checkbox"
id="damaged-product"
name="complaint"
value="damaged-product"
/>
<label for="damaged-product">Damaged Product</label>
</div>
<div>
<input
type="checkbox"
id="nonconforming-product"
name="complaint"
value="nonconforming-product"
/>
<label for="nonconforming-product">Nonconforming Product</label>
</div>
<div>
<input
type="checkbox"
id="delayed-dispatch"
name="complaint"
value="delayed-dispatch"
/>
<label for="delayed-dispatch">Delayed Dispatch</label>
</div>
<div>
<input
type="checkbox"
id="other-complaint"
name="complaint"
value="other"
/>
<label for="other-complaint">Other</label>
</div>
</fieldset>
<fieldset id="complaint-description-container">
<legend>Description of Complaint Reason</legend>
<textarea
placeholder="Describe the reason of your complaint in at least 20 characters"
name="complaint-textarea"
id="complaint-description"
></textarea>
</fieldset>
<fieldset id="solutions-group">
<legend>Desired Solution</legend>
<input
type="radio"
name="solutions"
id="refund"
value="refund"
/>
<label for="refund">Refund</label>
<input
type="radio"
name="solutions"
id="exchange"
value="exchange"
/>
<label for="exchange">Exchange</label>
<input
type="radio"
name="solutions"
id="other-solution"
value="other"
/>
<label for="other-solution">Other</label>
</fieldset>
<fieldset id="solution-description-container">
<legend>Description of Desired Solution</legend>
<textarea placeholder="Describe the desired solution to your issue in at least 20 characters"
name="solution-textarea" id="solution-description"></textarea>
</fieldset>
<div id="btn-container">
<button type="submit" id="submit-btn">Submit</button>
<span id="message-box" aria-live="polite"></span>
</div>
</form>
<script src="script.js"></script>
</body>
</html>
/* file: script.js */
// #region Element Variables
const form = document.getElementById("form");
const fullName = document.getElementById("full-name");
const email = document.getElementById("email");
const orderNo = document.getElementById("order-no");
const productCode = document.getElementById("product-code");
const quantity = document.getElementById("quantity");
const complaintsGroup = document.getElementById("complaints-group");
const complaintCheckboxesArray = Array.from(complaintsGroup.querySelectorAll("input[type='checkbox'][name='complaint']"));
const otherComplaintCheckbox = document.getElementById("other-complaint");
const complaintDescription = document.getElementById("complaint-description");
const solutionsGroup = document.getElementById("solutions-group");
const solutionRadiosArray = Array.from(solutionsGroup.querySelectorAll("input[type='radio'][name='solutions']"));
const otherSolutionRadio = document.getElementById("other-solution");
const solutionDescription = document.getElementById("solution-description");
// #endregion Element Variables
// #region Main Validation Functions
function validateForm() {
const validationObject = {
"full-name": fullName.validity.valid,
"email": email.validity.valid,
"order-no": orderNo.validity.valid,
"product-code": productCode.validity.valid,
"quantity": quantity.validity.valid,
"complaints-group": validateComplaintsGroup(),
"complaint-description": complaintDescription.validity.valid,
"solutions-group": validateSolutionsGroup(),
"solution-description": solutionDescription.validity.valid,
}
return validationObject;
}
function isValid(validationObject) {
for (const property in validationObject) {
if (!validationObject[property]) {
return false;
}
}
return true;
}
form.addEventListener("submit", (e) => {
e.preventDefault();
isValid(validateForm());
})
// #endregion Main Validation Functions
// #region General
function colorizeElement(element) {
element.style.borderColor = element.validity.valid ? "green" : "red";
}
const colorizationArray = [fullName, email, orderNo, productCode, quantity];
colorizationArray.forEach((element) => {
element.addEventListener("change", () => {
colorizeElement(element);
})
})
// #endregion General
// #region Complaints and Solutions
// #region Complaints
// #region Complaints Group
function validateComplaintsGroup() {
return complaintCheckboxesArray.some((checkbox) => checkbox.checked);
}
function colorizeComplaintsGroup() {
complaintsGroup.style.borderColor = validateComplaintsGroup() ? "green" : "red";
}
complaintCheckboxesArray.forEach((checkbox) => {
checkbox.addEventListener("change", colorizeComplaintsGroup);
})
// #endregion Complaints Group
// #region Complaint Description
function otherComplaintChange() {
if (otherComplaintCheckbox.checked) {
complaintDescription.setAttribute("required", "required");
complaintDescription.setAttribute("minlength", "20");
} else {
complaintDescription.removeAttribute("required");
complaintDescription.removeAttribute("minlength");
}
}
otherComplaintCheckbox.addEventListener("change", () => {
otherComplaintChange();
colorizeElement(complaintDescription);
});
function onComplaintDescriptionEvent() {
colorizeElement(complaintDescription);
}
complaintDescription.addEventListener("change", onComplaintDescriptionEvent);
complaintDescription.addEventListener("input", onComplaintDescriptionEvent);
// #endregion Complaint Description
// #endregion Complaints
// #region Solutions
// #region Solutions Group
function validateSolutionsGroup() {
return solutionRadiosArray.some((radio) => radio.checked);
}
function colorizeSolutionsGroup() {
solutionsGroup.style.borderColor = validateSolutionsGroup() ? "green" : "red";
}
solutionRadiosArray.forEach((radio) => {
radio.addEventListener("change", colorizeSolutionsGroup);
})
// #endregion Solutions Group
// #region Solution Description
function otherRadioChange() {
if (otherSolutionRadio.checked) {
solutionDescription.setAttribute("required", "required");
solutionDescription.setAttribute("minlength", "20");
} else {
solutionDescription.removeAttribute("required");
solutionDescription.removeAttribute("minlength");
}
}
solutionRadiosArray.forEach((element) => {
element.addEventListener("change", () => {
otherRadioChange();
colorizeElement(solutionDescription);
});
});
function onSolutionDescriptionEvent() {
colorizeElement(solutionDescription);
}
solutionDescription.addEventListener("change", onSolutionDescriptionEvent)
solutionDescription.addEventListener("input", onSolutionDescriptionEvent)
// #endregion Solution Description
// #endregion Solutions
// #endregion Complaints and Solutions