mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
test(frontend): add useChatSession + ChatMessagesContainer coverage tests
- useChatSession.test.ts: 5 tests for newestSequence and forwardPaginated memos - ChatMessagesContainer.test.tsx: 5 tests verifying top/bottom sentinel placement based on forwardPaginated prop
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { useChatSession } from "../useChatSession";
|
||||
|
||||
const mockUseGetV2GetSession = vi.fn();
|
||||
|
||||
vi.mock("@/app/api/__generated__/endpoints/chat/chat", () => ({
|
||||
useGetV2GetSession: (...args: unknown[]) => mockUseGetV2GetSession(...args),
|
||||
usePostV2CreateSession: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
getGetV2GetSessionQueryKey: (id: string) => ["session", id],
|
||||
getGetV2ListSessionsQueryKey: () => ["sessions"],
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQueryClient: () => ({
|
||||
invalidateQueries: vi.fn(),
|
||||
setQueryData: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("nuqs", () => ({
|
||||
parseAsString: { withDefault: (v: unknown) => v },
|
||||
useQueryState: () => ["sess-1", vi.fn()],
|
||||
}));
|
||||
|
||||
vi.mock("../helpers/convertChatSessionToUiMessages", () => ({
|
||||
convertChatSessionMessagesToUiMessages: vi.fn(() => ({
|
||||
messages: [],
|
||||
historicalDurations: new Map(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../helpers", () => ({
|
||||
resolveSessionDryRun: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
vi.mock("@sentry/nextjs", () => ({
|
||||
captureException: vi.fn(),
|
||||
}));
|
||||
|
||||
function makeQueryResult(data: object | null) {
|
||||
return {
|
||||
data: data ? { status: 200, data } : undefined,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
isFetching: false,
|
||||
refetch: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
describe("useChatSession — newestSequence and forwardPaginated", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("returns null / false when no session data", () => {
|
||||
mockUseGetV2GetSession.mockReturnValue(makeQueryResult(null));
|
||||
const { result } = renderHook(() => useChatSession());
|
||||
expect(result.current.newestSequence).toBeNull();
|
||||
expect(result.current.forwardPaginated).toBe(false);
|
||||
});
|
||||
|
||||
it("returns newestSequence from session data", () => {
|
||||
mockUseGetV2GetSession.mockReturnValue(
|
||||
makeQueryResult({
|
||||
messages: [],
|
||||
has_more_messages: true,
|
||||
oldest_sequence: 0,
|
||||
newest_sequence: 99,
|
||||
forward_paginated: false,
|
||||
active_stream: null,
|
||||
}),
|
||||
);
|
||||
const { result } = renderHook(() => useChatSession());
|
||||
expect(result.current.newestSequence).toBe(99);
|
||||
});
|
||||
|
||||
it("returns null for newestSequence when field is missing", () => {
|
||||
mockUseGetV2GetSession.mockReturnValue(
|
||||
makeQueryResult({
|
||||
messages: [],
|
||||
has_more_messages: false,
|
||||
oldest_sequence: 0,
|
||||
newest_sequence: null,
|
||||
forward_paginated: false,
|
||||
active_stream: null,
|
||||
}),
|
||||
);
|
||||
const { result } = renderHook(() => useChatSession());
|
||||
expect(result.current.newestSequence).toBeNull();
|
||||
});
|
||||
|
||||
it("returns forwardPaginated=true when session is forward-paginated", () => {
|
||||
mockUseGetV2GetSession.mockReturnValue(
|
||||
makeQueryResult({
|
||||
messages: [],
|
||||
has_more_messages: true,
|
||||
oldest_sequence: 0,
|
||||
newest_sequence: 49,
|
||||
forward_paginated: true,
|
||||
active_stream: null,
|
||||
}),
|
||||
);
|
||||
const { result } = renderHook(() => useChatSession());
|
||||
expect(result.current.forwardPaginated).toBe(true);
|
||||
});
|
||||
|
||||
it("returns forwardPaginated=false when session is backward-paginated", () => {
|
||||
mockUseGetV2GetSession.mockReturnValue(
|
||||
makeQueryResult({
|
||||
messages: [],
|
||||
has_more_messages: true,
|
||||
oldest_sequence: 50,
|
||||
newest_sequence: 99,
|
||||
forward_paginated: false,
|
||||
active_stream: null,
|
||||
}),
|
||||
);
|
||||
const { result } = renderHook(() => useChatSession());
|
||||
expect(result.current.forwardPaginated).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
import { render, screen, cleanup } from "@/tests/integrations/test-utils";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { ChatMessagesContainer } from "../ChatMessagesContainer";
|
||||
|
||||
const mockScrollEl = {
|
||||
scrollHeight: 100,
|
||||
scrollTop: 0,
|
||||
clientHeight: 500,
|
||||
};
|
||||
|
||||
vi.mock("use-stick-to-bottom", () => ({
|
||||
useStickToBottomContext: () => ({ scrollRef: { current: mockScrollEl } }),
|
||||
Conversation: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
ConversationContent: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
ConversationScrollButton: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ai-elements/conversation", () => ({
|
||||
Conversation: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
ConversationContent: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
ConversationScrollButton: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ai-elements/message", () => ({
|
||||
Message: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
MessageContent: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
MessageActions: ({ children }: { children: React.ReactNode }) => (
|
||||
<div>{children}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("../components/AssistantMessageActions", () => ({
|
||||
AssistantMessageActions: () => null,
|
||||
}));
|
||||
vi.mock("../components/CopyButton", () => ({ CopyButton: () => null }));
|
||||
vi.mock("../components/CollapsedToolGroup", () => ({
|
||||
CollapsedToolGroup: () => null,
|
||||
}));
|
||||
vi.mock("../components/MessageAttachments", () => ({
|
||||
MessageAttachments: () => null,
|
||||
}));
|
||||
vi.mock("../components/MessagePartRenderer", () => ({
|
||||
MessagePartRenderer: () => null,
|
||||
}));
|
||||
vi.mock("../components/ReasoningCollapse", () => ({
|
||||
ReasoningCollapse: () => null,
|
||||
}));
|
||||
vi.mock("../components/ThinkingIndicator", () => ({
|
||||
ThinkingIndicator: () => null,
|
||||
}));
|
||||
vi.mock("../../JobStatsBar/TurnStatsBar", () => ({
|
||||
TurnStatsBar: () => null,
|
||||
}));
|
||||
vi.mock("../../JobStatsBar/useElapsedTimer", () => ({
|
||||
useElapsedTimer: () => ({ elapsedSeconds: 0 }),
|
||||
}));
|
||||
vi.mock("../../CopilotPendingReviews/CopilotPendingReviews", () => ({
|
||||
CopilotPendingReviews: () => null,
|
||||
}));
|
||||
vi.mock("../helpers", () => ({
|
||||
buildRenderSegments: () => [],
|
||||
getTurnMessages: () => [],
|
||||
parseSpecialMarkers: () => ({ markerType: null }),
|
||||
splitReasoningAndResponse: (parts: unknown[]) => ({
|
||||
reasoningParts: [],
|
||||
responseParts: parts,
|
||||
}),
|
||||
}));
|
||||
|
||||
type ObserverCallback = (entries: { isIntersecting: boolean }[]) => void;
|
||||
class MockIntersectionObserver {
|
||||
static lastCallback: ObserverCallback | null = null;
|
||||
private callback: ObserverCallback;
|
||||
constructor(cb: ObserverCallback) {
|
||||
this.callback = cb;
|
||||
MockIntersectionObserver.lastCallback = cb;
|
||||
}
|
||||
observe() {}
|
||||
disconnect() {}
|
||||
unobserve() {}
|
||||
takeRecords() {
|
||||
return [];
|
||||
}
|
||||
root = null;
|
||||
rootMargin = "";
|
||||
thresholds = [];
|
||||
}
|
||||
|
||||
const BASE_PROPS = {
|
||||
messages: [],
|
||||
status: "ready" as const,
|
||||
error: undefined,
|
||||
isLoading: false,
|
||||
sessionID: "sess-1",
|
||||
hasMoreMessages: true,
|
||||
isLoadingMore: false,
|
||||
onLoadMore: vi.fn(),
|
||||
onRetry: vi.fn(),
|
||||
};
|
||||
|
||||
describe("ChatMessagesContainer", () => {
|
||||
beforeEach(() => {
|
||||
mockScrollEl.scrollHeight = 100;
|
||||
mockScrollEl.scrollTop = 0;
|
||||
mockScrollEl.clientHeight = 500;
|
||||
MockIntersectionObserver.lastCallback = null;
|
||||
vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("renders top sentinel when forwardPaginated is false (backward pagination)", () => {
|
||||
render(<ChatMessagesContainer {...BASE_PROPS} forwardPaginated={false} />);
|
||||
expect(
|
||||
screen.getByRole("button", { name: /load older messages/i }),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders top sentinel when forwardPaginated is undefined (default, backward)", () => {
|
||||
render(<ChatMessagesContainer {...BASE_PROPS} />);
|
||||
expect(
|
||||
screen.getByRole("button", { name: /load older messages/i }),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders bottom sentinel when forwardPaginated is true (forward pagination)", () => {
|
||||
render(<ChatMessagesContainer {...BASE_PROPS} forwardPaginated={true} />);
|
||||
expect(
|
||||
screen.getByRole("button", { name: /load older messages/i }),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it("hides sentinel when hasMoreMessages is false", () => {
|
||||
render(
|
||||
<ChatMessagesContainer
|
||||
{...BASE_PROPS}
|
||||
hasMoreMessages={false}
|
||||
forwardPaginated={true}
|
||||
/>,
|
||||
);
|
||||
expect(
|
||||
screen.queryByRole("button", { name: /load older messages/i }),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("hides sentinel when onLoadMore is not provided", () => {
|
||||
render(
|
||||
<ChatMessagesContainer
|
||||
{...BASE_PROPS}
|
||||
onLoadMore={undefined}
|
||||
forwardPaginated={true}
|
||||
/>,
|
||||
);
|
||||
expect(
|
||||
screen.queryByRole("button", { name: /load older messages/i }),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user