Skip to content

Commit 80f016e

Browse files
authored
Merge branch 'release-1.8.0' into fix-agent-component-watsonx
2 parents 3e9069a + b0f49db commit 80f016e

2 files changed

Lines changed: 151 additions & 80 deletions

File tree

src/frontend/src/pages/MainPage/pages/knowledgePage/components/KnowledgeBaseEmptyState.tsx

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,18 @@
1-
import { useQueryClient } from "@tanstack/react-query";
21
import { useState } from "react";
32
import ForwardedIconComponent from "@/components/common/genericIconComponent";
43
import { Button } from "@/components/ui/button";
5-
import Loading from "@/components/ui/loading";
64
import KnowledgeBaseUploadModal from "@/modals/knowledgeBaseUploadModal/KnowledgeBaseUploadModal";
75
import useAlertStore from "@/stores/alertStore";
6+
import { useOptimisticKnowledgeBase } from "../hooks/useOptimisticKnowledgeBase";
87

98
const KnowledgeBaseEmptyState = ({
109
handleCreateKnowledge,
1110
}: {
1211
handleCreateKnowledge: () => void;
1312
}) => {
1413
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
15-
const [isCreating, setIsCreating] = useState(false);
16-
const [wasSubmitted, setWasSubmitted] = useState(false);
1714
const setSuccessData = useAlertStore((state) => state.setSuccessData);
18-
const queryClient = useQueryClient();
19-
20-
if (isCreating) {
21-
return (
22-
<div className="flex h-full w-full flex-col items-center justify-center gap-3">
23-
<Loading size={36} />
24-
<span className="text-sm text-muted-foreground pt-3">
25-
Setting up your knowledge base...
26-
</span>
27-
</div>
28-
);
29-
}
15+
const { captureSubmit, applyOptimisticUpdate } = useOptimisticKnowledgeBase();
3016

3117
return (
3218
<div className="flex h-full w-full flex-col items-center justify-center gap-8 pb-8">
@@ -51,16 +37,12 @@ const KnowledgeBaseEmptyState = ({
5137
open={isUploadModalOpen}
5238
setOpen={(open) => {
5339
setIsUploadModalOpen(open);
54-
if (!open && wasSubmitted) {
55-
setIsCreating(true);
56-
queryClient.invalidateQueries({
57-
queryKey: ["useGetKnowledgeBases"],
58-
});
59-
setWasSubmitted(false);
40+
if (!open) {
41+
applyOptimisticUpdate();
6042
}
6143
}}
6244
onSubmit={(data) => {
63-
setWasSubmitted(true);
45+
captureSubmit(data);
6446
setSuccessData({
6547
title: `Knowledge base "${data.sourceName}" created`,
6648
});
Lines changed: 146 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,76 @@
11
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2-
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2+
import { act, fireEvent, render, screen } from "@testing-library/react";
33
import React from "react";
4-
import { BrowserRouter } from "react-router-dom";
4+
import KnowledgeBaseEmptyState from "../KnowledgeBaseEmptyState";
55

6-
// Mock all the dependencies to avoid complex imports
7-
jest.mock("@/stores/flowsManagerStore", () => ({
6+
// Mock dependencies
7+
jest.mock("@/stores/alertStore", () => ({
88
__esModule: true,
9-
default: jest.fn(),
9+
default: jest.fn((selector) =>
10+
selector({
11+
setSuccessData: jest.fn(),
12+
setErrorData: jest.fn(),
13+
}),
14+
),
1015
}));
1116

12-
jest.mock("@/hooks/flows/use-add-flow", () => ({
13-
__esModule: true,
14-
default: jest.fn(),
15-
}));
16-
17-
jest.mock("@/customization/hooks/use-custom-navigate", () => ({
18-
useCustomNavigate: jest.fn(),
19-
}));
17+
const mockCaptureSubmit = jest.fn();
18+
const mockApplyOptimisticUpdate = jest.fn().mockReturnValue(true);
2019

21-
jest.mock("@/stores/foldersStore", () => ({
22-
useFolderStore: jest.fn(),
20+
jest.mock("../../hooks/useOptimisticKnowledgeBase", () => ({
21+
useOptimisticKnowledgeBase: () => ({
22+
captureSubmit: mockCaptureSubmit,
23+
applyOptimisticUpdate: mockApplyOptimisticUpdate,
24+
}),
2325
}));
2426

25-
jest.mock("@/customization/utils/analytics", () => ({
26-
track: jest.fn(),
27-
}));
28-
29-
jest.mock("@/utils/reactflowUtils", () => ({
30-
updateIds: jest.fn(),
31-
}));
27+
// Mock the modal component
28+
jest.mock("@/modals/knowledgeBaseUploadModal/KnowledgeBaseUploadModal", () => {
29+
return function MockKnowledgeBaseUploadModal({
30+
open,
31+
setOpen,
32+
onSubmit,
33+
}: {
34+
open: boolean;
35+
setOpen: (open: boolean) => void;
36+
onSubmit: (data: any) => void;
37+
}) {
38+
return open ? (
39+
<div data-testid="upload-modal">
40+
<button data-testid="modal-close" onClick={() => setOpen(false)}>
41+
Close
42+
</button>
43+
<button
44+
data-testid="modal-submit"
45+
onClick={() => {
46+
onSubmit({
47+
sourceName: "TestKB",
48+
files: [new File(["content"], "test.txt")],
49+
embeddingModel: null,
50+
});
51+
setOpen(false);
52+
}}
53+
>
54+
Submit
55+
</button>
56+
</div>
57+
) : null;
58+
};
59+
});
3260

33-
// Mock the component itself to test in isolation
34-
jest.mock("../KnowledgeBaseEmptyState", () => {
35-
const MockKnowledgeBaseEmptyState = () => (
36-
<div data-testid="knowledge-base-empty-state">
37-
<h3>No knowledge bases</h3>
38-
<p>Create your first knowledge base to get started.</p>
39-
<button data-testid="create-knowledge-btn">Create Knowledge</button>
40-
</div>
41-
);
42-
MockKnowledgeBaseEmptyState.displayName = "KnowledgeBaseEmptyState";
43-
return {
44-
__esModule: true,
45-
default: MockKnowledgeBaseEmptyState,
61+
jest.mock("@/components/common/genericIconComponent", () => {
62+
return function MockIcon() {
63+
return <span data-testid="mock-icon" />;
4664
};
4765
});
4866

49-
const KnowledgeBaseEmptyState = require("../KnowledgeBaseEmptyState").default;
67+
jest.mock("@/components/ui/button", () => ({
68+
Button: ({ children, onClick, ...props }: any) => (
69+
<button onClick={onClick} {...props}>
70+
{children}
71+
</button>
72+
),
73+
}));
5074

5175
const createTestWrapper = () => {
5276
const queryClient = new QueryClient({
@@ -57,49 +81,114 @@ const createTestWrapper = () => {
5781
});
5882

5983
return ({ children }: { children: React.ReactNode }) => (
60-
<QueryClientProvider client={queryClient}>
61-
<BrowserRouter>{children}</BrowserRouter>
62-
</QueryClientProvider>
84+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
6385
);
6486
};
6587

6688
describe("KnowledgeBaseEmptyState", () => {
89+
const mockHandleCreateKnowledge = jest.fn();
90+
6791
beforeEach(() => {
6892
jest.clearAllMocks();
6993
});
7094

7195
it("renders empty state message correctly", () => {
72-
render(<KnowledgeBaseEmptyState />, { wrapper: createTestWrapper() });
96+
render(
97+
<KnowledgeBaseEmptyState
98+
handleCreateKnowledge={mockHandleCreateKnowledge}
99+
/>,
100+
{ wrapper: createTestWrapper() },
101+
);
73102

74103
expect(screen.getByText("No knowledge bases")).toBeInTheDocument();
75104
expect(
76-
screen.getByText("Create your first knowledge base to get started."),
105+
screen.getByText(/Create powerful AI experiences/),
77106
).toBeInTheDocument();
78107
});
79108

80-
it("renders create knowledge button", () => {
81-
render(<KnowledgeBaseEmptyState />, { wrapper: createTestWrapper() });
109+
it("renders Add Knowledge button", () => {
110+
render(
111+
<KnowledgeBaseEmptyState
112+
handleCreateKnowledge={mockHandleCreateKnowledge}
113+
/>,
114+
{ wrapper: createTestWrapper() },
115+
);
82116

83-
const createButton = screen.getByTestId("create-knowledge-btn");
84-
expect(createButton).toBeInTheDocument();
85-
expect(createButton).toHaveTextContent("Create Knowledge");
117+
const addButton = screen.getByText("Add Knowledge");
118+
expect(addButton).toBeInTheDocument();
86119
});
87120

88-
it("handles create knowledge button click", () => {
89-
render(<KnowledgeBaseEmptyState />, { wrapper: createTestWrapper() });
121+
it("opens modal when Add Knowledge button is clicked", () => {
122+
render(
123+
<KnowledgeBaseEmptyState
124+
handleCreateKnowledge={mockHandleCreateKnowledge}
125+
/>,
126+
{ wrapper: createTestWrapper() },
127+
);
90128

91-
const createButton = screen.getByTestId("create-knowledge-btn");
92-
fireEvent.click(createButton);
129+
const addButton = screen.getByText("Add Knowledge");
130+
fireEvent.click(addButton);
93131

94-
// Since we're using a mock, we just verify the button is clickable
95-
expect(createButton).toBeInTheDocument();
132+
expect(screen.getByTestId("upload-modal")).toBeInTheDocument();
96133
});
97134

98-
it("renders with correct test id", () => {
99-
render(<KnowledgeBaseEmptyState />, { wrapper: createTestWrapper() });
135+
it("calls captureSubmit when form is submitted", () => {
136+
render(
137+
<KnowledgeBaseEmptyState
138+
handleCreateKnowledge={mockHandleCreateKnowledge}
139+
/>,
140+
{ wrapper: createTestWrapper() },
141+
);
142+
143+
const addButton = screen.getByText("Add Knowledge");
144+
fireEvent.click(addButton);
145+
146+
const submitButton = screen.getByTestId("modal-submit");
147+
fireEvent.click(submitButton);
148+
149+
expect(mockCaptureSubmit).toHaveBeenCalledWith({
150+
sourceName: "TestKB",
151+
files: expect.any(Array),
152+
embeddingModel: null,
153+
});
154+
});
100155

101-
expect(
102-
screen.getByTestId("knowledge-base-empty-state"),
103-
).toBeInTheDocument();
156+
it("calls applyOptimisticUpdate when modal closes after submission", () => {
157+
render(
158+
<KnowledgeBaseEmptyState
159+
handleCreateKnowledge={mockHandleCreateKnowledge}
160+
/>,
161+
{ wrapper: createTestWrapper() },
162+
);
163+
164+
const addButton = screen.getByText("Add Knowledge");
165+
fireEvent.click(addButton);
166+
167+
const submitButton = screen.getByTestId("modal-submit");
168+
fireEvent.click(submitButton);
169+
170+
expect(mockApplyOptimisticUpdate).toHaveBeenCalled();
171+
});
172+
173+
it("closes modal without calling applyOptimisticUpdate when closed without submission", () => {
174+
mockApplyOptimisticUpdate.mockClear();
175+
176+
render(
177+
<KnowledgeBaseEmptyState
178+
handleCreateKnowledge={mockHandleCreateKnowledge}
179+
/>,
180+
{ wrapper: createTestWrapper() },
181+
);
182+
183+
const addButton = screen.getByText("Add Knowledge");
184+
fireEvent.click(addButton);
185+
186+
expect(screen.getByTestId("upload-modal")).toBeInTheDocument();
187+
188+
const closeButton = screen.getByTestId("modal-close");
189+
fireEvent.click(closeButton);
190+
191+
// Modal should call applyOptimisticUpdate even on close (it returns false if no submission)
192+
expect(mockApplyOptimisticUpdate).toHaveBeenCalled();
104193
});
105194
});

0 commit comments

Comments
 (0)