clone-project-modal.test.jsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { fireEvent, screen, waitFor } from '@testing-library/react'
  2. import { expect } from 'chai'
  3. import sinon from 'sinon'
  4. import fetchMock from 'fetch-mock'
  5. import EditorCloneProjectModalWrapper from '../../../../../frontend/js/features/clone-project-modal/components/editor-clone-project-modal-wrapper'
  6. import { renderWithEditorContext } from '../../../helpers/render-with-context'
  7. describe('<EditorCloneProjectModalWrapper />', function () {
  8. beforeEach(function () {
  9. fetchMock.removeRoutes().clearHistory()
  10. })
  11. after(function () {
  12. fetchMock.removeRoutes().clearHistory()
  13. })
  14. const project = {
  15. _id: 'project-1',
  16. name: 'Test Project',
  17. }
  18. it('renders the translated modal title', async function () {
  19. const handleHide = sinon.stub()
  20. const openProject = sinon.stub()
  21. renderWithEditorContext(
  22. <EditorCloneProjectModalWrapper
  23. handleHide={handleHide}
  24. openProject={openProject}
  25. show
  26. />,
  27. { scope: { project } }
  28. )
  29. await screen.findByText('Copy Project')
  30. })
  31. it('posts the generated project name', async function () {
  32. fetchMock.post(
  33. 'express:/project/:projectId/clone',
  34. {
  35. status: 200,
  36. body: { project_id: 'cloned-project' },
  37. },
  38. { delay: 10 }
  39. )
  40. const handleHide = sinon.stub()
  41. const openProject = sinon.stub()
  42. renderWithEditorContext(
  43. <EditorCloneProjectModalWrapper
  44. handleHide={handleHide}
  45. openProject={openProject}
  46. show
  47. />,
  48. { scope: { project } }
  49. )
  50. const cancelButton = await screen.findByRole('button', { name: 'Cancel' })
  51. expect(cancelButton.disabled).to.be.false
  52. const submitButton = await screen.findByRole('button', { name: 'Copy' })
  53. expect(submitButton.disabled).to.be.false
  54. const input = await screen.getByLabelText('New Name')
  55. fireEvent.change(input, {
  56. target: { value: '' },
  57. })
  58. expect(submitButton.disabled).to.be.true
  59. fireEvent.change(input, {
  60. target: { value: 'A Cloned Project' },
  61. })
  62. expect(submitButton.disabled).to.be.false
  63. fireEvent.click(submitButton)
  64. expect(submitButton.disabled).to.be.true
  65. await fetchMock.callHistory.flush(true)
  66. expect(fetchMock.callHistory.done()).to.be.true
  67. const { url, options } = fetchMock.callHistory
  68. .calls('express:/project/:projectId/clone')
  69. .at(-1)
  70. expect(url).to.equal(
  71. 'https://www.test-overleaf.com/project/project-1/clone'
  72. )
  73. expect(JSON.parse(options.body)).to.deep.equal({
  74. projectName: 'A Cloned Project',
  75. tags: [],
  76. })
  77. await waitFor(() => {
  78. expect(openProject).to.be.calledOnce
  79. })
  80. const errorMessage = screen.queryByText('Sorry, something went wrong')
  81. expect(errorMessage).to.be.null
  82. await waitFor(() => {
  83. expect(submitButton.disabled).to.be.false
  84. expect(cancelButton.disabled).to.be.false
  85. })
  86. })
  87. it('handles a generic error response', async function () {
  88. const matcher = 'express:/project/:projectId/clone'
  89. fetchMock.postOnce(matcher, {
  90. status: 500,
  91. body: 'There was an error!',
  92. })
  93. const handleHide = sinon.stub()
  94. const openProject = sinon.stub()
  95. renderWithEditorContext(
  96. <EditorCloneProjectModalWrapper
  97. handleHide={handleHide}
  98. openProject={openProject}
  99. show
  100. />,
  101. { scope: { project } }
  102. )
  103. const button = await screen.findByRole('button', { name: 'Copy' })
  104. expect(button.disabled).to.be.false
  105. const cancelButton = await screen.findByRole('button', { name: 'Cancel' })
  106. expect(cancelButton.disabled).to.be.false
  107. fireEvent.click(button)
  108. expect(fetchMock.callHistory.done(matcher)).to.be.true
  109. expect(openProject).not.to.be.called
  110. await screen.findByText('Sorry, something went wrong')
  111. expect(button.disabled).to.be.false
  112. expect(cancelButton.disabled).to.be.false
  113. })
  114. it('handles a specific error response', async function () {
  115. const matcher = 'express:/project/:projectId/clone'
  116. fetchMock.postOnce(matcher, {
  117. status: 400,
  118. body: 'There was an error!',
  119. })
  120. const handleHide = sinon.stub()
  121. const openProject = sinon.stub()
  122. renderWithEditorContext(
  123. <EditorCloneProjectModalWrapper
  124. handleHide={handleHide}
  125. openProject={openProject}
  126. show
  127. />,
  128. { scope: { project } }
  129. )
  130. const button = await screen.findByRole('button', { name: 'Copy' })
  131. expect(button.disabled).to.be.false
  132. const cancelButton = await screen.findByRole('button', { name: 'Cancel' })
  133. expect(cancelButton.disabled).to.be.false
  134. fireEvent.click(button)
  135. await fetchMock.callHistory.flush(true)
  136. expect(fetchMock.callHistory.done(matcher)).to.be.true
  137. expect(openProject).not.to.be.called
  138. await screen.findByText('There was an error!')
  139. expect(button.disabled).to.be.false
  140. expect(cancelButton.disabled).to.be.false
  141. })
  142. })