Should I Open a Github Issue? Tests for "Build a Customer Complaint Form" Don't Dispatch Change Event on Checkbox

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 :waving_hand: :grinning_face_with_smiling_eyes:

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! :victory_hand:


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

yes please create a github issue

Okay, will do. Thanks!

Edit: Here’s the issue: Tests for "Build a Customer Complaint Form" Should Dispatch Events When Setting `checked` Property · Issue #70215 · freeCodeCamp/freeCodeCamp · GitHub