// npx vitest run src/__tests__/App.spec.tsx
import React from "react"
import { render, screen, act, cleanup } from "@/utils/test-utils"
import AppWithProviders from "../App"
vi.mock("@src/utils/vscode", () => ({
vscode: {
postMessage: vi.fn(),
},
}))
// Mock the ErrorBoundary component
vi.mock("@src/components/ErrorBoundary", () => ({
__esModule: true,
default: ({ children }: { children: React.ReactNode }) => <>{children}>,
}))
// Mock the telemetry client
vi.mock("@src/utils/TelemetryClient", () => ({
telemetryClient: {
capture: vi.fn(),
updateTelemetryState: vi.fn(),
},
}))
vi.mock("@src/components/chat/ChatView", () => ({
__esModule: true,
default: function ChatView({ isHidden }: { isHidden: boolean }) {
return (
Chat View
)
},
}))
vi.mock("@src/components/settings/SettingsView", () => ({
__esModule: true,
default: function SettingsView({ onDone }: { onDone: () => void }) {
return (
Settings View
)
},
}))
vi.mock("@src/components/history/HistoryView", () => ({
__esModule: true,
default: function HistoryView({ onDone }: { onDone: () => void }) {
return (
History View
)
},
}))
vi.mock("@src/components/mcp/McpView", () => ({
__esModule: true,
default: function McpView({ onDone }: { onDone: () => void }) {
return (
MCP View
)
},
}))
vi.mock("@src/components/modes/ModesView", () => ({
__esModule: true,
default: function ModesView({ onDone }: { onDone: () => void }) {
return (
Modes View
)
},
}))
vi.mock("@src/components/marketplace/MarketplaceView", () => ({
MarketplaceView: function MarketplaceView({ onDone }: { onDone: () => void }) {
return (
Marketplace View
)
},
}))
vi.mock("@src/components/cloud/CloudView", () => ({
CloudView: function CloudView({ onDone }: { onDone: () => void }) {
return (
Cloud View
)
},
}))
const mockUseExtensionState = vi.fn()
// Mock the HumanRelayDialog component
vi.mock("@src/components/human-relay/HumanRelayDialog", () => ({
HumanRelayDialog: ({ _children, isOpen, onClose }: any) => (
Human Relay Dialog
),
}))
// Mock i18next and react-i18next
vi.mock("i18next", () => {
const tFunction = (key: string) => key
const i18n = {
t: tFunction,
use: () => i18n,
init: () => Promise.resolve(tFunction),
changeLanguage: vi.fn(() => Promise.resolve()),
}
return { default: i18n }
})
vi.mock("react-i18next", () => {
const tFunction = (key: string) => key
return {
withTranslation: () => (Component: any) => {
const MockedComponent = (props: any) => {
return
}
MockedComponent.displayName = `withTranslation(${Component.displayName || Component.name || "Component"})`
return MockedComponent
},
Trans: ({ children }: { children: React.ReactNode }) => <>{children}>,
useTranslation: () => {
return {
t: tFunction,
i18n: {
t: tFunction,
changeLanguage: vi.fn(() => Promise.resolve()),
},
}
},
initReactI18next: {
type: "3rdParty",
init: vi.fn(),
},
}
})
// Mock TranslationProvider to pass through children
vi.mock("@src/i18n/TranslationContext", () => {
const tFunction = (key: string) => key
return {
__esModule: true,
default: ({ children }: { children: React.ReactNode }) => <>{children}>,
useAppTranslation: () => ({
t: tFunction,
i18n: {
t: tFunction,
changeLanguage: vi.fn(() => Promise.resolve()),
},
}),
}
})
vi.mock("@src/context/ExtensionStateContext", () => ({
useExtensionState: () => mockUseExtensionState(),
ExtensionStateContextProvider: ({ children }: { children: React.ReactNode }) => <>{children}>,
}))
// Mock environment variables
vi.mock("process.env", () => ({
NODE_ENV: "test",
PKG_VERSION: "1.0.0-test",
}))
describe("App", () => {
beforeEach(() => {
vi.clearAllMocks()
window.removeEventListener("message", () => {})
// Set up default mock return value
mockUseExtensionState.mockReturnValue({
didHydrateState: true,
showWelcome: false,
shouldShowAnnouncement: false,
experiments: {},
language: "en",
})
})
afterEach(() => {
cleanup()
window.removeEventListener("message", () => {})
})
const triggerMessage = (action: string) => {
const messageEvent = new MessageEvent("message", {
data: {
type: "action",
action,
},
})
window.dispatchEvent(messageEvent)
}
it("shows chat view by default", () => {
render()
const chatView = screen.getByTestId("chat-view")
expect(chatView).toBeInTheDocument()
expect(chatView.getAttribute("data-hidden")).toBe("false")
})
it("switches to settings view when receiving settingsButtonClicked action", async () => {
render()
act(() => {
triggerMessage("settingsButtonClicked")
})
const settingsView = await screen.findByTestId("settings-view")
expect(settingsView).toBeInTheDocument()
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("true")
})
it("switches to history view when receiving historyButtonClicked action", async () => {
render()
act(() => {
triggerMessage("historyButtonClicked")
})
const historyView = await screen.findByTestId("history-view")
expect(historyView).toBeInTheDocument()
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("true")
})
it("switches to MCP view when receiving mcpButtonClicked action", async () => {
render()
act(() => {
triggerMessage("mcpButtonClicked")
})
const mcpView = await screen.findByTestId("mcp-view")
expect(mcpView).toBeInTheDocument()
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("true")
})
it("switches to prompts view when receiving promptsButtonClicked action", async () => {
render()
act(() => {
triggerMessage("promptsButtonClicked")
})
const promptsView = await screen.findByTestId("prompts-view")
expect(promptsView).toBeInTheDocument()
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("true")
})
it("returns to chat view when clicking done in settings view", async () => {
render()
act(() => {
triggerMessage("settingsButtonClicked")
})
const settingsView = await screen.findByTestId("settings-view")
act(() => {
settingsView.click()
})
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("false")
expect(screen.queryByTestId("settings-view")).not.toBeInTheDocument()
})
it.each(["history", "mcp", "prompts"])("returns to chat view when clicking done in %s view", async (view) => {
render()
act(() => {
triggerMessage(`${view}ButtonClicked`)
})
const viewElement = await screen.findByTestId(`${view}-view`)
act(() => {
viewElement.click()
})
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("false")
expect(screen.queryByTestId(`${view}-view`)).not.toBeInTheDocument()
})
it("switches to marketplace view when receiving marketplaceButtonClicked action", async () => {
render()
act(() => {
triggerMessage("marketplaceButtonClicked")
})
const marketplaceView = await screen.findByTestId("marketplace-view")
expect(marketplaceView).toBeInTheDocument()
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("true")
})
it("returns to chat view when clicking done in marketplace view", async () => {
render()
act(() => {
triggerMessage("marketplaceButtonClicked")
})
const marketplaceView = await screen.findByTestId("marketplace-view")
act(() => {
marketplaceView.click()
})
const chatView = screen.getByTestId("chat-view")
expect(chatView.getAttribute("data-hidden")).toBe("false")
expect(screen.queryByTestId("marketplace-view")).not.toBeInTheDocument()
})
})