Skip to content

Commit

Permalink
feat: add vitest & react-testing-library
Browse files Browse the repository at this point in the history
  • Loading branch information
ivyjeong13 committed Jan 29, 2025
1 parent 35929b5 commit 08cbb3f
Show file tree
Hide file tree
Showing 21 changed files with 1,549 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/admin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ jobs:
- name: Run linter
run: make lint-admin

- name: Run tests
run: |
cd ui/admin
pnpm test
- name: Verify no changes
run: make no-changes
2 changes: 1 addition & 1 deletion ui/admin/app/components/agent/AgentIntroForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Form } from "~/components/ui/form";

const formSchema = z.object({
introductionMessage: z.string().optional(),
starterMessages: z.array(z.string()).optional(),
starterMessages: z.array(z.string()).optional().nullable(),
});

export type AgentInfoFormValues = z.infer<typeof formSchema>;
Expand Down
149 changes: 149 additions & 0 deletions ui/admin/app/components/agent/__tests__/Agent.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { faker } from "@faker-js/faker";
import {
HttpResponse,
cleanup,
fireEvent,
http,
render,
screen,
waitFor,
within,
} from "test";
import { overrideServer } from "test/server";

import { mockedAgent } from "~/lib/model/__mocks__/agents";
import { mockedDefaultModelAliases } from "~/lib/model/__mocks__/defaultModelAliases";
import {
mockedDatabaseToolReference,
mockedKnowledgeToolReference,
mockedTasksToolReference,
mockedToolReferences,
mockedWorkspaceFilesToolReference,
} from "~/lib/model/__mocks__/toolReferences";
import { mockedUsers } from "~/lib/model/__mocks__/users";
import { Agent as AgentModel } from "~/lib/model/agents";
import { Assistant } from "~/lib/model/assistants";
import { DefaultModelAlias } from "~/lib/model/defaultModelAliases";
import { KnowledgeFile, KnowledgeSource } from "~/lib/model/knowledge";
import { Thread } from "~/lib/model/threads";
import { ToolReference } from "~/lib/model/toolReferences";
import { User } from "~/lib/model/users";
import { ApiRoutes } from "~/lib/routers/apiRoutes";

import { Agent } from "~/components/agent/Agent";
import { AgentProvider } from "~/components/agent/AgentContext";

