Skip to content

Commit

Permalink
Merge branch 'main' into kevin
Browse files Browse the repository at this point in the history
  • Loading branch information
SmartManoj committed Nov 6, 2024
2 parents 6eab6e3 + 2771617 commit 0a55a49
Show file tree
Hide file tree
Showing 19 changed files with 3,800 additions and 2,976 deletions.
2,719 changes: 1,459 additions & 1,260 deletions docs/package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.5.2",
"@docusaurus/plugin-content-pages": "^3.5.2",
"@docusaurus/preset-classic": "^3.5.2",
"@docusaurus/theme-mermaid": "^3.5.2",
"@docusaurus/core": "^3.6.0",
"@docusaurus/plugin-content-pages": "^3.6.0",
"@docusaurus/preset-classic": "^3.6.0",
"@docusaurus/theme-mermaid": "^3.6.0",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.4.0",
Expand All @@ -29,7 +29,7 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.5.1",
"@docusaurus/tsconfig": "^3.5.2",
"@docusaurus/tsconfig": "^3.6.0",
"@docusaurus/types": "^3.5.1",
"typescript": "~5.6.3"
},
Expand Down
3,475 changes: 1,847 additions & 1,628 deletions docs/yarn.lock

Large diffs are not rendered by default.

149 changes: 143 additions & 6 deletions frontend/__tests__/components/chat/chat-interface.test.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,156 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { render, screen, within } from "@testing-library/react";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { act, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "test-utils";
import { ChatInterface } from "#/components/chat-interface";
import { SocketProvider } from "#/context/socket";
import { addUserMessage } from "#/state/chatSlice";
import { SUGGESTIONS } from "#/utils/suggestions";
import * as ChatSlice from "#/state/chatSlice";

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const renderChatInterface = (messages: (Message | ErrorMessage)[]) =>
render(<ChatInterface />, { wrapper: SocketProvider });
renderWithProviders(<ChatInterface />);

describe("Empty state", () => {
const { send: sendMock } = vi.hoisted(() => ({
send: vi.fn(),
}));

const { useSocket: useSocketMock } = vi.hoisted(() => ({
useSocket: vi.fn(() => ({ send: sendMock, runtimeActive: true })),
}));

beforeAll(() => {
vi.mock("#/context/socket", async (importActual) => ({
...(await importActual<typeof import("#/context/socket")>()),
useSocket: useSocketMock,
}));
});

describe.skip("ChatInterface", () => {
afterEach(() => {
vi.clearAllMocks();
});

it.todo("should render suggestions if empty");
it("should render suggestions if empty", () => {
const { store } = renderWithProviders(<ChatInterface />, {
preloadedState: {
chat: { messages: [] },
},
});

expect(screen.getByTestId("suggestions")).toBeInTheDocument();

act(() => {
store.dispatch(
addUserMessage({
content: "Hello",
imageUrls: [],
timestamp: new Date().toISOString(),
}),
);
});

expect(screen.queryByTestId("suggestions")).not.toBeInTheDocument();
});

it("should render the default suggestions", () => {
renderWithProviders(<ChatInterface />, {
preloadedState: {
chat: { messages: [] },
},
});

const suggestions = screen.getByTestId("suggestions");
const repoSuggestions = Object.keys(SUGGESTIONS.repo);

// check that there are at most 4 suggestions displayed
const displayedSuggestions = within(suggestions).getAllByRole("button");
expect(displayedSuggestions.length).toBeLessThanOrEqual(4);

// Check that each displayed suggestion is one of the repo suggestions
displayedSuggestions.forEach((suggestion) => {
expect(repoSuggestions).toContain(suggestion.textContent);
});
});

it.fails(
"should load the a user message to the input when selecting",
async () => {
// this is to test that the message is in the UI before the socket is called
useSocketMock.mockImplementation(() => ({
send: sendMock,
runtimeActive: false, // mock an inactive runtime setup
}));
const addUserMessageSpy = vi.spyOn(ChatSlice, "addUserMessage");
const user = userEvent.setup();
const { store } = renderWithProviders(<ChatInterface />, {
preloadedState: {
chat: { messages: [] },
},
});

const suggestions = screen.getByTestId("suggestions");
const displayedSuggestions = within(suggestions).getAllByRole("button");
const input = screen.getByTestId("chat-input");

await user.click(displayedSuggestions[0]);

// user message loaded to input
expect(addUserMessageSpy).not.toHaveBeenCalled();
expect(screen.queryByTestId("suggestions")).toBeInTheDocument();
expect(store.getState().chat.messages).toHaveLength(0);
expect(input).toHaveValue(displayedSuggestions[0].textContent);
},
);

it.fails(
"should send the message to the socket only if the runtime is active",
async () => {
useSocketMock.mockImplementation(() => ({
send: sendMock,
runtimeActive: false, // mock an inactive runtime setup
}));
const user = userEvent.setup();
const { rerender } = renderWithProviders(<ChatInterface />, {
preloadedState: {
chat: { messages: [] },
},
});

const suggestions = screen.getByTestId("suggestions");
const displayedSuggestions = within(suggestions).getAllByRole("button");

await user.click(displayedSuggestions[0]);
expect(sendMock).not.toHaveBeenCalled();

useSocketMock.mockImplementation(() => ({
send: sendMock,
runtimeActive: true, // mock an active runtime setup
}));
rerender(<ChatInterface />);

await waitFor(() =>
expect(sendMock).toHaveBeenCalledWith(expect.any(String)),
);
},
);
});

describe.skip("ChatInterface", () => {
beforeAll(() => {
// mock useScrollToBottom hook
vi.mock("#/hooks/useScrollToBottom", () => ({
useScrollToBottom: vi.fn(() => ({
scrollDomToBottom: vi.fn(),
onChatBodyScroll: vi.fn(),
hitBottom: vi.fn(),
})),
}));
});

afterEach(() => {
vi.clearAllMocks();
});

it("should render messages", () => {
const messages: Message[] = [
Expand Down
15 changes: 15 additions & 0 deletions frontend/__tests__/components/interactive-chat-box.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ describe("InteractiveChatBox", () => {
within(chatBox).getByTestId("upload-image-input");
});

it.fails("should set custom values", () => {
render(
<InteractiveChatBox
onSubmit={onSubmitMock}
onStop={onStopMock}
value="Hello, world!"
/>,
);

const chatBox = screen.getByTestId("interactive-chat-box");
const chatInput = within(chatBox).getByTestId("chat-input");

expect(chatInput).toHaveValue("Hello, world!");
});

it("should display the image previews when images are uploaded", async () => {
const user = userEvent.setup();
render(<InteractiveChatBox onSubmit={onSubmitMock} onStop={onStopMock} />);
Expand Down
30 changes: 30 additions & 0 deletions frontend/__tests__/components/suggestion-item.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { SuggestionItem } from "#/components/suggestion-item";

describe("SuggestionItem", () => {
const suggestionItem = { label: "suggestion1", value: "a long text value" };
const onClick = vi.fn();

afterEach(() => {
vi.clearAllMocks();
});

it("should render a suggestion", () => {
render(<SuggestionItem suggestion={suggestionItem} onClick={onClick} />);

expect(screen.getByTestId("suggestion")).toBeInTheDocument();
expect(screen.getByText(/suggestion1/i)).toBeInTheDocument();
});

it("should call onClick when clicking a suggestion", async () => {
const user = userEvent.setup();
render(<SuggestionItem suggestion={suggestionItem} onClick={onClick} />);

const suggestion = screen.getByTestId("suggestion");
await user.click(suggestion);

expect(onClick).toHaveBeenCalledWith("a long text value");
});
});
60 changes: 60 additions & 0 deletions frontend/__tests__/components/suggestions.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { Suggestions } from "#/components/suggestions";

describe("Suggestions", () => {
const firstSuggestion = {
label: "first-suggestion",
value: "value-of-first-suggestion",
};
const secondSuggestion = {
label: "second-suggestion",
value: "value-of-second-suggestion",
};
const suggestions = [firstSuggestion, secondSuggestion];

const onSuggestionClickMock = vi.fn();

afterEach(() => {
vi.clearAllMocks();
});

it("should render suggestions", () => {
render(
<Suggestions
suggestions={suggestions}
onSuggestionClick={onSuggestionClickMock}
/>,
);

expect(screen.getByTestId("suggestions")).toBeInTheDocument();
const suggestionElements = screen.getAllByTestId("suggestion");

expect(suggestionElements).toHaveLength(2);
expect(suggestionElements[0]).toHaveTextContent("first-suggestion");
expect(suggestionElements[1]).toHaveTextContent("second-suggestion");
});

it("should call onSuggestionClick when clicking a suggestion", async () => {
const user = userEvent.setup();
render(
<Suggestions
suggestions={suggestions}
onSuggestionClick={onSuggestionClickMock}
/>,
);

const suggestionElements = screen.getAllByTestId("suggestion");

await user.click(suggestionElements[0]);
expect(onSuggestionClickMock).toHaveBeenCalledWith(
"value-of-first-suggestion",
);

await user.click(suggestionElements[1]);
expect(onSuggestionClickMock).toHaveBeenCalledWith(
"value-of-second-suggestion",
);
});
});
17 changes: 17 additions & 0 deletions frontend/__tests__/initial-query.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, it, expect } from "vitest";
import store from "../src/store";
import { setInitialQuery, clearInitialQuery } from "../src/state/initial-query-slice";

describe("Initial Query Behavior", () => {
it("should clear initial query when clearInitialQuery is dispatched", () => {
// Set up initial query in the store
store.dispatch(setInitialQuery("test query"));
expect(store.getState().initalQuery.initialQuery).toBe("test query");

// Clear the initial query
store.dispatch(clearInitialQuery());

// Verify initial query is cleared
expect(store.getState().initalQuery.initialQuery).toBeNull();
});
});
5 changes: 5 additions & 0 deletions frontend/__tests__/routes/_oh.app.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { describe, it } from "vitest";

describe("App", () => {
it.todo("should render");
});
Loading

0 comments on commit 0a55a49

Please sign in to comment.