How to test Material2 snack-bar?
Asked Answered
M

4

6

I have a project on Angular 5 and I encountered with following issue. I have a component ComponentWithSnackBar which triggers displaying of a snack-bar:

showSnackBar() {
  this.snackBar.open('Message text', 'Close', {
    duration: 5000,
    verticalPosition: 'top',
  });
}

It works as expected. But I have no ideas how I can test it. I try to write test:

describe('ComponentWithSnackBar', () => {
  let snackBar: MatSnackBar;
  let overlayContainer: OverlayContainer;
  let overlayContainerElement: HTMLElement;

  function createComponent<T>(component: Type<T>, providers: Provider[] = [], declarations: any[] = []): ComponentFixture<T> {
    TestBed.configureTestingModule({
      imports: [AppModule, RouterTestingModule, NoopAnimationsModule],
      declarations: declarations,
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers,
    }).compileComponents();

    inject([MatSnackBar, OverlayContainer], (sb: MatSnackBar, oc: OverlayContainer) => {
      snackBar = sb;
      overlayContainer = oc;
      overlayContainerElement = oc.getContainerElement();
    })();

    return TestBed.createComponent<T>(component);
  }

  it(`Should display snack-bar`, fakeAsync(() => {
    const fixture = createComponent(ComponentWithSnackBar);
    const component: ComponentWithSnackBar = fixture.debugElement.componentInstance;

    fixture.detectChanges();
    const button = fixture.debugElement.query(By.css('button')); //button which triggers method showSnackBar 
    button.triggerEventHandler('click', null);
    fixture.detectChanges();
    flush();

    const messageElement = overlayContainerElement.querySelector('snack-bar-container');
    expect(messageElement.textContent).toContain('Message text');
  }));
});

As a result I get error

TypeError: Cannot read property 'textContent' of null

What I do wrong? Thanks for advance!

Marcosmarcotte answered 3/4, 2018 at 22:40 Comment(0)
O
7

The following worked for me:

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let element: any;
  let overlayContainer: OverlayContainer;
  let overlayContainerElement: HTMLElement;
  let snackBar: MatSnackBar;

....

beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    element = fixture.nativeElement;
    fixture.detectChanges();
  });

beforeEach(inject([MatSnackBar, OverlayContainer],
    (sb: MatSnackBar, oc: OverlayContainer) => {
      snackBar = sb;
      overlayContainer = oc;
      overlayContainerElement = oc.getContainerElement();
    }));

....

 it('should show my success message', () => {
      
   const containerElement = overlayContainerElement.querySelector('simple-snack-bar');
   
   expect(containerElement.textContent).toContain('My success text');
 });
Ovation answered 1/2, 2019 at 22:18 Comment(2)
The Github link in the preamble is dead @OvationRheometer
@Rheometer thanx, i deleted it, because it is no longer accessibleOvation
M
1

I decided do not test fact of displaying of snack-bar, but only fact of call method snackBar.open() with correct arguments:

expect(snackBarSpy.open.calls.count()).toEqual(1);
expect(snackBarSpy.open.calls.first().args).toEqual([message, 'Close', {duration: 5000, verticalPosition: 'top'}]);
Marcosmarcotte answered 4/4, 2018 at 10:3 Comment(0)
S
0

I ran into a similar issue when trying to test a component with a material dialog. I didn't find the solution but I figured I'd share what I do know:

If you put a breakpoint on the test when running and inspect the scope, the overlayContainerElement (which is the div with the class cdk-overlay-container) has no child nodes.

Also if you add the following to the end of the test before the expect statement:

console.log("overlay wrapper", document.getElementsByClassName('cdk-global-overlay-wrapper')[0]);
// and or
console.log("snackbar text", document.getElementsByClassName('snack-bar-container')[0]); // or whatever class you gave this item

They both log undefined.

My theory is that Angular change detection does not get run on the components that are involved with displaying material parts such as the dialog or snackbar which is why they are empty/not showing during the test.

Can you also share the code for the ComponentWithSnackBar?

Swashbuckler answered 4/4, 2018 at 0:44 Comment(1)
Thanks for your answer. Actually, ComponentWithSnackBar is not real component. It`s just simplified version of my component. I think, your have a correct theory.Marcosmarcotte
A
-1
const mockSnackbar = jasmine.createSpyObj(['open']);

it('test desc'()=>{
    component.showSnackBar()
        expect(mockSnackbar.open).tohavebeencalled();
        expect(mockSnackbar.open).tohavebeencalledwith('Message text',
            'Close', {
                duration: 5000,
                verticalPosition: 'top',
            });
})
Antihistamine answered 9/8, 2019 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.