describe(Agent, () => {
const toolReferences = {
database: mockedDatabaseToolReference,
knowledge: mockedKnowledgeToolReference,
tasks: mockedTasksToolReference,
"workspace-files": mockedWorkspaceFilesToolReference,
};

const setupServer = (agent: AgentModel) => {
return overrideServer([
http.get(ApiRoutes.agents.getById(agent.id).path, () => {
return HttpResponse.json<AgentModel>(agent);
}),
http.put(ApiRoutes.agents.getById(agent.id).path, () => {
return HttpResponse.json<AgentModel>(agent);
}),
http.get(ApiRoutes.assistants.getAssistants().path, () => {
return HttpResponse.json<{ items: Assistant[] }>({
items: [],
});
}),
http.get(ApiRoutes.users.base().path, () => {
return HttpResponse.json<{ items: User[] }>({
items: mockedUsers,
});
}),
...Object.entries(toolReferences).map(([id, toolReference]) =>
http.get(ApiRoutes.toolReferences.getById(id).path, () => {
return HttpResponse.json<ToolReference>(toolReference);
})
),
http.get(ApiRoutes.toolReferences.base({ type: "tool" }).path, () => {
return HttpResponse.json<{ items: ToolReference[] }>({
items: mockedToolReferences,
});
}),
http.get(ApiRoutes.defaultModelAliases.getAliases().path, () => {
return HttpResponse.json<{ items: DefaultModelAlias[] }>({
items: mockedDefaultModelAliases,
});
}),
http.get(
ApiRoutes.knowledgeFiles.getKnowledgeFiles("agents", agent.id).path,
() => {
return HttpResponse.json<{ items: KnowledgeFile[] }>({
items: [],
});
}
),
http.get(
ApiRoutes.knowledgeSources.getKnowledgeSources("agents", agent.id).path,
() => {
return HttpResponse.json<{
items: KnowledgeSource[] | null;
}>({
items: null,
});
}
),
http.get(ApiRoutes.threads.getByAgent(agent.id).path, () => {
return HttpResponse.json<{ items: Thread[] | null }>({
items: null,
});
}),
]);
};

afterEach(() => {
cleanup();
});

it.each([
["title", mockedAgent.name, undefined],
[
"description",
mockedAgent.description || "Add a description...",
"placeholder",
],
["instructions", "Instructions", "textbox", 2],
["introductions", "Introductions", "textbox"],
])("Updating %s triggers save", async (_label, searchFor, as, index = 0) => {
setupServer(mockedAgent);
render(
<AgentProvider agent={mockedAgent}>
<Agent />
</AgentProvider>
);

const modifiedValue = faker.word.words({ count: { min: 2, max: 5 } });

if (!as) {
fireEvent.change(screen.getByDisplayValue(searchFor), {
target: { value: modifiedValue },
});
} else if (as === "placeholder") {
fireEvent.change(screen.getByPlaceholderText(searchFor), {
target: { value: modifiedValue },
});
} else if (as === "textbox") {
const heading = screen.getByRole("heading", { name: searchFor });
const textbox = within(heading.parentElement!).queryAllByRole("textbox")[
index ?? 0
];

fireEvent.change(textbox, {
target: { value: modifiedValue },
});
}

await waitFor(() => screen.getByText(/Saving|Saved/i), {
timeout: 2000,
});
});
});
4 changes: 2 additions & 2 deletions ui/admin/app/components/chat/ChatContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface ChatContextType {
isRunning: boolean;
isInvoking: boolean;
introductionMessage?: string;
starterMessages?: string[];
starterMessages?: string[] | null;
agentName?: string;
icons?: AgentIcons | null;
}
Expand All @@ -49,7 +49,7 @@ export function ChatProvider({
onCreateThreadId?: (threadId: string) => void;
readOnly?: boolean;
introductionMessage?: string;
starterMessages?: string[];
starterMessages?: string[] | null;
agentName?: string;
icons?: AgentIcons | null;
}) {
Expand Down
44 changes: 44 additions & 0 deletions ui/admin/app/lib/model/__mocks__/agents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Agent } from "~/lib/model/agents";

export const mockedAgent: Agent = {
id: "a17m9ht",
created: "2025-01-29T12:49:07-05:00",
links: {
invoke: "http://localhost:8080/api/invoke/a17m9ht",
},
type: "agent",
name: "Grouchy Bacon",
icons: null,
description: "",
temperature: null,
cache: null,
alias: "",
prompt: "",
knowledgeDescription: "",
agents: null,
workflows: null,
tools: ["knowledge", "workspace-files", "database", "tasks"],
availableThreadTools: null,
defaultThreadTools: null,
oauthApps: null,
introductionMessage: "",
starterMessages: null,
params: null,
model: "",
env: null,
aliasAssigned: false,
toolInfo: {
database: {
authorized: true,
},
knowledge: {
authorized: true,
},
tasks: {
authorized: true,
},
"workspace-files": {
authorized: true,
},
},
};
24 changes: 24 additions & 0 deletions ui/admin/app/lib/model/__mocks__/defaultModelAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DefaultModelAlias } from "~/lib/model/defaultModelAliases";

