webui: Migrate from @open-wc/testing to Playwright
diff --git a/loop/webui/src/web-components/sketch-container-status.test.ts b/loop/webui/src/web-components/sketch-container-status.test.ts
index 1eae4ee..4a0c397 100644
--- a/loop/webui/src/web-components/sketch-container-status.test.ts
+++ b/loop/webui/src/web-components/sketch-container-status.test.ts
@@ -1,209 +1,161 @@
-import { html, fixture, expect } from "@open-wc/testing";
-import "./sketch-container-status";
-import type { SketchContainerStatus } from "./sketch-container-status";
+import { test, expect } from "@sand4rt/experimental-ct-web";
+import { SketchContainerStatus } from "./sketch-container-status";
import { State } from "../types";
-describe("SketchContainerStatus", () => {
- // Mock complete state for testing
- const mockCompleteState: State = {
- hostname: "test-host",
- working_dir: "/test/dir",
- initial_commit: "abcdef1234567890",
- message_count: 42,
+// Mock complete state for testing
+const mockCompleteState: State = {
+ hostname: "test-host",
+ working_dir: "/test/dir",
+ initial_commit: "abcdef1234567890",
+ message_count: 42,
+ os: "linux",
+ title: "Test Session",
+ total_usage: {
+ input_tokens: 1000,
+ output_tokens: 2000,
+ cache_read_input_tokens: 300,
+ cache_creation_input_tokens: 400,
+ total_cost_usd: 0.25,
+ },
+};
+
+test("render props", async ({ mount }) => {
+ const component = await mount(SketchContainerStatus, {
+ props: {
+ state: mockCompleteState,
+ },
+ });
+ await expect(component.locator("#hostname")).toContainText(
+ mockCompleteState.hostname,
+ );
+ // Check that all expected elements exist
+ await expect(component.locator("#workingDir")).toContainText(
+ mockCompleteState.working_dir,
+ );
+ await expect(component.locator("#initialCommit")).toContainText(
+ mockCompleteState.initial_commit.substring(0, 8),
+ );
+
+ await expect(component.locator("#messageCount")).toContainText(
+ mockCompleteState.message_count + "",
+ );
+ await expect(component.locator("#inputTokens")).toContainText(
+ mockCompleteState.total_usage.input_tokens + "",
+ );
+ await expect(component.locator("#outputTokens")).toContainText(
+ mockCompleteState.total_usage.output_tokens + "",
+ );
+
+ await expect(component.locator("#cacheReadInputTokens")).toContainText(
+ mockCompleteState.total_usage.cache_read_input_tokens + "",
+ );
+ await expect(component.locator("#cacheCreationInputTokens")).toContainText(
+ mockCompleteState.total_usage.cache_creation_input_tokens + "",
+ );
+ await expect(component.locator("#totalCost")).toContainText(
+ "$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
+ );
+});
+
+test("renders with undefined state", async ({ mount }) => {
+ const component = await mount(SketchContainerStatus, {});
+
+ // Elements should exist but be empty
+ await expect(component.locator("#hostname")).toContainText("");
+ await expect(component.locator("#workingDir")).toContainText("");
+ await expect(component.locator("#initialCommit")).toContainText("");
+ await expect(component.locator("#messageCount")).toContainText("");
+ await expect(component.locator("#inputTokens")).toContainText("");
+ await expect(component.locator("#outputTokens")).toContainText("");
+ await expect(component.locator("#totalCost")).toContainText("$0.00");
+});
+
+test("renders with partial state data", async ({ mount }) => {
+ const partialState: Partial<State> = {
+ hostname: "partial-host",
+ message_count: 10,
os: "linux",
- title: "Test Session",
+ title: "Partial Test",
total_usage: {
- input_tokens: 1000,
- output_tokens: 2000,
- cache_read_input_tokens: 300,
- cache_creation_input_tokens: 400,
- total_cost_usd: 0.25,
+ input_tokens: 500,
},
};
- it("renders with complete state data", async () => {
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status
- .state=${mockCompleteState}
- ></sketch-container-status>
- `);
-
- // Check that all expected elements exist
- expect(el.shadowRoot!.querySelector("#hostname")).to.exist;
- expect(el.shadowRoot!.querySelector("#workingDir")).to.exist;
- expect(el.shadowRoot!.querySelector("#initialCommit")).to.exist;
- expect(el.shadowRoot!.querySelector("#messageCount")).to.exist;
- expect(el.shadowRoot!.querySelector("#inputTokens")).to.exist;
- expect(el.shadowRoot!.querySelector("#outputTokens")).to.exist;
- expect(el.shadowRoot!.querySelector("#cacheReadInputTokens")).to.exist;
- expect(el.shadowRoot!.querySelector("#cacheCreationInputTokens")).to.exist;
- expect(el.shadowRoot!.querySelector("#totalCost")).to.exist;
-
- // Verify content of displayed elements
- expect(el.shadowRoot!.querySelector("#hostname")!.textContent).to.equal(
- "test-host",
- );
- expect(el.shadowRoot!.querySelector("#workingDir")!.textContent).to.equal(
- "/test/dir",
- );
- expect(
- el.shadowRoot!.querySelector("#initialCommit")!.textContent,
- ).to.equal("abcdef12"); // Only first 8 chars
- expect(el.shadowRoot!.querySelector("#messageCount")!.textContent).to.equal(
- "42",
- );
- expect(el.shadowRoot!.querySelector("#inputTokens")!.textContent).to.equal(
- "1000",
- );
- expect(el.shadowRoot!.querySelector("#outputTokens")!.textContent).to.equal(
- "2000",
- );
- expect(
- el.shadowRoot!.querySelector("#cacheReadInputTokens")!.textContent,
- ).to.equal("300");
- expect(
- el.shadowRoot!.querySelector("#cacheCreationInputTokens")!.textContent,
- ).to.equal("400");
- expect(el.shadowRoot!.querySelector("#totalCost")!.textContent).to.equal(
- "$0.25",
- );
+ const component = await mount(SketchContainerStatus, {
+ props: {
+ state: partialState as State,
+ },
});
- it("renders with undefined state", async () => {
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status></sketch-container-status>
- `);
+ // Check that elements with data are properly populated
+ await expect(component.locator("#hostname")).toContainText("partial-host");
+ await expect(component.locator("#messageCount")).toContainText("10");
+ await expect(component.locator("#inputTokens")).toContainText("500");
- // Elements should exist but be empty
- expect(el.shadowRoot!.querySelector("#hostname")!.textContent).to.equal("");
- expect(el.shadowRoot!.querySelector("#workingDir")!.textContent).to.equal(
- "",
- );
- expect(
- el.shadowRoot!.querySelector("#initialCommit")!.textContent,
- ).to.equal("");
- expect(el.shadowRoot!.querySelector("#messageCount")!.textContent).to.equal(
- "",
- );
- expect(el.shadowRoot!.querySelector("#inputTokens")!.textContent).to.equal(
- "",
- );
- expect(el.shadowRoot!.querySelector("#outputTokens")!.textContent).to.equal(
- "",
- );
- expect(el.shadowRoot!.querySelector("#totalCost")!.textContent).to.equal(
- "$0.00",
- );
- });
+ // Check that elements without data are empty
+ await expect(component.locator("#workingDir")).toContainText("");
+ await expect(component.locator("#initialCommit")).toContainText("");
+ await expect(component.locator("#outputTokens")).toContainText("");
+ await expect(component.locator("#totalCost")).toContainText("$0.00");
+});
- it("renders with partial state data", async () => {
- const partialState: Partial<State> = {
- hostname: "partial-host",
- message_count: 10,
- os: "linux",
- title: "Partial Test",
+test("handles cost formatting correctly", async ({ mount }) => {
+ // Test with different cost values
+ const testCases = [
+ { cost: 0, expected: "$0.00" },
+ { cost: 0.1, expected: "$0.10" },
+ { cost: 1.234, expected: "$1.23" },
+ { cost: 10.009, expected: "$10.01" },
+ ];
+
+ for (const testCase of testCases) {
+ const stateWithCost = {
+ ...mockCompleteState,
total_usage: {
- input_tokens: 500,
+ ...mockCompleteState.total_usage,
+ total_cost_usd: testCase.cost,
},
};
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status
- .state=${partialState as State}
- ></sketch-container-status>
- `);
+ const component = await mount(SketchContainerStatus, {
+ props: {
+ state: stateWithCost,
+ },
+ });
+ await expect(component.locator("#totalCost")).toContainText(
+ testCase.expected,
+ );
+ await component.unmount();
+ }
+});
- // Check that elements with data are properly populated
- expect(el.shadowRoot!.querySelector("#hostname")!.textContent).to.equal(
- "partial-host",
- );
- expect(el.shadowRoot!.querySelector("#messageCount")!.textContent).to.equal(
- "10",
- );
- expect(el.shadowRoot!.querySelector("#inputTokens")!.textContent).to.equal(
- "500",
- );
+test("truncates commit hash to 8 characters", async ({ mount }) => {
+ const stateWithLongCommit = {
+ ...mockCompleteState,
+ initial_commit: "1234567890abcdef1234567890abcdef12345678",
+ };
- // Check that elements without data are empty
- expect(el.shadowRoot!.querySelector("#workingDir")!.textContent).to.equal(
- "",
- );
- expect(
- el.shadowRoot!.querySelector("#initialCommit")!.textContent,
- ).to.equal("");
- expect(el.shadowRoot!.querySelector("#outputTokens")!.textContent).to.equal(
- "",
- );
- expect(el.shadowRoot!.querySelector("#totalCost")!.textContent).to.equal(
- "$0.00",
- );
+ const component = await mount(SketchContainerStatus, {
+ props: {
+ state: stateWithLongCommit,
+ },
});
- it("handles cost formatting correctly", async () => {
- // Test with different cost values
- const testCases = [
- { cost: 0, expected: "$0.00" },
- { cost: 0.1, expected: "$0.10" },
- { cost: 1.234, expected: "$1.23" },
- { cost: 10.009, expected: "$10.01" },
- ];
+ await expect(component.locator("#initialCommit")).toContainText("12345678");
+});
- for (const testCase of testCases) {
- const stateWithCost = {
- ...mockCompleteState,
- total_usage: {
- ...mockCompleteState.total_usage,
- total_cost_usd: testCase.cost,
- },
- };
-
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status
- .state=${stateWithCost}
- ></sketch-container-status>
- `);
-
- expect(el.shadowRoot!.querySelector("#totalCost")!.textContent).to.equal(
- testCase.expected,
- );
- }
+test("has correct link elements", async ({ mount }) => {
+ const component = await mount(SketchContainerStatus, {
+ props: {
+ state: mockCompleteState,
+ },
});
- it("truncates commit hash to 8 characters", async () => {
- const stateWithLongCommit = {
- ...mockCompleteState,
- initial_commit: "1234567890abcdef1234567890abcdef12345678",
- };
+ // Check for logs link
+ const logsLink = component.locator("a").filter({ hasText: "Logs" });
+ await expect(logsLink).toHaveAttribute("href", "logs");
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status
- .state=${stateWithLongCommit}
- ></sketch-container-status>
- `);
-
- expect(
- el.shadowRoot!.querySelector("#initialCommit")!.textContent,
- ).to.equal("12345678");
- });
-
- it("has correct link elements", async () => {
- const el: SketchContainerStatus = await fixture(html`
- <sketch-container-status
- .state=${mockCompleteState}
- ></sketch-container-status>
- `);
-
- const links = Array.from(el.shadowRoot!.querySelectorAll("a"));
- expect(links.length).to.equal(2);
-
- // Check for logs link
- const logsLink = links.find((link) => link.textContent === "Logs");
- expect(logsLink).to.exist;
- expect(logsLink!.getAttribute("href")).to.equal("logs");
-
- // Check for download link
- const downloadLink = links.find((link) => link.textContent === "Download");
- expect(downloadLink).to.exist;
- expect(downloadLink!.getAttribute("href")).to.equal("download");
- });
+ // Check for download link
+ const downloadLink = component.locator("a").filter({ hasText: "Download" });
+ await expect(downloadLink).toHaveAttribute("href", "download");
});