blob: f8dc68542e1488ab25bf4f09a6ee858d9c2dce0f [file] [log] [blame]
Sean McCulloughb29f8912025-04-20 15:39:11 -07001import { test, expect } from "@sand4rt/experimental-ct-web";
2import { SketchContainerStatus } from "./sketch-container-status";
Sean McCullough86b56862025-04-18 13:04:03 -07003import { State } from "../types";
4
Sean McCulloughb29f8912025-04-20 15:39:11 -07005// Mock complete state for testing
6const mockCompleteState: State = {
7 hostname: "test-host",
8 working_dir: "/test/dir",
9 initial_commit: "abcdef1234567890",
10 message_count: 42,
11 os: "linux",
12 title: "Test Session",
13 total_usage: {
14 input_tokens: 1000,
15 output_tokens: 2000,
16 cache_read_input_tokens: 300,
17 cache_creation_input_tokens: 400,
18 total_cost_usd: 0.25,
Sean McCulloughd9f13372025-04-21 15:08:49 -070019 start_time: "",
20 messages: 0,
21 tool_uses: {},
Sean McCulloughb29f8912025-04-20 15:39:11 -070022 },
Philip Zeyliger99a9a022025-04-27 15:15:25 +000023 outstanding_llm_calls: 0,
24 outstanding_tool_calls: [],
Philip Zeyligerc72fff52025-04-29 20:17:54 +000025 session_id: "test-session-id",
26 ssh_available: false,
Philip Zeyliger2c4db092025-04-28 16:57:50 -070027 in_container: true,
28 first_message_index: 0,
Sean McCulloughb29f8912025-04-20 15:39:11 -070029};
30
31test("render props", async ({ mount }) => {
32 const component = await mount(SketchContainerStatus, {
33 props: {
34 state: mockCompleteState,
35 },
36 });
37 await expect(component.locator("#hostname")).toContainText(
38 mockCompleteState.hostname,
39 );
40 // Check that all expected elements exist
41 await expect(component.locator("#workingDir")).toContainText(
42 mockCompleteState.working_dir,
43 );
44 await expect(component.locator("#initialCommit")).toContainText(
45 mockCompleteState.initial_commit.substring(0, 8),
46 );
47
Autoformatter5c70bfe2025-04-25 21:28:00 +000048 await expect(component.locator("#messageCount")).toContainText(
Sean McCulloughb29f8912025-04-20 15:39:11 -070049 mockCompleteState.message_count + "",
50 );
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000051 const expectedTotalInputTokens =
52 mockCompleteState.total_usage.input_tokens +
53 mockCompleteState.total_usage.cache_read_input_tokens +
54 mockCompleteState.total_usage.cache_creation_input_tokens;
Sean McCulloughb29f8912025-04-20 15:39:11 -070055 await expect(component.locator("#inputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +000056 expectedTotalInputTokens.toLocaleString(),
Sean McCulloughb29f8912025-04-20 15:39:11 -070057 );
58 await expect(component.locator("#outputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +000059 mockCompleteState.total_usage.output_tokens.toLocaleString(),
Sean McCulloughb29f8912025-04-20 15:39:11 -070060 );
Sean McCulloughb29f8912025-04-20 15:39:11 -070061 await expect(component.locator("#totalCost")).toContainText(
62 "$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
63 );
64});
65
66test("renders with undefined state", async ({ mount }) => {
67 const component = await mount(SketchContainerStatus, {});
68
69 // Elements should exist but be empty
70 await expect(component.locator("#hostname")).toContainText("");
71 await expect(component.locator("#workingDir")).toContainText("");
72 await expect(component.locator("#initialCommit")).toContainText("");
73 await expect(component.locator("#messageCount")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000074 await expect(component.locator("#inputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -070075 await expect(component.locator("#outputTokens")).toContainText("");
76 await expect(component.locator("#totalCost")).toContainText("$0.00");
77});
78
79test("renders with partial state data", async ({ mount }) => {
80 const partialState: Partial<State> = {
81 hostname: "partial-host",
82 message_count: 10,
Sean McCullough86b56862025-04-18 13:04:03 -070083 os: "linux",
Sean McCulloughb29f8912025-04-20 15:39:11 -070084 title: "Partial Test",
Philip Zeyligerc72fff52025-04-29 20:17:54 +000085 session_id: "partial-session",
86 ssh_available: false,
Sean McCullough86b56862025-04-18 13:04:03 -070087 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -070088 input_tokens: 500,
Sean McCulloughd9f13372025-04-21 15:08:49 -070089 start_time: "",
90 messages: 0,
91 output_tokens: 0,
92 cache_read_input_tokens: 0,
93 cache_creation_input_tokens: 0,
94 total_cost_usd: 0,
95 tool_uses: {},
Sean McCullough71941bd2025-04-18 13:31:48 -070096 },
Sean McCullough86b56862025-04-18 13:04:03 -070097 };
98
Sean McCulloughb29f8912025-04-20 15:39:11 -070099 const component = await mount(SketchContainerStatus, {
100 props: {
101 state: partialState as State,
102 },
Sean McCullough86b56862025-04-18 13:04:03 -0700103 });
104
Sean McCulloughb29f8912025-04-20 15:39:11 -0700105 // Check that elements with data are properly populated
106 await expect(component.locator("#hostname")).toContainText("partial-host");
107 await expect(component.locator("#messageCount")).toContainText("10");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000108
109 const expectedTotalInputTokens =
110 partialState.total_usage.input_tokens +
111 partialState.total_usage.cache_read_input_tokens +
112 partialState.total_usage.cache_creation_input_tokens;
113 await expect(component.locator("#inputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +0000114 expectedTotalInputTokens.toLocaleString(),
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000115 );
Sean McCullough86b56862025-04-18 13:04:03 -0700116
Sean McCulloughb29f8912025-04-20 15:39:11 -0700117 // Check that elements without data are empty
118 await expect(component.locator("#workingDir")).toContainText("");
119 await expect(component.locator("#initialCommit")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000120 await expect(component.locator("#outputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -0700121 await expect(component.locator("#totalCost")).toContainText("$0.00");
122});
Sean McCullough86b56862025-04-18 13:04:03 -0700123
Sean McCulloughb29f8912025-04-20 15:39:11 -0700124test("handles cost formatting correctly", async ({ mount }) => {
125 // Test with different cost values
126 const testCases = [
127 { cost: 0, expected: "$0.00" },
128 { cost: 0.1, expected: "$0.10" },
129 { cost: 1.234, expected: "$1.23" },
130 { cost: 10.009, expected: "$10.01" },
131 ];
132
133 for (const testCase of testCases) {
134 const stateWithCost = {
135 ...mockCompleteState,
Sean McCullough86b56862025-04-18 13:04:03 -0700136 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -0700137 ...mockCompleteState.total_usage,
138 total_cost_usd: testCase.cost,
Sean McCullough71941bd2025-04-18 13:31:48 -0700139 },
Sean McCullough86b56862025-04-18 13:04:03 -0700140 };
141
Sean McCulloughb29f8912025-04-20 15:39:11 -0700142 const component = await mount(SketchContainerStatus, {
143 props: {
144 state: stateWithCost,
145 },
146 });
147 await expect(component.locator("#totalCost")).toContainText(
148 testCase.expected,
149 );
150 await component.unmount();
151 }
152});
Sean McCullough86b56862025-04-18 13:04:03 -0700153
Sean McCulloughb29f8912025-04-20 15:39:11 -0700154test("truncates commit hash to 8 characters", async ({ mount }) => {
155 const stateWithLongCommit = {
156 ...mockCompleteState,
157 initial_commit: "1234567890abcdef1234567890abcdef12345678",
158 };
Sean McCullough71941bd2025-04-18 13:31:48 -0700159
Sean McCulloughb29f8912025-04-20 15:39:11 -0700160 const component = await mount(SketchContainerStatus, {
161 props: {
162 state: stateWithLongCommit,
163 },
Sean McCullough86b56862025-04-18 13:04:03 -0700164 });
165
Sean McCulloughb29f8912025-04-20 15:39:11 -0700166 await expect(component.locator("#initialCommit")).toContainText("12345678");
167});
Sean McCullough86b56862025-04-18 13:04:03 -0700168
Sean McCulloughb29f8912025-04-20 15:39:11 -0700169test("has correct link elements", async ({ mount }) => {
170 const component = await mount(SketchContainerStatus, {
171 props: {
172 state: mockCompleteState,
173 },
Sean McCullough86b56862025-04-18 13:04:03 -0700174 });
175
Sean McCulloughb29f8912025-04-20 15:39:11 -0700176 // Check for logs link
177 const logsLink = component.locator("a").filter({ hasText: "Logs" });
178 await expect(logsLink).toHaveAttribute("href", "logs");
Sean McCullough86b56862025-04-18 13:04:03 -0700179
Sean McCulloughb29f8912025-04-20 15:39:11 -0700180 // Check for download link
181 const downloadLink = component.locator("a").filter({ hasText: "Download" });
182 await expect(downloadLink).toHaveAttribute("href", "download");
Sean McCullough86b56862025-04-18 13:04:03 -0700183});