layout-dropdown-button.test.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import sinon from 'sinon'
  2. import fetchMock from 'fetch-mock'
  3. import { expect } from 'chai'
  4. import { screen, waitFor } from '@testing-library/react'
  5. import LayoutDropdownButton from '../../../../../frontend/js/features/editor-navigation-toolbar/components/layout-dropdown-button'
  6. import { renderWithEditorContext } from '../../../helpers/render-with-context'
  7. import * as eventTracking from '@/infrastructure/event-tracking'
  8. import type { LayoutContextOwnStates } from '@/shared/context/layout-context'
  9. describe('<LayoutDropdownButton />', function () {
  10. let openStub: sinon.SinonStub
  11. let sendMBSpy: sinon.SinonSpy
  12. const defaultLayout: Partial<LayoutContextOwnStates> = {
  13. pdfLayout: 'flat',
  14. view: 'pdf',
  15. chatIsOpen: false,
  16. }
  17. beforeEach(function () {
  18. openStub = sinon.stub(window, 'open')
  19. sendMBSpy = sinon.spy(eventTracking, 'sendMB')
  20. })
  21. afterEach(function () {
  22. openStub.restore()
  23. sendMBSpy.restore()
  24. fetchMock.removeRoutes().clearHistory()
  25. })
  26. it('should mark current layout option as selected', async function () {
  27. // Selected is aria-label, visually we show a checkmark
  28. renderWithEditorContext(<LayoutDropdownButton />, {
  29. layoutContext: defaultLayout,
  30. })
  31. screen.getByRole('button', { name: 'Layout' }).click()
  32. await waitFor(() =>
  33. expect(
  34. screen
  35. .getByRole('menuitem', {
  36. name: 'Editor & PDF',
  37. })
  38. .getAttribute('aria-selected')
  39. ).to.equal('false')
  40. )
  41. expect(
  42. screen
  43. .getByRole('menuitem', {
  44. name: 'PDF only (hide editor)',
  45. })
  46. .getAttribute('aria-selected')
  47. ).to.equal('true')
  48. expect(
  49. screen
  50. .getByRole('menuitem', {
  51. name: 'Editor only (hide PDF)',
  52. })
  53. .getAttribute('aria-selected')
  54. ).to.equal('false')
  55. expect(
  56. screen
  57. .getByRole('menuitem', {
  58. name: 'PDF in separate tab',
  59. })
  60. .getAttribute('aria-selected')
  61. ).to.equal('false')
  62. })
  63. it('should not select any option in history view', async function () {
  64. // Selected is aria-label, visually we show a checkmark
  65. renderWithEditorContext(<LayoutDropdownButton />, {
  66. layoutContext: { ...defaultLayout, view: 'history' },
  67. })
  68. screen.getByRole('button', { name: 'Layout' }).click()
  69. await waitFor(() =>
  70. expect(
  71. screen
  72. .getByRole('menuitem', {
  73. name: 'Editor & PDF',
  74. })
  75. .getAttribute('aria-selected')
  76. ).to.equal('false')
  77. )
  78. expect(
  79. screen
  80. .getByRole('menuitem', {
  81. name: 'PDF only (hide editor)',
  82. })
  83. .getAttribute('aria-selected')
  84. ).to.equal('false')
  85. expect(
  86. screen
  87. .getByRole('menuitem', {
  88. name: 'Editor only (hide PDF)',
  89. })
  90. .getAttribute('aria-selected')
  91. ).to.equal('false')
  92. expect(
  93. screen
  94. .getByRole('menuitem', {
  95. name: 'PDF in separate tab',
  96. })
  97. .getAttribute('aria-selected')
  98. ).to.equal('false')
  99. })
  100. it('should treat file and editor views the same way', async function () {
  101. // Selected is aria-label, visually we show a checkmark
  102. renderWithEditorContext(<LayoutDropdownButton />, {
  103. layoutContext: {
  104. pdfLayout: 'flat',
  105. view: 'file',
  106. chatIsOpen: false,
  107. },
  108. })
  109. screen.getByRole('button', { name: 'Layout' }).click()
  110. await waitFor(() =>
  111. expect(
  112. screen
  113. .getByRole('menuitem', {
  114. name: 'Editor & PDF',
  115. })
  116. .getAttribute('aria-selected')
  117. ).to.equal('false')
  118. )
  119. expect(
  120. screen
  121. .getByRole('menuitem', {
  122. name: 'PDF only (hide editor)',
  123. })
  124. .getAttribute('aria-selected')
  125. ).to.equal('false')
  126. expect(
  127. screen
  128. .getByRole('menuitem', {
  129. name: 'Editor only (hide PDF)',
  130. })
  131. .getAttribute('aria-selected')
  132. ).to.equal('true')
  133. expect(
  134. screen
  135. .getByRole('menuitem', {
  136. name: 'PDF in separate tab',
  137. })
  138. .getAttribute('aria-selected')
  139. ).to.equal('false')
  140. })
  141. describe('on detach', async function () {
  142. const originalBroadcastChannel = window.BroadcastChannel
  143. beforeEach(async function () {
  144. // @ts-expect-error
  145. window.BroadcastChannel = true // ensure that window.BroadcastChannel is truthy
  146. renderWithEditorContext(<LayoutDropdownButton />, {
  147. layoutContext: { ...defaultLayout, view: 'editor' },
  148. })
  149. screen.getByRole('button', { name: 'Layout' }).click()
  150. await waitFor(() =>
  151. screen
  152. .getByRole('menuitem', {
  153. name: 'PDF in separate tab',
  154. })
  155. .click()
  156. )
  157. })
  158. afterEach(function () {
  159. window.BroadcastChannel = originalBroadcastChannel
  160. })
  161. it('should show processing', async function () {
  162. await screen.findByText('Layout processing')
  163. })
  164. it('should record event', function () {
  165. sinon.assert.calledWith(sendMBSpy, 'project-layout-detach')
  166. })
  167. })
  168. describe('on layout change / reattach', async function () {
  169. beforeEach(async function () {
  170. window.metaAttributesCache.set('ol-detachRole', 'detacher')
  171. renderWithEditorContext(<LayoutDropdownButton />, {
  172. layoutContext: { ...defaultLayout, view: 'editor' },
  173. })
  174. screen.getByRole('button', { name: 'Layout' }).click()
  175. await waitFor(() =>
  176. screen
  177. .getByRole('menuitem', {
  178. name: 'Editor only (hide PDF)',
  179. })
  180. .click()
  181. )
  182. })
  183. it('should not show processing', function () {
  184. const processingText = screen.queryByText('Layout processing')
  185. expect(processingText).to.not.exist
  186. })
  187. it('should record events', function () {
  188. sinon.assert.calledWith(sendMBSpy, 'project-layout-reattach')
  189. sinon.assert.calledWith(sendMBSpy, 'project-layout-change', {
  190. layout: 'flat',
  191. view: 'editor',
  192. page: '/detacher',
  193. 'editor-redesign': 'enabled',
  194. })
  195. })
  196. it('should select new menu item', function () {
  197. screen.getByRole('menuitem', {
  198. name: 'Editor only (hide PDF)',
  199. })
  200. })
  201. })
  202. })