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