password-section.test.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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)).to.deep.equal({
  43. currentPassword: 'foobar',
  44. newPassword1: 'barbaz',
  45. newPassword2: 'barbaz',
  46. })
  47. })
  48. it('disables button on invalid form', async function () {
  49. const updateMock = fetchMock.post('/user/password/update', 200)
  50. render(<PasswordSection />)
  51. fireEvent.click(
  52. screen.getByRole('button', {
  53. name: 'Change',
  54. })
  55. )
  56. expect(updateMock.called()).to.be.false
  57. })
  58. it('validates inputs', async function () {
  59. render(<PasswordSection />)
  60. const button = screen.getByRole('button', {
  61. name: 'Change',
  62. })
  63. expect(button.disabled).to.be.true
  64. fireEvent.change(screen.getByLabelText('Current Password'), {
  65. target: { value: 'foobar' },
  66. })
  67. expect(button.disabled).to.be.true
  68. fireEvent.change(screen.getByLabelText('New Password'), {
  69. target: { value: 'barbaz' },
  70. })
  71. expect(button.disabled).to.be.true
  72. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  73. target: { value: 'bar' },
  74. })
  75. screen.getByText('Doesn’t match')
  76. expect(button.disabled).to.be.true
  77. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  78. target: { value: 'barbaz' },
  79. })
  80. expect(button.disabled).to.be.false
  81. })
  82. it('sets browser validation attributes', async function () {
  83. window.metaAttributesCache.set('ol-passwordStrengthOptions', {
  84. length: {
  85. min: 3,
  86. },
  87. })
  88. render(<PasswordSection />)
  89. const currentPasswordInput = screen.getByLabelText('Current Password')
  90. const newPassword1Input = screen.getByLabelText('New Password')
  91. const newPassword2Input = screen.getByLabelText('Confirm New Password')
  92. expect(newPassword1Input.minLength).to.equal(3)
  93. // not required before changes
  94. expect(currentPasswordInput.required).to.be.false
  95. expect(newPassword1Input.required).to.be.false
  96. expect(newPassword2Input.required).to.be.false
  97. fireEvent.change(currentPasswordInput, {
  98. target: { value: 'foobar' },
  99. })
  100. fireEvent.change(newPassword1Input, {
  101. target: { value: 'barbaz' },
  102. })
  103. fireEvent.change(newPassword2Input, {
  104. target: { value: 'barbaz' },
  105. })
  106. expect(currentPasswordInput.required).to.be.true
  107. expect(newPassword1Input.required).to.be.true
  108. expect(newPassword2Input.required).to.be.true
  109. })
  110. it('shows inflight state and success message', async function () {
  111. let finishUpdateCall
  112. fetchMock.post(
  113. '/user/password/update',
  114. new Promise(resolve => (finishUpdateCall = resolve))
  115. )
  116. render(<PasswordSection />)
  117. submitValidForm()
  118. await screen.findByText('Saving…')
  119. finishUpdateCall({
  120. status: 200,
  121. body: {
  122. message: {
  123. type: 'success',
  124. email: 'tim.alby@overleaf.com',
  125. text: 'Password changed',
  126. },
  127. },
  128. })
  129. await screen.findByRole('button', {
  130. name: 'Change',
  131. })
  132. screen.getByText('Password changed')
  133. })
  134. it('shows server error', async function () {
  135. fetchMock.post('/user/password/update', 500)
  136. render(<PasswordSection />)
  137. submitValidForm()
  138. await screen.findByText(
  139. 'Something went wrong talking to the server :(. Please try again.'
  140. )
  141. })
  142. it('shows server error message', async function () {
  143. fetchMock.post('/user/password/update', {
  144. status: 400,
  145. body: {
  146. message: 'Your old password is wrong',
  147. },
  148. })
  149. render(<PasswordSection />)
  150. submitValidForm()
  151. await screen.findByText('Your old password is wrong')
  152. })
  153. })
  154. function submitValidForm() {
  155. fireEvent.change(screen.getByLabelText('Current Password'), {
  156. target: { value: 'foobar' },
  157. })
  158. fireEvent.change(screen.getByLabelText('New Password'), {
  159. target: { value: 'barbaz' },
  160. })
  161. fireEvent.change(screen.getByLabelText('Confirm New Password'), {
  162. target: { value: 'barbaz' },
  163. })
  164. fireEvent.click(
  165. screen.getByRole('button', {
  166. name: 'Change',
  167. })
  168. )
  169. }