export const mockedDefaultModelAliases: DefaultModelAlias[] = [
{
alias: "text-embedding",
model: "m1-openai-model-provider-60708",
},
{
alias: "llm-mini",
model: "m1-openai-model-provider-d3c94",
},
{
alias: "vision",
model: "m1-openai-model-provider-b69d9",
},
{
alias: "image-generation",
model: "m1-openai-model-provider-58920",
},
{
alias: "llm",
model: "m1-openai-model-provider-b69d9",
},
];
86 changes: 86 additions & 0 deletions ui/admin/app/lib/model/__mocks__/toolReferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ToolReference } from "~/lib/model/toolReferences";

export const mockedDatabaseToolReference: ToolReference = {
id: "database",
created: "2025-01-29T11:10:12-05:00",
revision: "1",
metadata: {
category: "Capability",
icon: "https//www.mockimagelocation.com/database.svg",
},
type: "toolreference",
name: "Database",
toolType: "tool",
reference: "github.com/obot-platform/tools/database",
active: true,
resolved: true,
builtin: true,
description: "Tools for interacting with a database",
};

export const mockedKnowledgeToolReference: ToolReference = {
id: "knowledge",
created: "2025-01-29T11:10:12-05:00",
revision: "1",
metadata: {
category: "Capability",
icon: "https//www.mockimagelocation.com/knowledge.svg",
noUserAuth: "knowledge",
},
type: "toolreference",
name: "Knowledge",
toolType: "tool",
reference: "github.com/obot-platform/tools/knowledge",
active: true,
resolved: true,
builtin: true,
description: "Obtain search result from the knowledge set",
credentials: ["mock.com/credentials"],
params: {
Query: "A search query that will be evaluated against the knowledge set",
},
};

export const mockedTasksToolReference: ToolReference = {
id: "tasks",
created: "2025-01-29T11:10:12-05:00",
revision: "1",
metadata: {
category: "Capability",
icon: "https//www.mockimagelocation.com/tasks.svg",
},
type: "toolreference",
name: "Tasks",
toolType: "tool",
reference: "github.com/obot-platform/tools/tasks",
active: true,
resolved: true,
builtin: true,
description: "Manage and execute tasks",
};

export const mockedWorkspaceFilesToolReference: ToolReference = {
id: "workspace-files",
created: "2025-01-29T11:10:12-05:00",
revision: "2695",
metadata: {
category: "Capability",
icon: "https//www.mockimagelocation.com/workspacefiles.svg",
},
type: "toolreference",
name: "Workspace Files",
toolType: "tool",
reference: "github.com/obot-platform/tools/workspace-files",
active: true,
resolved: true,
builtin: true,
description:
"Adds the capability for users to read and write workspace files",
};

export const mockedToolReferences: ToolReference[] = [
mockedDatabaseToolReference,
mockedKnowledgeToolReference,
mockedTasksToolReference,
mockedWorkspaceFilesToolReference,
];
27 changes: 27 additions & 0 deletions ui/admin/app/lib/model/__mocks__/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { User } from "~/lib/model/users";

// Admin User
export const mockedUser: User = {
id: "1",
created: "2025-01-28T13:11:39.243624-05:00",
username: "107221547212253478536",
role: 1,
explicitAdmin: true,
email: "[email protected]",
iconURL: "https://mock.lh3.googleusercontent.com/a/user1-",
timezone: "America/New_York",
};

// Regular User
export const mockedUser2: User = {
id: "2",
created: "2025-01-28T13:11:39.243624-05:00",
username: "103221547202223478436",
role: 10,
explicitAdmin: false,
email: "[email protected]",
iconURL: "https://mock.lh3.googleusercontent.com/a/user2-",
timezone: "America/Los_Angeles",
};

export const mockedUsers: User[] = [mockedUser, mockedUser2];
9 changes: 9 additions & 0 deletions ui/admin/app/lib/model/__mocks__/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Version } from "~/lib/model/version";

export const mockedVersion: Version = {
authEnabled: true,
dockerSupported: false,
emailDomain: "",
gptscript: "v0.9.5",
obot: "v0.0.0-dev",
};
Loading

0 comments on commit 08cbb3f

Please sign in to comment.