password-section.test.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import { expect } from 'chai'
  2. import { fireEvent, screen, render } from '@testing-library/react'
  3. import fetchMock from 'fetch-mock'
  4. import PasswordSection from '../../../../../frontend/js/features/settings/components/password-section'
  5. describe('<PasswordSection />', function () {
  6. beforeEach(function () {
  7. window.metaAttributesCache = window.metaAttributesCache || new Map()
  8. window.metaAttributesCache.set('ol-ExposedSettings', {
  9. isOverleaf: true,
  10. })
  11. window.metaAttributesCache.set(
  12. 'ol-isExternalAuthenticationSystemUsed',
  13. false
  14. )
  15. window.metaAttributesCache.set('ol-hasPassword', true)
  16. })
  17. afterEach(function () {
  18. window.metaAttributesCache = new Map()
  19. fetchMock.reset()
  20. })
  21. it('shows password managed externally message', async function () {
  22. window.metaAttributesCache.set('ol-ExposedSettings', {
  23. isOverleaf: false,
  24. })
  25. window.metaAttributesCache.set(
  26. 'ol-isExternalAuthenticationSystemUsed',
  27. true
  28. )
  29. render(<PasswordSection />)
  30. screen.getByText('Password settings are managed externally')
  31. })
  32. it('shows no existing password message', async function () {
  33. window.metaAttributesCache.set('ol-hasPassword', false)
  34. render(<PasswordSection />)
  35. screen.getByText('Please use the password reset form to set your password')
  36. })
  37. it('submits all inputs', async function () {
  38. const updateMock = fetchMock.post('/user/password/update', 200)
  39. render(<PasswordSection />)
  40. submitValidForm()
  41. expect(updateMock.called()).to.be.true
  42. expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
  43. {
  44. currentPassword: 'foobar',
  45. newPassword1: 'barbaz',
  46. newPassword2: 'barbaz',
  47. }
  48. )
  49. })
  50. it('disables button on invalid form', async function () {
  51. const updateMock = fetchMock.post('/user/password/update', 200)
  52. render(<PasswordSection />)
  53. fireEvent.click(
  54. screen.getByRole('button', {
  55. name: 'Change',
  56. })
  57. )
  58. expect(updateMock.called()).to.be.false
  59. })
  60. it('validates inputs', async function () {
  61. render(<PasswordSection />)
  62. const button = screen.getByRole('button', {
  63. name: 'Change',
  64. }) as HTMLButtonElement
  65. expect(button.disabled).to.be.true
  66. fireEvent.change(screen.getByLabelText('Current Password'), {
  67. target: { value: 'foobar' },
  68. })
  69. expect(button.disabled).to.be.true
  70. fireEvent.change(screen.getByLabelText('New Password'), {
  71. target: { value: 'barbaz' },
  72. })
  73. expect(button.disabled).to.be.true
  74. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  75. target: { value: 'bar' },
  76. })
  77. screen.getByText('Doesn’t match')
  78. expect(button.disabled).to.be.true
  79. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  80. target: { value: 'barbaz' },
  81. })
  82. expect(button.disabled).to.be.false
  83. })
  84. it('sets browser validation attributes', async function () {
  85. window.metaAttributesCache.set('ol-passwordStrengthOptions', {
  86. length: {
  87. min: 3,
  88. },
  89. })
  90. render(<PasswordSection />)
  91. const currentPasswordInput = screen.getByLabelText(
  92. 'Current Password'
  93. ) as HTMLInputElement
  94. const newPassword1Input = screen.getByLabelText(
  95. 'New Password'
  96. ) as HTMLInputElement
  97. const newPassword2Input = screen.getByLabelText(
  98. 'Confirm New Password'
  99. ) as HTMLInputElement
  100. expect(newPassword1Input.minLength).to.equal(3)
  101. // not required before changes
  102. expect(currentPasswordInput.required).to.be.false
  103. expect(newPassword1Input.required).to.be.false
  104. expect(newPassword2Input.required).to.be.false
  105. fireEvent.change(currentPasswordInput, {
  106. target: { value: 'foobar' },
  107. })
  108. fireEvent.change(newPassword1Input, {
  109. target: { value: 'barbaz' },
  110. })
  111. fireEvent.change(newPassword2Input, {
  112. target: { value: 'barbaz' },
  113. })
  114. expect(currentPasswordInput.required).to.be.true
  115. expect(newPassword1Input.required).to.be.true
  116. expect(newPassword2Input.required).to.be.true
  117. })
  118. it('shows inflight state and success message', async function () {
  119. let finishUpdateCall: (value: any) => void = () => {}
  120. fetchMock.post(
  121. '/user/password/update',
  122. new Promise(resolve => (finishUpdateCall = resolve))
  123. )
  124. render(<PasswordSection />)
  125. submitValidForm()
  126. await screen.findByText('Saving…')
  127. finishUpdateCall({
  128. status: 200,
  129. body: {
  130. message: {
  131. type: 'success',
  132. email: 'tim.alby@overleaf.com',
  133. text: 'Password changed',
  134. },
  135. },
  136. })
  137. await screen.findByRole('button', {
  138. name: 'Change',
  139. })
  140. screen.getByText('Password changed')
  141. })
  142. it('shows server error', async function () {
  143. fetchMock.post('/user/password/update', 500)
  144. render(<PasswordSection />)
  145. submitValidForm()
  146. await screen.findByText('Something went wrong. Please try again.')
  147. })
  148. it('shows server error message', async function () {
  149. fetchMock.post('/user/password/update', {
  150. status: 400,
  151. body: {
  152. message: 'Your old password is wrong',
  153. },
  154. })
  155. render(<PasswordSection />)
  156. submitValidForm()
  157. await screen.findByText('Your old password is wrong')
  158. })
  159. })
  160. function submitValidForm() {
  161. fireEvent.change(screen.getByLabelText('Current Password'), {
  162. target: { value: 'foobar' },
  163. })
  164. fireEvent.change(screen.getByLabelText('New Password'), {
  165. target: { value: 'barbaz' },
  166. })
  167. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  168. target: { value: 'barbaz' },
  169. })
  170. fireEvent.click(
  171. screen.getByRole('button', {
  172. name: 'Change',
  173. })
  174. )
  175. }