-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ChatWrapper, tests and docs
- Loading branch information
Showing
20 changed files
with
968 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"inform-ai": patch | ||
--- | ||
|
||
More jest tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"inform-ai": minor | ||
--- | ||
|
||
Built-in ChatWrapper component |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"inform-ai": patch | ||
--- | ||
|
||
Docs and tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,11 +36,13 @@ | |
"@types/react": "npm:[email protected]", | ||
"@types/react-dom": "npm:[email protected]", | ||
"@types/uuid": "^10.0.0", | ||
"ai": "^3.3.6", | ||
"autoprefixer": "^10.4.20", | ||
"concurrently": "^8.2.2", | ||
"eslint": "^8.57.0", | ||
"eslint-plugin-react": "^7.35.0", | ||
"eslint-plugin-react-hooks": "^4.6.2", | ||
"identity-obj-proxy": "^3.0.0", | ||
"jest": "^29.7.0", | ||
"jest-environment-jsdom": "^29.7.0", | ||
"postcss": "^8.4.41", | ||
|
@@ -56,7 +58,6 @@ | |
"@ai-sdk/openai": "^0.0.44", | ||
"@testing-library/dom": "^10.4.0", | ||
"@testing-library/react": "^16.0.0", | ||
"ai": "^3.3.6", | ||
"clsx": "^2.1.1", | ||
"nanoid": "^5.0.7", | ||
"react": "19.0.0-rc-cc1ec60d0d-20240607", | ||
|
@@ -65,5 +66,8 @@ | |
"rollup-plugin-postcss": "^4.0.2", | ||
"uuid": "^10.0.0", | ||
"zod": "^3.23.8" | ||
}, | ||
"peerDependencies": { | ||
"ai": "^3.3.6" | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { render, screen, fireEvent, waitFor } from "@testing-library/react"; | ||
import { ChatBox } from "../ui/ChatBox"; | ||
|
||
describe("ChatBox", () => { | ||
it("renders correctly", () => { | ||
render(<ChatBox onSubmit={async () => true} />); | ||
expect(screen.getByRole("textbox")).toBeInTheDocument(); | ||
}); | ||
|
||
it("honors autoFocus", () => { | ||
render(<ChatBox onSubmit={async () => true} autoFocus={true} />); | ||
expect(screen.getByRole("textbox")).toHaveFocus(); | ||
}); | ||
|
||
it("honors placeholder", () => { | ||
render(<ChatBox onSubmit={async () => true} placeholder="test placeholder" />); | ||
expect(screen.getByPlaceholderText("test placeholder")).toBeInTheDocument(); | ||
}); | ||
|
||
it('clears the input if "onSubmit" returns true', async () => { | ||
const onSubmit = jest.fn(async () => true); | ||
|
||
render(<ChatBox onSubmit={onSubmit} />); | ||
const input = screen.getByRole("textbox"); | ||
const button = screen.getByRole("button"); | ||
|
||
fireEvent.change(input, { target: { value: "test" } }); | ||
expect(input).toHaveValue("test"); | ||
|
||
fireEvent.click(button); | ||
|
||
await waitFor(() => { | ||
expect(input).toHaveValue(""); | ||
}); | ||
expect(onSubmit).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('does not clear the input if "onSubmit" returns false', async () => { | ||
const onSubmit = jest.fn(async () => false); | ||
|
||
render(<ChatBox onSubmit={onSubmit} />); | ||
const input = screen.getByRole("textbox"); | ||
const form = screen.getByRole("form"); | ||
|
||
fireEvent.change(input, { target: { value: "test" } }); | ||
|
||
expect(input).toHaveValue("test"); | ||
|
||
fireEvent.submit(form); | ||
|
||
expect(onSubmit).toHaveBeenCalledTimes(1); | ||
expect(input).toHaveValue("test"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; | ||
import { ChatWrapper } from "../ui/ChatWrapper"; | ||
import { InformAIProvider, useInformAIContext } from "../InformAIContext"; | ||
import { useState } from "react"; | ||
import { useInformAI } from "../useInformAI"; | ||
import { FormattedMessage } from "../types"; | ||
|
||
describe("ChatWrapper", () => { | ||
let currentMessages: any[]; | ||
|
||
const AppChatWrapper = ({ submitUserMessage = jest.fn() }: { submitUserMessage?: jest.Mock }) => { | ||
const [messages, setMessages] = useState<any[]>([]); | ||
|
||
currentMessages = messages; | ||
|
||
return <ChatWrapper setMessages={setMessages} submitUserMessage={submitUserMessage} messages={messages} />; | ||
}; | ||
|
||
it("renders correctly", () => { | ||
render( | ||
<InformAIProvider> | ||
<AppChatWrapper /> | ||
</InformAIProvider> | ||
); | ||
|
||
//just checks that the component renders the ChatBox | ||
expect(screen.getByRole("textbox")).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders user messages correctly", async () => { | ||
render( | ||
<InformAIProvider> | ||
<AppChatWrapper /> | ||
</InformAIProvider> | ||
); | ||
|
||
fireEvent.change(screen.getByRole("textbox"), { | ||
target: { value: "test message" }, | ||
}); | ||
fireEvent.submit(screen.getByRole("form")); | ||
|
||
await waitFor(() => screen.getByText("test message")); | ||
}); | ||
|
||
describe("collecting messages to send to the LLM", () => { | ||
let contextValues: ReturnType<typeof useInformAIContext> | undefined = undefined; | ||
let mockSubmitUserMessage: jest.Mock; | ||
|
||
const AppComponent = () => { | ||
const { addState } = useInformAI({ | ||
name: "MyAppComponent", | ||
props: { key: "value" }, | ||
prompt: "MyAppComponent prompt", | ||
}); | ||
|
||
contextValues = useInformAIContext(); | ||
|
||
const handleClick = () => { | ||
addState({ props: { key: "newValue" } }); | ||
}; | ||
|
||
return <div onClick={handleClick}>clickable element</div>; | ||
}; | ||
|
||
beforeEach(async () => { | ||
mockSubmitUserMessage = jest.fn(async () => ({ | ||
id: "response-id", | ||
content: "response message", | ||
role: "assistant", | ||
})); | ||
|
||
render( | ||
<InformAIProvider> | ||
<AppComponent /> | ||
<AppChatWrapper submitUserMessage={mockSubmitUserMessage} /> | ||
</InformAIProvider> | ||
); | ||
|
||
//after this we should have 2 state messages in the context | ||
fireEvent.click(screen.getByText("clickable element")); | ||
|
||
expect(contextValues!.messages.length).toBe(2); | ||
|
||
//submit a message from the user | ||
fireEvent.change(screen.getByRole("textbox"), { | ||
target: { value: "test message" }, | ||
}); | ||
|
||
await act(async () => { | ||
fireEvent.submit(screen.getByRole("form")); | ||
}); | ||
}); | ||
|
||
it("sends the correct deduped state and user messages to submitUserMessage", async () => { | ||
//test that the correct messages were sent to submitUserMessage | ||
expect(mockSubmitUserMessage).toHaveBeenCalledTimes(1); | ||
const [submittedMessages] = mockSubmitUserMessage.mock.calls[0]; | ||
|
||
//the 2 state messages for the component should have been deduped into 1 (plus the user message) | ||
expect(submittedMessages.length).toBe(2); | ||
|
||
const stateMessage = submittedMessages[0] as FormattedMessage; | ||
expect(stateMessage.content).toContain('Component props: {"key":"newValue"}'); | ||
expect(stateMessage.role).toEqual("system"); | ||
expect(stateMessage.id.length).toEqual(10); | ||
|
||
const userMessage = submittedMessages[1] as FormattedMessage; | ||
expect(userMessage.content).toEqual("test message"); | ||
expect(userMessage.role).toEqual("user"); | ||
expect(userMessage.id.length).toEqual(10); | ||
}); | ||
|
||
it("ends up with the correct messages", async () => { | ||
//we should end up with 3 messages - a state message, a user message, and an LLM response | ||
expect(currentMessages.length).toBe(3); | ||
|
||
const stateMessage = currentMessages[0] as FormattedMessage; | ||
expect(stateMessage.content).toContain('Component props: {"key":"newValue"}'); | ||
expect(stateMessage.role).toEqual("system"); | ||
expect(stateMessage.id.length).toEqual(10); | ||
|
||
const userMessage = currentMessages[1] as FormattedMessage; | ||
expect(userMessage.role).toEqual("user"); | ||
expect(userMessage.id.length).toEqual(10); | ||
|
||
const responseMessage = currentMessages[2] as FormattedMessage; | ||
expect(responseMessage.role).toEqual("assistant"); | ||
expect(responseMessage.content).toEqual("response message"); | ||
expect(responseMessage.id).toEqual("response-id"); | ||
}); | ||
|
||
it("clears the text input", async () => { | ||
expect(screen.getByRole("textbox")).toHaveValue(""); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.