Skip to content

Commit 1aa61ab

Browse files
authored
Fix duplicated message issue with switching tabs while streaming (#1814)
* Fix duplicated message issue with switching tabs while streaming * Add a test to ensure fetchEventSource is called w/ openWhenHidden=true
1 parent 4af7a1c commit 1aa61ab

File tree

6 files changed

+182
-5
lines changed

6 files changed

+182
-5
lines changed

aap_chatbot/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aap_chatbot/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@ansible/ansible-ai-connect-chatbot",
33
"type": "module",
4-
"version": "0.1.7",
4+
"version": "0.1.8",
55
"main": "./dist/ansible-chatbot.umd.cjs",
66
"module": "./dist/ansible-chatbot.js",
77
"types": "./dist/index.d.ts",

aap_chatbot/src/useChatbot/useChatbot.test.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
import { describe, it, expect } from "vitest";
2-
import { feedbackMessage } from "./useChatbot";
1+
import { describe, it, expect, vi, beforeEach } from "vitest";
2+
import { renderHook, waitFor } from "@testing-library/react";
3+
import { feedbackMessage, useChatbot } from "./useChatbot";
34
import type { MessageProps } from "@patternfly/chatbot/dist/dynamic/Message";
45
import { Sentiment } from "../Constants";
56
import type { ChatFeedback } from "../types/Message";
7+
import * as fetchEventSourceModule from "@microsoft/fetch-event-source";
8+
import axios from "axios";
69

710
const CONVERSATION_ID = "123e4567-e89b-12d3-a456-426614174000";
811

12+
// Mock fetchEventSource
13+
vi.mock("@microsoft/fetch-event-source", () => ({
14+
fetchEventSource: vi.fn(),
15+
}));
16+
17+
// Mock axios
18+
vi.mock("axios");
19+
920
describe("feedbackMessage", () => {
1021
it("should return a message with a thank you note for positive feedback", () => {
1122
const feedback: ChatFeedback = {
@@ -71,3 +82,43 @@ describe("feedbackMessage", () => {
7182
expect(message.content).toMatch(/\n\n/);
7283
});
7384
});
85+
86+
describe("useChatbot - fetchEventSource openWhenHidden", () => {
87+
beforeEach(() => {
88+
vi.clearAllMocks();
89+
// Mock axios.get for health check
90+
vi.mocked(axios.get).mockResolvedValue({
91+
status: 200,
92+
data: { "streaming-chatbot-service": "ok" },
93+
});
94+
// Mock fetchEventSource to resolve immediately
95+
vi.mocked(fetchEventSourceModule.fetchEventSource).mockResolvedValue(
96+
undefined,
97+
);
98+
});
99+
100+
it("should call fetchEventSource with openWhenHidden set to true", async () => {
101+
const { result } = renderHook(() => useChatbot());
102+
103+
// Wait for the health check to complete and streaming to be enabled
104+
await waitFor(() => {
105+
expect(result.current.isStreamingSupported()).toBe(true);
106+
});
107+
108+
// Send a message to trigger fetchEventSource
109+
await result.current.handleSend("test query");
110+
111+
// Wait for fetchEventSource to be called
112+
await waitFor(() => {
113+
expect(fetchEventSourceModule.fetchEventSource).toHaveBeenCalled();
114+
});
115+
116+
// Verify that fetchEventSource was called with openWhenHidden: true
117+
expect(fetchEventSourceModule.fetchEventSource).toHaveBeenCalledWith(
118+
expect.any(String),
119+
expect.objectContaining({
120+
openWhenHidden: true,
121+
}),
122+
);
123+
});
124+
});

aap_chatbot/src/useChatbot/useChatbot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ export const useChatbot = () => {
493493
? "/api/lightspeed/v1/ai/streaming_chat/"
494494
: "http://localhost:8080/v1/streaming_query",
495495
{
496+
openWhenHidden: true,
496497
method: "POST",
497498
headers: {
498499
"Content-Type": "application/json",
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest";
2+
import { renderHook, waitFor } from "@testing-library/react";
3+
import { feedbackMessage, useChatbot } from "./useChatbot";
4+
import type { MessageProps } from "@patternfly/chatbot/dist/dynamic/Message";
5+
import { Sentiment } from "../Constants";
6+
import type { ChatFeedback } from "../types/Message";
7+
import * as fetchEventSourceModule from "@microsoft/fetch-event-source";
8+
import axios from "axios";
9+
10+
const CONVERSATION_ID = "123e4567-e89b-12d3-a456-426614174000";
11+
12+
// Mock fetchEventSource
13+
vi.mock("@microsoft/fetch-event-source", () => ({
14+
fetchEventSource: vi.fn(),
15+
}));
16+
17+
// Mock axios
18+
vi.mock("axios");
19+
20+
describe("feedbackMessage", () => {
21+
it("should return a message with a thank you note for positive feedback", () => {
22+
const feedback: ChatFeedback = {
23+
sentiment: Sentiment.THUMBS_UP,
24+
query: "",
25+
response: {
26+
conversation_id: "",
27+
response: "",
28+
referenced_documents: [],
29+
truncated: false,
30+
},
31+
message: {
32+
role: "user",
33+
content: "This is a test message",
34+
name: "User",
35+
avatar: "user_avatar",
36+
quickResponses: [],
37+
referenced_documents: [],
38+
},
39+
};
40+
const message: MessageProps = feedbackMessage(feedback, CONVERSATION_ID);
41+
42+
expect(message.role).toBe("bot");
43+
expect(message.content).toBe("Thank you for your feedback!");
44+
expect(message.quickResponses).toEqual([]);
45+
});
46+
47+
it("should return a message with a thank you note and a quick response for negative feedback", () => {
48+
const feedback: ChatFeedback = {
49+
sentiment: Sentiment.THUMBS_DOWN,
50+
query: "",
51+
response: {
52+
conversation_id: "",
53+
response: "",
54+
referenced_documents: [],
55+
truncated: false,
56+
},
57+
message: {
58+
role: "user",
59+
content: "This is a test message",
60+
name: "User",
61+
avatar: "user_avatar",
62+
quickResponses: [],
63+
referenced_documents: [],
64+
},
65+
};
66+
const message: MessageProps = feedbackMessage(feedback, CONVERSATION_ID);
67+
68+
expect(message.role).toBe("bot");
69+
expect(message.content).toMatch(
70+
"Thank you for your feedback. If you have more to share, please click the button below (_requires GitHub login_). " +
71+
"\n\n Do not include any personal information or other sensitive information in your feedback. " +
72+
"Feedback may be used to improve Red Hat's products or services.",
73+
);
74+
expect(message.quickResponses).toEqual([
75+
{
76+
content: "Sure!",
77+
id: "response",
78+
onClick: expect.any(Function),
79+
},
80+
]);
81+
// Check for the two line breaks
82+
expect(message.content).toMatch(/\n\n/);
83+
});
84+
});
85+
86+
describe("useChatbot - fetchEventSource openWhenHidden", () => {
87+
beforeEach(() => {
88+
vi.clearAllMocks();
89+
// Mock axios.get for health check
90+
vi.mocked(axios.get).mockResolvedValue({
91+
status: 200,
92+
data: { "streaming-chatbot-service": "ok" },
93+
});
94+
// Mock fetchEventSource to resolve immediately
95+
vi.mocked(fetchEventSourceModule.fetchEventSource).mockResolvedValue(
96+
undefined,
97+
);
98+
});
99+
100+
it("should call fetchEventSource with openWhenHidden set to true", async () => {
101+
const { result } = renderHook(() => useChatbot());
102+
103+
// Wait for the health check to complete and streaming to be enabled
104+
await waitFor(() => {
105+
expect(result.current.isStreamingSupported()).toBe(true);
106+
});
107+
108+
// Send a message to trigger fetchEventSource
109+
await result.current.handleSend("test query");
110+
111+
// Wait for fetchEventSource to be called
112+
await waitFor(() => {
113+
expect(fetchEventSourceModule.fetchEventSource).toHaveBeenCalled();
114+
});
115+
116+
// Verify that fetchEventSource was called with openWhenHidden: true
117+
expect(fetchEventSourceModule.fetchEventSource).toHaveBeenCalledWith(
118+
expect.any(String),
119+
expect.objectContaining({
120+
openWhenHidden: true,
121+
}),
122+
);
123+
});
124+
});

ansible_ai_connect_chatbot/src/useChatbot/useChatbot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ export const useChatbot = () => {
488488
? "/api/v1/ai/streaming_chat/"
489489
: "http://localhost:8080/v1/streaming_query",
490490
{
491+
openWhenHidden: true,
491492
method: "POST",
492493
headers: {
493494
"Content-Type": "application/json",

0 commit comments

Comments
 (0)