feat(UI): Adding linter and prettier for UI (#3156)

This commit is contained in:
Francisco Arceo 2025-08-14 15:58:43 -06:00 committed by GitHub
parent 61582f327c
commit e69acbafbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 1452 additions and 1226 deletions

View file

@ -14,7 +14,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={true}
error={null}
id="test-id"
/>,
/>
);
// Use the data-slot attribute for Skeletons
const skeletons = container.querySelectorAll('[data-slot="skeleton"]');
@ -28,10 +28,10 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{ name: "Error", message: "Network Error" }}
id="err-id"
/>,
/>
);
expect(
screen.getByText(/Error loading details for ID err-id: Network Error/),
screen.getByText(/Error loading details for ID err-id: Network Error/)
).toBeInTheDocument();
});
@ -42,11 +42,11 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{ name: "Error", message: "" }}
id="err-id"
/>,
/>
);
// Use regex to match the error message regardless of whitespace
expect(
screen.getByText(/Error loading details for ID\s*err-id\s*:/),
screen.getByText(/Error loading details for ID\s*err-id\s*:/)
).toBeInTheDocument();
});
@ -57,11 +57,11 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{} as Error}
id="err-id"
/>,
/>
);
// Use regex to match the error message regardless of whitespace
expect(
screen.getByText(/Error loading details for ID\s*err-id\s*:/),
screen.getByText(/Error loading details for ID\s*err-id\s*:/)
).toBeInTheDocument();
});
@ -72,10 +72,10 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id="notfound-id"
/>,
/>
);
expect(
screen.getByText("No details found for ID: notfound-id."),
screen.getByText("No details found for ID: notfound-id.")
).toBeInTheDocument();
});
@ -100,7 +100,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
/>,
/>
);
// Input
expect(screen.getByText("Input")).toBeInTheDocument();
@ -112,7 +112,7 @@ describe("ChatCompletionDetailView", () => {
expect(screen.getByText("Properties")).toBeInTheDocument();
expect(screen.getByText("Created:")).toBeInTheDocument();
expect(
screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("ID:")).toBeInTheDocument();
expect(screen.getByText("comp_123")).toBeInTheDocument();
@ -150,7 +150,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
/>,
/>
);
// Output should include the tool call block (should be present twice: input and output)
const toolCallLabels = screen.getAllByText("Tool Call");
@ -178,13 +178,13 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
/>,
/>
);
// Input section should be present but empty
expect(screen.getByText("Input")).toBeInTheDocument();
// Output section should show fallback message
expect(
screen.getByText("No message found in assistant's choice."),
screen.getByText("No message found in assistant's choice.")
).toBeInTheDocument();
// Properties should show N/A for finish reason
expect(screen.getByText("Finish Reason:")).toBeInTheDocument();

View file

@ -53,14 +53,14 @@ export function ChatCompletionDetailView({
{completion.choices?.[0]?.message?.tool_calls &&
Array.isArray(completion.choices[0].message.tool_calls) &&
!completion.input_messages?.some(
(im) =>
im =>
im.role === "assistant" &&
im.tool_calls &&
Array.isArray(im.tool_calls) &&
im.tool_calls.length > 0,
im.tool_calls.length > 0
)
? completion.choices[0].message.tool_calls.map(
(toolCall: any, index: number) => {
(toolCall: { function?: { name?: string } }, index: number) => {
const assistantToolCallMessage: ChatMessage = {
role: "assistant",
tool_calls: [toolCall],
@ -72,7 +72,7 @@ export function ChatCompletionDetailView({
message={assistantToolCallMessage}
/>
);
},
}
)
: null}
</CardContent>
@ -89,7 +89,7 @@ export function ChatCompletionDetailView({
/>
) : (
<p className="text-gray-500 italic text-sm">
No message found in assistant's choice.
No message found in assistant&apos;s choice.
</p>
)}
</CardContent>
@ -120,13 +120,18 @@ export function ChatCompletionDetailView({
value={
<div>
<ul className="list-disc list-inside pl-4 mt-1">
{toolCalls.map((toolCall: any, index: number) => (
<li key={index}>
<span className="text-gray-900 font-medium">
{toolCall.function?.name || "N/A"}
</span>
</li>
))}
{toolCalls.map(
(
toolCall: { function?: { name?: string } },
index: number
) => (
<li key={index}>
<span className="text-gray-900 font-medium">
{toolCall.function?.name || "N/A"}
</span>
</li>
)
)}
</ul>
</div>
}

View file

@ -83,7 +83,7 @@ describe("ChatCompletionsTable", () => {
// Default pass-through implementations
truncateText.mockImplementation((text: string | undefined) => text);
extractTextFromContentPart.mockImplementation((content: unknown) =>
typeof content === "string" ? content : "extracted text",
typeof content === "string" ? content : "extracted text"
);
extractDisplayableText.mockImplementation((message: unknown) => {
const msg = message as { content?: string };
@ -138,7 +138,7 @@ describe("ChatCompletionsTable", () => {
if (row) {
fireEvent.click(row);
expect(mockPush).toHaveBeenCalledWith(
"/logs/chat-completions/completion_123",
"/logs/chat-completions/completion_123"
);
} else {
throw new Error('Row with "Test prompt" not found for router mock test.');
@ -162,7 +162,7 @@ describe("ChatCompletionsTable", () => {
expect(tableCaption).toBeInTheDocument();
if (tableCaption) {
const captionSkeleton = tableCaption.querySelector(
'[data-slot="skeleton"]',
'[data-slot="skeleton"]'
);
expect(captionSkeleton).toBeInTheDocument();
}
@ -172,7 +172,7 @@ describe("ChatCompletionsTable", () => {
expect(tableBody).toBeInTheDocument();
if (tableBody) {
const bodySkeletons = tableBody.querySelectorAll(
'[data-slot="skeleton"]',
'[data-slot="skeleton"]'
);
expect(bodySkeletons.length).toBeGreaterThan(0);
}
@ -192,14 +192,14 @@ describe("ChatCompletionsTable", () => {
render(<ChatCompletionsTable {...defaultProps} />);
expect(
screen.getByText("Unable to load chat completions"),
screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
test.each([{ name: "Error", message: "" }, {}])(
"renders default error message when error has no message",
(errorObject) => {
errorObject => {
mockedUsePagination.mockReturnValue({
data: [],
status: "error",
@ -210,14 +210,14 @@ describe("ChatCompletionsTable", () => {
render(<ChatCompletionsTable {...defaultProps} />);
expect(
screen.getByText("Unable to load chat completions"),
screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
screen.getByText(
"An unexpected error occurred while loading the data.",
),
"An unexpected error occurred while loading the data."
)
).toBeInTheDocument();
},
}
);
});
@ -225,7 +225,7 @@ describe("ChatCompletionsTable", () => {
test('renders "No chat completions found." and no table when data array is empty', () => {
render(<ChatCompletionsTable {...defaultProps} />);
expect(
screen.getByText("No chat completions found."),
screen.getByText("No chat completions found.")
).toBeInTheDocument();
// Ensure that the table structure is NOT rendered in the empty state
@ -292,7 +292,7 @@ describe("ChatCompletionsTable", () => {
// Table caption
expect(
screen.getByText("A list of your recent chat completions."),
screen.getByText("A list of your recent chat completions.")
).toBeInTheDocument();
// Table headers
@ -306,14 +306,14 @@ describe("ChatCompletionsTable", () => {
expect(screen.getByText("Test output")).toBeInTheDocument();
expect(screen.getByText("llama-test-model")).toBeInTheDocument();
expect(
screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("Another input")).toBeInTheDocument();
expect(screen.getByText("Another output")).toBeInTheDocument();
expect(screen.getByText("llama-another-model")).toBeInTheDocument();
expect(
screen.getByText(new Date(1710001000 * 1000).toLocaleString()),
screen.getByText(new Date(1710001000 * 1000).toLocaleString())
).toBeInTheDocument();
});
});
@ -328,7 +328,7 @@ describe("ChatCompletionsTable", () => {
return typeof text === "string" && text.length > effectiveMaxLength
? text.slice(0, effectiveMaxLength) + "..."
: text;
},
}
);
const longInput =
@ -368,7 +368,7 @@ describe("ChatCompletionsTable", () => {
// The truncated text should be present for both input and output
const truncatedTexts = screen.getAllByText(
longInput.slice(0, 10) + "...",
longInput.slice(0, 10) + "..."
);
expect(truncatedTexts.length).toBe(2); // one for input, one for output
});
@ -420,7 +420,7 @@ describe("ChatCompletionsTable", () => {
// Verify the extracted text appears in the table
expect(screen.getByText("Extracted input")).toBeInTheDocument();
expect(
screen.getByText("Extracted output from assistant"),
screen.getByText("Extracted output from assistant")
).toBeInTheDocument();
});
});

View file

@ -5,6 +5,7 @@ import {
UsePaginationOptions,
ListChatCompletionsResponse,
} from "@/lib/types";
import { ListChatCompletionsParams } from "@/lib/llama-stack-client";
import { LogsTable, LogTableRow } from "@/components/logs/logs-table";
import {
extractTextFromContentPart,
@ -38,14 +39,14 @@ export function ChatCompletionsTable({
limit: number;
model?: string;
order?: string;
},
}
) => {
const response = await client.chat.completions.list({
after: params.after,
limit: params.limit,
...(params.model && { model: params.model }),
...(params.order && { order: params.order }),
} as any);
} as ListChatCompletionsParams);
return response as ListChatCompletionsResponse;
};

View file

@ -37,21 +37,26 @@ export function ChatMessageItem({ message }: ChatMessageItemProps) {
) {
return (
<>
{message.tool_calls.map((toolCall: any, index: number) => {
const formattedToolCall = formatToolCallToString(toolCall);
const toolCallContent = (
<ToolCallBlock>
{formattedToolCall || "Error: Could not display tool call"}
</ToolCallBlock>
);
return (
<MessageBlock
key={index}
label="Tool Call"
content={toolCallContent}
/>
);
})}
{message.tool_calls.map(
(
toolCall: { function?: { name?: string; arguments?: unknown } },
index: number
) => {
const formattedToolCall = formatToolCallToString(toolCall);
const toolCallContent = (
<ToolCallBlock>
{formattedToolCall || "Error: Could not display tool call"}
</ToolCallBlock>
);
return (
<MessageBlock
key={index}
label="Tool Call"
content={toolCallContent}
/>
);
}
)}
</>
);
} else {