fix(web): émet chat.attached pour les sessions agents structurées au lieu de UNSUPPORTED — #229

This commit is contained in:
2026-08-20 15:10:41 +02:00
parent 1b8534fbf9
commit 023d483ba2
5 changed files with 203 additions and 35 deletions

View File

@ -89,6 +89,98 @@ describe("CustomAgentChatView", () => {
__resetProjectCliInteractionActivityForTests();
});
it("shows the technical open failure on mobile with retry and native TUI fallback", async () => {
const openNativeTui = vi.fn();
const agent = {
launchAgentChat: vi
.fn()
.mockRejectedValueOnce(new Error("spawn codex ENOENT"))
.mockResolvedValueOnce({ sessionId: "chat-session-1" }),
reattachAgentChat: vi.fn(async (sessionId: string) => ({
sessionId,
scrollback: [],
})),
sendAgentChat: vi.fn(async () => {}),
cancelAgentChat: vi.fn(async () => {}),
closeAgentChat: vi.fn(async () => {}),
};
render(
<DIProvider
gateways={{
agent,
system: { pickFile: vi.fn(async () => null) },
} as unknown as Gateways}
>
<CustomAgentChatView
projectId="project-1"
agentId="agent-1"
agentName="Worker"
profile={profile}
cwd="/repo"
nodeId="node-1"
sessionId={null}
conversationId={null}
mobile
onOpenNativeTui={openNativeTui}
onSessionId={vi.fn()}
onConversationId={vi.fn()}
/>
</DIProvider>,
);
const alert = await screen.findByRole("alert");
expect(within(alert).getByText("Le CLI custom n'a pas pu démarrer.")).toBeTruthy();
expect(within(alert).getByText("Cause: spawn codex ENOENT")).toBeTruthy();
expect(screen.queryByText(/Vérifiez la configuration du profil/)).toBeNull();
fireEvent.click(within(alert).getByRole("button", { name: "Ouvrir en TUI native" }));
expect(openNativeTui).toHaveBeenCalledTimes(1);
fireEvent.click(within(alert).getByRole("button", { name: "Réessayer" }));
await waitFor(() => expect(agent.launchAgentChat).toHaveBeenCalledTimes(2));
await waitFor(() => expect(screen.queryByRole("alert")).toBeNull());
});
it("uses the same technical open failure on desktop", async () => {
const agent = {
launchAgentChat: vi.fn(async () => {
throw new Error("permission denied: /opt/codex");
}),
reattachAgentChat: vi.fn(),
sendAgentChat: vi.fn(async () => {}),
cancelAgentChat: vi.fn(async () => {}),
closeAgentChat: vi.fn(async () => {}),
};
render(
<DIProvider
gateways={{
agent,
system: { pickFile: vi.fn(async () => null) },
} as unknown as Gateways}
>
<CustomAgentChatView
projectId="project-1"
agentId="agent-1"
agentName="Worker"
profile={profile}
cwd="/repo"
nodeId="node-1"
sessionId={null}
conversationId={null}
onSessionId={vi.fn()}
onConversationId={vi.fn()}
/>
</DIProvider>,
);
const alert = await screen.findByRole("alert");
expect(within(alert).getByText("Cause: permission denied: /opt/codex")).toBeTruthy();
expect(screen.getByRole("button", { name: "Réessayer" })).toBeTruthy();
expect(screen.queryByRole("button", { name: "Ouvrir en TUI native" })).toBeNull();
});
it("repaints attachment labels from user prompt scrollback", async () => {
const agent = {
launchAgentChat: vi.fn(),