Home > database >  TypeError: input.setFocus is not a function when I run all Jasmine unit tests associated with the An
TypeError: input.setFocus is not a function when I run all Jasmine unit tests associated with the An

Time:12-01

With a @ViewChild decorator I grab the ion-input element to set focus with the setFocus() method. And this works as expected. I don't know why, but I can't figure out why all the Jasmine unit tests of the component fails. I've tried mocking the setFocus() method, but without success. Can anyone help me with this and know what I'm doing wrong?

Template with the @ViewChild reference #input_username

  <ion-input id="inputusername"
                 #input_username
                 color="dark"
                 role="textbox"
                 aria-label=""
                 
                 placeholder=""
                 type="text"
                 formControlName="name"
                 ngDefaultControl>
   </ion-input>

Component method that sets the focus with setFocus()

private _setFocusDefaultInput(input: ElementRef): void {
    const ionInput = input['el']; // get the DOM element from Ionic
    ionInput.setFocus();
}

The _setFocusDefaultInput() method is called inside the ngAfterViewInit() method

ngAfterViewInit(): void {
    setTimeout(() => { 
      this._setFocusDefaultInput(this.inputUsername); 
    }, 800);
  }

Error message I get when I run ALL the Jasmine unit tests that belongs to this component:

Failed: input.setFocus is not a function
    error properties: Object({ longStack: 'TypeError: input.setFocus is not a function
        at LoginPage._setFocusDefaultInput (src/app/login/login/login.page.ts:123:11)
        at src/app/login/login/login.page.ts:54:12
        at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:429:1)
        at AsyncTestZoneSpec.onInvokeTask (node_modules/zone.js/dist/zone-testing.js:1231:33)
        at ProxyZoneSpec.onInvokeTask (node_modules/zone.js/dist/zone-testing.js:328:43)
        at ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:428:1)
        at Object.onInvokeTask (node_modules/zone.js/dist/zone.js:307:88)
        at ZoneDelegate.invokeTask (nod ...
    TypeError: input.setFocus is not a function

I'v tried to mock the setFocus() method with the following code, but without success

TestBed configuration

  {
    provide: ElementRef,
    useClass: ElementRefMock
  },
  {
    provide: IonInput,
    useClass: IonInputMock
  }

Mocking classes

class ElementRefMock {
   setFocus(){}
   el: {
   setFocus: () => {}
   };
 }

class IonInputMock {
  setFocus(){}
  el: {
  setFocus: () => {}
};

CodePudding user response:

I am thinking that each individual test runs faster than the timeout of 800ms and so the view of the input is destroyed but you still want to refer to it about 800ms later. Let's say a test took 200ms, 600ms the focus logic runs but within the 200ms the view has been destroyed and we are referring to something that does not exist.

Try this to debug:

private _setFocusDefaultInput(input: ElementRef): void {
    const ionInput = input['el']; // get the DOM element from Ionic
    // See what you get for the ionInput
    console.log(ionInput);
    // If ionInput is undefined, put a question mark between the . and setFocus
    // This way, it won't run if ionInput is undefined or null
    // You can of course put an if check as well.
    ionInput?.setFocus();
}

CodePudding user response:

The previous answer solved my problem, but I have found a new solution that no longer needs the setTimeout method to set the focus correctly on the Ionic input field. The Jasmine unit test issue I ran into have also been resolved with this implementation

  1. I replaced the ElementRef interface with the IonInput interface to directly call setFocus():

    
      @ViewChild('input_username') inputUsername: IonInput;
      
  2. I implemented the ionViewDidEnter() Ionic lifecycle method. This method is not called until all Ionic elements have been successfully rendered into the DOM. That's when I want to set the focus on the Ionic input field:

    
      ionViewDidEnter(): void {
        this.inputUsername.setFocus();
      }
      
  • Related