0

I am attempting to unit test whether my button has been focused on, but I cannot seem to get the spy to work correctly?

I saw [this post][1], but it hasn't helped fully.

Am I missing something obvious?

component.ts

ngOnInit() {
    // autofocus on the cancel button to guard against mistakes
    document.getElementById('cancel').focus();
  }

2 Answers 2

2

The focus is flawed to start with.

When using Angular, you should not use document to fetch your elements.

Use a viewChild instead.

@ViewChild('cancel') cancelButton: ElementRef<HtmlButtonElement>;
ngAfterViewInit() {
  this.cancelButton.nativeElement.focus();
}

Now your test looks like this

it('should focus cancel button', () => {
  spyOn(component.cancelButton.nativeElement, 'focus');
  component.ngAfterViewInit();
  expect(component.cancelButton.nativeElement.focus).toHaveBeenCalledWith();
});

EDIT If you still want to use your way, consider using By.css() :

it('should autofocus on cancel button on init', () => {
  const cancelButton = fixture.debugElement.query(By.css('#cancel'));
  spyOn(cancelButton, 'focus');
  component.ngOnInit();
  expect(cancelButton.focus).toHaveBeenCalled();
});
Sign up to request clarification or add additional context in comments.

5 Comments

Yes, take advantage of fixture instead of relying on document to get html elements.
@trichetriche - I implemented the @ViewChild but it is saying that it can't find name 'cancelButton'?
is your button something like <button #cancel>Cancel</button> ? You need to give it a template variable reference with #cancel
Yeah I just noticed that after googling. It also appears that you don't need the first colon ':' after ('cancel') :? And it should be HTML not Html in HtmlButtonElement :-)
@physicsboy woops, TY for the typo ! Also, ElementRef<HtmlButtonElement> is used to type your element : when you write this.cancelButton.nativeElement, if you append a dot, you will have intellisense for an HTML button element. Not mandatory, but practical !
2

Recall ngOnInit() after your spy has been created in your spec, as pointed by @trichietrichie

Also, take advantage of fixture instead of relying on document to fetch html elements.

beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ ConfirmationComponent ],
      providers: [ MessageService]
    });

    fixture = TestBed.createComponent(ConfirmationComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();    
    component.ngOnInit();
  });

  it('should autofocus on cancel button on init', () => {
    const cancelButton = fixture.debugElement.query(By.css('#cancel'));
    spyOn(cancelButton.nativeElement, 'focus'); // create spy here   
    component.ngOnInit();
    expect(cancelButton.focus).toHaveBeenCalled();
  });

9 Comments

or recall ngOnInit so you don't create [it.count] times a spy that you use once ?
Hmm, I see the issue, so he has to postpone the ngOnInit() invokation.
Yes & no : ngOnInit will be called at every component instanciation (which is at every it since we create it in a beforeEach) : but ngOnInit being just a specifically named function, you can just call it a second time, this time with a spy so that you can monitor it. So not really postponed, but recalled.
Again, no ! lifecycle hooks are here to ensure you can have access to specific elements (for instance, you won't get ViewContainerRef in ngOnInit, but you will in ngAfterViewInit). Hooks are just functions called by Angular. For instance, I implemented something similar with a decorator, that is getting trigerred by events. The functions ran are called ngOnCustomEvent(eventType: string) : nothing prevents me from running them when I want, but I know for a fact that they will be ran when a custom event occurs, without me having to call them by hand.
Even better than removing the downvote ! Fully valid answers deserve upvotes :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.