mirror of
https://github.com/supabase/supabase.git
synced 2026-06-20 10:32:40 +08:00
## What kind of change does this PR introduce? UI update. ## What is the current behavior? The webhook endpoint details view showed all mock deliveries in a single table with a Studio-specific sortable header treatment, no pagination, and uneven row heights when the retry action was absent. ## What is the new behavior? Moves the deliveries table onto TanStack table state, adopts the shared `TableHeadSort` header UI, keeps the existing delivery search, and adds simple previous/next pagination controls at the bottom. The mock deliveries are expanded so pagination and sorting can be exercised in both organisation and project flows, and the actions column now reserves a consistent button footprint so every row keeps the same minimum height. | Before | After | | --- | --- | | <img width="1728" height="997" alt="Webhooks Settings Chisel Toolshed Supabase-A4712323-6FD9-471B-B1F4-234B20686018" src="https://github.com/user-attachments/assets/2745e5fd-1ef7-4f3a-872c-1fd8d10dce41" /> | <img width="1728" height="997" alt="Webhooks Settings Chisel Toolshed Supabase-D482241B-F324-4EA2-9B89-133AD5E10F17" src="https://github.com/user-attachments/assets/5bfcefd8-cb58-46c0-a863-dcf80e4da4a3" /> | ## Additional context This keeps the view on the Data Table path now, while aligning the sortable headers with the design-system table pattern instead of the older Studio-local `DataTableColumnHeader` helper.
142 lines
4.6 KiB
TypeScript
142 lines
4.6 KiB
TypeScript
import { render, screen, waitFor } from '@testing-library/react'
|
||
import userEvent from '@testing-library/user-event'
|
||
import type { ComponentProps } from 'react'
|
||
import { describe, expect, it, vi } from 'vitest'
|
||
|
||
import { PLATFORM_WEBHOOKS_MOCK_DATA } from './PlatformWebhooks.mock'
|
||
import { PlatformWebhooksEndpointDetails } from './PlatformWebhooksEndpointDetails'
|
||
|
||
vi.mock('components/ui/DataTable/DataTableColumn/DataTableColumnStatusCode', () => ({
|
||
DataTableColumnStatusCode: ({ value }: { value: number }) => <span>{value}</span>,
|
||
}))
|
||
|
||
vi.mock('components/ui/ButtonTooltip', () => ({
|
||
ButtonTooltip: ({
|
||
icon,
|
||
children,
|
||
size: _size,
|
||
tooltip: _tooltip,
|
||
type: _type,
|
||
...props
|
||
}: any) => (
|
||
<button type="button" {...props}>
|
||
{icon}
|
||
{children}
|
||
</button>
|
||
),
|
||
}))
|
||
|
||
vi.mock('ui-patterns', async () => {
|
||
const actual = await vi.importActual<typeof import('ui-patterns')>('ui-patterns')
|
||
|
||
return {
|
||
...actual,
|
||
TimestampInfo: ({ utcTimestamp, className }: { utcTimestamp: string; className?: string }) => (
|
||
<span className={className}>{utcTimestamp}</span>
|
||
),
|
||
}
|
||
})
|
||
|
||
describe('PlatformWebhooksEndpointDetails', () => {
|
||
const selectedEndpoint = PLATFORM_WEBHOOKS_MOCK_DATA.organization.endpoints[0]
|
||
const allDeliveries = PLATFORM_WEBHOOKS_MOCK_DATA.organization.deliveries.filter(
|
||
(delivery) => delivery.endpointId === selectedEndpoint.id
|
||
)
|
||
|
||
const renderComponent = (
|
||
props?: Partial<ComponentProps<typeof PlatformWebhooksEndpointDetails>>
|
||
) =>
|
||
render(
|
||
<PlatformWebhooksEndpointDetails
|
||
deliverySearch=""
|
||
filteredDeliveries={allDeliveries}
|
||
selectedEndpoint={selectedEndpoint}
|
||
onDeliverySearchChange={vi.fn()}
|
||
onOpenDelivery={vi.fn()}
|
||
onRetryDelivery={vi.fn()}
|
||
{...props}
|
||
/>
|
||
)
|
||
|
||
it('renders paginated deliveries with previous and next controls', async () => {
|
||
const user = userEvent.setup()
|
||
|
||
renderComponent()
|
||
|
||
expect(screen.getByText('Showing 1 to 5 of 12 deliveries')).toBeInTheDocument()
|
||
expect(screen.queryByText('organization.member_removed')).not.toBeInTheDocument()
|
||
|
||
await user.click(screen.getByLabelText('Next page'))
|
||
|
||
expect(screen.getByText('Showing 6 to 10 of 12 deliveries')).toBeInTheDocument()
|
||
expect(screen.getByText('organization.member_removed')).toBeInTheDocument()
|
||
|
||
await user.click(screen.getByLabelText('Previous page'))
|
||
|
||
expect(screen.getByText('Showing 1 to 5 of 12 deliveries')).toBeInTheDocument()
|
||
expect(screen.queryByText('organization.member_removed')).not.toBeInTheDocument()
|
||
})
|
||
|
||
it('sorts deliveries by event type from the header', async () => {
|
||
const user = userEvent.setup()
|
||
|
||
renderComponent()
|
||
|
||
await user.click(screen.getByRole('button', { name: 'Event type' }))
|
||
|
||
expect(screen.getAllByRole('row')[1]).toHaveTextContent('organization.member_invited')
|
||
|
||
await user.click(screen.getByRole('button', { name: 'Event type' }))
|
||
|
||
expect(screen.getAllByRole('row')[1]).toHaveTextContent('project.updated')
|
||
})
|
||
|
||
it('resets to the first page when the delivery search changes', async () => {
|
||
const user = userEvent.setup()
|
||
const projectDeliveries = allDeliveries.filter((delivery) =>
|
||
delivery.eventType.includes('project')
|
||
)
|
||
const { rerender } = renderComponent()
|
||
|
||
await user.click(screen.getByLabelText('Next page'))
|
||
expect(screen.getByText('Showing 6 to 10 of 12 deliveries')).toBeInTheDocument()
|
||
|
||
rerender(
|
||
<PlatformWebhooksEndpointDetails
|
||
deliverySearch="project"
|
||
filteredDeliveries={projectDeliveries}
|
||
selectedEndpoint={selectedEndpoint}
|
||
onDeliverySearchChange={vi.fn()}
|
||
onOpenDelivery={vi.fn()}
|
||
onRetryDelivery={vi.fn()}
|
||
/>
|
||
)
|
||
|
||
await waitFor(() => {
|
||
expect(screen.getByText('Showing 1 to 5 of 8 deliveries')).toBeInTheDocument()
|
||
})
|
||
})
|
||
|
||
it('retries a failed delivery without opening the delivery row', async () => {
|
||
const user = userEvent.setup()
|
||
const onOpenDelivery = vi.fn()
|
||
const onRetryDelivery = vi.fn()
|
||
|
||
renderComponent({ onOpenDelivery, onRetryDelivery })
|
||
|
||
await user.click(screen.getByLabelText('Retry org-delivery-2'))
|
||
|
||
expect(onRetryDelivery).toHaveBeenCalledWith('org-delivery-2')
|
||
expect(onOpenDelivery).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('renders a zero response code instead of the placeholder', () => {
|
||
renderComponent({
|
||
filteredDeliveries: [{ ...allDeliveries[0], id: 'org-delivery-zero', responseCode: 0 }],
|
||
})
|
||
|
||
expect(screen.getByText('0')).toBeInTheDocument()
|
||
expect(screen.queryByText('–')).not.toBeInTheDocument()
|
||
})
|
||
})
|