LogsPreviewer.test.tsx226 lines · main
1import { screen, waitFor } from '@testing-library/react'
2import userEvent from '@testing-library/user-event'
3import dayjs from 'dayjs'
4import utc from 'dayjs/plugin/utc'
5import { beforeEach, describe, expect, test, vi } from 'vitest'
6
7import { LOGS_API_MOCKS } from './logs.mocks'
8import { LogsTableName } from '@/components/interfaces/Settings/Logs/Logs.constants'
9import {
10 calculateBarClickTimeRange,
11 LogsPreviewer,
12} from '@/components/interfaces/Settings/Logs/LogsPreviewer'
13import useLogsPreview from '@/hooks/analytics/useLogsPreview'
14import { customRender, customRenderHook } from '@/tests/lib/custom-render'
15import { addAPIMock } from '@/tests/lib/msw'
16
17vi.mock('@/components/interfaces/Settings/Logs/LogTable', () => ({
18 LogTable: ({ data }: { data: any[] }) => (
19 <div data-testid="log-table-mock">
20 {data.map((row) => (
21 <div key={row.id} data-testid="log-row">
22 {row.event_message}
23 </div>
24 ))}
25 </div>
26 ),
27}))
28
29dayjs.extend(utc)
30
31vi.mock('common', async (importOriginal) => {
32 const actual = await importOriginal()
33 return {
34 useParams: vi.fn().mockReturnValue({}),
35 useIsLoggedIn: vi.fn(),
36 isBrowser: false,
37 LOCAL_STORAGE_KEYS: (actual as any).LOCAL_STORAGE_KEYS,
38 ...(actual as any),
39 }
40})
41
42vi.mock('@/lib/gotrue', async (importOriginal) => ({
43 ...(await importOriginal()),
44 auth: { onAuthStateChange: vi.fn() },
45}))
46
47beforeEach(() => {
48 addAPIMock({
49 method: 'get',
50 path: '/platform/projects/:ref/analytics/endpoints/logs.all',
51 response: LOGS_API_MOCKS,
52 })
53})
54
55test('search loads with whatever is on the URL', async () => {
56 customRender(
57 <LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />,
58 {
59 nuqs: {
60 searchParams: {
61 s: 'test-search-box-value',
62 },
63 },
64 }
65 )
66
67 await waitFor(() => {
68 expect(screen.getByRole('textbox')).toHaveValue('test-search-box-value')
69 })
70
71 await waitFor(() => {
72 expect(screen.getByRole('textbox')).not.toHaveValue('WRONGVALUE!🪿')
73 })
74})
75
76test('useLogsPreview returns data from MSW', async () => {
77 const { result } = customRenderHook(() =>
78 useLogsPreview({
79 projectRef: 'default',
80 table: LogsTableName.EDGE,
81 })
82 )
83
84 await waitFor(() => {
85 expect(result.current.isLoading).toBe(false)
86 })
87
88 await waitFor(() => {
89 expect(result.current.logData.length).toBeGreaterThan(0)
90 })
91
92 expect(result.current.logData).toEqual(LOGS_API_MOCKS.result)
93})
94
95test('LogsPreviewer passes API data to LogTable', async () => {
96 customRender(
97 <LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
98 )
99
100 const firstLogEventMessage = LOGS_API_MOCKS.result[0].event_message
101
102 await waitFor(() => {
103 expect(screen.getAllByText(firstLogEventMessage)[0]).toBeInTheDocument()
104 })
105})
106
107test('can toggle log event chart', async () => {
108 customRender(
109 <LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
110 )
111
112 expect(screen.getByRole('button', { name: /Chart/i })).toBeInTheDocument()
113
114 await waitFor(() => {
115 expect(screen.getByTestId('logs-bar-chart')).toBeInTheDocument()
116 })
117
118 await userEvent.click(screen.getByRole('button', { name: /Chart/i }))
119
120 await waitFor(() => {
121 expect(screen.queryByTestId('logs-bar-chart')).not.toBeInTheDocument()
122 })
123})
124
125test('can click load older', async () => {
126 customRender(
127 <LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
128 )
129
130 const loadOlder = await waitFor(
131 async () => await screen.findByRole('button', { name: /Load older/i }),
132 { timeout: 10000 }
133 )
134
135 const handleClick = vi.fn()
136 loadOlder.addEventListener('click', handleClick)
137
138 await userEvent.click(loadOlder)
139
140 expect(handleClick).toHaveBeenCalled()
141})
142
143describe('calculateBarClickTimeRange', () => {
144 const clickedTime = '2024-01-15T12:30:00.000Z'
145
146 test('uses 15-second range for time ranges less than 2 minutes', () => {
147 const rangeStart = '2024-01-15T12:00:00.000Z'
148 const rangeEnd = '2024-01-15T12:01:30.000Z' // 1.5 minutes
149
150 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
151
152 expect(result.start).toBe('2024-01-15T12:29:52.500Z') // 7.5 seconds before
153 expect(result.end).toBe('2024-01-15T12:30:07.500Z') // 7.5 seconds after
154 })
155
156 test('uses 2-minute range for time ranges between 2 minutes and 1 hour', () => {
157 const rangeStart = '2024-01-15T12:00:00.000Z'
158 const rangeEnd = '2024-01-15T12:30:00.000Z' // 30 minutes
159
160 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
161
162 expect(result.start).toBe('2024-01-15T12:29:00.000Z') // 1 minute before
163 expect(result.end).toBe('2024-01-15T12:31:00.000Z') // 1 minute after
164 })
165
166 test('uses 5-minute range for time ranges between 1 and 12 hours', () => {
167 const rangeStart = '2024-01-15T10:00:00.000Z'
168 const rangeEnd = '2024-01-15T14:00:00.000Z' // 4 hours
169
170 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
171
172 expect(result.start).toBe('2024-01-15T12:27:30.000Z') // 2.5 minutes before
173 expect(result.end).toBe('2024-01-15T12:32:30.000Z') // 2.5 minutes after
174 })
175
176 test('uses 1-hour range for time ranges 12 hours or more', () => {
177 const rangeStart = '2024-01-15T00:00:00.000Z'
178 const rangeEnd = '2024-01-15T24:00:00.000Z' // 24 hours
179
180 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
181
182 expect(result.start).toBe('2024-01-15T12:00:00.000Z') // 30 minutes before
183 expect(result.end).toBe('2024-01-15T13:00:00.000Z') // 30 minutes after
184 })
185
186 test('handles edge case of exactly 2 minutes range', () => {
187 const rangeStart = '2024-01-15T12:00:00.000Z'
188 const rangeEnd = '2024-01-15T12:02:00.000Z' // exactly 2 minutes
189
190 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
191
192 expect(result.start).toBe('2024-01-15T12:29:00.000Z') // 1 minute before
193 expect(result.end).toBe('2024-01-15T12:31:00.000Z') // 1 minute after
194 })
195
196 test('handles edge case of exactly 1 hour range', () => {
197 const rangeStart = '2024-01-15T12:00:00.000Z'
198 const rangeEnd = '2024-01-15T13:00:00.000Z' // exactly 1 hour
199
200 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
201
202 expect(result.start).toBe('2024-01-15T12:27:30.000Z') // 2.5 minutes before
203 expect(result.end).toBe('2024-01-15T12:32:30.000Z') // 2.5 minutes after
204 })
205
206 test('handles edge case of exactly 12 hours range', () => {
207 const rangeStart = '2024-01-15T00:00:00.000Z'
208 const rangeEnd = '2024-01-15T12:00:00.000Z' // exactly 12 hours
209
210 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, clickedTime)
211
212 expect(result.start).toBe('2024-01-15T12:00:00.000Z') // 30 minutes before
213 expect(result.end).toBe('2024-01-15T13:00:00.000Z') // 30 minutes after
214 })
215
216 test('handles different clicked timestamps correctly', () => {
217 const rangeStart = '2024-01-15T00:00:00.000Z'
218 const rangeEnd = '2024-01-15T24:00:00.000Z' // 24 hours
219 const differentClickedTime = '2024-01-15T06:15:30.000Z'
220
221 const result = calculateBarClickTimeRange(rangeStart, rangeEnd, differentClickedTime)
222
223 expect(result.start).toBe('2024-01-15T05:45:30.000Z') // 30 minutes before
224 expect(result.end).toBe('2024-01-15T06:45:30.000Z') // 30 minutes after
225 })
226})