You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.spec.tsx 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import React from 'react'
  2. import { act, render, screen, waitFor } from '@testing-library/react'
  3. import Toast, { ToastProvider, useToastContext } from '.'
  4. import '@testing-library/jest-dom'
  5. // Mock timers for testing timeouts
  6. jest.useFakeTimers()
  7. const TestComponent = () => {
  8. const { notify, close } = useToastContext()
  9. return (
  10. <div>
  11. <button onClick={() => notify({ message: 'Notification message', type: 'info' })}>
  12. Show Toast
  13. </button>
  14. <button onClick={close}>Close Toast</button>
  15. </div>
  16. )
  17. }
  18. describe('Toast', () => {
  19. describe('Toast Component', () => {
  20. test('renders toast with correct type and message', () => {
  21. render(
  22. <ToastProvider>
  23. <Toast type="success" message="Success message" />
  24. </ToastProvider>,
  25. )
  26. expect(screen.getByText('Success message')).toBeInTheDocument()
  27. })
  28. test('renders with different types', () => {
  29. const { rerender } = render(
  30. <ToastProvider>
  31. <Toast type="success" message="Success message" />
  32. </ToastProvider>,
  33. )
  34. expect(document.querySelector('.text-text-success')).toBeInTheDocument()
  35. rerender(
  36. <ToastProvider>
  37. <Toast type="error" message="Error message" />
  38. </ToastProvider>,
  39. )
  40. expect(document.querySelector('.text-text-destructive')).toBeInTheDocument()
  41. })
  42. test('renders with custom component', () => {
  43. render(
  44. <ToastProvider>
  45. <Toast
  46. message="Message with custom component"
  47. customComponent={<span data-testid="custom-component">Custom</span>}
  48. />
  49. </ToastProvider>,
  50. )
  51. expect(screen.getByTestId('custom-component')).toBeInTheDocument()
  52. })
  53. test('renders children content', () => {
  54. render(
  55. <ToastProvider>
  56. <Toast message="Message with children">
  57. <span>Additional information</span>
  58. </Toast>
  59. </ToastProvider>,
  60. )
  61. expect(screen.getByText('Additional information')).toBeInTheDocument()
  62. })
  63. test('does not render close button when close is undefined', () => {
  64. // Create a modified context where close is undefined
  65. const CustomToastContext = React.createContext({ notify: () => { }, close: undefined })
  66. // Create a wrapper component using the custom context
  67. const Wrapper = ({ children }: any) => (
  68. <CustomToastContext.Provider value={{ notify: () => { }, close: undefined }}>
  69. {children}
  70. </CustomToastContext.Provider>
  71. )
  72. render(
  73. <Wrapper>
  74. <Toast message="No close button" type="info" />
  75. </Wrapper>,
  76. )
  77. expect(screen.getByText('No close button')).toBeInTheDocument()
  78. // Ensure the close button is not rendered
  79. expect(document.querySelector('.h-4.w-4.shrink-0.text-text-tertiary')).not.toBeInTheDocument()
  80. })
  81. })
  82. describe('ToastProvider and Context', () => {
  83. test('shows and hides toast using context', async () => {
  84. render(
  85. <ToastProvider>
  86. <TestComponent />
  87. </ToastProvider>,
  88. )
  89. // No toast initially
  90. expect(screen.queryByText('Notification message')).not.toBeInTheDocument()
  91. // Show toast
  92. act(() => {
  93. screen.getByText('Show Toast').click()
  94. })
  95. expect(screen.getByText('Notification message')).toBeInTheDocument()
  96. // Close toast
  97. act(() => {
  98. screen.getByText('Close Toast').click()
  99. })
  100. expect(screen.queryByText('Notification message')).not.toBeInTheDocument()
  101. })
  102. test('automatically hides toast after duration', async () => {
  103. render(
  104. <ToastProvider>
  105. <TestComponent />
  106. </ToastProvider>,
  107. )
  108. // Show toast
  109. act(() => {
  110. screen.getByText('Show Toast').click()
  111. })
  112. expect(screen.getByText('Notification message')).toBeInTheDocument()
  113. // Fast-forward timer
  114. act(() => {
  115. jest.advanceTimersByTime(3000) // Default for info type is 3000ms
  116. })
  117. // Toast should be gone
  118. await waitFor(() => {
  119. expect(screen.queryByText('Notification message')).not.toBeInTheDocument()
  120. })
  121. })
  122. })
  123. describe('Toast.notify static method', () => {
  124. test('creates and removes toast from DOM', async () => {
  125. act(() => {
  126. // Call the static method
  127. Toast.notify({ message: 'Static notification', type: 'warning' })
  128. })
  129. // Toast should be in document
  130. expect(screen.getByText('Static notification')).toBeInTheDocument()
  131. // Fast-forward timer
  132. act(() => {
  133. jest.advanceTimersByTime(6000) // Default for warning type is 6000ms
  134. })
  135. // Toast should be removed
  136. await waitFor(() => {
  137. expect(screen.queryByText('Static notification')).not.toBeInTheDocument()
  138. })
  139. })
  140. test('calls onClose callback after duration', async () => {
  141. const onCloseMock = jest.fn()
  142. act(() => {
  143. Toast.notify({
  144. message: 'Closing notification',
  145. type: 'success',
  146. onClose: onCloseMock,
  147. })
  148. })
  149. // Fast-forward timer
  150. act(() => {
  151. jest.advanceTimersByTime(3000) // Default for success type is 3000ms
  152. })
  153. // onClose should be called
  154. await waitFor(() => {
  155. expect(onCloseMock).toHaveBeenCalled()
  156. })
  157. })
  158. })
  159. })