Search code examples
angularprimengts-jest

PrimeNG p-menu command not working in jest


I am trying to open a confirmation dialog via p-menu command. I am able to display menu items by clicking on p-menu. But when I click on menuitem, the command is not executed and the confirmation dialog is not displayed.

This is my component template:

<div>
<div role="list">
  <div
    *ngFor="let user of users"
    role="listitem"
    [attr.aria-label]="user.firstName + ' ' + user.lastName"
  >
    <span>{{ user.firstName }} {{ user.lastName }}</span>
    <button
      class="as-button p-button-transparent"
      aria-label="Menu"
      type="button"
      pButton
      icon="pi pi-ellipsis-v"
      (click)="activeMenu = user; menu.toggle($event)"
    ></button>
    <p-menu #menu [model]="menuItems" [popup]="true"></p-menu>
  </div>
</div>
<p-confirmDialog></p-confirmDialog>

relevant component code:

activeMenu: ConnectionUser | null = null
public menuItems = [
  {
    label: 'Remove',
    command: () => {
      this.showConfirmationDialog()
    }
  }
]
showConfirmationDialog() {
  this.confirmationService.confirm({
    message: 'Are you sure you want to remove the connection?',
    header: 'Remove connection',
    rejectButtonStyleClass: 'p-button-secondary',
    acceptButtonStyleClass: 'p-button-danger',
    acceptLabel: 'Remove',
    rejectLabel: 'Cancel',
    defaultFocus: 'reject',
    accept: () => {
      if (this.activeMenu) {
        this.removeConnection(this.activeMenu._id)
      }
    }
  })
}

This are my jest imports:

imports: [ConnectionsModule, NoopAnimationsModule, SharedModule, RouterTestingModule],

And the failing jest test:

it('should remove connection', async () => {
  const userEv = userEvent.setup({ delay: null })

  await userEv.click(
    within(await screen.findByRole('listitem', { name: 'Joe Doe' })).getByRole('button', {
      name: 'Menu'
    })
  )
  fixture.detectChanges()
  await userEv.click(await screen.findByRole('menuitem', { name: 'Remove' }))
  fixture.detectChanges()
  // ERROR: The button has not been found
  await userEv.click(await screen.findByRole('button', { name: 'Remove' }))

  expect(screen.queryByRole('listitem')).not.toBeInTheDocument()
})

I have tried detectChanges and waitFor but non of it would show the confirmation.

Here is the p-menu rendered by jest:

<p-menu
                        class="p-element ng-tns-c215187649-2 ng-star-inserted"
                        ng-reflect-model="[object Object]"
                        ng-reflect-popup="true"
                      >
                        <div
                          class="ng-trigger ng-trigger-overlayAnimation ng-tns-c215187649-2 p-menu p-component p-menu-overlay ng-star-inserted"
                          data-pc-name="menu"
                          id="pn_id_5"
                          ng-reflect-ng-class="[object Object]"
                          style="z-index: 1001; visibility: visible; display: none; transform-origin: top; top: 0px; left: 0px;"
                        >
                          <ul
                            class="p-menu-list p-reset ng-tns-c215187649-2"
                            data-pc-section="menu"
                            id="pn_id_5_list"
                            role="menu"
                            tabindex="0"
                          >
                            <li
                              aria-disabled="false"
                              aria-label="Remove"
                              class="p-element p-menuitem ng-tns-c215187649-2 ng-star-inserted"
                              data-p-disabled="false"
                              data-p-focused="false"
                              data-pc-section="menuitem"
                              id="pn_id_5_0"
                              ng-reflect-content=""
                              ng-reflect-id="pn_id_5_0"
                              ng-reflect-item="[object Object]"
                              ng-reflect-ng-class="[object Object]"
                              ptooltip=""
                              role="menuitem"
                              style=""
                            >
                              <div
                                class="p-menuitem-content"
                                data-pc-section="content"
                              >
                                <a
                                  aria-hidden="true"
                                  class="p-ripple p-element p-menuitem-link ng-star-inserted"
                                  data-pc-section="action"
                                  ng-reflect-ng-class="[object Object]"
                                  pripple=""
                                  tabindex="-1"
                                  target="undefined"
                                >
                                  <span
                                    class="p-menuitem-text ng-star-inserted"
                                  >
                                    Remove
                                  </span>
                                </a>
                              </div>
                            </li>
                          </ul>
                        </div>
                      </p-menu>

Can it be that it is not being triggered as in the rendered html the p-menu-overlay has display: none; style? On the other hand the menuitem button is found by jest and the click has not failed.

I have placed a debug in the command function (this.showConfirmationDialog()) and it is never called.


Solution

  • I could not figure out why it was not called so I went with a workaround to call the command directly.

    component.menuItems[0].command()
    

    I have also added missing ConfirmationService provider, to display the confirmation dialog.

    This is what I have endup with:

    component.menuItems[0].command()
    fixture.detectChanges()
    // Confirm dialog
    await userEvent.click(await screen.findByRole('button', { name: 'Remove' }))
    fixture.detectChanges()