5

I have an lwc component which validates some input fields before performing a post request to the server. I'm writing a unit test where I'm testing that the post request is called when all input fields are valid (required validation, max-length validation, min-length validation, etc.).

However, when debugging this unit test I noticed that the element's validity object is undefined causing the post request to not trigger.

This is the method that performs the post request:

postIdea() {

    const allValid = [
      ...this.template.querySelectorAll(
        ".slds-form lightning-input, .slds-form lightning-combobox"
      )
    ].reduce((validSoFar, inputCmp) => {
      inputCmp.reportValidity();
      return validSoFar && inputCmp.checkValidity();
    }, true);

    if (allValid) {
      createIdea({
        heading: this.heading,
        ideaBody: this.ideaBody,
        category: this.selectedCategory,
        zone: this.zone
      }).then(response => {
        if (response.isSuccess) {
          this.resetForm();
          this.postingIdea = !this.postingIdea;
        } else {
          this.hasErrors = true;
        }
      });
    }
  }

variable allValid is always undefined which causes this to misbehave when running the unit test. But it behaves ok when seeing the real thing in the browser.

This is the unit test I wrote:

it("should post idea and close modal", () => {
    const element = createElement("c-post-idea", { is: PostIdea });
    document.body.appendChild(element);
    getCategoriesWireAdapter.emit(mockedCategories);
    createIdea.mockResolvedValue({ isSucess: true });

    return Promise.resolve()
      .then(() => {
        const button = element.shadowRoot.querySelector("lightning-button");
        button.dispatchEvent(new CustomEvent("click"));
      })
      .then(() => {
        const headingTextInput = element.shadowRoot.querySelector(
          "lightning-input"
        );
        const categorySelectInput = element.shadowRoot.querySelector(
          "lightning-combobox"
        );
        const descriptionRichText = element.shadowRoot.querySelector(
          "lightning-input-rich-text"
        );

        headingTextInput.value = "this is a new idea";
        headingTextInput.dispatchEvent(new CustomEvent("change"));

        categorySelectInput.value = "bar";
        categorySelectInput.dispatchEvent(new CustomEvent("change"));

        descriptionRichText.value = "this is the description of my idea";
        descriptionRichText.dispatchEvent(new CustomEvent("change"));

        const postButton = element.shadowRoot.querySelector(
          ".post-idea-button"
        );

        postButton.dispatchEvent(new CustomEvent("click"));
      })
      .then(() => {
        expect(createIdea).toHaveBeenCalled();
      })
      .then(() => {
        const modal = element.shadowRoot.querySelector(".slds-modal");
        expect(modal).toBeNull();
      });
  });

2 Answers 2

2

In the base component stubs Salesforce provides, checkValidity() is provided as an empty function that returns undefined for inputs and comboboxes.

According to the theory of testing that Salesforce puts forward, you want to avoid any dependencies on the internals of the lightning-input and lightning-combobox base components, such as what their methods return. If you want to test what happens in your if (allValid) block, you'll probably need to put that in a separate function, unfortunately.

Here's a modified version of your code:

// Not readily testable, since it relies on the output of an external function, checkValidity();
postIdea() {

    const allValid = [
      ...this.template.querySelectorAll(
        ".slds-form lightning-input, .slds-form lightning-combobox"
      )
    ].reduce((validSoFar, inputCmp) => {
      inputCmp.reportValidity();
      return validSoFar && inputCmp.checkValidity();
    }, true);

    if (allValid) {
        this.doCreateIdea(); // Moved this logic to another function
    }
  }

// You can test this part with a mock, if you want
doCreateIdea() {
    createIdea({
        heading: this.heading,
        ideaBody: this.ideaBody,
        category: this.selectedCategory,
        zone: this.zone
    }).then(response => {
        if (response.isSuccess) {
          this.resetForm();
          this.postingIdea = !this.postingIdea;
        } else {
          this.hasErrors = true;
        }
    });
}

Side note: you can override the default base component stubs, but it may be more effort than it's worth -- and it will apparently apply across all the tests in your project. If anyone knows of a way to extend the functionality of these mocks in a localized way without too much overhead, I would be personally interested in that solution.

4
  • so you are saying I should take that logic into another module, and then mock it out?
    – Daniel
    Commented Dec 3, 2019 at 16:16
  • Just updated my answer to provide sample code (and elaborated on another option you may want to investigate). Commented Dec 3, 2019 at 19:06
  • @MatthewSouther, We tried this approach on our end, But in our case we couldn't able to call from doCreateIdea method from jest directly. Commented Jun 3, 2021 at 8:17
  • I see what you mean. If the added complexity is worth it to you, you might consider using a helper file. See my answer here for some hints on getting started: salesforce.stackexchange.com/questions/264131. This construct allows individual functions to be imported and executed by the test without the use of @api. Commented Jun 3, 2021 at 16:01
4

After analysis was able to resolve this with the hep of this link https://books.google.co.in/books?id=wlu8DwAAQBAJ&pg=PA512&lpg=PA512&dq=how+to+mock+this.template.querySelector(%22lightning-datatable%22).getSelectedRows()%3B&source=bl&ots=k2JonNaQ-h&sig=ACfU3U34Z8Q2V1I2m_l7pj8raxXd0MccWA&hl=en&sa=X&redir_esc=y#v=onepage&q=how%20to%20mock%20this.template.querySelector(%22lightning-datatable%22).getSelectedRows()%3B&f=false

The code can be modified this way to make the validity true for each element

  const headingTextInput = element.shadowRoot.querySelector("lightning-input");
  headingTextInput .checkValidity = jest.fn().mockReturnValue(true);   

        

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .