daili-usage-keeper / web /src /components /usage /RequestEventsDetailsCard.test.tsx
pjpjq's picture
fix: build usage keeper from source
b034029 verified
import React from 'react';
import { describe, expect, it } from 'vitest';
import { renderToStaticMarkup } from 'react-dom/server';
import { RequestEventsDetailsCard } from './RequestEventsDetailsCard';
import type { UsageEvent } from '@/lib/types';
const events: UsageEvent[] = [
{
id: 101,
timestamp: '2026-04-23T02:00:00.000Z',
model: 'claude-sonnet',
source: 'Provider A',
source_raw: 'source-a',
source_type: 'openai',
auth_index: '1',
failed: false,
latency_ms: 120,
tokens: {
input_tokens: 100,
output_tokens: 60,
reasoning_tokens: 20,
cached_tokens: 20,
total_tokens: 200,
},
},
];
const renderCard = (props: Partial<React.ComponentProps<typeof RequestEventsDetailsCard>> = {}) =>
renderToStaticMarkup(
<RequestEventsDetailsCard
events={events}
loading={false}
page={1}
pageSize={20}
pageSizeOptions={[20, 50, 100, 500, 1000]}
totalCount={120}
totalPages={6}
modelOptions={['claude-sonnet', 'claude-opus']}
sourceOptions={[{ value: 'source-a', label: 'Provider A' }, { value: 'source-b', label: 'Provider B' }]}
modelFilter="__all__"
sourceFilter="__all__"
resultFilter="__all__"
onPageChange={() => undefined}
onPageSizeChange={() => undefined}
onModelFilterChange={() => undefined}
onSourceFilterChange={() => undefined}
onResultFilterChange={() => undefined}
{...props}
/>,
);
const countOccurrences = (text: string, value: string) => text.split(value).length - 1;
describe('RequestEventsDetailsCard pagination', () => {
it('renders total events, current page, page size options, and disabled page buttons', () => {
const html = renderCard();
expect(html).toContain('120 total events');
expect(html).toContain('Page (1/6)');
expect(html).toContain('20');
expect(html).toContain('50');
expect(html).toContain('100');
expect(html).toContain('500');
expect(html).toContain('1000');
expect(html).toContain('Previous');
expect(html).toContain('Next');
expect(html).toContain('disabled');
});
it('uses backend source values while showing resolved source labels', () => {
const html = renderCard({ sourceFilter: 'source-a' });
expect(countOccurrences(html, 'Provider A')).toBeGreaterThanOrEqual(2);
expect(html).toContain('aria-label="Source"><span class="_triggerText_c80422 ">Provider A</span>');
});
it('uses backend model and source options instead of current page grouping', () => {
const html = renderCard({ modelFilter: 'claude-opus', sourceFilter: 'source-b' });
expect(html).toContain('aria-label="Model"><span class="_triggerText_c80422 ">claude-opus</span>');
expect(html).toContain('aria-label="Source"><span class="_triggerText_c80422 ">Provider B</span>');
});
it('renders a Result filter and no Credential filter control', () => {
const html = renderCard({ resultFilter: 'failed' });
expect(html).toContain('aria-label="Result"');
expect(html).toContain('Failure');
expect(html).not.toContain('aria-label="Credential"');
});
it('keeps selected filters visible when backend options do not include them', () => {
const html = renderCard({
modelFilter: 'claude-haiku',
sourceFilter: 'source-c',
});
expect(html).toContain('claude-haiku');
expect(html).toContain('source-c');
});
it('falls back to a computed page count when metadata is not populated', () => {
const html = renderCard({ totalPages: 0, totalCount: 120, pageSize: 20 });
expect(html).toContain('Page (1/6)');
});
it('groups filters and pager controls in a compact toolbar', () => {
const html = renderCard();
expect(html).toContain('_requestEventsFiltersGroup_');
expect(html).toContain('_requestEventsPaginationControls_');
expect(html).toContain('_requestEventsPaginationItem_');
expect(html).toContain('_requestEventsPagerButton_');
expect(html).toContain('_requestEventsPageSizeSelectCompact_');
expect(html).toContain('_requestEventsActions_');
expect(html).toContain('_requestEventsTableMeta_');
});
it('hides export buttons while keeping clear filters available', () => {
const html = renderCard({ modelFilter: 'claude-sonnet' });
expect(html).toContain('Clear Filters');
expect(html).not.toContain('Export CSV');
expect(html).not.toContain('Export JSON');
});
});