sso-widget.test.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import {
  4. screen,
  5. fireEvent,
  6. render,
  7. waitFor,
  8. within,
  9. } from '@testing-library/react'
  10. import { FetchError } from '../../../../../../frontend/js/infrastructure/fetch-json'
  11. import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/sso-widget'
  12. describe('<SSOLinkingWidget />', function () {
  13. const defaultProps = {
  14. providerId: 'integration_id',
  15. title: 'Integration',
  16. description: 'Integration description',
  17. helpPath: '/help/integration',
  18. linkPath: '/integration/link',
  19. onUnlink: () => Promise.resolve(),
  20. }
  21. it('should render', function () {
  22. render(<SSOLinkingWidget {...defaultProps} />)
  23. screen.getByText('Integration')
  24. screen.getByText('Integration description')
  25. expect(
  26. screen
  27. .getByRole('link', { name: 'Learn more about Integration' })
  28. .getAttribute('href')
  29. ).to.equal('/help/integration')
  30. })
  31. describe('when unlinked', function () {
  32. it('should render a link to `linkPath`', function () {
  33. render(<SSOLinkingWidget {...defaultProps} linked={false} />)
  34. expect(
  35. screen
  36. .getByRole('link', { name: 'Link Integration' })
  37. .getAttribute('href')
  38. ).to.equal('/integration/link?intent=link')
  39. })
  40. })
  41. describe('when linked', function () {
  42. let unlinkFunction: sinon.SinonStub
  43. beforeEach(function () {
  44. unlinkFunction = sinon.stub()
  45. render(
  46. <SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
  47. )
  48. })
  49. it('should display an `unlink` button', function () {
  50. screen.getByRole('button', { name: 'Unlink Integration' })
  51. })
  52. it('should open a modal to confirm integration unlinking', function () {
  53. fireEvent.click(
  54. screen.getByRole('button', { name: 'Unlink Integration' })
  55. )
  56. screen.getByText('Unlink Integration Account')
  57. screen.getByText(
  58. 'Warning: When you unlink your account from Integration you will not be able to sign in using Integration anymore.'
  59. )
  60. })
  61. it('should cancel unlinking when clicking cancel in the confirmation modal', async function () {
  62. fireEvent.click(
  63. screen.getByRole('button', { name: 'Unlink Integration' })
  64. )
  65. const cancelBtn = screen.getByRole('button', {
  66. name: 'Cancel',
  67. hidden: false,
  68. })
  69. fireEvent.click(cancelBtn)
  70. await screen.findByRole('button', { name: 'Cancel', hidden: true })
  71. expect(unlinkFunction).not.to.have.been.called
  72. })
  73. })
  74. describe('unlinking an account', function () {
  75. let confirmBtn: HTMLElement, unlinkFunction: sinon.SinonStub
  76. beforeEach(function () {
  77. unlinkFunction = sinon.stub()
  78. render(
  79. <SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
  80. )
  81. fireEvent.click(
  82. screen.getByRole('button', { name: 'Unlink Integration' })
  83. )
  84. confirmBtn = within(screen.getByRole('dialog')).getByRole('button', {
  85. name: 'Unlink',
  86. hidden: false,
  87. })
  88. })
  89. it('should make an `unlink` request', function () {
  90. unlinkFunction.resolves()
  91. fireEvent.click(confirmBtn)
  92. expect(unlinkFunction).to.have.been.called
  93. })
  94. it('should display feedback while the request is inflight', async function () {
  95. unlinkFunction.returns(
  96. new Promise<void>(resolve => {
  97. setTimeout(resolve, 500)
  98. })
  99. )
  100. fireEvent.click(confirmBtn)
  101. await waitFor(
  102. () => expect(screen.getByRole('button', { name: 'Unlinking' })).to.exist
  103. )
  104. })
  105. })
  106. describe('when unlinking fails', function () {
  107. beforeEach(function () {
  108. const unlinkFunction = sinon
  109. .stub()
  110. .rejects(new FetchError('unlinking failed', ''))
  111. render(
  112. <SSOLinkingWidget {...defaultProps} linked onUnlink={unlinkFunction} />
  113. )
  114. fireEvent.click(
  115. screen.getByRole('button', { name: 'Unlink Integration' })
  116. )
  117. const confirmBtn = within(screen.getByRole('dialog')).getByRole(
  118. 'button',
  119. {
  120. name: 'Unlink',
  121. hidden: false,
  122. }
  123. )
  124. fireEvent.click(confirmBtn)
  125. })
  126. it('should display an error message ', async function () {
  127. await screen.findByText('Something went wrong. Please try again.')
  128. })
  129. it('should display the unlink button ', async function () {
  130. await screen.findByRole('button', { name: 'Unlink Integration' })
  131. })
  132. })
  133. })