CommonDialog.spec.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { flushPromises } from '@vue/test-utils'
  3. import { renderComponent } from '#tests/support/components/index.ts'
  4. import { getDialogMeta, openDialog } from '#mobile/composables/useDialog.ts'
  5. import CommonDialog from '../CommonDialog.vue'
  6. const html = String.raw
  7. describe('visuals for common dialog', () => {
  8. beforeEach(() => {
  9. const { dialogsOptions } = getDialogMeta()
  10. dialogsOptions.set('dialog', {
  11. name: 'dialog',
  12. component: vi.fn().mockResolvedValue({}),
  13. refocus: true,
  14. })
  15. })
  16. it('rendering with label and content', () => {
  17. const view = renderComponent(CommonDialog, {
  18. props: {
  19. name: 'dialog',
  20. label: 'Some Label',
  21. },
  22. slots: {
  23. default: 'Content Slot',
  24. },
  25. })
  26. expect(view.getByText('Some Label')).toBeInTheDocument()
  27. expect(view.getByText('Content Slot')).toBeInTheDocument()
  28. expect(view.getByText('Done')).toBeInTheDocument()
  29. })
  30. it('can render label as slot', () => {
  31. const view = renderComponent(CommonDialog, {
  32. props: {
  33. name: 'dialog',
  34. },
  35. slots: {
  36. label: 'Some Label',
  37. },
  38. })
  39. expect(view.getByText('Some Label')).toBeInTheDocument()
  40. })
  41. it('can close dialog with keyboard and clicks', async () => {
  42. const view = renderComponent(CommonDialog, {
  43. props: {
  44. name: 'dialog',
  45. },
  46. })
  47. await flushPromises()
  48. await view.events.keyboard('{Escape}')
  49. const emitted = view.emitted()
  50. expect(emitted.close).toHaveLength(1)
  51. await view.events.click(view.getByRole('button', { name: /Done/ }))
  52. expect(emitted.close).toHaveLength(2)
  53. })
  54. it('has an accessible name', async () => {
  55. const view = renderComponent(CommonDialog, {
  56. props: {
  57. name: 'dialog',
  58. },
  59. })
  60. expect(view.getByRole('dialog')).toHaveAccessibleName('dialog')
  61. await view.rerender({
  62. label: 'foobar',
  63. })
  64. expect(view.getByRole('dialog')).toHaveAccessibleName('foobar')
  65. })
  66. it('traps focus inside the dialog', async () => {
  67. const externalForm = document.createElement('form')
  68. externalForm.innerHTML = html`
  69. <input data-test-id="form_input" type="text" />
  70. <select data-test-id="form_select" type="text" />
  71. `
  72. document.body.appendChild(externalForm)
  73. const view = renderComponent(CommonDialog, {
  74. props: {
  75. name: 'dialog',
  76. },
  77. slots: {
  78. default: html`
  79. <input data-test-id="input" type="text" />
  80. <div data-test-id="div" tabindex="0" />
  81. <select data-test-id="select">
  82. <option value="1">1</option>
  83. </select>
  84. `,
  85. },
  86. })
  87. view.getByTestId('input').focus()
  88. expect(view.getByTestId('input')).toHaveFocus()
  89. await view.events.keyboard('{Tab}')
  90. expect(view.getByTestId('div')).toHaveFocus()
  91. await view.events.keyboard('{Tab}')
  92. expect(view.getByTestId('select')).toHaveFocus()
  93. await view.events.keyboard('{Tab}')
  94. expect(view.getByRole('button', { name: 'Done' })).toHaveFocus()
  95. await view.events.keyboard('{Tab}')
  96. expect(view.getByTestId('input')).toHaveFocus()
  97. })
  98. it('autofocuses the first focusable element', async () => {
  99. const view = renderComponent(CommonDialog, {
  100. props: {
  101. name: 'dialog',
  102. },
  103. slots: {
  104. default: html`
  105. <div data-test-id="div" tabindex="0" />
  106. <select data-test-id="select">
  107. <option value="1">1</option>
  108. </select>
  109. `,
  110. },
  111. })
  112. await flushPromises()
  113. expect(view.getByTestId('div')).toHaveFocus()
  114. })
  115. it('focuses "Done", if there is nothing focusable in dialog', async () => {
  116. const view = renderComponent(CommonDialog, {
  117. props: {
  118. name: 'dialog',
  119. },
  120. })
  121. await flushPromises()
  122. expect(view.getByRole('button', { name: 'Done' })).toHaveFocus()
  123. })
  124. it('refocuses element that opened dialog', async () => {
  125. const button = document.createElement('button')
  126. button.setAttribute('aria-haspopup', 'dialog')
  127. button.setAttribute('aria-controls', 'dialog-dialog')
  128. button.setAttribute('data-test-id', 'button')
  129. document.body.appendChild(button)
  130. button.focus()
  131. expect(button).toHaveFocus()
  132. await openDialog('dialog', {})
  133. const view = renderComponent(CommonDialog, {
  134. props: {
  135. name: 'dialog',
  136. },
  137. })
  138. await flushPromises()
  139. expect(view.getByRole('button', { name: 'Done' })).toHaveFocus()
  140. await view.events.keyboard('{Escape}')
  141. expect(button).toHaveFocus()
  142. })
  143. // closing with pulling down is tested inside e2e
  144. })