use-tutorial.spec.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import useTutorial from '@/shared/hooks/promotions/use-tutorial'
  2. import { useEffect, useState } from 'react'
  3. import { EditorProviders } from '../../helpers/editor-providers'
  4. import { makeTutorialProvider } from '../../helpers/make-tutorial-provider'
  5. const TutorialTester = ({
  6. tutorial,
  7. failSilently,
  8. }: {
  9. tutorial: string
  10. failSilently?: boolean
  11. }) => {
  12. const {
  13. tryShowingPopup,
  14. dismissTutorial,
  15. checkCompletion,
  16. showPopup,
  17. completeTutorial,
  18. } = useTutorial(tutorial)
  19. const [error, setError] = useState(false)
  20. useEffect(() => {
  21. if (!checkCompletion()) {
  22. tryShowingPopup()
  23. }
  24. }, [checkCompletion, tryShowingPopup])
  25. if (error) {
  26. return <div>{tutorial} error</div>
  27. }
  28. if (!showPopup) {
  29. return null
  30. }
  31. return (
  32. <div>
  33. <p>{tutorial} active</p>
  34. <button onClick={() => dismissTutorial('promo-dismiss')}>Dismiss</button>
  35. <button
  36. onClick={() =>
  37. completeTutorial(
  38. { action: 'complete', event: 'promo-click' },
  39. { failSilently }
  40. ).catch(_ => {
  41. setError(true)
  42. })
  43. }
  44. >
  45. Complete
  46. </button>
  47. </div>
  48. )
  49. }
  50. describe('useTutorial', function () {
  51. beforeEach(function () {
  52. cy.intercept('POST', '/tutorial/test-tutorial/complete', {
  53. statusCode: 200,
  54. }).as('completeTutorial')
  55. })
  56. describe('with a tutorial that is not completed', function () {
  57. it('shows the popup', function () {
  58. cy.mount(
  59. <EditorProviders>
  60. <TutorialTester tutorial="test-tutorial" />
  61. </EditorProviders>
  62. )
  63. cy.findByText('test-tutorial active').should('be.visible')
  64. })
  65. it('dismisses the popup', function () {
  66. cy.mount(
  67. <EditorProviders>
  68. <TutorialTester tutorial="test-tutorial" />
  69. </EditorProviders>
  70. )
  71. cy.findByRole('button', { name: 'Dismiss' }).click()
  72. cy.findByText('test-tutorial active').should('not.exist')
  73. cy.wait('@completeTutorial')
  74. })
  75. it('completes the tutorial', function () {
  76. cy.mount(
  77. <EditorProviders>
  78. <TutorialTester tutorial="test-tutorial" />
  79. </EditorProviders>
  80. )
  81. cy.findByRole('button', { name: 'Complete' }).click()
  82. cy.findByText('test-tutorial active').should('not.exist')
  83. cy.wait('@completeTutorial')
  84. })
  85. })
  86. describe('with a tutorial that is already completed', function () {
  87. it('does not show the popup', function () {
  88. cy.mount(
  89. <EditorProviders
  90. providers={{
  91. TutorialProvider: makeTutorialProvider({
  92. inactiveTutorials: ['test-tutorial'],
  93. }),
  94. }}
  95. >
  96. <TutorialTester tutorial="test-tutorial" />
  97. </EditorProviders>
  98. )
  99. cy.findByText('test-tutorial active').should('not.exist')
  100. })
  101. })
  102. describe('with a tutorial that fails to complete', function () {
  103. it('fails silently by default', function () {
  104. cy.intercept('POST', '/tutorial/test-tutorial/complete', {
  105. statusCode: 500,
  106. }).as('completeTutorialFailure')
  107. cy.mount(
  108. <EditorProviders>
  109. <TutorialTester tutorial="test-tutorial" />
  110. </EditorProviders>
  111. )
  112. cy.findByRole('button', { name: 'Complete' }).click()
  113. cy.findByText('test-tutorial active').should('not.exist')
  114. cy.wait('@completeTutorialFailure')
  115. })
  116. it('throws an error if failSilently is set to false', function () {
  117. cy.intercept('POST', '/tutorial/test-tutorial/complete', {
  118. statusCode: 500,
  119. }).as('completeTutorialFailure')
  120. cy.mount(
  121. <EditorProviders>
  122. <TutorialTester tutorial="test-tutorial" failSilently={false} />
  123. </EditorProviders>
  124. )
  125. cy.findByRole('button', { name: 'Complete' }).click()
  126. cy.wait('@completeTutorialFailure')
  127. cy.findByText('test-tutorial error').should('be.visible')
  128. cy.findByText('test-tutorial active').should('not.exist')
  129. })
  130. })
  131. describe('for two tutorials at the same time', function () {
  132. beforeEach(function () {
  133. cy.intercept('POST', '/tutorial/test-tutorial-1/complete', {
  134. statusCode: 200,
  135. }).as('completeTutorial1')
  136. cy.intercept('POST', '/tutorial/test-tutorial-2/complete', {
  137. statusCode: 200,
  138. }).as('completeTutorial2')
  139. })
  140. it('only shows one popup at a time', function () {
  141. cy.mount(
  142. <EditorProviders>
  143. <TutorialTester tutorial="test-tutorial-1" />
  144. <TutorialTester tutorial="test-tutorial-2" />
  145. </EditorProviders>
  146. )
  147. cy.findAllByText(/active/).should('have.length', 1)
  148. })
  149. it('shows the second popup after the first is completed', function () {
  150. cy.mount(
  151. <EditorProviders>
  152. <TutorialTester tutorial="test-tutorial-1" />
  153. <TutorialTester tutorial="test-tutorial-2" />
  154. </EditorProviders>
  155. )
  156. cy.findByText('test-tutorial-1 active').should('be.visible')
  157. cy.findByText('test-tutorial-2 active').should('not.exist')
  158. cy.findByRole('button', { name: 'Complete' }).click()
  159. cy.wait('@completeTutorial1')
  160. cy.findByText('test-tutorial-1 active').should('not.exist')
  161. cy.findByText('test-tutorial-2 active').should('be.visible')
  162. })
  163. })
  164. })