1

I have been hacking at this for a few days. I have an LWC with a wired Apex method. When it gets into the renderedCallBack() and tries to assign focus to an element the test fails with a message of 'cannot read property focus of null' the dom element in question also shows as null when I try to console.log it.

The component gets passed data into a public property and renders when the data from the wire service loads.

I have copied the examples from the LWC recipes repo about testing the wire service. I assume that I am making some mistakes when it comes to emitting the data from the wire adapter. The component is not rendering and this leads to the failure when rendereredCallback() tries to assign focus.

HTML

 <template if:true={userInfo}>     
           
                <lightning-input label="Parent First Name"
                                 type="text"
                                 value={patient.LegalGuardian__r.FirstName}
                                 onchange={handleChange}
                                 name="firstName"
                                 required
                                 data-id="parentFirstName">
                </lightning-input>              
        </div>
    </template>

JS

export default class VerifyGuardianScreen extends Screen {
  @api patient;
  userInfo;
  error;
  isLoaded = false;  
  

  renderedCallback() {
    let target = this.template.querySelector(`[data-id="parentFirstName"]`);
    console.log(`target ${target}`);
    target.focus();
  } 

  @wire(getUserInfo)
  wiredUser(value) {
    this.provisionedValue = value;
    const { data, error } = this.provisionedValue;
    if (data) {
      this.userInfo = data;
    } else if (error) {
      this.error = error;
     
    }
  }

And my test: pardon the lack of assertions.

import { createElement } from "lwc";
import VerifyGuardianScreen from "c/verifyGuardianScreen";
import getUserInfo from "@salesforce/apex/OnboardingControllers.getUserInfo";

const PATIENT_MOCK = require("../../onboardingMocks/over18Patient.json");
const USER_MOCK = require("../../onboardingMocks/userInfo.json");

jest.mock(
  "@salesforce/apex/OnboardingControllers.getUserInfo",
  () => {
    const { createApexTestWireAdapter } = require("@salesforce/sfdx-lwc-jest");
    return {
      default: createApexTestWireAdapter(jest.fn())
    };
  },
  { virtual: true }
);

describe("c-verify-guardian-screen test suite", () => {
  afterEach(() => {
    if (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }
    jest.clearAllMocks();
  });
  it("it should display custom labels", () => {
    const element = createElement("c-verify-guardian-screen", {
      is: VerifyGuardianScreen
    });
    document.body.appendChild(element);
    getUserInfo.emit(USER_MOCK);
    element.patient = PATIENT_MOCK;
    return Promise.resolve().then(() => {});
  });
});

this permutation of the test setup also fails.

 it("it should display custom labels", () => {
    const element = createElement("c-verify-guardian-screen", {
      is: VerifyGuardianScreen
    });
    element.patient = PATIENT_MOCK;
    document.body.appendChild(element);
    getUserInfo.emit(USER_MOCK);
    return Promise.resolve().then(() => {});
  });

1 Answer 1

2

Two things:

  1. You probably should handle the scenario where there's nothing to focus on in your component. That would seem reasonable considering you're hiding that element if there's no userData. If the element is not found, you shouldn't be doing a focus. That may happen in real-world usage when the wire returns an error.
  2. If you do the above, then the jest test becomes easier as well as your component can now handle not receiving certain info. You'll just want to set your api field before creating the component/element.

For #2 above, a public property (patient) would be getting set when created (<c-mycomponent patient={passedInData}...).

As such, to mimic the real-world scenario - you'd want to set your api value before creating the component/element in your jest test.

element.patient = PATIENT_MOCK;
document.body.appendChild(element);
//mock wire and wait for DOM updates to assert
getUserInfo.emit(USER_MOCK);
return Promise.resolve().then(() => {});

If you don't want to do #1 above, you just need to emit the mocked data for the wire before appending the component - otherwise, you risk having a renderedCallback() occur where there is no mocked data returned in your wire to set your input field to visible (thus leading to trying to focus on an element that doesn't exist).

element.patient = PATIENT_MOCK;
getUserInfo.emit(USER_MOCK);
//now add component to render
document.body.appendChild(element);

3
  • I have tried a few permutations like that. I just updated my example. It still fails when I update the API value prior to appending the element to the DOM. 'can not read property focus of null' Feb 22, 2022 at 22:47
  • 1
    Yes, I missed your userData conditional. I'd probably recommend updating your renderedCallback to properly handle a real scenario (that the input is hidden) as what would happen if the wire errored in production? Feb 22, 2022 at 23:42
  • 1
    I am still struggling with this. I got through the rendered call back. But the LWC still throws errors related to the API data. I will start a new thread on that. Feb 23, 2022 at 15:04

You must log in to answer this question.

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