clone-project-modal.test.jsx 4.9 KB

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