clone-project-modal.test.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import React from 'react'
  2. import { fireEvent, render, screen } from '@testing-library/react'
  3. import { expect } from 'chai'
  4. import CloneProjectModalContent from '../../../../../frontend/js/features/clone-project-modal/components/clone-project-modal-content'
  5. import CloneProjectModal from '../../../../../frontend/js/features/clone-project-modal/components/clone-project-modal'
  6. import sinon from 'sinon'
  7. import fetchMock from 'fetch-mock'
  8. const cancel = sinon.stub()
  9. const cloneProject = sinon.stub()
  10. const handleHide = sinon.stub()
  11. describe('<CloneProjectModal />', function() {
  12. afterEach(function() {
  13. fetchMock.reset()
  14. })
  15. it('posts the generated project name', async function() {
  16. const matcher = 'express:/project/:projectId/clone'
  17. fetchMock.postOnce(
  18. matcher,
  19. () => {
  20. return {
  21. project_id: 'test'
  22. }
  23. },
  24. {
  25. body: {
  26. projectName: 'A Project (Copy)'
  27. }
  28. }
  29. )
  30. render(
  31. <CloneProjectModal
  32. handleHide={handleHide}
  33. projectId="project-1"
  34. projectName="A Project"
  35. show
  36. />
  37. )
  38. const button = await screen.findByRole('button', {
  39. name: 'Copy',
  40. hidden: true // TODO: this shouldn't be needed
  41. })
  42. const cancelButton = await screen.findByRole('button', {
  43. name: 'Cancel',
  44. hidden: true // TODO: this shouldn't be needed
  45. })
  46. expect(button.disabled).to.be.false
  47. expect(cancelButton.disabled).to.be.false
  48. fireEvent.click(button)
  49. expect(fetchMock.done(matcher)).to.be.true
  50. // TODO: window.location?
  51. const errorMessage = screen.queryByText('Sorry, something went wrong')
  52. expect(errorMessage).to.be.null
  53. expect(button.disabled).to.be.true
  54. expect(cancelButton.disabled).to.be.true
  55. })
  56. it('handles a generic error response', async function() {
  57. const matcher = 'express:/project/:projectId/clone'
  58. fetchMock.postOnce(matcher, {
  59. status: 500,
  60. body: 'There was an error!'
  61. })
  62. render(
  63. <CloneProjectModal
  64. handleHide={handleHide}
  65. projectId="project-2"
  66. projectName="A Project"
  67. show
  68. />
  69. )
  70. const button = await screen.findByRole('button', {
  71. name: 'Copy',
  72. hidden: true // TODO: this shouldn't be needed
  73. })
  74. const cancelButton = await screen.findByRole('button', {
  75. name: 'Cancel',
  76. hidden: true // TODO: this shouldn't be needed
  77. })
  78. expect(button.disabled).to.be.false
  79. expect(cancelButton.disabled).to.be.false
  80. fireEvent.click(button)
  81. expect(fetchMock.done(matcher)).to.be.true
  82. await screen.findByText('Sorry, something went wrong')
  83. expect(button.disabled).to.be.false
  84. expect(cancelButton.disabled).to.be.false
  85. })
  86. it('handles a specific error response', async function() {
  87. const matcher = 'express:/project/:projectId/clone'
  88. fetchMock.postOnce(matcher, {
  89. status: 400,
  90. body: 'There was an error!'
  91. })
  92. render(
  93. <CloneProjectModal
  94. handleHide={handleHide}
  95. projectId="project-3"
  96. projectName="A Project"
  97. show
  98. />
  99. )
  100. const button = await screen.findByRole('button', {
  101. name: 'Copy',
  102. hidden: true // TODO: this shouldn't be needed
  103. })
  104. const cancelButton = await screen.findByRole('button', {
  105. name: 'Cancel',
  106. hidden: true // TODO: this shouldn't be needed
  107. })
  108. expect(button.disabled).to.be.false
  109. expect(cancelButton.disabled).to.be.false
  110. fireEvent.click(button)
  111. expect(fetchMock.done(matcher)).to.be.true
  112. await screen.findByText('There was an error!')
  113. expect(button.disabled).to.be.false
  114. expect(cancelButton.disabled).to.be.false
  115. })
  116. })
  117. describe('<CloneProjectModalContent />', function() {
  118. it('renders the translated modal title', async function() {
  119. render(
  120. <CloneProjectModalContent
  121. cloneProject={cloneProject}
  122. cancel={cancel}
  123. inFlight={false}
  124. />
  125. )
  126. await screen.findByText('Copy Project')
  127. })
  128. it('shows the copy button', async function() {
  129. render(
  130. <CloneProjectModalContent
  131. cloneProject={cloneProject}
  132. cancel={cancel}
  133. inFlight={false}
  134. />
  135. )
  136. const button = await screen.findByRole('button', { name: 'Copy' })
  137. expect(button.disabled).to.be.false
  138. })
  139. it('disables the copy button when loading', async function() {
  140. render(
  141. <CloneProjectModalContent
  142. cloneProject={cloneProject}
  143. cancel={cancel}
  144. inFlight
  145. />
  146. )
  147. const button = await screen.findByText(
  148. (content, element) =>
  149. element.nodeName === 'BUTTON' &&
  150. element.textContent.trim().match(/^Copying…$/)
  151. )
  152. expect(button.disabled).to.be.true
  153. })
  154. it('renders a generic error message', async function() {
  155. render(
  156. <CloneProjectModalContent
  157. cloneProject={cloneProject}
  158. cancel={cancel}
  159. inFlight={false}
  160. error
  161. />
  162. )
  163. await screen.findByText('Sorry, something went wrong')
  164. })
  165. it('renders a specific error message', async function() {
  166. render(
  167. <CloneProjectModalContent
  168. cloneProject={cloneProject}
  169. cancel={cancel}
  170. inFlight={false}
  171. error={{
  172. message: 'Uh oh!'
  173. }}
  174. />
  175. )
  176. await screen.findByText('Uh oh!')
  177. })
  178. it('displays a project name', async function() {
  179. render(
  180. <CloneProjectModalContent
  181. cloneProject={cloneProject}
  182. cancel={cancel}
  183. inFlight={false}
  184. projectName="A copy of a project"
  185. />
  186. )
  187. const input = await screen.getByLabelText('New Name')
  188. expect(input.value).to.equal('A copy of a project (Copy)')
  189. })
  190. })