|
| 1 | +import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test" |
| 2 | +import type { Prompt } from "@/context/prompt" |
| 3 | + |
| 4 | +let createPromptSubmit: typeof import("./submit").createPromptSubmit |
| 5 | + |
| 6 | +const createdClients: string[] = [] |
| 7 | +const createdSessions: string[] = [] |
| 8 | +const sentShell: string[] = [] |
| 9 | +const syncedDirectories: string[] = [] |
| 10 | + |
| 11 | +let selected = "/repo/worktree-a" |
| 12 | + |
| 13 | +const promptValue: Prompt = [{ type: "text", content: "ls", start: 0, end: 2 }] |
| 14 | + |
| 15 | +const clientFor = (directory: string) => ({ |
| 16 | + session: { |
| 17 | + create: async () => { |
| 18 | + createdSessions.push(directory) |
| 19 | + return { data: { id: `session-${createdSessions.length}` } } |
| 20 | + }, |
| 21 | + shell: async () => { |
| 22 | + sentShell.push(directory) |
| 23 | + return { data: undefined } |
| 24 | + }, |
| 25 | + prompt: async () => ({ data: undefined }), |
| 26 | + command: async () => ({ data: undefined }), |
| 27 | + abort: async () => ({ data: undefined }), |
| 28 | + }, |
| 29 | + worktree: { |
| 30 | + create: async () => ({ data: { directory: `${directory}/new` } }), |
| 31 | + }, |
| 32 | +}) |
| 33 | + |
| 34 | +beforeAll(async () => { |
| 35 | + const rootClient = clientFor("/repo/main") |
| 36 | + |
| 37 | + mock.module("@solidjs/router", () => ({ |
| 38 | + useNavigate: () => () => undefined, |
| 39 | + useParams: () => ({}), |
| 40 | + })) |
| 41 | + |
| 42 | + mock.module("@opencode-ai/sdk/v2/client", () => ({ |
| 43 | + createOpencodeClient: (input: { directory: string }) => { |
| 44 | + createdClients.push(input.directory) |
| 45 | + return clientFor(input.directory) |
| 46 | + }, |
| 47 | + })) |
| 48 | + |
| 49 | + mock.module("@opencode-ai/ui/toast", () => ({ |
| 50 | + showToast: () => 0, |
| 51 | + })) |
| 52 | + |
| 53 | + mock.module("@opencode-ai/util/encode", () => ({ |
| 54 | + base64Encode: (value: string) => value, |
| 55 | + })) |
| 56 | + |
| 57 | + mock.module("@/context/local", () => ({ |
| 58 | + useLocal: () => ({ |
| 59 | + model: { |
| 60 | + current: () => ({ id: "model", provider: { id: "provider" } }), |
| 61 | + variant: { current: () => undefined }, |
| 62 | + }, |
| 63 | + agent: { |
| 64 | + current: () => ({ name: "agent" }), |
| 65 | + }, |
| 66 | + }), |
| 67 | + })) |
| 68 | + |
| 69 | + mock.module("@/context/prompt", () => ({ |
| 70 | + usePrompt: () => ({ |
| 71 | + current: () => promptValue, |
| 72 | + reset: () => undefined, |
| 73 | + set: () => undefined, |
| 74 | + context: { |
| 75 | + add: () => undefined, |
| 76 | + remove: () => undefined, |
| 77 | + items: () => [], |
| 78 | + }, |
| 79 | + }), |
| 80 | + })) |
| 81 | + |
| 82 | + mock.module("@/context/layout", () => ({ |
| 83 | + useLayout: () => ({ |
| 84 | + handoff: { |
| 85 | + setTabs: () => undefined, |
| 86 | + }, |
| 87 | + }), |
| 88 | + })) |
| 89 | + |
| 90 | + mock.module("@/context/sdk", () => ({ |
| 91 | + useSDK: () => ({ |
| 92 | + directory: "/repo/main", |
| 93 | + client: rootClient, |
| 94 | + url: "http://localhost:4096", |
| 95 | + }), |
| 96 | + })) |
| 97 | + |
| 98 | + mock.module("@/context/sync", () => ({ |
| 99 | + useSync: () => ({ |
| 100 | + data: { command: [] }, |
| 101 | + session: { |
| 102 | + optimistic: { |
| 103 | + add: () => undefined, |
| 104 | + remove: () => undefined, |
| 105 | + }, |
| 106 | + }, |
| 107 | + set: () => undefined, |
| 108 | + }), |
| 109 | + })) |
| 110 | + |
| 111 | + mock.module("@/context/global-sync", () => ({ |
| 112 | + useGlobalSync: () => ({ |
| 113 | + child: (directory: string) => { |
| 114 | + syncedDirectories.push(directory) |
| 115 | + return [{}, () => undefined] |
| 116 | + }, |
| 117 | + }), |
| 118 | + })) |
| 119 | + |
| 120 | + mock.module("@/context/platform", () => ({ |
| 121 | + usePlatform: () => ({ |
| 122 | + fetch: fetch, |
| 123 | + }), |
| 124 | + })) |
| 125 | + |
| 126 | + mock.module("@/context/language", () => ({ |
| 127 | + useLanguage: () => ({ |
| 128 | + t: (key: string) => key, |
| 129 | + }), |
| 130 | + })) |
| 131 | + |
| 132 | + const mod = await import("./submit") |
| 133 | + createPromptSubmit = mod.createPromptSubmit |
| 134 | +}) |
| 135 | + |
| 136 | +beforeEach(() => { |
| 137 | + createdClients.length = 0 |
| 138 | + createdSessions.length = 0 |
| 139 | + sentShell.length = 0 |
| 140 | + syncedDirectories.length = 0 |
| 141 | + selected = "/repo/worktree-a" |
| 142 | +}) |
| 143 | + |
| 144 | +describe("prompt submit worktree selection", () => { |
| 145 | + test("reads the latest worktree accessor value per submit", async () => { |
| 146 | + const submit = createPromptSubmit({ |
| 147 | + info: () => undefined, |
| 148 | + imageAttachments: () => [], |
| 149 | + commentCount: () => 0, |
| 150 | + mode: () => "shell", |
| 151 | + working: () => false, |
| 152 | + editor: () => undefined, |
| 153 | + queueScroll: () => undefined, |
| 154 | + promptLength: (value) => value.reduce((sum, part) => sum + ("content" in part ? part.content.length : 0), 0), |
| 155 | + addToHistory: () => undefined, |
| 156 | + resetHistoryNavigation: () => undefined, |
| 157 | + setMode: () => undefined, |
| 158 | + setPopover: () => undefined, |
| 159 | + newSessionWorktree: () => selected, |
| 160 | + onNewSessionWorktreeReset: () => undefined, |
| 161 | + onSubmit: () => undefined, |
| 162 | + }) |
| 163 | + |
| 164 | + const event = { preventDefault: () => undefined } as unknown as Event |
| 165 | + |
| 166 | + await submit.handleSubmit(event) |
| 167 | + selected = "/repo/worktree-b" |
| 168 | + await submit.handleSubmit(event) |
| 169 | + |
| 170 | + expect(createdClients).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) |
| 171 | + expect(createdSessions).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) |
| 172 | + expect(sentShell).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) |
| 173 | + expect(syncedDirectories).toEqual(["/repo/worktree-a", "/repo/worktree-b"]) |
| 174 | + }) |
| 175 | +}) |
0 commit comments