blob: b7dfd011ceae1da789793c17fe1fbeaed6f095ed [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 },
23};
24
25test("render props", async ({ mount }) => {
26 const component = await mount(SketchContainerStatus, {
27 props: {
28 state: mockCompleteState,
29 },
30 });
31 await expect(component.locator("#hostname")).toContainText(
32 mockCompleteState.hostname,
33 );
34 // Check that all expected elements exist
35 await expect(component.locator("#workingDir")).toContainText(
36 mockCompleteState.working_dir,
37 );
38 await expect(component.locator("#initialCommit")).toContainText(
39 mockCompleteState.initial_commit.substring(0, 8),
40 );
41
42 await expect(component.locator("#messageCount")).toContainText(
43 mockCompleteState.message_count + "",
44 );
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000045 const expectedTotalInputTokens =
46 mockCompleteState.total_usage.input_tokens +
47 mockCompleteState.total_usage.cache_read_input_tokens +
48 mockCompleteState.total_usage.cache_creation_input_tokens;
Sean McCulloughb29f8912025-04-20 15:39:11 -070049 await expect(component.locator("#inputTokens")).toContainText(
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000050 expectedTotalInputTokens + "",
Sean McCulloughb29f8912025-04-20 15:39:11 -070051 );
52 await expect(component.locator("#outputTokens")).toContainText(
53 mockCompleteState.total_usage.output_tokens + "",
54 );
Sean McCulloughb29f8912025-04-20 15:39:11 -070055 await expect(component.locator("#totalCost")).toContainText(
56 "$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
57 );
58});
59
60test("renders with undefined state", async ({ mount }) => {
61 const component = await mount(SketchContainerStatus, {});
62
63 // Elements should exist but be empty
64 await expect(component.locator("#hostname")).toContainText("");
65 await expect(component.locator("#workingDir")).toContainText("");
66 await expect(component.locator("#initialCommit")).toContainText("");
67 await expect(component.locator("#messageCount")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +000068 await expect(component.locator("#inputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -070069 await expect(component.locator("#outputTokens")).toContainText("");
70 await expect(component.locator("#totalCost")).toContainText("$0.00");
71});
72
73test("renders with partial state data", async ({ mount }) => {
74 const partialState: Partial<State> = {
75 hostname: "partial-host",
76 message_count: 10,
Sean McCullough86b56862025-04-18 13:04:03 -070077 os: "linux",
Sean McCulloughb29f8912025-04-20 15:39:11 -070078 title: "Partial Test",
Sean McCullough86b56862025-04-18 13:04:03 -070079 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -070080 input_tokens: 500,
Sean McCulloughd9f13372025-04-21 15:08:49 -070081 start_time: "",
82 messages: 0,
83 output_tokens: 0,
84 cache_read_input_tokens: 0,
85 cache_creation_input_tokens: 0,
86 total_cost_usd: 0,
87 tool_uses: {},
Sean McCullough71941bd2025-04-18 13:31:48 -070088 },
Sean McCullough86b56862025-04-18 13:04:03 -070089 };
90
Sean McCulloughb29f8912025-04-20 15:39:11 -070091 const component = await mount(SketchContainerStatus, {
92 props: {
93 state: partialState as State,
94 },
Sean McCullough86b56862025-04-18 13:04:03 -070095 });
96
Sean McCulloughb29f8912025-04-20 15:39:11 -070097 // Check that elements with data are properly populated
98 await expect(component.locator("#hostname")).toContainText("partial-host");
99 await expect(component.locator("#messageCount")).toContainText("10");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000100
101 const expectedTotalInputTokens =
102 partialState.total_usage.input_tokens +
103 partialState.total_usage.cache_read_input_tokens +
104 partialState.total_usage.cache_creation_input_tokens;
105 await expect(component.locator("#inputTokens")).toContainText(
106 expectedTotalInputTokens + "",
107 );
Sean McCullough86b56862025-04-18 13:04:03 -0700108
Sean McCulloughb29f8912025-04-20 15:39:11 -0700109 // Check that elements without data are empty
110 await expect(component.locator("#workingDir")).toContainText("");
111 await expect(component.locator("#initialCommit")).toContainText("");
Josh Bleecher Snyder35889972025-04-24 20:48:16 +0000112 await expect(component.locator("#outputTokens")).toContainText("0");
Sean McCulloughb29f8912025-04-20 15:39:11 -0700113 await expect(component.locator("#totalCost")).toContainText("$0.00");
114});
Sean McCullough86b56862025-04-18 13:04:03 -0700115
Sean McCulloughb29f8912025-04-20 15:39:11 -0700116test("handles cost formatting correctly", async ({ mount }) => {
117 // Test with different cost values
118 const testCases = [
119 { cost: 0, expected: "$0.00" },
120 { cost: 0.1, expected: "$0.10" },
121 { cost: 1.234, expected: "$1.23" },
122 { cost: 10.009, expected: "$10.01" },
123 ];
124
125 for (const testCase of testCases) {
126 const stateWithCost = {
127 ...mockCompleteState,
Sean McCullough86b56862025-04-18 13:04:03 -0700128 total_usage: {
Sean McCulloughb29f8912025-04-20 15:39:11 -0700129 ...mockCompleteState.total_usage,
130 total_cost_usd: testCase.cost,
Sean McCullough71941bd2025-04-18 13:31:48 -0700131 },
Sean McCullough86b56862025-04-18 13:04:03 -0700132 };
133
Sean McCulloughb29f8912025-04-20 15:39:11 -0700134 const component = await mount(SketchContainerStatus, {
135 props: {
136 state: stateWithCost,
137 },
138 });
139 await expect(component.locator("#totalCost")).toContainText(
140 testCase.expected,
141 );
142 await component.unmount();
143 }
144});
Sean McCullough86b56862025-04-18 13:04:03 -0700145
Sean McCulloughb29f8912025-04-20 15:39:11 -0700146test("truncates commit hash to 8 characters", async ({ mount }) => {
147 const stateWithLongCommit = {
148 ...mockCompleteState,
149 initial_commit: "1234567890abcdef1234567890abcdef12345678",
150 };
Sean McCullough71941bd2025-04-18 13:31:48 -0700151
Sean McCulloughb29f8912025-04-20 15:39:11 -0700152 const component = await mount(SketchContainerStatus, {
153 props: {
154 state: stateWithLongCommit,
155 },
Sean McCullough86b56862025-04-18 13:04:03 -0700156 });
157
Sean McCulloughb29f8912025-04-20 15:39:11 -0700158 await expect(component.locator("#initialCommit")).toContainText("12345678");
159});
Sean McCullough86b56862025-04-18 13:04:03 -0700160
Sean McCulloughb29f8912025-04-20 15:39:11 -0700161test("has correct link elements", async ({ mount }) => {
162 const component = await mount(SketchContainerStatus, {
163 props: {
164 state: mockCompleteState,
165 },
Sean McCullough86b56862025-04-18 13:04:03 -0700166 });
167
Sean McCulloughb29f8912025-04-20 15:39:11 -0700168 // Check for logs link
169 const logsLink = component.locator("a").filter({ hasText: "Logs" });
170 await expect(logsLink).toHaveAttribute("href", "logs");
Sean McCullough86b56862025-04-18 13:04:03 -0700171
Sean McCulloughb29f8912025-04-20 15:39:11 -0700172 // Check for download link
173 const downloadLink = component.locator("a").filter({ hasText: "Download" });
174 await expect(downloadLink).toHaveAttribute("href", "download");
Sean McCullough86b56862025-04-18 13:04:03 -0700175});