import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import relativeTime from 'dayjs/plugin/relativeTime'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import { expect, test, vi } from 'vitest'
import { LogTable } from '@/components/interfaces/Settings/Logs/LogTable'
import { customRender as render } from '@/tests/lib/custom-render'
dayjs.extend(customParseFormat)
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(relativeTime)
vi.mock('next/router', () => import('next-router-mock'))
vi.mock('react-data-grid', () => ({
default: ({ columns, rows, renderers, role, headerRowHeight }: any) => (
{headerRowHeight !== 0 && (
{columns.map((col: any, colIdx: number) => (
{col.renderHeaderCell ? col.renderHeaderCell({}) : col.name}
))}
)}
{rows.map((row: any, rowIdx: number) => (
{columns.map((col: any, colIdx: number) => (
{col.renderCell?.({ row, rowIdx, isCellSelected: false })}
))}
))}
{rows.length === 0 && renderers?.noRowsFallback}
),
Row: ({ row, ...props }: any) => ,
}))
const fakeMicroTimestamp = dayjs().unix() * 1000
const LOG_DATA = {
id: 'some-uuid',
timestamp: 1621323232312,
event_message: 'event message',
metadata: {
my_key: 'something_value',
},
}
test('can display log data', async () => {
render(
<>
>
)
await screen.findAllByText(LOG_DATA.timestamp)
})
test('Shows total results', async () => {
render()
await screen.getByText(/results \(1\)/i)
})
test('can run if no queryType provided', async () => {
const mockRun = vi.fn()
render(
)
const run = await screen.findByText('Run')
await userEvent.click(run)
// expect(mockRun).toBeCalled()
})
test('can run if no queryType provided', async () => {
const mockRun = vi.fn()
render(
)
const run = await screen.findByText('Run')
await userEvent.click(run)
// expect(mockRun).toBeCalled()
})
test('dedupes log lines with exact id', async () => {
// chronological mode requires 4 columns
render(
)
// should only have one element, this line will fail if there are >1 element
await screen.findByText('timestamp')
})
test('can display standard preview table columns', async () => {
render(
)
await waitFor(() => screen.getByText(/some event message/))
await expect(screen.findByText(/12345/)).rejects.toThrow()
await expect(screen.findByText(fakeMicroTimestamp)).rejects.toThrow()
})
test("closes the selection if the selected row's data changes", async () => {
const { rerender } = render(
)
const text = await screen.findByText(/some event message/)
await userEvent.click(text)
rerender(
)
await expect(screen.findByText(/some event message/)).rejects.toThrow()
await screen.findByText(/some other message/)
})
enum QueryType {
Functions = 'functions',
Api = 'api',
Auth = 'auth',
}
test.each([
{
queryType: QueryType.Functions,
data: [
{
event_message: 'This is a error log\n',
event_type: 'log',
function_id: '001b0b08-331c-403e-810c-a2004b03a019',
level: 'error',
timestamp: 1659545029083869,
id: '3475cf6f-2929-4296-ab44-ce2c17069937',
},
],
includes: [/ERROR/],
excludes: ['undefined', 'null'],
},
{
queryType: QueryType.Functions,
data: [
{
event_message: 'This is a uncaughtExceptop\n',
event_type: 'uncaughtException',
function_id: '001b0b08-331c-403e-810c-a2004b03a019',
timestamp: 1659545029083869,
id: '4475cf6f-2929-4296-ab44-ce2c17069937',
level: undefined,
},
],
includes: [/uncaughtException/],
excludes: [/ERROR/],
},
{
queryType: QueryType.Api,
data: [
{
event_message: 'This is a uncaughtException\n',
path: 'this-is-some-path',
method: 'POST',
status_code: 500,
timestamp: 1659545029083869,
id: '4475cf6f-2929-4296-ab44-ce2c17069937',
},
],
includes: [/POST/, 'this-is-some-path'],
excludes: [],
},
{
queryType: QueryType.Auth,
data: [
{
event_message: JSON.stringify({ msg: 'some message', path: '/auth-path', level: 'info' }),
msg: 'some message',
path: '/auth-path',
level: 'info',
timestamp: 1659545029083869,
id: '4475cf6f-2929-4296-ab44-ce2c17069937',
},
],
includes: [/auth\-path/, /some message/, /INFO/],
excludes: [/\{/, /\}/],
},
])('table col renderer for $queryType', async ({ queryType, data, includes, excludes }) => {
render()
await Promise.all([
...includes.map((text) => screen.findByText(text)),
...excludes.map((text) => expect(screen.findByText(text)).rejects.toThrow()),
])
})
test('error message handling', async () => {
// Render LogTable with error as a string
render()
expect(screen.getByText(`some error message`)).toBeTruthy()
// Rerender LogTable with error as null
render()
// Add any additional assertions if LogTable behaves differently when error is null
})
test('no results message handling', async () => {
render()
await screen.findByText(/No results/)
await screen.findByText(/Try another search/)
})
test('custom error message: Resources exceeded during query execution', async () => {
const errorFromLogflare = {
error: {
code: 400,
errors: [
{
domain: 'global',
message:
'Resources exceeded during query execution: The query could not be executed in the allotted memory. Peak usage: 122% of limit.\nTop memory consumer(s):\n ORDER BY operations: 99%\n other/unattributed: 1%\n',
reason: 'resourcesExceeded',
},
],
message:
'Resources exceeded during query execution: The query could not be executed in the allotted memory. Peak usage: 122% of limit.\nTop memory consumer(s):\n ORDER BY operations: 99%\n other/unattributed: 1%\n',
status: 'INVALID_ARGUMENT',
},
}
// logs explorer, custom query
const { rerender } = render()
// prompt user to reduce selected tables
await screen.findByText(/This query requires too much memory to be executed/)
await screen.findByText(
/Avoid selecting entire objects and instead select specific keys using dot notation/
)
// previewer, prompt to reduce time range
rerender()
await screen.findByText(/This query requires too much memory to be executed/)
await screen.findByText(/Avoid querying across a large datetime range/)
await screen.findByText(/Please contact support if this error persists/)
})