diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/CopilotPage.test.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/CopilotPage.test.tsx
new file mode 100644
index 0000000000..71791b5694
--- /dev/null
+++ b/autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/CopilotPage.test.tsx
@@ -0,0 +1,168 @@
+import { render, screen, cleanup } from "@/tests/integrations/test-utils";
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { CopilotPage } from "../CopilotPage";
+
+// Mock child components that are complex and not under test here
+vi.mock("../components/ChatContainer/ChatContainer", () => ({
+ ChatContainer: () =>
,
+}));
+vi.mock("../components/ChatSidebar/ChatSidebar", () => ({
+ ChatSidebar: () => ,
+}));
+vi.mock("../components/DeleteChatDialog/DeleteChatDialog", () => ({
+ DeleteChatDialog: () => null,
+}));
+vi.mock("../components/MobileDrawer/MobileDrawer", () => ({
+ MobileDrawer: () => null,
+}));
+vi.mock("../components/MobileHeader/MobileHeader", () => ({
+ MobileHeader: () => null,
+}));
+vi.mock("../components/NotificationBanner/NotificationBanner", () => ({
+ NotificationBanner: () => null,
+}));
+vi.mock("../components/NotificationDialog/NotificationDialog", () => ({
+ NotificationDialog: () => null,
+}));
+vi.mock("../components/RateLimitResetDialog/RateLimitResetDialog", () => ({
+ RateLimitResetDialog: () => null,
+}));
+vi.mock("../components/ScaleLoader/ScaleLoader", () => ({
+ ScaleLoader: () => ,
+}));
+vi.mock("../components/ArtifactPanel/ArtifactPanel", () => ({
+ ArtifactPanel: () => null,
+}));
+vi.mock("@/components/ui/sidebar", () => ({
+ SidebarProvider: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+}));
+
+// Mock hooks that hit the network
+vi.mock("@/app/api/__generated__/endpoints/chat/chat", () => ({
+ useGetV2GetCopilotUsage: () => ({
+ data: undefined,
+ isSuccess: false,
+ isError: false,
+ }),
+}));
+vi.mock("@/hooks/useCredits", () => ({
+ default: () => ({ credits: null, fetchCredits: vi.fn() }),
+}));
+vi.mock("@/services/feature-flags/use-get-flag", () => ({
+ Flag: {
+ ENABLE_PLATFORM_PAYMENT: "ENABLE_PLATFORM_PAYMENT",
+ ARTIFACTS: "ARTIFACTS",
+ CHAT_MODE_OPTION: "CHAT_MODE_OPTION",
+ },
+ useGetFlag: () => false,
+}));
+
+// Build the base mock return value for useCopilotPage
+const basePageState = {
+ sessionId: null as string | null,
+ messages: [],
+ status: "ready" as const,
+ error: undefined,
+ stop: vi.fn(),
+ isReconnecting: false,
+ isSyncing: false,
+ createSession: vi.fn(),
+ onSend: vi.fn(),
+ isLoadingSession: false,
+ isSessionError: false,
+ isCreatingSession: false,
+ isUploadingFiles: false,
+ isUserLoading: false,
+ isLoggedIn: true,
+ hasMoreMessages: false,
+ isLoadingMore: false,
+ loadMore: vi.fn(),
+ isMobile: false,
+ isDrawerOpen: false,
+ sessions: [],
+ isLoadingSessions: false,
+ handleOpenDrawer: vi.fn(),
+ handleCloseDrawer: vi.fn(),
+ handleDrawerOpenChange: vi.fn(),
+ handleSelectSession: vi.fn(),
+ handleNewChat: vi.fn(),
+ sessionToDelete: null,
+ isDeleting: false,
+ handleConfirmDelete: vi.fn(),
+ handleCancelDelete: vi.fn(),
+ historicalDurations: {},
+ rateLimitMessage: null,
+ dismissRateLimit: vi.fn(),
+ isDryRun: false,
+ sessionDryRun: false,
+};
+
+const mockUseCopilotPage = vi.fn(() => basePageState);
+
+vi.mock("../useCopilotPage", () => ({
+ useCopilotPage: () => mockUseCopilotPage(),
+}));
+
+afterEach(() => {
+ cleanup();
+ mockUseCopilotPage.mockReset();
+ mockUseCopilotPage.mockImplementation(() => basePageState);
+});
+
+describe("CopilotPage test-mode banner", () => {
+ it("does not show test-mode banner when there is no active session", () => {
+ render();
+ expect(
+ screen.queryByText(/test mode.*this session runs agents/i),
+ ).toBeNull();
+ });
+
+ it("does not show test-mode banner when session exists but sessionDryRun is false", () => {
+ mockUseCopilotPage.mockReturnValue({
+ ...basePageState,
+ sessionId: "session-abc",
+ sessionDryRun: false,
+ });
+ render();
+ expect(
+ screen.queryByText(/test mode.*this session runs agents/i),
+ ).toBeNull();
+ });
+
+ it("shows test-mode banner when session exists and sessionDryRun is true", () => {
+ mockUseCopilotPage.mockReturnValue({
+ ...basePageState,
+ sessionId: "session-abc",
+ sessionDryRun: true,
+ });
+ render();
+ expect(
+ screen.getByText(/test mode.*this session runs agents/i),
+ ).toBeDefined();
+ });
+
+ it("does not show test-mode banner when sessionDryRun is true but no sessionId", () => {
+ mockUseCopilotPage.mockReturnValue({
+ ...basePageState,
+ sessionId: null,
+ sessionDryRun: true,
+ });
+ render();
+ expect(
+ screen.queryByText(/test mode.*this session runs agents/i),
+ ).toBeNull();
+ });
+
+ it("shows loading spinner when user is loading", () => {
+ mockUseCopilotPage.mockReturnValue({
+ ...basePageState,
+ isUserLoading: true,
+ isLoggedIn: false,
+ });
+ render();
+ expect(screen.getByTestId("scale-loader")).toBeDefined();
+ expect(screen.queryByTestId("chat-container")).toBeNull();
+ });
+});