Files
overleaf-cep/services/web/test/frontend/shared/hooks/use-tutorial.spec.tsx
Mathias Jakobsen c67885919b Merge pull request #33141 from overleaf/mj-tutorials-show-one
[web] Ensure only one tutorial shows at once

GitOrigin-RevId: 797c677a3d45635451485d79ed1c0705819ed5ad
2026-05-01 08:07:29 +00:00

187 lines
5.2 KiB
TypeScript

import useTutorial from '@/shared/hooks/promotions/use-tutorial'
import { useEffect, useState } from 'react'
import { EditorProviders } from '../../helpers/editor-providers'
import { makeTutorialProvider } from '../../helpers/make-tutorial-provider'
const TutorialTester = ({
tutorial,
failSilently,
}: {
tutorial: string
failSilently?: boolean
}) => {
const {
tryShowingPopup,
dismissTutorial,
checkCompletion,
showPopup,
completeTutorial,
} = useTutorial(tutorial)
const [error, setError] = useState(false)
useEffect(() => {
if (!checkCompletion()) {
tryShowingPopup()
}
}, [checkCompletion, tryShowingPopup])
if (error) {
return <div>{tutorial} error</div>
}
if (!showPopup) {
return null
}
return (
<div>
<p>{tutorial} active</p>
<button onClick={() => dismissTutorial('promo-dismiss')}>Dismiss</button>
<button
onClick={() =>
completeTutorial(
{ action: 'complete', event: 'promo-click' },
{ failSilently }
).catch(_ => {
setError(true)
})
}
>
Complete
</button>
</div>
)
}
describe('useTutorial', function () {
beforeEach(function () {
cy.intercept('POST', '/tutorial/test-tutorial/complete', {
statusCode: 200,
}).as('completeTutorial')
})
describe('with a tutorial that is not completed', function () {
it('shows the popup', function () {
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial" />
</EditorProviders>
)
cy.findByText('test-tutorial active').should('be.visible')
})
it('dismisses the popup', function () {
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial" />
</EditorProviders>
)
cy.findByRole('button', { name: 'Dismiss' }).click()
cy.findByText('test-tutorial active').should('not.exist')
cy.wait('@completeTutorial')
})
it('completes the tutorial', function () {
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial" />
</EditorProviders>
)
cy.findByRole('button', { name: 'Complete' }).click()
cy.findByText('test-tutorial active').should('not.exist')
cy.wait('@completeTutorial')
})
})
describe('with a tutorial that is already completed', function () {
it('does not show the popup', function () {
cy.mount(
<EditorProviders
providers={{
TutorialProvider: makeTutorialProvider({
inactiveTutorials: ['test-tutorial'],
}),
}}
>
<TutorialTester tutorial="test-tutorial" />
</EditorProviders>
)
cy.findByText('test-tutorial active').should('not.exist')
})
})
describe('with a tutorial that fails to complete', function () {
it('fails silently by default', function () {
cy.intercept('POST', '/tutorial/test-tutorial/complete', {
statusCode: 500,
}).as('completeTutorialFailure')
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial" />
</EditorProviders>
)
cy.findByRole('button', { name: 'Complete' }).click()
cy.findByText('test-tutorial active').should('not.exist')
cy.wait('@completeTutorialFailure')
})
it('throws an error if failSilently is set to false', function () {
cy.intercept('POST', '/tutorial/test-tutorial/complete', {
statusCode: 500,
}).as('completeTutorialFailure')
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial" failSilently={false} />
</EditorProviders>
)
cy.findByRole('button', { name: 'Complete' }).click()
cy.wait('@completeTutorialFailure')
cy.findByText('test-tutorial error').should('be.visible')
cy.findByText('test-tutorial active').should('not.exist')
})
})
describe('for two tutorials at the same time', function () {
beforeEach(function () {
cy.intercept('POST', '/tutorial/test-tutorial-1/complete', {
statusCode: 200,
}).as('completeTutorial1')
cy.intercept('POST', '/tutorial/test-tutorial-2/complete', {
statusCode: 200,
}).as('completeTutorial2')
})
it('only shows one popup at a time', function () {
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial-1" />
<TutorialTester tutorial="test-tutorial-2" />
</EditorProviders>
)
cy.findAllByText(/active/).should('have.length', 1)
})
it('shows the second popup after the first is completed', function () {
cy.mount(
<EditorProviders>
<TutorialTester tutorial="test-tutorial-1" />
<TutorialTester tutorial="test-tutorial-2" />
</EditorProviders>
)
cy.findByText('test-tutorial-1 active').should('be.visible')
cy.findByText('test-tutorial-2 active').should('not.exist')
cy.findByRole('button', { name: 'Complete' }).click()
cy.wait('@completeTutorial1')
cy.findByText('test-tutorial-1 active').should('not.exist')
cy.findByText('test-tutorial-2 active').should('be.visible')
})
})
})