| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- import { screen, waitFor, within } from '@testing-library/react'
- import userEvent from '@testing-library/user-event'
- import { toast } from 'sonner'
- import { beforeEach, describe, expect, it, vi } from 'vitest'
- import { TEMPLATES_SCHEMAS } from './AuthTemplatesValidation'
- import { TemplateEditor } from './TemplateEditor'
- import { render } from '@/tests/helpers'
- const {
- resetTemplateMock,
- updateAuthConfigMock,
- useAuthConfigQueryMock,
- useAuthTemplateResetMutationMock,
- useAuthConfigUpdateMutationMock,
- useAsyncCheckPermissionsMock,
- validateSpamMock,
- } = vi.hoisted(() => ({
- resetTemplateMock: vi.fn(),
- updateAuthConfigMock: vi.fn(),
- useAuthConfigQueryMock: vi.fn(),
- useAuthTemplateResetMutationMock: vi.fn(),
- useAuthConfigUpdateMutationMock: vi.fn(),
- useAsyncCheckPermissionsMock: vi.fn(),
- validateSpamMock: vi.fn(),
- }))
- vi.mock(import('common'), async (importOriginal) => {
- const actual = await importOriginal()
- return {
- ...actual,
- useParams: vi.fn().mockReturnValue({ ref: 'project-ref' }),
- }
- })
- vi.mock('@/components/ui/CodeEditor/CodeEditor', () => ({
- CodeEditor: ({
- value,
- onInputChange,
- }: {
- value: string
- onInputChange: (value: string) => void
- }) => (
- <textarea
- aria-label="Body source"
- value={value}
- onChange={(event) => onInputChange(event.target.value)}
- />
- ),
- }))
- vi.mock('@/components/ui-patterns/Dialogs/PreventNavigationOnUnsavedChanges', () => ({
- PreventNavigationOnUnsavedChanges: () => null,
- }))
- vi.mock('@/data/auth/auth-config-query', () => ({
- useAuthConfigQuery: useAuthConfigQueryMock,
- }))
- vi.mock('@/data/auth/auth-config-update-mutation', () => ({
- useAuthConfigUpdateMutation: useAuthConfigUpdateMutationMock,
- }))
- vi.mock('@/data/auth/auth-template-reset-mutation', () => ({
- useAuthTemplateResetMutation: useAuthTemplateResetMutationMock,
- }))
- vi.mock('@/data/auth/validate-spam-mutation', () => ({
- useValidateSpamMutation: () => ({ mutate: validateSpamMock }),
- }))
- vi.mock('@/hooks/misc/useCheckPermissions', () => ({
- useAsyncCheckPermissions: useAsyncCheckPermissionsMock,
- }))
- vi.mock('sonner', () => ({
- toast: {
- success: vi.fn(),
- error: vi.fn(),
- },
- }))
- const confirmationTemplate = TEMPLATES_SCHEMAS.find((template) => template.id === 'CONFIRMATION')!
- const createAuthConfig = ({
- subject = 'Confirm your email address',
- body,
- hasCustomBody,
- hasCustomSubject = false,
- }: {
- subject?: string
- body: string
- hasCustomBody: boolean
- hasCustomSubject?: boolean
- }) => ({
- MAILER_SUBJECTS_CONFIRMATION: subject,
- MAILER_SUBJECTS_CUSTOM_CONTENTS: {
- MAILER_SUBJECTS_CONFIRMATION: hasCustomSubject,
- },
- MAILER_TEMPLATES_CONFIRMATION_CONTENT: body,
- MAILER_TEMPLATES_CUSTOM_CONTENTS: {
- MAILER_TEMPLATES_CONFIRMATION_CONTENT: hasCustomBody,
- },
- SMTP_HOST: 'smtp.example.com',
- SMTP_PASS: 'password',
- SMTP_USER: 'user',
- })
- const renderTemplateEditor = ({
- body = '<p>Default template</p>',
- canUpdateConfig = true,
- hasCustomBody = false,
- hasCustomSubject = false,
- }: {
- body?: string
- canUpdateConfig?: boolean
- hasCustomBody?: boolean
- hasCustomSubject?: boolean
- } = {}) => {
- useAuthConfigQueryMock.mockReturnValue({
- data: createAuthConfig({ body, hasCustomBody, hasCustomSubject }),
- isSuccess: true,
- })
- useAsyncCheckPermissionsMock.mockReturnValue({ can: canUpdateConfig })
- useAuthConfigUpdateMutationMock.mockReturnValue({ mutate: updateAuthConfigMock })
- useAuthTemplateResetMutationMock.mockReturnValue({ mutate: resetTemplateMock })
- return render(<TemplateEditor template={confirmationTemplate} />)
- }
- describe('TemplateEditor reset to default', () => {
- beforeEach(() => {
- vi.clearAllMocks()
- validateSpamMock.mockImplementation((_vars, callbacks) => callbacks?.onSuccess?.({ rules: [] }))
- })
- const resetAuthConfig = createAuthConfig({
- subject: 'Confirm your email address',
- body: '<h2>Confirm your email address</h2>\n\n<p>Follow the link below to confirm this email address and finish signing up.</p>\n<p><a href="{{ .ConfirmationURL }}">Confirm email address</a></p>',
- hasCustomBody: false,
- hasCustomSubject: false,
- })
- it('hides reset when the API does not mark the body as custom', () => {
- renderTemplateEditor()
- expect(screen.queryByRole('button', { name: 'Reset template' })).not.toBeInTheDocument()
- })
- it('shows reset when the API marks the body as custom', () => {
- renderTemplateEditor({ hasCustomBody: true })
- expect(screen.getByRole('button', { name: 'Reset template' })).toBeInTheDocument()
- })
- it('shows reset when the API marks the subject as custom', () => {
- renderTemplateEditor({ hasCustomSubject: true })
- expect(screen.getByRole('button', { name: 'Reset template' })).toBeInTheDocument()
- })
- it('keeps reset visible while there are unsaved editor changes', async () => {
- const user = userEvent.setup()
- renderTemplateEditor({ hasCustomBody: true })
- await user.clear(screen.getByLabelText('Body source'))
- await user.type(screen.getByLabelText('Body source'), '<p>Unsaved body</p>')
- expect(screen.getByRole('button', { name: 'Reset template' })).toBeInTheDocument()
- })
- it('warns that reset discards unsaved changes', async () => {
- const user = userEvent.setup()
- renderTemplateEditor({ hasCustomBody: true })
- await user.clear(screen.getByLabelText('Body source'))
- await user.type(screen.getByLabelText('Body source'), '<p>Unsaved body</p>')
- await user.click(screen.getByRole('button', { name: 'Reset template' }))
- expect(
- await screen.findByText(
- 'This will discard your unsaved changes and use the default subject line and email body content.'
- )
- ).toBeInTheDocument()
- })
- it('resets the template through the dedicated reset endpoint after confirmation', async () => {
- const user = userEvent.setup()
- resetTemplateMock.mockImplementation((_vars, callbacks) =>
- callbacks?.onSuccess?.(resetAuthConfig)
- )
- renderTemplateEditor({ hasCustomBody: true })
- await user.click(screen.getByRole('button', { name: 'Reset template' }))
- const dialog = await screen.findByRole('alertdialog')
- await user.click(within(dialog).getByRole('button', { name: 'Reset' }))
- await waitFor(() =>
- expect(resetTemplateMock).toHaveBeenCalledWith(
- {
- projectRef: 'project-ref',
- template: 'confirmation',
- },
- expect.any(Object)
- )
- )
- expect(toast.success).toHaveBeenCalledWith('Email template reset to default')
- })
- it('does not reset through the auth config update payload', async () => {
- const user = userEvent.setup()
- resetTemplateMock.mockImplementation((_vars, callbacks) =>
- callbacks?.onSuccess?.(resetAuthConfig)
- )
- renderTemplateEditor({ hasCustomBody: true })
- await user.click(screen.getByRole('button', { name: 'Reset template' }))
- const dialog = await screen.findByRole('alertdialog')
- await user.click(within(dialog).getByRole('button', { name: 'Reset' }))
- await waitFor(() => expect(resetTemplateMock).toHaveBeenCalled())
- expect(updateAuthConfigMock).not.toHaveBeenCalled()
- })
- it('uses the reset response as the new editor state', async () => {
- const user = userEvent.setup()
- resetTemplateMock.mockImplementation((_vars, callbacks) =>
- callbacks?.onSuccess?.(resetAuthConfig)
- )
- renderTemplateEditor({
- body: '<p>Custom body</p>',
- hasCustomBody: true,
- hasCustomSubject: true,
- })
- await user.click(screen.getByRole('button', { name: 'Reset template' }))
- const dialog = await screen.findByRole('alertdialog')
- await user.click(within(dialog).getByRole('button', { name: 'Reset' }))
- await waitFor(() => {
- expect(screen.getByDisplayValue('Confirm your email address')).toBeInTheDocument()
- expect(screen.getByLabelText('Body source')).toHaveValue(
- '<h2>Confirm your email address</h2>\n\n<p>Follow the link below to confirm this email address and finish signing up.</p>\n<p><a href="{{ .ConfirmationURL }}">Confirm email address</a></p>'
- )
- })
- })
- it('disables reset when the user cannot update auth config', () => {
- renderTemplateEditor({ hasCustomBody: true, canUpdateConfig: false })
- expect(screen.getByRole('button', { name: 'Reset template' })).toBeDisabled()
- })
- })
|