| ... | @@ -11,6 +11,7 @@ import { | ... | @@ -11,6 +11,7 @@ import { |
| 11 | type WsWelcomePayload, | 11 | type WsWelcomePayload, |
| 12 | WS_CHANNELS, | 12 | WS_CHANNELS, |
| 13 | WS_METHODS, | 13 | WS_METHODS, |
| | 14 | OrchestrationSessionStatus, |
| 14 | } from "@t3tools/contracts"; | 15 | } from "@t3tools/contracts"; |
| 15 | import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; | 16 | import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; |
| 16 | import { HttpResponse, http, ws } from "msw"; | 17 | import { HttpResponse, http, ws } from "msw"; |
| ... | @@ -153,6 +154,7 @@ function createSnapshotForTargetUser(options: { | ... | @@ -153,6 +154,7 @@ function createSnapshotForTargetUser(options: { |
| 153 | targetMessageId: MessageId; | 154 | targetMessageId: MessageId; |
| 154 | targetText: string; | 155 | targetText: string; |
| 155 | targetAttachmentCount?: number; | 156 | targetAttachmentCount?: number; |
| | 157 | sessionStatus?: OrchestrationSessionStatus; |
| 156 | }): OrchestrationReadModel { | 158 | }): OrchestrationReadModel { |
| 157 | const messages: Array<OrchestrationReadModel["threads"][number]["messages"][number]> = []; | 159 | const messages: Array<OrchestrationReadModel["threads"][number]["messages"][number]> = []; |
| 158 | | 160 | |
| ... | @@ -222,7 +224,7 @@ function createSnapshotForTargetUser(options: { | ... | @@ -222,7 +224,7 @@ function createSnapshotForTargetUser(options: { |
| 222 | checkpoints: [], | 224 | checkpoints: [], |
| 223 | session: { | 225 | session: { |
| 224 | threadId: THREAD_ID, | 226 | threadId: THREAD_ID, |
| 225 | status: "ready", | 227 | status: options.sessionStatus ?? "ready", |
| 226 | providerName: "codex", | 228 | providerName: "codex", |
| 227 | runtimeMode: "full-access", | 229 | runtimeMode: "full-access", |
| 228 | activeTurnId: null, | 230 | activeTurnId: null, |
| ... | @@ -1009,6 +1011,28 @@ describe("ChatView timeline estimator parity (full app)", () => { | ... | @@ -1009,6 +1011,28 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 1009 | } | 1011 | } |
| 1010 | }); | 1012 | }); |
| 1011 | | 1013 | |
| | 1014 | it("shows a pointer cursor for the running stop button", async () => { |
| | 1015 | const mounted = await mountChatView({ |
| | 1016 | viewport: DEFAULT_VIEWPORT, |
| | 1017 | snapshot: createSnapshotForTargetUser({ |
| | 1018 | targetMessageId: "msg-user-stop-button-cursor" as MessageId, |
| | 1019 | targetText: "stop button cursor target", |
| | 1020 | sessionStatus: "running", |
| | 1021 | }), |
| | 1022 | }); |
| | 1023 | |
| | 1024 | try { |
| | 1025 | const stopButton = await waitForElement( |
| | 1026 | () => document.querySelector<HTMLButtonElement>('button[aria-label="Stop generation"]'), |
| | 1027 | "Unable to find stop generation button.", |
| | 1028 | ); |
| | 1029 | |
| | 1030 | expect(getComputedStyle(stopButton).cursor).toBe("pointer"); |
| | 1031 | } finally { |
| | 1032 | await mounted.cleanup(); |
| | 1033 | } |
| | 1034 | }); |
| | 1035 | |
| 1012 | it("keeps the new thread selected after clicking the new-thread button", async () => { | 1036 | it("keeps the new thread selected after clicking the new-thread button", async () => { |
| 1013 | const mounted = await mountChatView({ | 1037 | const mounted = await mountChatView({ |
| 1014 | viewport: DEFAULT_VIEWPORT, | 1038 | viewport: DEFAULT_VIEWPORT, |