How to test mat dialog in angular 18?

Discussão em 'Angular' iniciado por newuser147, Janeiro 23, 2025.

  1. newuser147

    newuser147 Guest

    I have this standalone component in angular 18:

    import {
    Component,
    ElementRef,
    OnInit,
    ViewChild,
    } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
    import { ProductService } from '@services/product.service';
    import { Product } from '@models/product.model';
    import { AppModule } from './app.module';
    import { MatIconModule } from '@angular/material/icon';
    import { MatDialog, MatDialogModule } from '@angular/material/dialog';
    import { UpdateProductComponent } from './modals/update.product/update.product.component';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatSnackBar } from '@angular/material/snack-bar';
    import { PaginatedProducts } from '@models/paginate.product.model';

    @Component({
    selector: 'app-root',
    standalone: true,
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
    imports: [
    MatProgressSpinnerModule,
    CommonModule,
    MatProgressSpinnerModule,
    AppModule,
    MatIconModule,
    MatFormFieldModule,
    MatDialogModule,
    ],
    })
    export class AppComponent implements OnInit {
    isLoading = true;
    errorMessage = '';
    products: Product[] = [];
    isDown = false;
    startX = 0;
    scrollLeft = 0;

    queryPaginationProduct: any = {};

    constructor(
    private productService: ProductService,
    public dialog: MatDialog,
    public snackBar: MatSnackBar,
    ) {}

    ngOnInit() {
    this.queryPaginationProduct = {
    limit: 10,
    page: 1,
    total: 10,
    };
    this.getProducts();
    }

    getProducts() {
    this.isLoading = true;
    this.productService
    .getProducts(
    this.queryPaginationProduct.page,
    this.queryPaginationProduct.limit,
    )
    .subscribe({
    next: (response: PaginatedProducts) => {
    this.products = response.products;
    this.isLoading = false;
    this.queryPaginationProduct = {
    limit: response.limit,
    page: response.page,
    total: response.total,
    };

    this.snackBar.open(response.message, 'Cerrar', {
    duration: 5000,
    horizontalPosition: 'right',
    verticalPosition: 'bottom',
    });
    console.log('snackBar.open called with:', response.message);
    },
    error: (error) => {
    this.errorMessage = 'Failed to load data';
    this.isLoading = false;
    console.error(error);
    },
    });
    }

    onPageChange(event: any): void {
    this.queryPaginationProduct.page = event.pageIndex + 1;
    this.queryPaginationProduct.limit = event.pageSize;

    this.getProducts();
    }

    trackByFn(index: any, item: Product) {
    return item.id;
    }

    onUpdate(item: Product) {
    const dialogRef = this.dialog.open(UpdateProductComponent, {
    width: '800px',
    disableClose: true,
    hasBackdrop: true,
    data: item,
    });

    dialogRef.afterClosed().subscribe((result) => {
    if (result.status === 'ok') {
    this.getProducts();
    }

    this.snackBar.open(result.message, 'Cerrar', {
    duration: 5000,
    horizontalPosition: 'right',
    verticalPosition: 'bottom',
    });
    });
    }
    }



    And I was trying to create the unit test of the function that opens the matdialog with this approach, that mocks the mat dialog component of angular material:

    import { TestBed, ComponentFixture } from '@angular/core/testing';
    import { Subject, of } from 'rxjs';
    import { MatDialog, MatDialogRef } from '@angular/material/dialog';
    import { MatSnackBar } from '@angular/material/snack-bar';

    import { AppComponent } from './app.component';
    import { UpdateProductComponent } from './modals/update.product/update.product.component';
    import { Product } from '@models/product.model';
    import { NoopAnimationsModule, provideAnimations } from '@angular/platform-browser/animations';
    import { provideHttpClient, withFetch } from '@angular/common/http';

    describe('AppComponent (fully stubbed MatDialog)', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<AppComponent>;

    let matDialogStub: {
    openDialogs: MatDialogRef<any>[];
    afterOpened: Subject<MatDialogRef<any>>;
    afterAllClosed: Subject<void>;
    open: jasmine.Spy;
    closeAll: () => void;
    };

    let snackBar: jasmine.SpyObj<MatSnackBar>;

    beforeEach(async () => {
    const afterOpenedSubject = new Subject<MatDialogRef<any>>();
    const afterAllClosedSubject = new Subject<void>();

    matDialogStub = {
    openDialogs: [],

    afterOpened: afterOpenedSubject,

    afterAllClosed: afterAllClosedSubject,

    open: jasmine.createSpy('open').and.callFake((componentOrTemplateRef, config) => {
    const mockDialogRef = {
    afterClosed: () => of({ status: 'ok', message: 'Product updated successfully' }),

    close: () => {
    const index = matDialogStub.openDialogs.indexOf(mockDialogRef as any);
    if (index > -1) {
    matDialogStub.openDialogs.splice(index, 1);
    }
    if (matDialogStub.openDialogs.length === 0) {
    afterAllClosedSubject.next();
    }
    },
    } as MatDialogRef<UpdateProductComponent>;

    matDialogStub.openDialogs.push(mockDialogRef);
    afterOpenedSubject.next(mockDialogRef);

    return mockDialogRef;
    }),

    closeAll: () => {
    matDialogStub.openDialogs.length = 0;
    afterAllClosedSubject.next();
    },
    };

    const snackBarSpy = jasmine.createSpyObj('MatSnackBar', ['open']);

    await TestBed.configureTestingModule({
    imports: [AppComponent, UpdateProductComponent, NoopAnimationsModule],

    providers: [
    { provide: MatDialog, useValue: matDialogStub },

    { provide: MatSnackBar, useValue: snackBarSpy },

    provideAnimations(),
    provideHttpClient(withFetch()),
    ],
    })
    .overrideComponent(AppComponent, {
    remove: {
    imports: [
    ],
    },
    })
    .compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;

    snackBar = TestBed.inject(MatSnackBar) as jasmine.SpyObj<MatSnackBar>;
    });

    it('should open UpdateProductComponent and handle afterClosed = {status: "ok"}', () => {
    spyOn(component, 'getProducts');

    const mockProduct: Product = {
    id: 123,
    name: 'Test Product',
    category: 'Test Category',
    price: 100,
    quantity: 5,
    };

    component.onUpdate(mockProduct);

    expect(matDialogStub.open).toHaveBeenCalledWith(UpdateProductComponent, {
    width: '800px',
    disableClose: true,
    hasBackdrop: true,
    data: mockProduct,
    });

    expect(component.getProducts).toHaveBeenCalled();

    expect(snackBar.open).toHaveBeenCalledWith(
    'Product updated successfully',
    'Cerrar',
    {
    duration: 5000,
    horizontalPosition: 'right',
    verticalPosition: 'bottom',
    }
    );
    });

    });



    However, the test is not working because I got this error:

    Expected spy MatSnackBar.open to have been called with: [ 'Something went wrong', 'Cerrar', Object({ duration: 5000, horizontalPosition: 'right', verticalPosition: 'bottom' }) ] but it was never called.

    Basically none of the spies are working.

    Continue reading...

Compartilhe esta Página