webui: Add test for sketch-app-shell component
Created a basic test for the top-level Sketch App Shell component with API mocking. Reorganized mock data to be shared between tests and demo.
Co-Authored-By: sketch
diff --git a/webui/src/web-components/demo/mocks/fixtures/dummy.ts b/webui/src/web-components/demo/mocks/fixtures/dummy.ts
deleted file mode 100644
index e45371f..0000000
--- a/webui/src/web-components/demo/mocks/fixtures/dummy.ts
+++ /dev/null
@@ -1,372 +0,0 @@
-import { AgentMessage, State } from "../../../../types";
-
-export const initialMessages: AgentMessage[] = [
- {
- type: "user",
- end_of_turn: false,
- content:
- "add a line to dummy.txt. Doesn't matter what it is and don't bother running tests it's just a dummy repo. Please commit afterward",
- timestamp: "2025-04-24T10:32:00.679873+01:00",
- conversation_id: "",
- idx: 0,
- },
- {
- type: "agent",
- end_of_turn: false,
- content:
- "I'll help you add a line to dummy.txt and commit the change. Let me first check if this file exists and create it if needed.",
- tool_calls: [
- {
- name: "title",
- input: '{"title":"Add a line to dummy.txt and commit the change"}',
- tool_call_id: "toolu_01HSQ8xb9Ba8oP1gJPv1eD2s",
- },
- ],
- timestamp: "2025-04-24T10:32:03.988439+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 4,
- cache_creation_input_tokens: 2228,
- cache_read_input_tokens: 0,
- output_tokens: 92,
- cost_usd: 0.009747,
- },
- start_time: "2025-04-24T10:32:00.680125+01:00",
- end_time: "2025-04-24T10:32:03.988434+01:00",
- elapsed: 3308355125,
- idx: 1,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "title",
- input: '{"title":"Add a line to dummy.txt and commit the change"}',
- tool_result: "Title set to: Add a line to dummy.txt and commit the change",
- tool_call_id: "toolu_01HSQ8xb9Ba8oP1gJPv1eD2s",
- timestamp: "2025-04-24T10:32:03.988567+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:03.988553+01:00",
- end_time: "2025-04-24T10:32:03.988566+01:00",
- elapsed: 13500,
- idx: 2,
- },
- {
- type: "agent",
- end_of_turn: false,
- content: "",
- tool_calls: [
- {
- name: "bash",
- input: '{"command":"ls -la"}',
- tool_call_id: "toolu_01RtkhetY8bqC6U1QBfVbukS",
- },
- ],
- timestamp: "2025-04-24T10:32:06.146332+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 6,
- cache_creation_input_tokens: 116,
- cache_read_input_tokens: 2228,
- output_tokens: 54,
- cost_usd: 0.0019314,
- },
- start_time: "2025-04-24T10:32:04.007226+01:00",
- end_time: "2025-04-24T10:32:06.146325+01:00",
- elapsed: 2139128375,
- idx: 3,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "bash",
- input: '{"command":"ls -la"}',
- tool_result:
- "total 1712\ndrwxr-xr-x 29 pokey staff 928 Apr 15 13:06 .\ndrwxr-xr-x 397 pokey staff 12704 Apr 23 09:53 ..\n-rw-r--r--@ 1 pokey staff 292 Jan 8 2024 .editorconfig\ndrwxr-xr-x 17 pokey staff 544 Apr 24 10:32 .git\ndrwxr-xr-x@ 3 pokey staff 96 Jan 8 2024 .github\n-rw-r--r--@ 1 pokey staff 777 Jan 8 2024 .gitignore\n-rw-r--r--@ 1 pokey staff 0 Apr 15 13:06 .PROMPT_DRAFT.md\n-rw------- 1 pokey staff 11811 Oct 4 2022 .tags\n-rw-r--r--@ 1 pokey staff 762 Jan 8 2024 .travis.yml\ndrwxr-xr-x 3 pokey staff 96 Apr 9 10:51 .vscode\n-rw-r--r--@ 1 pokey staff 156 Jan 8 2024 AUTHORS.rst\n-rw-r--r--@ 1 pokey staff 3249 Jan 8 2024 CONTRIBUTING.rst\ndrwxr-xr-x@ 12 pokey staff 384 Jan 8 2024 docs\n-rw-r--r-- 1 pokey staff 6644 Apr 23 15:31 dummy.txt\n-rw-r--r--@ 1 pokey staff 89 Jan 8 2024 HISTORY.rst\n-rw-r--r--@ 1 pokey staff 1070 Jan 8 2024 LICENSE\n-rw-r--r--@ 1 pokey staff 2303 Jan 8 2024 Makefile\n-rw-r--r--@ 1 pokey staff 264 Jan 8 2024 MANIFEST.in\n-rw-r--r-- 1 pokey staff 794413 Mar 7 2017 myVim.log\n-rw-r--r--@ 1 pokey staff 1043 Apr 8 12:21 README.rst\n-rw-r--r--@ 1 pokey staff 145 Jan 8 2024 requirements_dev.txt\n-rw-r--r--@ 1 pokey staff 342 Jan 8 2024 setup.cfg\n-rw-r--r--@ 1 pokey staff 1559 Jan 8 2024 setup.py\ndrwxr-xr-x 6 pokey staff 192 Jan 8 2024 spaghetti\ndrwxr-xr-x 9 pokey staff 288 Feb 26 2017 spaghetti.egg-info\ndrwxr-xr-x@ 5 pokey staff 160 Apr 4 19:07 tests\n-rw-r--r--@ 1 pokey staff 430 Jan 8 2024 tox.ini\n-rw-r--r--@ 1 pokey staff 3753 Jan 8 2024 travis_pypi_setup.py\ndrwxr-xr-x 2 pokey staff 64 Mar 11 2017 UltiSnips\n",
- tool_call_id: "toolu_01RtkhetY8bqC6U1QBfVbukS",
- timestamp: "2025-04-24T10:32:06.176176+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:06.146414+01:00",
- end_time: "2025-04-24T10:32:06.176175+01:00",
- elapsed: 29760750,
- idx: 4,
- },
- {
- type: "agent",
- end_of_turn: false,
- content:
- "I see that dummy.txt already exists. Let me add a new line to it.",
- tool_calls: [
- {
- name: "patch",
- input:
- '{"path":"/Users/pokey/src/spaghetti/dummy.txt","patches":[{"operation":"append_eof","newText":"\\nAdding a new dummy line here - requested change"}]}',
- tool_call_id: "toolu_01KjY2a2MWhfvwy6Pmci8SX2",
- },
- ],
- timestamp: "2025-04-24T10:32:09.724348+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 6,
- cache_creation_input_tokens: 970,
- cache_read_input_tokens: 2344,
- output_tokens: 141,
- cost_usd: 0.0064737,
- },
- start_time: "2025-04-24T10:32:06.195746+01:00",
- end_time: "2025-04-24T10:32:09.724335+01:00",
- elapsed: 3528638042,
- idx: 5,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "patch",
- input:
- '{"path":"/Users/pokey/src/spaghetti/dummy.txt","patches":[{"operation":"append_eof","newText":"\\nAdding a new dummy line here - requested change"}]}',
- tool_result: "- Applied all patches\n",
- tool_call_id: "toolu_01KjY2a2MWhfvwy6Pmci8SX2",
- timestamp: "2025-04-24T10:32:09.728267+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:09.725039+01:00",
- end_time: "2025-04-24T10:32:09.728265+01:00",
- elapsed: 3225875,
- idx: 6,
- },
- {
- type: "agent",
- end_of_turn: false,
- content: "Now let me commit this change:",
- tool_calls: [
- {
- name: "bash",
- input:
- '{"command":"GIT_AUTHOR_NAME=\\"Pokey Rule\\" GIT_AUTHOR_EMAIL=\\"755842+pokey@users.noreply.github.com\\" git add dummy.txt && git commit -m \\"Add a new line to dummy.txt\\" -m \\"Co-Authored-By: sketch\\" -m \\"Add a line to dummy.txt. Doesn\'t matter what it is and don\'t bother running tests it\'s just a dummy repo.\\""}',
- tool_call_id: "toolu_01NQXuuU4JdUHn29S35oGLxF",
- },
- ],
- timestamp: "2025-04-24T10:32:13.306331+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 6,
- cache_creation_input_tokens: 146,
- cache_read_input_tokens: 3314,
- output_tokens: 160,
- cost_usd: 0.0039597,
- },
- start_time: "2025-04-24T10:32:09.76053+01:00",
- end_time: "2025-04-24T10:32:13.306203+01:00",
- elapsed: 3545723083,
- idx: 7,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "bash",
- input:
- '{"command":"GIT_AUTHOR_NAME=\\"Pokey Rule\\" GIT_AUTHOR_EMAIL=\\"755842+pokey@users.noreply.github.com\\" git add dummy.txt && git commit -m \\"Add a new line to dummy.txt\\" -m \\"Co-Authored-By: sketch\\" -m \\"Add a line to dummy.txt. Doesn\'t matter what it is and don\'t bother running tests it\'s just a dummy repo.\\""}',
- tool_result:
- "branchless: processing 2 updates: branch main, ref HEAD\nbranchless: processed commit: 898178a Add a new line to dummy.txt\n[main 898178a] Add a new line to dummy.txt\n 1 file changed, 2 insertions(+), 1 deletion(-)\n",
- tool_call_id: "toolu_01NQXuuU4JdUHn29S35oGLxF",
- timestamp: "2025-04-24T10:32:13.652683+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:13.307345+01:00",
- end_time: "2025-04-24T10:32:13.652678+01:00",
- elapsed: 345337875,
- idx: 8,
- },
- {
- type: "commit",
- end_of_turn: false,
- content: "",
- commits: [
- {
- hash: "898178a0c6681ce2b641cbe8994c22ed8bbb7dd7",
- subject: "Add a new line to dummy.txt",
- body: "Co-Authored-By: sketch\n\nAdd a line to dummy.txt. Doesn't matter what it is and don't bother running tests it's just a dummy repo.",
- },
- ],
- timestamp: "2025-04-24T10:32:13.690825+01:00",
- conversation_id: "",
- idx: 9,
- },
- {
- type: "agent",
- end_of_turn: false,
- content: "",
- tool_calls: [
- {
- name: "done",
- input:
- '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"not applicable","description":"The change was trivial - just adding a line to a dummy.txt file."}}}',
- tool_call_id: "toolu_01YMBDmBoCcE5tfZvbXAgWEa",
- },
- ],
- timestamp: "2025-04-24T10:32:19.07007+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 6,
- cache_creation_input_tokens: 244,
- cache_read_input_tokens: 3460,
- output_tokens: 241,
- cost_usd: 0.005586,
- },
- start_time: "2025-04-24T10:32:13.74753+01:00",
- end_time: "2025-04-24T10:32:19.070058+01:00",
- elapsed: 5322601792,
- idx: 10,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "done",
- input:
- '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"not applicable","description":"The change was trivial - just adding a line to a dummy.txt file."}}}',
- tool_result:
- "codereview tool has not been run for commit 898178a0c6681ce2b641cbe8994c22ed8bbb7dd7",
- tool_error: true,
- tool_call_id: "toolu_01YMBDmBoCcE5tfZvbXAgWEa",
- timestamp: "2025-04-24T10:32:19.121365+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:19.070249+01:00",
- end_time: "2025-04-24T10:32:19.121358+01:00",
- elapsed: 51109583,
- idx: 11,
- },
- {
- type: "agent",
- end_of_turn: false,
- content: "Let me run a code review as required:",
- tool_calls: [
- {
- name: "codereview",
- input: "{}",
- tool_call_id: "toolu_01Fgg5vaURskyxGcbwHoBu7z",
- },
- ],
- timestamp: "2025-04-24T10:32:21.918149+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 7,
- cache_creation_input_tokens: 253,
- cache_read_input_tokens: 3704,
- output_tokens: 54,
- cost_usd: 0.0028909499999999998,
- },
- start_time: "2025-04-24T10:32:19.14049+01:00",
- end_time: "2025-04-24T10:32:21.918133+01:00",
- elapsed: 2777681917,
- idx: 12,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "codereview",
- input: "{}",
- tool_result: "OK",
- tool_call_id: "toolu_01Fgg5vaURskyxGcbwHoBu7z",
- timestamp: "2025-04-24T10:32:22.516658+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:21.918261+01:00",
- end_time: "2025-04-24T10:32:22.516654+01:00",
- elapsed: 598402125,
- idx: 13,
- },
- {
- type: "agent",
- end_of_turn: false,
- content: "Now let me try the done call again:",
- tool_calls: [
- {
- name: "done",
- input:
- '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"yes","description":"Code review completed and no issues found. The change was trivial - just adding a line to a dummy.txt file."}}}',
- tool_call_id: "toolu_016TBxz5Ma8y3if3rY4DZBXE",
- },
- ],
- timestamp: "2025-04-24T10:32:26.926407+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 6,
- cache_creation_input_tokens: 60,
- cache_read_input_tokens: 3957,
- output_tokens: 217,
- cost_usd: 0.0046851,
- },
- start_time: "2025-04-24T10:32:22.534359+01:00",
- end_time: "2025-04-24T10:32:26.926391+01:00",
- elapsed: 4392093250,
- idx: 14,
- },
- {
- type: "tool",
- end_of_turn: false,
- content: "",
- tool_name: "done",
- input:
- '{"checklist_items":{"git_commit":{"status":"yes","description":"Created a git commit for the code changes with the appropriate commit message that includes \'Co-Authored-By: sketch\' and the original user prompt."},"wrote_tests":{"status":"not applicable","description":"No functional code was changed, just added a line to a dummy file."},"passes_tests":{"status":"not applicable","description":"User explicitly mentioned not to run tests as this is just a dummy repo."},"code_reviewed":{"status":"yes","description":"Code review completed and no issues found. The change was trivial - just adding a line to a dummy.txt file."}}}',
- tool_result:
- "Please ask the user to review your work. Be concise - users are more likely to read shorter comments.",
- tool_call_id: "toolu_016TBxz5Ma8y3if3rY4DZBXE",
- timestamp: "2025-04-24T10:32:26.978771+01:00",
- conversation_id: "37s-g6xg",
- start_time: "2025-04-24T10:32:26.92661+01:00",
- end_time: "2025-04-24T10:32:26.978758+01:00",
- elapsed: 52149125,
- idx: 15,
- },
- {
- type: "agent",
- end_of_turn: true,
- content:
- 'I\'ve completed your request:\n\n1. Added a new line to dummy.txt: "Adding a new dummy line here - requested change"\n2. Created a git commit with the change using the specified author information\n3. Included "Co-Authored-By: sketch" in the commit message as required\n\nThe change has been successfully committed to the repository. Is there anything else you\'d like me to help with?',
- timestamp: "2025-04-24T10:32:29.072661+01:00",
- conversation_id: "37s-g6xg",
- usage: {
- input_tokens: 5,
- cache_creation_input_tokens: 250,
- cache_read_input_tokens: 4017,
- output_tokens: 92,
- cost_usd: 0.0035376,
- },
- start_time: "2025-04-24T10:32:26.99749+01:00",
- end_time: "2025-04-24T10:32:29.072654+01:00",
- elapsed: 2075193375,
- turnDuration: 28393844125,
- idx: 16,
- },
-];
-
-export const initialState: State = {
- message_count: initialMessages.length,
- total_usage: {
- start_time: "2025-04-24T10:32:00.679057+01:00",
- messages: 8,
- input_tokens: 46,
- output_tokens: 1051,
- cache_read_input_tokens: 23024,
- cache_creation_input_tokens: 4267,
- total_cost_usd: 0.038811450000000004,
- tool_uses: {
- bash: 2,
- codereview: 1,
- done: 2,
- patch: 1,
- title: 1,
- },
- },
- initial_commit: "a6c5a08a451ef1082774a7affb6af58775e7bc16",
- title: "Add a line to dummy.txt and commit the change",
- hostname: "MacBook-Pro-9.local",
- working_dir: "/Users/pokey/src/spaghetti",
- os: "darwin",
- git_origin: "git@github.com:pokey/spaghetti.git",
- inside_hostname: "MacBook-Pro-9.local",
- inside_os: "darwin",
- inside_working_dir: "/Users/pokey/src/spaghetti",
-};
diff --git a/webui/src/web-components/demo/mocks/handlers.ts b/webui/src/web-components/demo/mocks/handlers.ts
index 21bacd7..c05a6c4 100644
--- a/webui/src/web-components/demo/mocks/handlers.ts
+++ b/webui/src/web-components/demo/mocks/handlers.ts
@@ -1,5 +1,5 @@
import { http, HttpResponse, delay } from "msw";
-import { initialState, initialMessages } from "./fixtures/dummy";
+import { initialState, initialMessages } from "../../../fixtures/dummy";
// Mock state updates for long-polling simulation
let currentState = { ...initialState };
diff --git a/webui/src/web-components/sketch-app-shell.test.ts b/webui/src/web-components/sketch-app-shell.test.ts
new file mode 100644
index 0000000..0b3ae6a
--- /dev/null
+++ b/webui/src/web-components/sketch-app-shell.test.ts
@@ -0,0 +1,88 @@
+import { test, expect } from "@sand4rt/experimental-ct-web";
+import { SketchAppShell } from "./sketch-app-shell";
+import { initialMessages, initialState } from "../fixtures/dummy";
+
+test("renders app shell with mocked API", async ({ page, mount }) => {
+ // Mock the state API response
+ await page.route("**/state", async (route) => {
+ await route.fulfill({ json: initialState });
+ });
+
+ // Mock the messages API response
+ await page.route("**/messages*", async (route) => {
+ const url = new URL(route.request().url());
+ const startIndex = parseInt(url.searchParams.get("start") || "0");
+ await route.fulfill({ json: initialMessages.slice(startIndex) });
+ });
+
+ // Mount the component
+ const component = await mount(SketchAppShell);
+
+ // Wait for initial data to load
+ await page.waitForTimeout(500);
+
+ // Verify the title is displayed correctly
+ await expect(component.locator(".chat-title")).toContainText(
+ initialState.title,
+ );
+
+ // Verify core components are rendered
+ await expect(component.locator("sketch-container-status")).toBeVisible();
+ await expect(component.locator("sketch-timeline")).toBeVisible();
+ await expect(component.locator("sketch-chat-input")).toBeVisible();
+ await expect(component.locator("sketch-view-mode-select")).toBeVisible();
+
+ // Default view should be chat view
+ await expect(component.locator(".chat-view.view-active")).toBeVisible();
+});
+
+const emptyState = {
+ message_count: 0,
+ total_usage: {
+ start_time: "2025-04-25T19:07:24.94241+01:00",
+ messages: 0,
+ input_tokens: 0,
+ output_tokens: 0,
+ cache_read_input_tokens: 0,
+ cache_creation_input_tokens: 0,
+ total_cost_usd: 0,
+ tool_uses: {},
+ },
+ initial_commit: "08e2cf2eaf043df77f8468d90bb21d0083de2132",
+ title: "",
+ hostname: "MacBook-Pro-9.local",
+ working_dir: "/Users/pokey/src/sketch",
+ os: "darwin",
+ git_origin: "git@github.com:boldsoftware/sketch.git",
+ inside_hostname: "MacBook-Pro-9.local",
+ inside_os: "darwin",
+ inside_working_dir: "/Users/pokey/src/sketch",
+};
+
+test("renders app shell with empty state", async ({ page, mount }) => {
+ // Mock the state API response
+ await page.route("**/state", async (route) => {
+ await route.fulfill({ json: emptyState });
+ });
+
+ // Mock the messages API response
+ await page.route("**/messages*", async (route) => {
+ await route.fulfill({ json: [] });
+ });
+
+ // Mount the component
+ const component = await mount(SketchAppShell);
+
+ // Wait for initial data to load
+ await page.waitForTimeout(500);
+
+ // Verify the title is displayed correctly
+ await expect(component.locator(".chat-title")).toContainText(
+ emptyState.title,
+ );
+
+ // Verify core components are rendered
+ await expect(component.locator("sketch-container-status")).toBeVisible();
+ await expect(component.locator("sketch-chat-input")).toBeVisible();
+ await expect(component.locator("sketch-view-mode-select")).toBeVisible();
+});