Topic: Unable to query a mdb 5 pro element from the DOM in angular 13 unit tests

Felix Wu priority asked 4 months ago


I have a unit test to check if a dialog opens after a button click, but I have been unable to query an MDB element (the dialog content) from the DOM. I am not sure about all configurations at tsconfig.spec.json either.

this is my tsconfig.spec.json:

{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": ["jasmine"] }, "files": ["src/test.ts", "src/polyfills.ts"], "include": [ "src//.spec.ts", "src//.d.ts", "../node_modules/angular-bootstrap-md/index.ts", "../node_modules/ng-mdb-pro/*_/_.ts" ]}

And this is my test:

*_Expected behavior_*fit(should open a dialog when the button is clicked, () => {

const button: HTMLElement = fixture.nativeElement.querySelector('.ms-auto');

button.click();

fixture.detectChanges();

const dialog: HTMLElement = document.body.querySelector('.modal-content')!;

expect(dialog).toBeTruthy();

});

Actual behavior

the const dialog is null :(

Resources (screenshots, code snippets etc.)

Thanks a lot for any help provided.


Michał Duszak staff commented 4 months ago

Hi, could you please share your template and component code related to this test?


Felix Wu priority commented 4 months ago

Hi Michal, Thanks for reaching out to me. The code samples are below.


Michał Duszak staff answered 4 months ago


Hello, I was unable to reproduce this issue. I tried the same tsconfig.spec.json as yours. Here is my spec.ts working code:

import {
  ComponentFixture,
  fakeAsync,
  TestBed,
  tick,
} from '@angular/core/testing';

import { Component, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MdbModalModule, MdbModalService } from 'mdb-angular-ui-kit/modal';

@Component({
  template: `
    <button class="ms-auto" type="filled" (click)="openStoreDialog()">
      Add a Store
    </button>
  `,
  providers: [MdbModalService],
})
class BasicComponent {
  constructor(public dialog: MdbModalService) {}
  modalRef;

  openStoreDialog = () => {
    this.modalRef = this.dialog.open(DialogCreateStoreComponent, {
      modalClass: 'modal-dialog-centered',
      ignoreBackdropClick: true,
    });
  };
}

@Component({
  template: `
    <div class="modal-header">
    <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
    <button
      type="button"
      class="btn-close"
      aria-label="Close"
      (click)="modalRef.close()"
    ></button>
  </div>
  <div class="modal-body">...</div>
  <div class="modal-footer">
    <button type="button" class="btn btn-secondary" (click)="modalRef.close()">
      Close
    </button>
    <button type="button" class="btn btn-primary">Save changes</button>
  </div>
  `,
  providers: [MdbModalService],
})
class DialogCreateStoreComponent {
  constructor() {}
}

@NgModule({
  declarations: [BasicComponent, DialogCreateStoreComponent],
  imports: [BrowserModule],
})
class TestModalModule {}

describe('MDB Modal', () => {
  let fixture: ComponentFixture<BasicComponent>;

  beforeEach(fakeAsync(() => {
    const module = TestBed.configureTestingModule({
      imports: [MdbModalModule, TestModalModule],
      teardown: { destroyAfterEach: false },
    });

    TestBed.compileComponents();
    fixture = module.createComponent(BasicComponent);
  }));

  it('should open a dialog when the button is clicked', fakeAsync(() => {
    const button: HTMLElement = fixture.nativeElement.querySelector('.ms-auto');

    button.click();

    fixture.detectChanges();
    tick(350);

    const dialog: HTMLElement = document.body.querySelector('.modal-content')!;
    expect(dialog).toBeTruthy();
  }));
});

Felix Wu priority commented 4 months ago

Thanks, Michal, but it did not work on my side :(


Michał Duszak staff commented 4 months ago

Could you reproduce this issue in a new project, so that I could directly face this error?


Felix Wu priority answered 4 months ago


<div class="container-fluid d-flex flex-column px-5 py-5">
    <div class="d-flex flex-row align-items-center">
        <h1>Stores</h1>
        <app-button-primary
          class="ms-auto"
          type="filled"
          (click)="openStoreDialog()"
        >
          Add a Store
        </app-button-primary>

    <input
      mdbInput
      type="search"
      class="form-control rounded border-0"
      placeholder="Ex. Giant Food."
      aria-label="Ex. Giant Food."
      aria-describedby="search-addon"
      id="search-input"
      (keyup)="search($event)"
      (search)="this.clearInputSearchbar($event)"
    />
  </div>
</mdb-form-control>

{{ header | titlecase }}

  <tbody
    class="datatable-body"
    *ngIf="table.data?.length! >= 1; else noMatches"
    id="storeTableBody"
  >
    <tr
      *ngFor="let data of table.data"
      scope="row"
      (click)="this.onClickItem(data.fkey)"
      class="clickable"
    >
      <td>
        {{ data.name }}
      </td>
      <td>
        {{ data.address }}
      </td>
      <td>
        {{ data.region }}
      </td>
      <td>
        {{ data.lastVisit | date: "dd/MM/YYYY HH:mm a" }}
      </td>
      <td>
        {{ data.products }}
        {{ data.products > 1 ? "products" : "product" }}
      </td>
      <td><i class="fa-solid fa-ellipsis-vertical"></i></td>
    </tr>
  </tbody>
</table>

No matching results found


Felix Wu priority answered 4 months ago


After importing MdbModalService...

openStoreDialog = () => { this.modalRef = this.dialog.open(DialogCreateStoreComponent, { modalClass: 'modal-dialog-centered', ignoreBackdropClick: true, }); };


Michał Duszak staff commented 4 months ago

Is DialogCreateStoreComponent a valid component? Does it contain a template with valid Modal element? Could you check if modal from DialogCreateStoreComponent injects to the DOM using console.log(document.body.innerHTML) like in the code below? Any errors are thrown during the tests?

it('should open a dialog when the button is clicked', () => { const button:                     
HTMLElement = fixture.nativeElement.querySelector('.ms-auto');

button.click();

fixture.detectChanges();

console.log(document.body.innerHTML)    

const dialog: HTMLElement = document.body.querySelector('.modal-content')!;
expect(dialog).toBeTruthy();
});

Felix Wu priority commented 4 months ago

Hello Michal,

yes, it is a valid component and there is no issue with the functionality. When I open it, it is there in the DOM and no errors are thrown. The problem with the unit test is when I query the nativeElement, the constant dialog is null and the test fails. I am not sure about the tsconfigspec.json either. What do I need to have there regarding mdb?

Thanks again.


Michał Duszak staff commented 4 months ago

Can you try running this test in fakeAsync so that we can wait until the animation ends?

it('should open a dialog when the button is clicked', fakeAsync(() => { 
const button: HTMLElement = fixture.nativeElement.querySelector('.ms-auto');

button.click();

fixture.detectChanges();
tick(350);

const dialog: HTMLElement = document.body.querySelector('.modal-content')!;
expect(dialog).toBeTruthy();
 }));

Felix Wu priority commented 4 months ago

I had already tried with fakeAsync, done, and waitForAsync...this is the test with fakeAsync:

last expect still fails:

`

it(should open a dialog when the button 'Add a Store' is clicked\, fakeAsync(() => { const button: HTMLElement = fixture.nativeElement.querySelector('.ms-auto');

button.click();

fixture.detectChanges();

flush();

const dialog: HTMLElement =
  fixture.nativeElement.querySelector('.modal-content')!;

expect(fakeDialogService.open).toHaveBeenCalledTimes(1);

expect(dialog).toBeTruthy();

}));`



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue

  • User: Priority
  • Premium support: Yes
  • Technology: MDB Angular
  • MDB Version: MDB5 1.3.0
  • Device: mac os
  • Browser: chrome
  • OS: ios
  • Provided sample code: Yes
  • Provided link: No