JEST Test LWC – render on conditional

lightning-web-componentslwc-jestunit-test

How to write a jest test on conditional rendering?
so I have a page in that page few input and combobox and when the combobox selected to a particular type which is accepted then I display the input field called amount.
my question how would you write a jest test to a conditional rendering, please see for detail below.

<lightning-combobox
   name="pType"  
   value={pType}
   options={options}
   onchange={handleChange}>
</lightning-combobox>

<lightning-input ....</lightning-input>
<lightning-input ....</lightning-input>
<lightning-input ....</lightning-input>

<div if:true={isSelected} >
         <lightning-input type="number" onchange={handleChange} label="amount" name="amount"></lightning-input>
</div> 

.JS:

isSelected = false; //hide amount field by default

handleChange(event) { 
 const {name} = event.target 
 const {value} = event.detail;

 ......
 if (name === 'pType') {
    if (value) {
       this.isSelected = value.toLowerCase() === 'accepted'
    } 
 }  
 ......
}

JEST test class:

import { createElement } from 'lwc';
import MyConditionalRendering from 'c/myConditionalRendering';

describe('c-my-conditional-rendering suite', ()=>{

    beforeEach(() => {
        const element = createElement('c-my-conditional-rendering', {
            is: MyConditionalRendering
        })
        document.body.appendChild(element)
    })
     
    it("show amount input field when combobox is accepted", ()=>{
        const element = document.querySelector('c-my-conditional-rendering')
        const inputElement = element.shadowRoot.querySelector('lightning-input');

        //lets select the type from the combobox:
        const comboBox = element.shadowRoot.querySelector("lightning-combobox"); 
        comboBox.dispatchEvent(new CustomEvent("change", {detail: {value: 'accepted'}}));

        //not sure how should I grab the input amount text field from here.
         
    }) 
    
}) 

Best Answer

On inital load, the input field won't display as the combobox doesn't have any value. As such, you need to do the following to replicate the scenario you care about:

  1. Set the value of the combobox to what you expect
  2. Fire the event that a change has occurred
  3. Wait for DOM updates
  4. Assert the amount input is now visible

I'll also add, with many different lightning-input elements in one LWC (and maybe always in general), you could consider leveraging the data attribute to have an easy way to query exactly what you want. As an example, below, you could add an attribute called data-test-id with some sort of descriptive name that makes sense to utilize in your jest test

<lightning-combobox
    name="pType"  
    value={pType}
    options={options}
    onchange={handleChange}>
    data-testid="combobox_pType_field"
>
</lightning-combobox>


<div if:true={isSelected} >
     <lightning-input 
         type="number" 
         onchange={handleChange} 
         label="amount" 
         name="amount"
         data-testid="amount_input_field" 
     >
     </lightning-input>
</div> 

In your jest test, you can use that data-attribute to query your specific input(s). As I said above, if you query for it on the initial load - your conditional will mean that the input for amount is not shown (as expected). You can assert that and then, once you set the right value and dispatch the event, wait for the DOM update (async, use a promise) to see that the input element now exists

const hiddenAmountInputElement = element.shadowRoot.querySelector('[data-test-id="amount_input_field"]');
expect(hiddenAmountInputElement).toBeNull();

//lets select the type from the combobox:
const comboBox = element.shadowRoot.querySelector('[data-test-id="comboBox_pType_field"]'); 
comboBox.dispatchEvent(new CustomEvent("change", {detail: {value: 'accepted'}}));

//Return a promise to wait for any asynchronous DOM updates
return Promise.resolve()
    .then(() => {
        const inputAmountElement = element.shadowRoot.querySelector('[data-test-id="amount_input_field"]');
        expect(amountInputElement).not.toBeNull();
    });
});

If you needed the value or anything, you can just access it like so

const inputAmountElement = element.shadowRoot.querySelector('[data-test-id="amount_input_field"]');
expect(amountInputElement.value).toBe('whatever amount');