zammad/zammad

View on GitHub
app/frontend/apps/desktop/components/CommonDialog/__tests__/CommonDialog.spec.ts

Summary

Maintainability
C
1 day
Test Coverage
// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

import { flushPromises } from '@vue/test-utils'

import { renderComponent } from '#tests/support/components/index.ts'

import { afterAll, beforeAll } from 'vitest'
import { getDialogMeta, openDialog } from '../useDialog.ts'
import CommonDialog from '../CommonDialog.vue'

const html = String.raw

describe('visuals for common dialog', () => {
  beforeAll(() => {
    const main = document.createElement('main')
    main.id = 'page-main-content'
    document.body.appendChild(main)
  })
  beforeEach(() => {
    const { dialogsOptions } = getDialogMeta()
    dialogsOptions.set('dialog', {
      name: 'dialog',
      component: vi.fn().mockResolvedValue({}),
      refocus: true,
    })
  })
  afterAll(() => {
    document.body.innerHTML = ''
  })

  it('rendering with header title and content', () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
        headerTitle: 'Some Title',
      },
      slots: {
        default: 'Content Slot',
      },
    })

    expect(view.getByText('Some Title')).toBeInTheDocument()
    expect(view.getByText('Content Slot')).toBeInTheDocument()
    expect(view.getByLabelText('Close dialog')).toBeInTheDocument()
  })

  it('can render header title as slot', () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
      },
      slots: {
        header: 'Some Title',
      },
    })

    expect(view.getByText('Some Title')).toBeInTheDocument()
  })

  it('can close dialog with keyboard and clicks', async () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
      },
      global: {
        stubs: {
          teleport: true,
        },
      },
    })

    await flushPromises()

    await view.events.keyboard('{Escape}')

    const emitted = view.emitted()

    expect(emitted.close).toHaveLength(1)
    expect(emitted.close[0]).toEqual([true])

    await view.events.click(view.getByLabelText('Close dialog'))
    expect(emitted.close).toHaveLength(2)

    await view.events.click(view.getByRole('button', { name: 'OK' }))
    expect(emitted.close).toHaveLength(3)
    expect(emitted.close[2]).toEqual([false])
  })

  it('rendering different footer button content', () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
        headerTitle: 'Some Title',
        footerActionOptions: {
          actionLabel: 'Yes, continue',
        },
      },
      slots: {
        default: 'Content Slot',
      },
    })

    expect(
      view.getByRole('button', { name: 'Yes, continue' }),
    ).toBeInTheDocument()
  })

  it('has an accessible name', async () => {
    const view = renderComponent(CommonDialog, {
      props: {
        headerTitle: 'foobar',
        name: 'dialog',
      },
    })

    expect(view.getByRole('dialog')).toHaveAccessibleName('foobar')
  })

  it('traps focus inside the dialog', async () => {
    const externalForm = document.createElement('form')
    externalForm.innerHTML = html`
      <input data-test-id="form_input" type="text" />
      <select data-test-id="form_select" type="text" />
    `
    document.body.appendChild(externalForm)

    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
      },
      slots: {
        default: html`
          <input data-test-id="input" type="text" />
          <div data-test-id="div" tabindex="0" />
          <select data-test-id="select">
            <option value="1">1</option>
          </select>
        `,
      },
    })

    view.getByTestId('input').focus()
    expect(view.getByTestId('input')).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByTestId('div')).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByTestId('select')).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByRole('button', { name: 'Cancel & Go Back' })).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByRole('button', { name: 'OK' })).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByLabelText('Close dialog')).toHaveFocus()

    await view.events.keyboard('{Tab}')
    expect(view.getByTestId('input')).toHaveFocus()
  })

  it('autofocuses the first focusable element', async () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
      },
      slots: {
        default: html`
          <div data-test-id="div" tabindex="0" />
          <select data-test-id="select">
            <option value="1">1</option>
          </select>
        `,
      },
    })

    await flushPromises()

    expect(view.getByTestId('div')).toHaveFocus()
  })

  it('focuses close, if there is nothing focusable in dialog', async () => {
    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
        hideFooter: true,
      },
    })

    await flushPromises()

    expect(view.getByLabelText('Close dialog')).toHaveFocus()
  })

  it('refocuses element that opened dialog', async () => {
    const button = document.createElement('button')
    button.setAttribute('aria-haspopup', 'dialog')
    button.setAttribute('aria-controls', 'dialog-dialog')
    button.setAttribute('data-test-id', 'button')
    document.body.appendChild(button)

    button.focus()

    expect(button).toHaveFocus()

    await openDialog('dialog', {})

    const view = renderComponent(CommonDialog, {
      props: {
        name: 'dialog',
        hideFooter: true,
      },
    })

    await flushPromises()

    expect(view.getByLabelText('Close dialog')).toHaveFocus()

    await view.events.keyboard('{Escape}')

    expect(button).toHaveFocus()
  })
})