modal-form.test.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { fireEvent, screen, render, waitFor } from '@testing-library/react'
  4. import fetchMock, { type FetchMock } from 'fetch-mock'
  5. import LeaveModalForm from '../../../../../../frontend/js/features/settings/components/leave/modal-form'
  6. import * as useLocationModule from '../../../../../../frontend/js/shared/hooks/use-location'
  7. import getMeta from '@/utils/meta'
  8. describe('<LeaveModalForm />', function () {
  9. beforeEach(function () {
  10. window.metaAttributesCache.set('ol-usersEmail', 'foo@bar.com')
  11. Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
  12. })
  13. afterEach(function () {
  14. fetchMock.removeRoutes().clearHistory()
  15. })
  16. it('validates form', async function () {
  17. const setIsFormValid = sinon.stub()
  18. render(
  19. <LeaveModalForm
  20. setInFlight={() => {}}
  21. isFormValid={false}
  22. setIsFormValid={setIsFormValid}
  23. />
  24. )
  25. const emailInput = screen.getByLabelText('Email')
  26. fireEvent.change(emailInput, { target: { value: 'FOO@bar.com' } })
  27. const passwordInput = screen.getByLabelText('Password')
  28. fireEvent.change(passwordInput, { target: { value: 'foobar' } })
  29. const checkbox = screen.getByLabelText(
  30. 'I understand this will delete all projects in my Overleaf account with email address foo@bar.com'
  31. )
  32. fireEvent.click(checkbox)
  33. const setIsFormValidCalls = setIsFormValid.getCalls()
  34. const lastSetIsFormValidCall = setIsFormValidCalls.pop()
  35. expect(lastSetIsFormValidCall!.args[0]).to.be.true
  36. for (const setIsFormValidCall of setIsFormValidCalls) {
  37. expect(setIsFormValidCall.args[0]).to.be.false
  38. }
  39. })
  40. describe('submits', async function () {
  41. let setInFlight: sinon.SinonStub
  42. let setIsFormValid: sinon.SinonStub
  43. let deleteMock: FetchMock
  44. let assignStub: sinon.SinonStub
  45. beforeEach(function () {
  46. setInFlight = sinon.stub()
  47. setIsFormValid = sinon.stub()
  48. deleteMock = fetchMock.post('/user/delete', 200)
  49. assignStub = sinon.stub()
  50. this.locationStub = sinon.stub(useLocationModule, 'useLocation').returns({
  51. assign: assignStub,
  52. replace: sinon.stub(),
  53. reload: sinon.stub(),
  54. setHash: sinon.stub(),
  55. })
  56. Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: true })
  57. })
  58. afterEach(function () {
  59. fetchMock.removeRoutes().clearHistory()
  60. this.locationStub.restore()
  61. })
  62. it('with valid form', async function () {
  63. render(
  64. <LeaveModalForm
  65. setInFlight={setInFlight}
  66. isFormValid
  67. setIsFormValid={setIsFormValid}
  68. />
  69. )
  70. fireEvent.submit(screen.getByLabelText('Email'))
  71. sinon.assert.calledOnce(setInFlight)
  72. sinon.assert.calledWithMatch(setInFlight, true)
  73. expect(deleteMock.callHistory.called()).to.be.true
  74. await waitFor(() => {
  75. sinon.assert.calledTwice(setInFlight)
  76. sinon.assert.calledWithMatch(setInFlight, false)
  77. sinon.assert.calledOnce(assignStub)
  78. sinon.assert.calledWith(assignStub, '/')
  79. })
  80. })
  81. it('with invalid form', async function () {
  82. render(
  83. <LeaveModalForm
  84. setInFlight={setInFlight}
  85. isFormValid={false}
  86. setIsFormValid={setIsFormValid}
  87. />
  88. )
  89. fireEvent.submit(screen.getByLabelText('Email'))
  90. expect(deleteMock.callHistory.called()).to.be.false
  91. sinon.assert.notCalled(setInFlight)
  92. })
  93. })
  94. it('handles credentials error without Saas tip', async function () {
  95. Object.assign(getMeta('ol-ExposedSettings'), { isOverleaf: false })
  96. fetchMock.post('/user/delete', 403)
  97. render(
  98. <LeaveModalForm
  99. setInFlight={() => {}}
  100. isFormValid
  101. setIsFormValid={() => {}}
  102. />
  103. )
  104. fireEvent.submit(screen.getByLabelText('Email'))
  105. await waitFor(() => {
  106. screen.getByText(/Your email or password is incorrect. Please try again/)
  107. })
  108. expect(screen.queryByText(/If you cannot remember your password/)).to.not
  109. .exist
  110. })
  111. it('handles credentials error with Saas tip', async function () {
  112. fetchMock.post('/user/delete', 403)
  113. render(
  114. <LeaveModalForm
  115. setInFlight={() => {}}
  116. isFormValid
  117. setIsFormValid={() => {}}
  118. />
  119. )
  120. fireEvent.submit(screen.getByLabelText('Email'))
  121. await waitFor(() => {
  122. screen.getByText(/Your email or password is incorrect. Please try again/)
  123. })
  124. screen.getByText(/If you cannot remember your password/)
  125. const link = screen.getByRole('link', { name: 'reset your password' })
  126. expect(link.getAttribute('href')).to.equal('/user/password/reset')
  127. })
  128. it('handles subscription error', async function () {
  129. fetchMock.post('/user/delete', {
  130. status: 422,
  131. body: {
  132. error: 'SubscriptionAdminDeletionError',
  133. },
  134. })
  135. render(
  136. <LeaveModalForm
  137. setInFlight={() => {}}
  138. isFormValid
  139. setIsFormValid={() => {}}
  140. />
  141. )
  142. fireEvent.submit(screen.getByLabelText('Email'))
  143. await waitFor(() => {
  144. screen.getByText(
  145. 'You cannot delete your account while on a subscription. Please cancel your subscription and try again. If you keep seeing this message please contact us.'
  146. )
  147. })
  148. })
  149. it('handles generic error', async function () {
  150. fetchMock.post('/user/delete', 500)
  151. render(
  152. <LeaveModalForm
  153. setInFlight={() => {}}
  154. isFormValid
  155. setIsFormValid={() => {}}
  156. />
  157. )
  158. fireEvent.submit(screen.getByLabelText('Email'))
  159. await waitFor(() => {
  160. screen.getByText(
  161. 'Sorry, something went wrong deleting your account. Please try again in a minute.'
  162. )
  163. })
  164. })
  165. })