blob: db43a6d9e99290eba046881db93f743c5157d667 [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,
Sean McCulloughb29f8912025-04-20 15:39:11 -070027};
28
29test("render props", async ({ mount }) => {
30 const component = await mount(SketchContainerStatus, {
31 props: {
32 state: mockCompleteState,
33 },
34 });
35 await expect(component.locator("#hostname")).toContainText(
36 mockCompleteState.hostname,
37 );
38 // Check that all expected elements exist
39 await expect(component.locator("#workingDir")).toContainText(
40 mockCompleteState.working_dir,
41 );
42 await expect(component.locator("#initialCommit")).toContainText(
43 mockCompleteState.initial_commit.substring(0, 8),
44 );
45
Autoformatter5c70bfe2025-04-25 21:28:00 +000046 await expect(component.locator("#messageCount")).toContainText(
Sean McCulloughb29f8912025-04-20 15:39:11 -070047 mockCompleteState.message_count + "",
48 );
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000049 const expectedTotalInputTokens =
50 mockCompleteState.total_usage.input_tokens +
51 mockCompleteState.total_usage.cache_read_input_tokens +
52 mockCompleteState.total_usage.cache_creation_input_tokens;
Sean McCulloughb29f8912025-04-20 15:39:11 -070053 await expect(component.locator("#inputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +000054 expectedTotalInputTokens.toLocaleString(),
Sean McCulloughb29f8912025-04-20 15:39:11 -070055 );
56 await expect(component.locator("#outputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +000057 mockCompleteState.total_usage.output_tokens.toLocaleString(),
Sean McCulloughb29f8912025-04-20 15:39:11 -070058 );
Sean McCulloughb29f8912025-04-20 15:39:11 -070059 await expect(component.locator("#totalCost")).toContainText(
60 "$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
61 );
62});
63
64test("renders with undefined state", async ({ mount }) => {
65 const component = await mount(SketchContainerStatus, {});
66
67 // Elements should exist but be empty
68 await expect(component.locator("#hostname")).toContainText("");
69 await expect(component.locator("#workingDir")).toContainText("");
70 await expect(component.locator("#initialCommit")).toContainText("");
71 await expect(component.locator("#messageCount")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000072 await expect(component.locator("#inputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -070073 await expect(component.locator("#outputTokens")).toContainText("");
74 await expect(component.locator("#totalCost")).toContainText("$0.00");
75});
76
77test("renders with partial state data", async ({ mount }) => {
78 const partialState: Partial<State> = {
79 hostname: "partial-host",
80 message_count: 10,
Sean McCullough86b56862025-04-18 13:04:03 -070081 os: "linux",
Sean McCulloughb29f8912025-04-20 15:39:11 -070082 title: "Partial Test",
Philip Zeyligerc72fff52025-04-29 20:17:54 +000083 session_id: "partial-session",
84 ssh_available: false,
Sean McCullough86b56862025-04-18 13:04:03 -070085 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -070086 input_tokens: 500,
Sean McCulloughd9f13372025-04-21 15:08:49 -070087 start_time: "",
88 messages: 0,
89 output_tokens: 0,
90 cache_read_input_tokens: 0,
91 cache_creation_input_tokens: 0,
92 total_cost_usd: 0,
93 tool_uses: {},
Sean McCullough71941bd2025-04-18 13:31:48 -070094 },
Sean McCullough86b56862025-04-18 13:04:03 -070095 };
96
Sean McCulloughb29f8912025-04-20 15:39:11 -070097 const component = await mount(SketchContainerStatus, {
98 props: {
99 state: partialState as State,
100 },
Sean McCullough86b56862025-04-18 13:04:03 -0700101 });
102
Sean McCulloughb29f8912025-04-20 15:39:11 -0700103 // Check that elements with data are properly populated
104 await expect(component.locator("#hostname")).toContainText("partial-host");
105 await expect(component.locator("#messageCount")).toContainText("10");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000106
107 const expectedTotalInputTokens =
108 partialState.total_usage.input_tokens +
109 partialState.total_usage.cache_read_input_tokens +
110 partialState.total_usage.cache_creation_input_tokens;
111 await expect(component.locator("#inputTokens")).toContainText(
Josh Bleecher Snydera0801ad2025-04-25 19:34:53 +0000112 expectedTotalInputTokens.toLocaleString(),
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000113 );
Sean McCullough86b56862025-04-18 13:04:03 -0700114
Sean McCulloughb29f8912025-04-20 15:39:11 -0700115 // Check that elements without data are empty
116 await expect(component.locator("#workingDir")).toContainText("");
117 await expect(component.locator("#initialCommit")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000118 await expect(component.locator("#outputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -0700119 await expect(component.locator("#totalCost")).toContainText("$0.00");
120});
Sean McCullough86b56862025-04-18 13:04:03 -0700121
Sean McCulloughb29f8912025-04-20 15:39:11 -0700122test("handles cost formatting correctly", async ({ mount }) => {
123 // Test with different cost values
124 const testCases = [
125 { cost: 0, expected: "$0.00" },
126 { cost: 0.1, expected: "$0.10" },
127 { cost: 1.234, expected: "$1.23" },
128 { cost: 10.009, expected: "$10.01" },
129 ];
130
131 for (const testCase of testCases) {
132 const stateWithCost = {
133 ...mockCompleteState,
Sean McCullough86b56862025-04-18 13:04:03 -0700134 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -0700135 ...mockCompleteState.total_usage,
136 total_cost_usd: testCase.cost,
Sean McCullough71941bd2025-04-18 13:31:48 -0700137 },
Sean McCullough86b56862025-04-18 13:04:03 -0700138 };
139
Sean McCulloughb29f8912025-04-20 15:39:11 -0700140 const component = await mount(SketchContainerStatus, {
141 props: {
142 state: stateWithCost,
143 },
144 });
145 await expect(component.locator("#totalCost")).toContainText(
146 testCase.expected,
147 );
148 await component.unmount();
149 }
150});
Sean McCullough86b56862025-04-18 13:04:03 -0700151
Sean McCulloughb29f8912025-04-20 15:39:11 -0700152test("truncates commit hash to 8 characters", async ({ mount }) => {
153 const stateWithLongCommit = {
154 ...mockCompleteState,
155 initial_commit: "1234567890abcdef1234567890abcdef12345678",
156 };
Sean McCullough71941bd2025-04-18 13:31:48 -0700157
Sean McCulloughb29f8912025-04-20 15:39:11 -0700158 const component = await mount(SketchContainerStatus, {
159 props: {
160 state: stateWithLongCommit,
161 },
Sean McCullough86b56862025-04-18 13:04:03 -0700162 });
163
Sean McCulloughb29f8912025-04-20 15:39:11 -0700164 await expect(component.locator("#initialCommit")).toContainText("12345678");
165});
Sean McCullough86b56862025-04-18 13:04:03 -0700166
Sean McCulloughb29f8912025-04-20 15:39:11 -0700167test("has correct link elements", async ({ mount }) => {
168 const component = await mount(SketchContainerStatus, {
169 props: {
170 state: mockCompleteState,
171 },
Sean McCullough86b56862025-04-18 13:04:03 -0700172 });
173
Sean McCulloughb29f8912025-04-20 15:39:11 -0700174 // Check for logs link
175 const logsLink = component.locator("a").filter({ hasText: "Logs" });
176 await expect(logsLink).toHaveAttribute("href", "logs");
Sean McCullough86b56862025-04-18 13:04:03 -0700177
Sean McCulloughb29f8912025-04-20 15:39:11 -0700178 // Check for download link
179 const downloadLink = component.locator("a").filter({ hasText: "Download" });
180 await expect(downloadLink).toHaveAttribute("href", "download");
Sean McCullough86b56862025-04-18 13:04:03 -0700181});