project-list-root.test.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { fireEvent, screen, waitFor, within } from '@testing-library/react'
  2. import { expect } from 'chai'
  3. import fetchMock from 'fetch-mock'
  4. import sinon from 'sinon'
  5. import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root'
  6. import { renderWithProjectListContext } from '../helpers/render-with-context'
  7. const userId = '624333f147cfd8002622a1d3'
  8. describe('<ProjectListRoot />', function () {
  9. const originalLocation = window.location
  10. const locationStub = sinon.stub()
  11. beforeEach(function () {
  12. window.metaAttributesCache = new Map()
  13. window.metaAttributesCache.set('ol-tags', [])
  14. window.metaAttributesCache.set('ol-ExposedSettings', { templateLinks: [] })
  15. window.user_id = userId
  16. Object.defineProperty(window, 'location', {
  17. value: { assign: locationStub },
  18. })
  19. })
  20. afterEach(function () {
  21. window.user_id = undefined
  22. fetchMock.reset()
  23. Object.defineProperty(window, 'location', {
  24. value: originalLocation,
  25. })
  26. })
  27. describe('checkboxes', function () {
  28. let allCheckboxes: Array<HTMLInputElement> = []
  29. let toolbar: HTMLElement
  30. let project1Id: string | null, project2Id: string | null
  31. beforeEach(async function () {
  32. renderWithProjectListContext(<ProjectListRoot />)
  33. await fetchMock.flush(true)
  34. await waitFor(() => {
  35. screen.findByRole('table')
  36. })
  37. })
  38. describe('all projects', function () {
  39. beforeEach(function () {
  40. allCheckboxes = screen.getAllByRole<HTMLInputElement>('checkbox')
  41. // first one is the select all checkbox
  42. fireEvent.click(allCheckboxes[1])
  43. fireEvent.click(allCheckboxes[2])
  44. project1Id = allCheckboxes[1].getAttribute('data-project-id')
  45. project2Id = allCheckboxes[2].getAttribute('data-project-id')
  46. toolbar = screen.getByRole('toolbar')
  47. })
  48. it('downloads all selected projects and then unselects them', async function () {
  49. const downloadButton = within(toolbar).getByLabelText('Download')
  50. fireEvent.click(downloadButton)
  51. await waitFor(() => {
  52. expect(locationStub).to.have.been.called
  53. })
  54. sinon.assert.calledWithMatch(
  55. locationStub,
  56. `/project/download/zip?project_ids=${project1Id},${project2Id}`
  57. )
  58. const allCheckboxes = screen.getAllByRole<HTMLInputElement>('checkbox')
  59. const allCheckboxesChecked = allCheckboxes.filter(c => c.checked)
  60. expect(allCheckboxesChecked.length).to.equal(0)
  61. })
  62. it('opens archive modal for all selected projects and archives all', async function () {
  63. fetchMock.post(
  64. `express:/project/${project1Id}/archive`,
  65. {
  66. status: 200,
  67. },
  68. { delay: 0 }
  69. )
  70. fetchMock.post(
  71. `express:/project/${project2Id}/archive`,
  72. {
  73. status: 200,
  74. },
  75. { delay: 0 }
  76. )
  77. const archiveButton = within(toolbar).getByLabelText('Archive')
  78. fireEvent.click(archiveButton)
  79. const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
  80. fireEvent.click(confirmBtn)
  81. expect(confirmBtn.disabled).to.be.true
  82. await fetchMock.flush(true)
  83. expect(fetchMock.done()).to.be.true
  84. const requests = fetchMock.calls()
  85. const [projectRequest1Url, projectRequest1Headers] = requests[2]
  86. expect(projectRequest1Url).to.equal(`/project/${project1Id}/archive`)
  87. expect(projectRequest1Headers?.method).to.equal('POST')
  88. const [projectRequest2Url, projectRequest2Headers] = requests[3]
  89. expect(projectRequest2Url).to.equal(`/project/${project2Id}/archive`)
  90. expect(projectRequest2Headers?.method).to.equal('POST')
  91. })
  92. it('opens trash modal for all selected projects and trashes all', async function () {
  93. fetchMock.post(
  94. `express:/project/${project1Id}/trash`,
  95. {
  96. status: 200,
  97. },
  98. { delay: 0 }
  99. )
  100. fetchMock.post(
  101. `express:/project/${project2Id}/trash`,
  102. {
  103. status: 200,
  104. },
  105. { delay: 0 }
  106. )
  107. const archiveButton = within(toolbar).getByLabelText('Trash')
  108. fireEvent.click(archiveButton)
  109. const confirmBtn = screen.getByText('Confirm') as HTMLButtonElement
  110. fireEvent.click(confirmBtn)
  111. expect(confirmBtn.disabled).to.be.true
  112. await fetchMock.flush(true)
  113. expect(fetchMock.done()).to.be.true
  114. const requests = fetchMock.calls()
  115. const [projectRequest1Url, projectRequest1Headers] = requests[2]
  116. expect(projectRequest1Url).to.equal(`/project/${project1Id}/trash`)
  117. expect(projectRequest1Headers?.method).to.equal('POST')
  118. const [projectRequest2Url, projectRequest2Headers] = requests[3]
  119. expect(projectRequest2Url).to.equal(`/project/${project2Id}/trash`)
  120. expect(projectRequest2Headers?.method).to.equal('POST')
  121. })
  122. })
  123. describe('archived projects', function () {
  124. beforeEach(function () {
  125. const filterButton = screen.getByText('Archived Projects')
  126. fireEvent.click(filterButton)
  127. allCheckboxes = screen.getAllByRole<HTMLInputElement>('checkbox')
  128. expect(allCheckboxes.length === 2).to.be.true
  129. // first one is the select all checkbox
  130. fireEvent.click(allCheckboxes[1])
  131. project1Id = allCheckboxes[1].getAttribute('data-project-id')
  132. })
  133. it('does not show the archive button in toolbar when archive view selected', function () {
  134. expect(screen.queryByLabelText('Archive')).to.be.null
  135. })
  136. })
  137. describe('trashed projects', function () {
  138. beforeEach(function () {
  139. const filterButton = screen.getByText('Trashed Projects')
  140. fireEvent.click(filterButton)
  141. allCheckboxes = screen.getAllByRole<HTMLInputElement>('checkbox')
  142. // first one is the select all checkbox
  143. fireEvent.click(allCheckboxes[1])
  144. project1Id = allCheckboxes[1].getAttribute('data-project-id')
  145. })
  146. it('does not show the trash button in toolbar when archive view selected', function () {
  147. expect(screen.queryByLabelText('Trash')).to.be.null
  148. })
  149. it('clears selected projects when filter changed', function () {
  150. const filterButton = screen.getByText('All Projects')
  151. fireEvent.click(filterButton)
  152. const allCheckboxes = screen.getAllByRole<HTMLInputElement>('checkbox')
  153. const allCheckboxesChecked = allCheckboxes.filter(c => c.checked)
  154. expect(allCheckboxesChecked.length).to.equal(0)
  155. })
  156. })
  157. })
  158. })