Issue
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=""
                 class="input"
                 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: () => {}
};
                        Solution
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();
}
                        Answered By - AliF50
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.