Improve Integration Test Setup and re-add LogPreviewer tests (#35358)
* fix logs previewer and msw * refactor api mocking for better dx * update readme * comment out error handler for vitest * rm unnecessary tests * fix custom render nuqs type * add logs search test * rm unnecessary import * update readme with customRender and customRednerHook * rm unnecessary api handler * Move the NODE_ENV to the studio build command in turbo.json. * Update apps/studio/tests/README.md Co-authored-by: Joshen Lim <joshenlimek@gmail.com> * add cursor rule --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
6
.cursor/rules/unit-integration-testing.mdc
Normal file
6
.cursor/rules/unit-integration-testing.mdc
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
description:
|
||||
globs: apps/studio/**/*.test.ts,apps/studio/**/*.test.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
Make sure to follow the guidelines in this file to write tests: [README.md](mdc:apps/studio/tests/README.md)
|
||||
@@ -5,6 +5,7 @@ export * from './infrastructure'
|
||||
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
|
||||
|
||||
export const API_URL = (() => {
|
||||
if (process.env.NODE_ENV === 'test') return 'http://localhost:3000/api'
|
||||
// If running in platform, use API_URL from the env var
|
||||
if (IS_PLATFORM) return process.env.NEXT_PUBLIC_API_URL!
|
||||
// If running in browser, let it add the host
|
||||
|
||||
@@ -1,6 +1,75 @@
|
||||
# UI Testing Notes
|
||||
|
||||
### `<Popover>` vs `<Dropdown>`
|
||||
## Rules
|
||||
|
||||
- All tests should be run consistently (avoid situations whereby tests fails "sometimes")
|
||||
|
||||
- Group tests in folders based on the feature they are testing. Avoid file/folder based folder names since those can change and we will forget to update the tests.
|
||||
|
||||
Examples: /logs /reports /projects /database-settings /auth
|
||||
|
||||
## Custom Render and Custom Render Hook
|
||||
|
||||
`customRender` and `customRenderHook` are wrappers around `render` and `renderHook` that add some necessary providers like `QueryClientProvider`, `TooltipProvider` and `NuqsTestingAdapter`.
|
||||
|
||||
Generally use those instead of the default `render` and `renderHook` functions.
|
||||
|
||||
```ts
|
||||
import { customRender, customRenderHook } from 'tests/lib/custom-render'
|
||||
|
||||
customRender(<MyComponent />)
|
||||
customRenderHook(() => useMyHook())
|
||||
```
|
||||
|
||||
## Mocking API Requests
|
||||
|
||||
To mock API requests, we use the `msw` library.
|
||||
|
||||
Global mocks can be found in `tests/lib/msw-global-api-mocks.ts`.
|
||||
|
||||
To mock an endpoint you can use the `addAPIMock` function. Make sure to add the mock in the `beforeEach` hook. It won't work with `beforeAll` if you have many tests.
|
||||
|
||||
```ts
|
||||
beforeEach(() => {
|
||||
addAPIMock({
|
||||
method: 'get',
|
||||
path: '/api/my-endpoint',
|
||||
response: {
|
||||
data: { foo: 'bar' },
|
||||
},
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### API Mocking Tips:
|
||||
|
||||
- Keep mocks in the same folder as the tests that use them
|
||||
- Add a test to verify the mock is working
|
||||
|
||||
This will make debugging and updating the mocks easier.
|
||||
|
||||
```ts
|
||||
test('mock is working', async () => {
|
||||
const response = await fetch('/api/my-endpoint')
|
||||
expect(response.json()).resolves.toEqual({ data: { foo: 'bar' } })
|
||||
})
|
||||
```
|
||||
|
||||
## Mocking Nuqs URL Parameters
|
||||
|
||||
To render a component that uses Nuqs with some predefined query parameters, you can use `customRender` with the `nuqs` prop.
|
||||
|
||||
```ts
|
||||
customRender(<MyComponent />, {
|
||||
nuqs: {
|
||||
searchParams: {
|
||||
search: 'hello world',
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## `<Popover>` vs `<Dropdown>`
|
||||
|
||||
When simulating clicks on these components, do the following:
|
||||
|
||||
@@ -13,7 +82,3 @@ await userEvent.click('Hello world')
|
||||
import clickDropdown from 'tests/helpers'
|
||||
clickDropdown('Hello world')
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
- All tests should be run consistently (avoid situations whereby tests fails "sometimes")
|
||||
|
||||
@@ -9,8 +9,10 @@ test('MSW works as expected', async () => {
|
||||
expect(json).toEqual({ message: 'Hello from MSW!' })
|
||||
})
|
||||
|
||||
test('MSW fails on missing endpoints', async () => {
|
||||
test('MSW errors on missing endpoints', async () => {
|
||||
expect(async () => {
|
||||
await fetch(`${API_URL}/endpoint-that-doesnt-exist`)
|
||||
}).rejects.toThrowError()
|
||||
const res = await fetch(`${API_URL}/endpoint-that-doesnt-exist`)
|
||||
const json = await res.json()
|
||||
expect(json).toEqual({ message: '🚫 MSW missed' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import { routerMock } from 'tests/mocks/router'
|
||||
import { routerMock } from '../lib/route-mock'
|
||||
import { expect, suite, test } from 'vitest'
|
||||
import { RouterComponent } from './router'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { prettyDOM, screen, waitFor } from '@testing-library/react'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import LogTable from 'components/interfaces/Settings/Logs/LogTable'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -26,36 +26,29 @@ beforeAll(() => {
|
||||
|
||||
const fakeMicroTimestamp = dayjs().unix() * 1000
|
||||
|
||||
test.skip('can display log data', async () => {
|
||||
const LOG_DATA = {
|
||||
id: 'some-uuid',
|
||||
timestamp: 1621323232312,
|
||||
event_message: 'event message',
|
||||
metadata: {
|
||||
my_key: 'something_value',
|
||||
},
|
||||
}
|
||||
|
||||
test('can display log data', async () => {
|
||||
render(
|
||||
<>
|
||||
<LogTable
|
||||
projectRef="projectRef"
|
||||
data={[
|
||||
{
|
||||
id: 'some-uuid',
|
||||
timestamp: 1621323232312,
|
||||
event_message: 'event message',
|
||||
metadata: {
|
||||
my_key: 'something_value',
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<LogTable projectRef="default" data={[LOG_DATA]} />
|
||||
</>
|
||||
)
|
||||
|
||||
await screen.findByText(/event message/)
|
||||
await screen.findAllByText(LOG_DATA.timestamp)
|
||||
})
|
||||
|
||||
prettyDOM(screen.getByText(/event message/))
|
||||
test('Shows total results', async () => {
|
||||
render(<LogTable projectRef="default" data={[LOG_DATA]} />)
|
||||
|
||||
const row = await screen.findByText(/event message/)
|
||||
|
||||
await userEvent.click(row)
|
||||
|
||||
// [Joshen] commenting out for now - seems like we need to mock more stuff
|
||||
await screen.findByText(/my_key/)
|
||||
await screen.findByText(/something_value/)
|
||||
await screen.getByText(/results \(1\)/i)
|
||||
})
|
||||
|
||||
test('can run if no queryType provided', async () => {
|
||||
128
apps/studio/tests/features/logs/LogsPreviewer.test.tsx
Normal file
128
apps/studio/tests/features/logs/LogsPreviewer.test.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import { beforeEach, expect, test, vi } from 'vitest'
|
||||
import { LogsTableName } from 'components/interfaces/Settings/Logs/Logs.constants'
|
||||
import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer'
|
||||
import { customRender, customRenderHook } from 'tests/lib/custom-render'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import useLogsPreview from 'hooks/analytics/useLogsPreview'
|
||||
import { LOGS_API_MOCKS } from './logs.mocks'
|
||||
import { addAPIMock } from 'tests/lib/msw'
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
vi.mock('common', async (importOriginal) => {
|
||||
const actual = await importOriginal()
|
||||
return {
|
||||
useParams: vi.fn().mockReturnValue({}),
|
||||
useIsLoggedIn: vi.fn(),
|
||||
isBrowser: false,
|
||||
LOCAL_STORAGE_KEYS: (actual as any).LOCAL_STORAGE_KEYS,
|
||||
...(actual as any),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('lib/gotrue', async (importOriginal) => ({
|
||||
...(await importOriginal()),
|
||||
auth: { onAuthStateChange: vi.fn() },
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
addAPIMock({
|
||||
method: 'get',
|
||||
path: '/platform/projects/default/analytics/endpoints/logs.all',
|
||||
response: LOGS_API_MOCKS,
|
||||
})
|
||||
})
|
||||
|
||||
test('search loads with whatever is on the URL', async () => {
|
||||
customRender(
|
||||
<LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />,
|
||||
{
|
||||
nuqs: {
|
||||
searchParams: {
|
||||
s: 'test-search-box-value',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).toHaveValue('test-search-box-value')
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).not.toHaveValue('WRONGVALUE!🪿')
|
||||
})
|
||||
})
|
||||
|
||||
test('useLogsPreview returns data from MSW', async () => {
|
||||
const { result } = customRenderHook(() =>
|
||||
useLogsPreview({
|
||||
projectRef: 'default',
|
||||
table: LogsTableName.EDGE,
|
||||
})
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.logData.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
expect(result.current.logData).toEqual(LOGS_API_MOCKS.result)
|
||||
})
|
||||
|
||||
test('LogsPreviewer renders the expected data from the API', async () => {
|
||||
customRender(
|
||||
<LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('table')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const firstLogEventMessage = LOGS_API_MOCKS.result[0].event_message
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByText(firstLogEventMessage)[0]).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
test('can toggle log event chart', async () => {
|
||||
customRender(
|
||||
<LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: /Chart/i })).toBeInTheDocument()
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('logs-bar-chart')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /Chart/i }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('logs-bar-chart')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
test('can click load older', async () => {
|
||||
customRender(
|
||||
<LogsPreviewer queryType="api" projectRef="default" tableName={LogsTableName.EDGE} />
|
||||
)
|
||||
|
||||
const loadOlder = await waitFor(
|
||||
async () => await screen.findByRole('button', { name: /Load older/i })
|
||||
)
|
||||
|
||||
loadOlder.onclick = vi.fn()
|
||||
|
||||
await userEvent.click(loadOlder)
|
||||
|
||||
expect(loadOlder.onclick).toHaveBeenCalled()
|
||||
})
|
||||
@@ -4,7 +4,7 @@ import dayjs from 'dayjs'
|
||||
import { LogsExplorerPage } from 'pages/project/[ref]/logs/explorer/index'
|
||||
import { clickDropdown } from 'tests/helpers'
|
||||
import { customRender as render } from 'tests/lib/custom-render'
|
||||
import { routerMock } from 'tests/mocks/router'
|
||||
import { routerMock } from 'tests/lib/route-mock'
|
||||
import { beforeAll, describe, expect, test, vi } from 'vitest'
|
||||
|
||||
const router = routerMock
|
||||
22
apps/studio/tests/features/logs/logs.mocks.ts
Normal file
22
apps/studio/tests/features/logs/logs.mocks.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const LOGS_API_MOCKS = {
|
||||
result: [
|
||||
{
|
||||
id: 'uuid',
|
||||
event_message: 'foobar',
|
||||
timestamp: 1298085933,
|
||||
error_count: 1,
|
||||
warning_count: 2,
|
||||
ok_count: 3,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
id: 'uuid2',
|
||||
event_message: 'foobar',
|
||||
timestamp: 1298085933,
|
||||
error_count: 1,
|
||||
warning_count: 2,
|
||||
ok_count: 3,
|
||||
count: 2,
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { screen } from '@testing-library/react'
|
||||
import { beforeAll, test, vi } from 'vitest'
|
||||
|
||||
import { ApiReport } from 'pages/project/[ref]/reports/api-overview'
|
||||
import { render } from '../../../helpers'
|
||||
import { render } from 'tests/helpers'
|
||||
|
||||
// [Joshen] Mock data for ApiReport is in __mocks__/hooks/useApiReport
|
||||
// I don't think this is an ideal set up as the mock data is not clear in this file itself
|
||||
@@ -2,7 +2,7 @@ import { screen } from '@testing-library/react'
|
||||
import { test } from 'vitest'
|
||||
|
||||
import { StorageReport } from 'pages/project/[ref]/reports/storage'
|
||||
import { render } from '../../../helpers'
|
||||
import { render } from 'tests/helpers'
|
||||
|
||||
// [Joshen] Mock data for ApiReport is in __mocks__/hooks/useStorageReport
|
||||
// I don't think this is an ideal set up as the mock data is not clear in this file itself
|
||||
@@ -1,9 +1,9 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { render, RenderOptions } from '@testing-library/react'
|
||||
import { render, renderHook, RenderOptions } from '@testing-library/react'
|
||||
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
|
||||
import { TooltipProvider } from 'ui'
|
||||
|
||||
type AdapterProps = Parameters<typeof NuqsTestingAdapter>[0]
|
||||
type AdapterProps = Partial<Parameters<typeof NuqsTestingAdapter>[0]>
|
||||
|
||||
const CustomWrapper = ({
|
||||
children,
|
||||
@@ -14,7 +14,15 @@ const CustomWrapper = ({
|
||||
queryClient?: QueryClient
|
||||
nuqs?: AdapterProps
|
||||
}) => {
|
||||
const _queryClient = queryClient ?? new QueryClient()
|
||||
const _queryClient =
|
||||
queryClient ??
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={_queryClient}>
|
||||
@@ -41,3 +49,15 @@ export const customRender = (component: React.ReactElement, renderOptions?: Cust
|
||||
...renderOptions,
|
||||
})
|
||||
}
|
||||
|
||||
export const customRenderHook = (hook: () => any, renderOptions?: CustomRenderOpts) => {
|
||||
return renderHook(hook, {
|
||||
wrapper: ({ children }) =>
|
||||
CustomWrapper({
|
||||
children,
|
||||
queryClient: renderOptions?.queryClient,
|
||||
nuqs: renderOptions?.nuqs,
|
||||
}),
|
||||
...renderOptions,
|
||||
})
|
||||
}
|
||||
|
||||
26
apps/studio/tests/lib/msw-global-api-mocks.ts
Normal file
26
apps/studio/tests/lib/msw-global-api-mocks.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { HttpResponse, http } from 'msw'
|
||||
|
||||
export const GlobalAPIMocks = [
|
||||
http.get(`${API_URL}/msw/test`, () => {
|
||||
return HttpResponse.json({ message: 'Hello from MSW!' })
|
||||
}),
|
||||
http.get(`${API_URL}/platform/projects/default/databases`, () => {
|
||||
return HttpResponse.json([
|
||||
{
|
||||
cloud_provider: 'AWS',
|
||||
connectionString: '123',
|
||||
connection_string_read_only: '123',
|
||||
db_host: '123',
|
||||
db_name: 'postgres',
|
||||
db_port: 5432,
|
||||
identifier: 'default',
|
||||
inserted_at: '2025-02-16T22:24:42.115195',
|
||||
region: 'us-east-1',
|
||||
restUrl: 'https://default.supabase.co',
|
||||
size: 't4g.nano',
|
||||
status: 'ACTIVE_HEALTHY',
|
||||
},
|
||||
])
|
||||
}),
|
||||
]
|
||||
25
apps/studio/tests/lib/msw.ts
Normal file
25
apps/studio/tests/lib/msw.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { setupServer } from 'msw/node'
|
||||
import { GlobalAPIMocks } from './msw-global-api-mocks'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { API_URL } from 'lib/constants'
|
||||
|
||||
export const mswServer = setupServer(...GlobalAPIMocks)
|
||||
|
||||
export const addAPIMock = ({
|
||||
method,
|
||||
path,
|
||||
response,
|
||||
}: {
|
||||
method: keyof typeof http
|
||||
path: string
|
||||
response: any
|
||||
}) => {
|
||||
const fullPath = `${API_URL}${path}`
|
||||
console.log('[MSW] Adding mock:', method, fullPath)
|
||||
|
||||
mswServer.use(
|
||||
http[method](fullPath, () => {
|
||||
return HttpResponse.json(response)
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { HttpResponse, http } from 'msw'
|
||||
|
||||
const PROJECT_REF = 'default'
|
||||
|
||||
export const APIMock = [
|
||||
http.get(`${API_URL}/msw/test`, () => {
|
||||
return HttpResponse.json({ message: 'Hello from MSW!' })
|
||||
}),
|
||||
http.get(`${API_URL}/platform/projects/${PROJECT_REF}/analytics/endpoints/logs.all`, () => {
|
||||
return HttpResponse.json({
|
||||
totalRequests: [
|
||||
{
|
||||
count: 12,
|
||||
timestamp: '2024-05-13T20:00:00.000Z',
|
||||
},
|
||||
],
|
||||
errorCounts: [],
|
||||
responseSpeed: [
|
||||
{
|
||||
avg: 1017.0000000000001,
|
||||
timestamp: '2024-05-13T20:00:00',
|
||||
},
|
||||
],
|
||||
topRoutes: [
|
||||
{
|
||||
count: 6,
|
||||
method: 'GET',
|
||||
path: '/auth/v1/user',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
{
|
||||
count: 4,
|
||||
method: 'GET',
|
||||
path: '/rest/v1/',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
{
|
||||
count: 2,
|
||||
method: 'HEAD',
|
||||
path: '/rest/v1/',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
],
|
||||
topErrorRoutes: [],
|
||||
topSlowRoutes: [
|
||||
{
|
||||
avg: 1093.5,
|
||||
count: 4,
|
||||
method: 'GET',
|
||||
path: '/rest/v1/',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
{
|
||||
avg: 1037.5,
|
||||
count: 2,
|
||||
method: 'HEAD',
|
||||
path: '/rest/v1/',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
{
|
||||
avg: 959.1666666666667,
|
||||
count: 6,
|
||||
method: 'GET',
|
||||
path: '/auth/v1/health',
|
||||
search: null,
|
||||
status_code: 200,
|
||||
},
|
||||
],
|
||||
networkTraffic: [
|
||||
{
|
||||
egress_mb: 0.000666,
|
||||
ingress_mb: 0,
|
||||
timestamp: '2024-05-13T20:00:00.000Z',
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
]
|
||||
@@ -1,78 +0,0 @@
|
||||
import { findByText, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import { useRouter } from 'next/router'
|
||||
import { expect, test, vi } from 'vitest'
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
import { LogsTableName } from 'components/interfaces/Settings/Logs/Logs.constants'
|
||||
import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer'
|
||||
import { render } from '../../helpers'
|
||||
|
||||
// [Joshen] There's gotta be a much better way to mock these things so that it applies for ALL tests
|
||||
// Since these are easily commonly used things across all pages/components that we might be testing for
|
||||
vi.mock('common', async (importOriginal) => {
|
||||
const actual = await importOriginal()
|
||||
return {
|
||||
useParams: vi.fn().mockReturnValue({}),
|
||||
useIsLoggedIn: vi.fn(),
|
||||
isBrowser: false,
|
||||
LOCAL_STORAGE_KEYS: (actual as any).LOCAL_STORAGE_KEYS,
|
||||
}
|
||||
})
|
||||
vi.mock('lib/gotrue', () => ({
|
||||
auth: { onAuthStateChange: vi.fn() },
|
||||
}))
|
||||
|
||||
test.skip('Search will trigger a log refresh', async () => {
|
||||
render(<LogsPreviewer projectRef="123" queryType="auth" />)
|
||||
|
||||
await userEvent.type(screen.getByPlaceholderText(/Search events/), 'something{enter}')
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
// updates router query params
|
||||
const router = useRouter()
|
||||
expect(router.push).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
pathname: expect.any(String),
|
||||
query: expect.objectContaining({
|
||||
s: expect.stringContaining('something'),
|
||||
}),
|
||||
})
|
||||
)
|
||||
},
|
||||
{ timeout: 1500 }
|
||||
)
|
||||
const table = await screen.findByRole('table')
|
||||
await findByText(table, /some-message/, { selector: '*' }, { timeout: 1500 })
|
||||
})
|
||||
|
||||
test.skip('poll count for new messages', async () => {
|
||||
render(<LogsPreviewer queryType="api" projectRef="123" tableName={LogsTableName.EDGE} />)
|
||||
await waitFor(() => screen.queryByText(/200/) === null)
|
||||
// should display new logs count
|
||||
await waitFor(() => screen.getByText(/999/))
|
||||
|
||||
// Refresh button only exists with the queryType param, which no longer shows the id column
|
||||
await userEvent.click(screen.getByTitle('refresh'))
|
||||
await waitFor(() => screen.queryByText(/999/) === null)
|
||||
await screen.findByText(/200/)
|
||||
})
|
||||
|
||||
test.skip('stop polling for new count on error', async () => {
|
||||
render(<LogsPreviewer queryType="api" projectRef="123" tableName={LogsTableName.EDGE} />)
|
||||
await waitFor(() => screen.queryByText(/some-uuid123/) === null)
|
||||
// should display error
|
||||
await screen.findByText(/some logflare error/)
|
||||
// should not load refresh counts if no data from main query
|
||||
await expect(screen.findByText(/999/)).rejects.toThrowError()
|
||||
})
|
||||
|
||||
test.skip('log event chart', async () => {
|
||||
render(<LogsPreviewer queryType="api" projectRef="123" tableName={LogsTableName.EDGE} />)
|
||||
|
||||
await waitFor(() => screen.queryByText(/some-uuid123/) === null)
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { expect, test, vi } from 'vitest'
|
||||
|
||||
test.skip('filter input change and submit', async () => {
|
||||
const mockFn = vi.fn()
|
||||
// render(<PreviewFilterPanel onSearch={mockFn} queryUrl={'/'} />)
|
||||
expect(mockFn).not.toBeCalled()
|
||||
const search = screen.getByPlaceholderText(/Search/)
|
||||
await userEvent.type(search, '12345{enter}')
|
||||
expect(mockFn).toBeCalled()
|
||||
})
|
||||
|
||||
// test('filter input value', async () => {
|
||||
// render(<PreviewFilterPanel defaultSearchValue={'1234'} queryUrl={'/'} />)
|
||||
// await screen.findByDisplayValue('1234')
|
||||
// })
|
||||
|
||||
// test('Manual refresh', async () => {
|
||||
// const mockFn = vi.fn()
|
||||
// render(<PreviewFilterPanel onRefresh={mockFn} queryUrl={'/'} />)
|
||||
// const btn = await screen.findByTitle('refresh')
|
||||
// await userEvent.click(btn)
|
||||
// expect(mockFn).toBeCalled()
|
||||
// })
|
||||
// test('Datepicker dropdown', async () => {
|
||||
// const fn = vi.fn()
|
||||
// render(<PreviewFilterPanel onSearch={fn} queryUrl={'/'} />)
|
||||
// clickDropdown(await screen.findByText(/Last hour/))
|
||||
// await userEvent.click(await screen.findByText(/Last 3 hours/))
|
||||
// expect(fn).toBeCalled()
|
||||
// })
|
||||
|
||||
// test('shortened count to K', async () => {
|
||||
// render(<PreviewFilterPanel newCount={1234} queryUrl={'/'} />)
|
||||
// await screen.findByText(/1\.2K/)
|
||||
// })
|
||||
@@ -1,8 +0,0 @@
|
||||
// mock the fetch function
|
||||
import { test } from 'vitest'
|
||||
|
||||
test('on load, refresh user content', async () => {
|
||||
// get.mockResolvedValue({})
|
||||
// render(<LogsSavedPage />)
|
||||
// expect(get).toBeCalled()
|
||||
})
|
||||
@@ -1,14 +1,11 @@
|
||||
/// <reference types="@testing-library/jest-dom" />
|
||||
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
import { cleanup } from '@testing-library/react'
|
||||
import { setupServer } from 'msw/node'
|
||||
import { cleanup, configure } from '@testing-library/react'
|
||||
import { createDynamicRouteParser } from 'next-router-mock/dist/dynamic-routes'
|
||||
import { afterAll, afterEach, beforeAll, vi } from 'vitest'
|
||||
import { APIMock } from './mocks/api'
|
||||
import { routerMock } from './mocks/router'
|
||||
import { routerMock } from './lib/route-mock'
|
||||
import { mswServer } from './lib/msw'
|
||||
|
||||
export const mswServer = setupServer(...APIMock)
|
||||
mswServer.listen({ onUnhandledRequest: 'error' })
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
@@ -24,10 +21,17 @@ Object.defineProperty(window, 'matchMedia', {
|
||||
})),
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
console.log('🤖 Starting MSW Server')
|
||||
// Uncomment this if HTML in errors are being annoying.
|
||||
//
|
||||
// configure({
|
||||
// getElementError: (message, container) => {
|
||||
// const error = new Error(message ?? 'Element not found')
|
||||
// error.name = 'ElementNotFoundError'
|
||||
// return error
|
||||
// },
|
||||
// })
|
||||
|
||||
mswServer.listen({ onUnhandledRequest: 'error' })
|
||||
beforeAll(() => {
|
||||
vi.mock('next/router', () => require('next-router-mock'))
|
||||
vi.mock('next/navigation', async () => {
|
||||
const actual = await vi.importActual('next/navigation')
|
||||
|
||||
@@ -43,7 +43,7 @@ export const LogsBarChart = ({
|
||||
const endDate = dayjs(data[data?.length - 1]?.['timestamp']).format(DateTimeFormat)
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-y-3')}>
|
||||
<div data-testid="logs-bar-chart" className={cn('flex flex-col gap-y-3')}>
|
||||
<ChartContainer
|
||||
config={
|
||||
{
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"NEXT_PUBLIC_NODE_ENV",
|
||||
"NEXT_PUBLIC_GOTRUE_URL",
|
||||
"NEXT_PUBLIC_VERCEL_BRANCH_URL",
|
||||
"NODE_ENV",
|
||||
"SUPABASE_URL",
|
||||
// These envs are used in the packages
|
||||
"NEXT_PUBLIC_STORAGE_KEY",
|
||||
|
||||
Reference in New Issue
Block a user