Select an element inside a template if true in LWC Jest test

lightning-web-componentslwc-domlwc-jestunit-test

I have an LWC app that has a lightning-input and a lighning-button. It fetches data from the server for a record whose ID is specified. I have successfully mocked the server calls.
I am trying to select a div that is inside a template which is rendered only after the data is fetched from the server (which is mocked successfully, the variable required for the template to display indeed becomes true). However, when I run the test, it says 'Cannot read properties of null (reading 'textContent')' as for some reason, the querySelection fails to select the div 'nameField'. What am I doing wrong here?
I select the input field, set its value, dispatch the onchange event, and when the value updates, select the button and click it (manually on Jest). Value is fetched from the DB (mock result) successfully and the isDataFetched variable becomes true. However, I still can't select the div inside the template tag. What exactly am I doing wrong here? I have been trying to debug for quite some time now with no luck.

<template>
        <lightning-input type="text" onchange={handleInputChange} value={inputText} placeholder="Enter record id.." variant="label-hidden" class="slds-m-around_small"></lightning-input>


    <lightning-button variant="brand" label="Submit" title="Primary action" onclick={onSubmitClick} class="slds-m-around_small"></lightning-button>
   
 

    <template if:true={isDataFetched}>

        <div class="recordResultOuterWrapper">
            
            <div class="currentRecordCard">
                
                <div class="nameField">
                    {currentRecordDetails.NameField}
                </div>
                
            </div>
        </div>

       
    </template>


</template>
describe('Unit tests for recordResult.js', () => {

  afterEach(() => {
    while(document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }
  });
    
  it('Test to ensure current record is displayed correctly', () => 
  {
    getChildEntityDetails.mockResolvedValue(APEX_RECORDLIST_SUCCESS);
    getRecordById.mockResolvedValue(APEX_CURRENTRECORD_SUCCESS);
    getParentEntityDetails.mockResolvedValue(APEX_RECORDLIST_SUCCESS);

    const element = createElement('c-record-result', {
        is: recordResult
      });
     
  
      document.body.appendChild(element);

      const recordIdInput = element.shadowRoot.querySelector('lightning-input'); 
      recordIdInput.value='001Iw000002MIZ4IAO';
      recordIdInput.dispatchEvent(new CustomEvent('change')); //Update input with record id


      return Promise.resolve().then(()=> {
          const submitButton = element.shadowRoot.querySelector('lightning-button');
          submitButton.dispatchEvent(new CustomEvent("click")); //Click submit button after input is successfully updated
          return Promise.resolve().then(()=> {
            const nameFieldDiv = element.shadowRoot.querySelector('.nameField'); //Select nameField after submit button fetches data. This fails (it is null for some reason)
            expect(nameFieldDiv.textContent).toBe(APEX_CURRENTRECORD_SUCCESS.Name);
          });
      });
      
  });

  
});

Best Answer

// Helper function to wait until the microtask queue is empty. This is needed for promise
// timing when calling imperative Apex.
async function flushPromises() {
    return Promise.resolve();
}

Try to add following line after submitButton.dispatchEvent(new CustomEvent("click"));:

// Wait for any asynchronous DOM updates
await flushPromises();