|
|
@@ -1,975 +0,0 @@
|
|
|
-// npx vitest src/components/settings/__tests__/CodeIndexSettings.spec.tsx
|
|
|
-
|
|
|
-import { render, screen, fireEvent } from "@/utils/test-utils"
|
|
|
-import userEvent from "@testing-library/user-event"
|
|
|
-
|
|
|
-import { CodeIndexSettings } from "../CodeIndexSettings"
|
|
|
-
|
|
|
-import { vscode } from "@src/utils/vscode"
|
|
|
-
|
|
|
-vi.mock("@src/utils/vscode", () => ({
|
|
|
- vscode: {
|
|
|
- postMessage: vi.fn(),
|
|
|
- },
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("@src/i18n/TranslationContext", () => ({
|
|
|
- useAppTranslation: () => ({
|
|
|
- t: (key: string) => {
|
|
|
- const translations: Record<string, string> = {
|
|
|
- "settings:codeIndex.providerLabel": "Provider",
|
|
|
- "settings:codeIndex.selectProviderPlaceholder": "Select provider",
|
|
|
- "settings:codeIndex.openaiProvider": "OpenAI",
|
|
|
- "settings:codeIndex.ollamaProvider": "Ollama",
|
|
|
- "settings:codeIndex.openaiCompatibleProvider": "OpenAI Compatible",
|
|
|
- "settings:codeIndex.openaiKeyLabel": "OpenAI API Key",
|
|
|
- "settings:codeIndex.openaiCompatibleBaseUrlLabel": "Base URL",
|
|
|
- "settings:codeIndex.openaiCompatibleApiKeyLabel": "API Key",
|
|
|
- "settings:codeIndex.openaiCompatibleModelDimensionLabel": "Embedding Dimension",
|
|
|
- "settings:codeIndex.openaiCompatibleModelDimensionPlaceholder": "Enter dimension (e.g., 1536)",
|
|
|
- "settings:codeIndex.openaiCompatibleModelDimensionDescription": "The dimension of the embedding model",
|
|
|
- "settings:codeIndex.modelLabel": "Model",
|
|
|
- "settings:codeIndex.selectModelPlaceholder": "Select model",
|
|
|
- "settings:codeIndex.qdrantUrlLabel": "Qdrant URL",
|
|
|
- "settings:codeIndex.qdrantApiKeyLabel": "Qdrant API Key",
|
|
|
- "settings:codeIndex.ollamaUrlLabel": "Ollama URL",
|
|
|
- "settings:codeIndex.qdrantKeyLabel": "Qdrant API Key",
|
|
|
- "settings:codeIndex.enableLabel": "Enable Code Index",
|
|
|
- "settings:codeIndex.enableDescription": "Enable semantic search across your codebase",
|
|
|
- "settings:codeIndex.unsavedSettingsMessage": "Please save settings before indexing",
|
|
|
- "settings:codeIndex.startIndexingButton": "Start Indexing",
|
|
|
- "settings:codeIndex.clearIndexDataButton": "Clear Index Data",
|
|
|
- "settings:codeIndex.clearDataDialog.title": "Clear Index Data",
|
|
|
- "settings:codeIndex.clearDataDialog.description": "This will remove all indexed data",
|
|
|
- "settings:codeIndex.clearDataDialog.cancelButton": "Cancel",
|
|
|
- "settings:codeIndex.clearDataDialog.confirmButton": "Confirm",
|
|
|
- "settings:codeIndex.searchMinScoreLabel": "Search Score Threshold",
|
|
|
- "settings:codeIndex.searchMinScoreDescription":
|
|
|
- "Minimum similarity score (0.0-1.0) required for search results. Lower values return more results but may be less relevant. Higher values return fewer but more relevant results.",
|
|
|
- "settings:codeIndex.searchMinScoreResetTooltip": "Reset to default value (0.4)",
|
|
|
- "settings:codeIndex.advancedConfigLabel": "Advanced Configuration",
|
|
|
- }
|
|
|
- return translations[key] || key
|
|
|
- },
|
|
|
- }),
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("react-i18next", () => ({
|
|
|
- Trans: ({ children }: any) => <div>{children}</div>,
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("@src/utils/docLinks", () => ({
|
|
|
- buildDocLink: vi.fn(() => "https://docs.example.com"),
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("@src/components/ui", () => ({
|
|
|
- Select: ({ children, value, onValueChange }: any) => (
|
|
|
- <div data-testid="select" data-value={value}>
|
|
|
- <button onClick={() => onValueChange && onValueChange("test-change")}>{value}</button>
|
|
|
- {children}
|
|
|
- </div>
|
|
|
- ),
|
|
|
- SelectContent: ({ children }: any) => <div data-testid="select-content">{children}</div>,
|
|
|
- SelectItem: ({ children, value }: any) => (
|
|
|
- <div data-testid={`select-item-${value}`} data-value={value}>
|
|
|
- {children}
|
|
|
- </div>
|
|
|
- ),
|
|
|
- SelectTrigger: ({ children }: any) => <div data-testid="select-trigger">{children}</div>,
|
|
|
- SelectValue: ({ placeholder }: any) => <div data-testid="select-value">{placeholder}</div>,
|
|
|
- AlertDialog: ({ children }: any) => <div data-testid="alert-dialog">{children}</div>,
|
|
|
- AlertDialogAction: ({ children, onClick }: any) => (
|
|
|
- <button data-testid="alert-dialog-action" onClick={onClick}>
|
|
|
- {children}
|
|
|
- </button>
|
|
|
- ),
|
|
|
- AlertDialogCancel: ({ children }: any) => <button data-testid="alert-dialog-cancel">{children}</button>,
|
|
|
- AlertDialogContent: ({ children }: any) => <div data-testid="alert-dialog-content">{children}</div>,
|
|
|
- AlertDialogDescription: ({ children }: any) => <div data-testid="alert-dialog-description">{children}</div>,
|
|
|
- AlertDialogFooter: ({ children }: any) => <div data-testid="alert-dialog-footer">{children}</div>,
|
|
|
- AlertDialogHeader: ({ children }: any) => <div data-testid="alert-dialog-header">{children}</div>,
|
|
|
- AlertDialogTitle: ({ children }: any) => <div data-testid="alert-dialog-title">{children}</div>,
|
|
|
- AlertDialogTrigger: ({ children }: any) => <div data-testid="alert-dialog-trigger">{children}</div>,
|
|
|
- Slider: ({ value, onValueChange, "data-testid": dataTestId }: any) => (
|
|
|
- <input
|
|
|
- type="range"
|
|
|
- value={value[0]}
|
|
|
- onChange={(e) => onValueChange && onValueChange([parseFloat(e.target.value)])}
|
|
|
- data-testid={dataTestId}
|
|
|
- role="slider"
|
|
|
- />
|
|
|
- ),
|
|
|
- Button: ({ children, onClick, "data-testid": dataTestId, ...props }: any) => (
|
|
|
- <button onClick={onClick} data-testid={dataTestId} {...props}>
|
|
|
- {children}
|
|
|
- </button>
|
|
|
- ),
|
|
|
- Tooltip: ({ children }: any) => <div data-testid="tooltip">{children}</div>,
|
|
|
- TooltipContent: ({ children }: any) => <div data-testid="tooltip-content">{children}</div>,
|
|
|
- TooltipProvider: ({ children }: any) => <div data-testid="tooltip-provider">{children}</div>,
|
|
|
- TooltipTrigger: ({ children }: any) => <div data-testid="tooltip-trigger">{children}</div>,
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("@vscode/webview-ui-toolkit/react", () => ({
|
|
|
- VSCodeCheckbox: ({ checked, onChange, children }: any) => (
|
|
|
- <label>
|
|
|
- <input
|
|
|
- type="checkbox"
|
|
|
- checked={checked}
|
|
|
- onChange={(e) => onChange && onChange({ target: { checked: e.target.checked } })}
|
|
|
- data-testid="vscode-checkbox"
|
|
|
- />
|
|
|
- {children}
|
|
|
- </label>
|
|
|
- ),
|
|
|
- VSCodeTextField: ({ value, onInput, type, style, ...props }: any) => {
|
|
|
- const handleChange = (e: any) => {
|
|
|
- if (onInput) {
|
|
|
- onInput({ target: { value: e.target.value } })
|
|
|
- }
|
|
|
- }
|
|
|
- return (
|
|
|
- <input
|
|
|
- type={type || "text"}
|
|
|
- value={value || ""}
|
|
|
- onChange={handleChange}
|
|
|
- onInput={handleChange}
|
|
|
- data-testid="vscode-textfield"
|
|
|
- tabIndex={0}
|
|
|
- {...props}
|
|
|
- />
|
|
|
- )
|
|
|
- },
|
|
|
- VSCodeButton: ({ children, onClick, appearance }: any) => (
|
|
|
- <button onClick={onClick} data-testid="vscode-button" data-appearance={appearance}>
|
|
|
- {children}
|
|
|
- </button>
|
|
|
- ),
|
|
|
- VSCodeLink: ({ children, href }: any) => (
|
|
|
- <a href={href} data-testid="vscode-link">
|
|
|
- {children}
|
|
|
- </a>
|
|
|
- ),
|
|
|
-}))
|
|
|
-
|
|
|
-vi.mock("@radix-ui/react-progress", () => ({
|
|
|
- Root: ({ children, value }: any) => (
|
|
|
- <div data-testid="progress-root" data-value={value}>
|
|
|
- {children}
|
|
|
- </div>
|
|
|
- ),
|
|
|
- Indicator: ({ style }: any) => <div data-testid="progress-indicator" style={style} />,
|
|
|
-}))
|
|
|
-
|
|
|
-describe("CodeIndexSettings", () => {
|
|
|
- const mockSetCachedStateField = vi.fn()
|
|
|
- const mockSetApiConfigurationField = vi.fn()
|
|
|
-
|
|
|
- const defaultProps = {
|
|
|
- codebaseIndexModels: {
|
|
|
- openai: {
|
|
|
- "text-embedding-3-small": { dimension: 1536 },
|
|
|
- "text-embedding-3-large": { dimension: 3072 },
|
|
|
- },
|
|
|
- "openai-compatible": {
|
|
|
- "text-embedding-3-small": { dimension: 1536 },
|
|
|
- "custom-model": { dimension: 768 },
|
|
|
- },
|
|
|
- },
|
|
|
- codebaseIndexConfig: {
|
|
|
- codebaseIndexEnabled: true,
|
|
|
- codebaseIndexEmbedderProvider: "openai" as const,
|
|
|
- codebaseIndexEmbedderModelId: "text-embedding-3-small",
|
|
|
- codebaseIndexQdrantUrl: "http://localhost:6333",
|
|
|
- codebaseIndexSearchMinScore: 0.4,
|
|
|
- },
|
|
|
- apiConfiguration: {
|
|
|
- codeIndexOpenAiKey: "",
|
|
|
- codebaseIndexOpenAiCompatibleBaseUrl: "",
|
|
|
- codebaseIndexOpenAiCompatibleApiKey: "",
|
|
|
- codeIndexQdrantApiKey: "",
|
|
|
- },
|
|
|
- setCachedStateField: mockSetCachedStateField,
|
|
|
- setApiConfigurationField: mockSetApiConfigurationField,
|
|
|
- areSettingsCommitted: true,
|
|
|
- }
|
|
|
-
|
|
|
- beforeEach(() => {
|
|
|
- vi.clearAllMocks()
|
|
|
- // Mock window.addEventListener for message handling
|
|
|
- Object.defineProperty(window, "addEventListener", {
|
|
|
- value: vi.fn(),
|
|
|
- writable: true,
|
|
|
- })
|
|
|
- Object.defineProperty(window, "removeEventListener", {
|
|
|
- value: vi.fn(),
|
|
|
- writable: true,
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Provider Selection", () => {
|
|
|
- it("should render OpenAI Compatible provider option", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expect(screen.getByTestId("select-item-openai-compatible")).toBeInTheDocument()
|
|
|
- expect(screen.getByText("OpenAI Compatible")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should show OpenAI Compatible configuration fields when provider is selected", () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- expect(screen.getByText("Base URL")).toBeInTheDocument()
|
|
|
- expect(screen.getByText("API Key")).toBeInTheDocument()
|
|
|
- expect(screen.getAllByTestId("vscode-textfield")).toHaveLength(6) // Base URL, API Key, Embedding Dimension, Model ID, Qdrant URL, Qdrant Key (Search Min Score is now a slider)
|
|
|
- })
|
|
|
-
|
|
|
- it("should hide OpenAI Compatible fields when different provider is selected", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expect(screen.queryByText("Base URL")).not.toBeInTheDocument()
|
|
|
- expect(screen.getByText("OpenAI API Key")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- /**
|
|
|
- * Test provider switching functionality
|
|
|
- */
|
|
|
- // Provider selection functionality is tested through integration tests
|
|
|
- // Removed complex provider switching test that was difficult to mock properly
|
|
|
- })
|
|
|
-
|
|
|
- describe("OpenAI Compatible Configuration", () => {
|
|
|
- const openAICompatibleProps = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- it("should render base URL input field", () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- const textFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- const baseUrlField = textFields.find(
|
|
|
- (field) =>
|
|
|
- field.getAttribute("value") ===
|
|
|
- openAICompatibleProps.apiConfiguration.codebaseIndexOpenAiCompatibleBaseUrl,
|
|
|
- )
|
|
|
- expect(baseUrlField).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should render API key input field with password type", () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- const passwordFields = screen
|
|
|
- .getAllByTestId("vscode-textfield")
|
|
|
- .filter((field) => field.getAttribute("type") === "password")
|
|
|
- expect(passwordFields.length).toBeGreaterThan(0)
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setApiConfigurationField when base URL changes", async () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- // Find the Base URL field - it should be the first text field (not password) in the OpenAI Compatible section
|
|
|
- const textFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- // Filter for text type fields (not password) and find the one with empty value (base URL field)
|
|
|
- const textTypeFields = textFields.filter((field) => field.getAttribute("type") === "text")
|
|
|
- const baseUrlField = textTypeFields[0] // First text field should be base URL
|
|
|
-
|
|
|
- expect(baseUrlField).toBeDefined()
|
|
|
-
|
|
|
- // Use fireEvent to trigger the change
|
|
|
- fireEvent.change(baseUrlField!, { target: { value: "test" } })
|
|
|
-
|
|
|
- // Check that setApiConfigurationField was called with the right parameter name (accepts any value)
|
|
|
- expect(mockSetApiConfigurationField).toHaveBeenCalledWith("codebaseIndexOpenAiCompatibleBaseUrl", "test")
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setApiConfigurationField when API key changes", async () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- // Find the API Key field by looking for the text and then finding the password input
|
|
|
- screen.getByText("API Key")
|
|
|
- const passwordFields = screen
|
|
|
- .getAllByTestId("vscode-textfield")
|
|
|
- .filter((field) => field.getAttribute("type") === "password")
|
|
|
- const apiKeyField = passwordFields[0] // First password field in the OpenAI Compatible section
|
|
|
- expect(apiKeyField).toBeDefined()
|
|
|
-
|
|
|
- // Use fireEvent to trigger the change
|
|
|
- fireEvent.change(apiKeyField!, { target: { value: "test" } })
|
|
|
-
|
|
|
- // Check that setApiConfigurationField was called with the right parameter name (accepts any value)
|
|
|
- expect(mockSetApiConfigurationField).toHaveBeenCalledWith("codebaseIndexOpenAiCompatibleApiKey", "test")
|
|
|
- })
|
|
|
-
|
|
|
- it("should display current base URL value", () => {
|
|
|
- const propsWithValues = {
|
|
|
- ...openAICompatibleProps,
|
|
|
- apiConfiguration: {
|
|
|
- ...openAICompatibleProps.apiConfiguration,
|
|
|
- codebaseIndexOpenAiCompatibleBaseUrl: "https://existing-api.example.com/v1",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithValues} />)
|
|
|
-
|
|
|
- const textField = screen.getByDisplayValue("https://existing-api.example.com/v1")
|
|
|
- expect(textField).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should display current API key value", () => {
|
|
|
- const propsWithValues = {
|
|
|
- ...openAICompatibleProps,
|
|
|
- apiConfiguration: {
|
|
|
- ...openAICompatibleProps.apiConfiguration,
|
|
|
- codebaseIndexOpenAiCompatibleApiKey: "existing-api-key",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithValues} />)
|
|
|
-
|
|
|
- const textField = screen.getByDisplayValue("existing-api-key")
|
|
|
- expect(textField).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should display embedding dimension input field for OpenAI Compatible provider", () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- // Look for the embedding dimension label
|
|
|
- expect(screen.getByText("Embedding Dimension")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should hide embedding dimension input field for non-OpenAI Compatible providers", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- // Should not show embedding dimension for OpenAI provider
|
|
|
- expect(screen.queryByText("Embedding Dimension")).not.toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setApiConfigurationField when embedding dimension changes", async () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- // Find the embedding dimension input field by placeholder
|
|
|
- const dimensionField = screen.getByPlaceholderText("Enter dimension (e.g., 1536)")
|
|
|
- expect(dimensionField).toBeDefined()
|
|
|
-
|
|
|
- // Use fireEvent to trigger the change
|
|
|
- fireEvent.change(dimensionField!, { target: { value: "1024" } })
|
|
|
-
|
|
|
- // Check that setApiConfigurationField was called with the right parameter name
|
|
|
- expect(mockSetApiConfigurationField).toHaveBeenCalledWith(
|
|
|
- "codebaseIndexOpenAiCompatibleModelDimension",
|
|
|
- 1024,
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it("should display current embedding dimension value", () => {
|
|
|
- const propsWithDimension = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- apiConfiguration: {
|
|
|
- ...defaultProps.apiConfiguration,
|
|
|
- codebaseIndexOpenAiCompatibleModelDimension: 2048,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithDimension} />)
|
|
|
-
|
|
|
- const textField = screen.getByDisplayValue("2048")
|
|
|
- expect(textField).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should handle empty embedding dimension value", () => {
|
|
|
- const propsWithEmptyDimension = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- apiConfiguration: {
|
|
|
- ...defaultProps.apiConfiguration,
|
|
|
- codebaseIndexOpenAiCompatibleModelDimension: undefined,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithEmptyDimension} />)
|
|
|
-
|
|
|
- const dimensionField = screen.getByPlaceholderText("Enter dimension (e.g., 1536)")
|
|
|
- expect(dimensionField).toHaveValue("")
|
|
|
- })
|
|
|
-
|
|
|
- it("should validate embedding dimension input accepts only positive numbers", async () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- const dimensionField = screen.getByPlaceholderText("Enter dimension (e.g., 1536)")
|
|
|
- expect(dimensionField).toBeDefined()
|
|
|
-
|
|
|
- // Test that the field is a text input (implementation uses text with validation logic)
|
|
|
- expect(dimensionField).toHaveAttribute("type", "text")
|
|
|
-
|
|
|
- // Test that invalid input (non-numeric) doesn't trigger setApiConfigurationField
|
|
|
- fireEvent.change(dimensionField!, { target: { value: "invalid" } })
|
|
|
-
|
|
|
- // The implementation only accepts valid numbers
|
|
|
- // Verify that setApiConfigurationField was not called with invalid string values
|
|
|
- expect(mockSetApiConfigurationField).not.toHaveBeenCalledWith(
|
|
|
- "codebaseIndexOpenAiCompatibleModelDimension",
|
|
|
- "invalid",
|
|
|
- )
|
|
|
-
|
|
|
- // Test that numeric values (including negative) are accepted by the current implementation
|
|
|
- fireEvent.change(dimensionField!, { target: { value: "-5" } })
|
|
|
- expect(mockSetApiConfigurationField).toHaveBeenCalledWith("codebaseIndexOpenAiCompatibleModelDimension", -5)
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Model Selection", () => {
|
|
|
- /**
|
|
|
- * Test conditional rendering of Model ID input based on provider type
|
|
|
- */
|
|
|
- describe("Conditional Model Input Rendering", () => {
|
|
|
- it("should render VSCodeTextField for Model ID when provider is openai-compatible", () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- codebaseIndexEmbedderModelId: "custom-model-id",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- // Should render VSCodeTextField for Model ID
|
|
|
- const modelTextFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- const modelIdField = modelTextFields.find(
|
|
|
- (field) => field.getAttribute("placeholder") === "Enter custom model ID",
|
|
|
- )
|
|
|
- expect(modelIdField).toBeInTheDocument()
|
|
|
- expect(modelIdField).toHaveValue("custom-model-id")
|
|
|
-
|
|
|
- // Should NOT render Select dropdown for models (only provider select should exist)
|
|
|
- const selectElements = screen.getAllByTestId("select")
|
|
|
- expect(selectElements).toHaveLength(1) // Only provider select, no model select
|
|
|
- })
|
|
|
-
|
|
|
- it("should render Select dropdown for models when provider is openai", () => {
|
|
|
- const propsWithOpenAI = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai" as const,
|
|
|
- codebaseIndexEmbedderModelId: "text-embedding-3-small",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAI} />)
|
|
|
-
|
|
|
- // Should render Select dropdown for models (second select element)
|
|
|
- const selectElements = screen.getAllByTestId("select")
|
|
|
- expect(selectElements).toHaveLength(2) // Provider and model selects
|
|
|
- const modelSelect = selectElements[1] // Model select is second
|
|
|
- expect(modelSelect).toHaveAttribute("data-value", "text-embedding-3-small")
|
|
|
-
|
|
|
- // Should NOT render VSCodeTextField for Model ID (only other text fields)
|
|
|
- const modelTextFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- const modelIdField = modelTextFields.find(
|
|
|
- (field) => field.getAttribute("placeholder") === "Enter custom model ID",
|
|
|
- )
|
|
|
- expect(modelIdField).toBeUndefined()
|
|
|
- })
|
|
|
-
|
|
|
- it("should render Select dropdown for models when provider is ollama", () => {
|
|
|
- const propsWithOllama = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexModels: {
|
|
|
- ...defaultProps.codebaseIndexModels,
|
|
|
- ollama: {
|
|
|
- llama2: { dimension: 4096 },
|
|
|
- codellama: { dimension: 4096 },
|
|
|
- },
|
|
|
- },
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "ollama" as const,
|
|
|
- codebaseIndexEmbedderModelId: "llama2",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOllama} />)
|
|
|
-
|
|
|
- // Should render Select dropdown for models (second select element)
|
|
|
- const selectElements = screen.getAllByTestId("select")
|
|
|
- expect(selectElements).toHaveLength(2) // Provider and model selects
|
|
|
- const modelSelect = selectElements[1] // Model select is second
|
|
|
- expect(modelSelect).toHaveAttribute("data-value", "llama2")
|
|
|
-
|
|
|
- // Should NOT render VSCodeTextField for Model ID
|
|
|
- const modelTextFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- const modelIdField = modelTextFields.find(
|
|
|
- (field) => field.getAttribute("placeholder") === "Enter custom model ID",
|
|
|
- )
|
|
|
- expect(modelIdField).toBeUndefined()
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- /**
|
|
|
- * Test VSCodeTextField interactions for OpenAI-Compatible provider
|
|
|
- */
|
|
|
- describe("VSCodeTextField for OpenAI-Compatible Model ID", () => {
|
|
|
- const openAICompatibleProps = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- codebaseIndexEmbedderModelId: "existing-model",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- it("should display current Model ID value in VSCodeTextField", () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
- expect(modelIdField).toHaveValue("existing-model")
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setCachedStateField when Model ID changes", async () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
-
|
|
|
- // Use fireEvent to trigger the change
|
|
|
- fireEvent.change(modelIdField, { target: { value: "new-model" } })
|
|
|
-
|
|
|
- // Check that setCachedStateField was called with codebaseIndexConfig
|
|
|
- expect(mockSetCachedStateField).toHaveBeenCalledWith(
|
|
|
- "codebaseIndexConfig",
|
|
|
- expect.objectContaining({
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible",
|
|
|
- codebaseIndexEnabled: true,
|
|
|
- codebaseIndexQdrantUrl: "http://localhost:6333",
|
|
|
- codebaseIndexEmbedderModelId: "new-model",
|
|
|
- }),
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it("should handle empty Model ID value", () => {
|
|
|
- const propsWithEmptyModelId = {
|
|
|
- ...openAICompatibleProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...openAICompatibleProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderModelId: "",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithEmptyModelId} />)
|
|
|
-
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
- expect(modelIdField).toHaveValue("")
|
|
|
- })
|
|
|
-
|
|
|
- it("should show placeholder text for Model ID input", () => {
|
|
|
- render(<CodeIndexSettings {...openAICompatibleProps} />)
|
|
|
-
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
- expect(modelIdField).toBeInTheDocument()
|
|
|
- expect(modelIdField).toHaveAttribute("placeholder", "Enter custom model ID")
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- /**
|
|
|
- * Test Select dropdown interactions for other providers
|
|
|
- */
|
|
|
- describe("Select Dropdown for Other Providers", () => {
|
|
|
- it("should show available models for OpenAI provider in dropdown", () => {
|
|
|
- const propsWithOpenAI = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAI} />)
|
|
|
-
|
|
|
- expect(screen.getByTestId("select-item-text-embedding-3-small")).toBeInTheDocument()
|
|
|
- expect(screen.getByTestId("select-item-text-embedding-3-large")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should show available models for Ollama provider in dropdown", () => {
|
|
|
- const propsWithOllama = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexModels: {
|
|
|
- ...defaultProps.codebaseIndexModels,
|
|
|
- ollama: {
|
|
|
- llama2: { dimension: 4096 },
|
|
|
- codellama: { dimension: 4096 },
|
|
|
- },
|
|
|
- },
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "ollama" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOllama} />)
|
|
|
-
|
|
|
- expect(screen.getByTestId("select-item-llama2")).toBeInTheDocument()
|
|
|
- expect(screen.getByTestId("select-item-codellama")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setCachedStateField when model is selected from dropdown", async () => {
|
|
|
- const user = userEvent.setup()
|
|
|
- const propsWithOpenAI = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAI} />)
|
|
|
-
|
|
|
- // Get all select elements and find the model select (second one)
|
|
|
- const selectElements = screen.getAllByTestId("select")
|
|
|
- const modelSelect = selectElements[1] // Provider is first, Model is second
|
|
|
- const selectButton = modelSelect.querySelector("button")
|
|
|
- expect(selectButton).toBeInTheDocument()
|
|
|
- await user.click(selectButton!)
|
|
|
-
|
|
|
- expect(mockSetCachedStateField).toHaveBeenCalledWith("codebaseIndexConfig", {
|
|
|
- ...propsWithOpenAI.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderModelId: "test-change",
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- it("should display current model selection in dropdown", () => {
|
|
|
- const propsWithSelectedModel = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai" as const,
|
|
|
- codebaseIndexEmbedderModelId: "text-embedding-3-large",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithSelectedModel} />)
|
|
|
-
|
|
|
- // Get all select elements and find the model select (second one)
|
|
|
- const selectElements = screen.getAllByTestId("select")
|
|
|
- const modelSelect = selectElements[1] // Provider is first, Model is second
|
|
|
- expect(modelSelect).toHaveAttribute("data-value", "text-embedding-3-large")
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- /**
|
|
|
- * Test fallback behavior for OpenAI-Compatible provider
|
|
|
- */
|
|
|
- describe("OpenAI-Compatible Provider Model Fallback", () => {
|
|
|
- it("should show available models for OpenAI Compatible provider", () => {
|
|
|
- const propsWithOpenAICompatible = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithOpenAICompatible} />)
|
|
|
-
|
|
|
- // Note: For openai-compatible, we render VSCodeTextField, not Select dropdown
|
|
|
- // But the component still uses availableModelIds for other purposes
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
- expect(modelIdField).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should fall back to OpenAI models when OpenAI Compatible models are not available", () => {
|
|
|
- const propsWithoutCompatibleModels = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexModels: {
|
|
|
- openai: {
|
|
|
- "text-embedding-3-small": { dimension: 1536 },
|
|
|
- "text-embedding-3-large": { dimension: 3072 },
|
|
|
- },
|
|
|
- },
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithoutCompatibleModels} />)
|
|
|
-
|
|
|
- // Should still render VSCodeTextField for openai-compatible provider
|
|
|
- const modelIdField = screen.getByPlaceholderText("Enter custom model ID")
|
|
|
- expect(modelIdField).toBeInTheDocument()
|
|
|
- })
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Form Validation", () => {
|
|
|
- it("should handle empty configuration gracefully", () => {
|
|
|
- const emptyProps = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: undefined,
|
|
|
- apiConfiguration: {},
|
|
|
- }
|
|
|
-
|
|
|
- expect(() => render(<CodeIndexSettings {...emptyProps} />)).not.toThrow()
|
|
|
- })
|
|
|
-
|
|
|
- it("should handle missing model configuration", () => {
|
|
|
- const propsWithoutModels = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexModels: undefined,
|
|
|
- }
|
|
|
-
|
|
|
- expect(() => render(<CodeIndexSettings {...propsWithoutModels} />)).not.toThrow()
|
|
|
- })
|
|
|
-
|
|
|
- it("should handle empty API configuration fields", () => {
|
|
|
- const propsWithEmptyConfig = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "openai-compatible" as const,
|
|
|
- },
|
|
|
- apiConfiguration: {
|
|
|
- codebaseIndexOpenAiCompatibleBaseUrl: "",
|
|
|
- codebaseIndexOpenAiCompatibleApiKey: "",
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithEmptyConfig} />)
|
|
|
-
|
|
|
- const textFields = screen.getAllByTestId("vscode-textfield")
|
|
|
- expect(textFields[0]).toHaveValue("")
|
|
|
- expect(textFields[1]).toHaveValue("")
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Integration", () => {
|
|
|
- it("should request indexing status on mount", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expect(vscode.postMessage).toHaveBeenCalledWith({
|
|
|
- type: "requestIndexingStatus",
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- it("should set up message listener for status updates", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expect(window.addEventListener).toHaveBeenCalledWith("message", expect.any(Function))
|
|
|
- })
|
|
|
-
|
|
|
- it("should clean up message listener on unmount", () => {
|
|
|
- const { unmount } = render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- unmount()
|
|
|
-
|
|
|
- expect(window.removeEventListener).toHaveBeenCalledWith("message", expect.any(Function))
|
|
|
- })
|
|
|
-
|
|
|
- /**
|
|
|
- * Test indexing status updates
|
|
|
- */
|
|
|
- it("should update indexing status when receiving status update message", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- // Get the message handler that was registered
|
|
|
- const messageHandler = (window.addEventListener as any).mock.calls.find(
|
|
|
- (call: any) => call[0] === "message",
|
|
|
- )?.[1]
|
|
|
-
|
|
|
- expect(messageHandler).toBeDefined()
|
|
|
-
|
|
|
- // Simulate receiving a status update message
|
|
|
- const mockEvent = {
|
|
|
- data: {
|
|
|
- type: "indexingStatusUpdate",
|
|
|
- values: {
|
|
|
- systemStatus: "Indexing",
|
|
|
- message: "Processing files...",
|
|
|
- processedItems: 50,
|
|
|
- totalItems: 100,
|
|
|
- currentItemUnit: "files",
|
|
|
- },
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- messageHandler(mockEvent)
|
|
|
-
|
|
|
- // Check that the status indicator shows "Indexing"
|
|
|
- expect(screen.getByText(/Indexing/)).toBeInTheDocument()
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Search Minimum Score Slider", () => {
|
|
|
- const expandAdvancedConfig = () => {
|
|
|
- const advancedButton = screen.getByRole("button", { name: /Advanced Configuration/i })
|
|
|
- fireEvent.click(advancedButton)
|
|
|
- }
|
|
|
-
|
|
|
- it("should render advanced configuration toggle button", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expect(screen.getByRole("button", { name: /Advanced Configuration/i })).toBeInTheDocument()
|
|
|
- expect(screen.getByText("Advanced Configuration")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should render search minimum score slider with reset button when expanded", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expandAdvancedConfig()
|
|
|
-
|
|
|
- expect(screen.getByTestId("search-min-score-slider")).toBeInTheDocument()
|
|
|
- expect(screen.getByTestId("search-min-score-reset-button")).toBeInTheDocument()
|
|
|
- expect(screen.getByText("Search Score Threshold")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should display current search minimum score value when expanded", () => {
|
|
|
- const propsWithScore = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexSearchMinScore: 0.65,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithScore} />)
|
|
|
-
|
|
|
- expandAdvancedConfig()
|
|
|
-
|
|
|
- expect(screen.getByText("0.65")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should call setCachedStateField when slider value changes", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- expandAdvancedConfig()
|
|
|
-
|
|
|
- const slider = screen.getByTestId("search-min-score-slider")
|
|
|
- fireEvent.change(slider, { target: { value: "0.8" } })
|
|
|
-
|
|
|
- expect(mockSetCachedStateField).toHaveBeenCalledWith("codebaseIndexConfig", {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexSearchMinScore: 0.8,
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- it("should reset to default value when reset button is clicked", () => {
|
|
|
- const propsWithScore = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexSearchMinScore: 0.8,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithScore} />)
|
|
|
-
|
|
|
- expandAdvancedConfig()
|
|
|
-
|
|
|
- const resetButton = screen.getByTestId("search-min-score-reset-button")
|
|
|
- fireEvent.click(resetButton)
|
|
|
-
|
|
|
- expect(mockSetCachedStateField).toHaveBeenCalledWith("codebaseIndexConfig", {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexSearchMinScore: 0.4,
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- it("should use default value when no score is set", () => {
|
|
|
- const propsWithoutScore = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexSearchMinScore: undefined,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- render(<CodeIndexSettings {...propsWithoutScore} />)
|
|
|
-
|
|
|
- expandAdvancedConfig()
|
|
|
-
|
|
|
- expect(screen.getByText("0.40")).toBeInTheDocument()
|
|
|
- })
|
|
|
-
|
|
|
- it("should toggle advanced section visibility", () => {
|
|
|
- render(<CodeIndexSettings {...defaultProps} />)
|
|
|
-
|
|
|
- // Initially collapsed - should not see slider
|
|
|
- expect(screen.queryByTestId("search-min-score-slider")).not.toBeInTheDocument()
|
|
|
-
|
|
|
- // Expand advanced section
|
|
|
- expandAdvancedConfig()
|
|
|
- expect(screen.getByTestId("search-min-score-slider")).toBeInTheDocument()
|
|
|
-
|
|
|
- // Collapse again
|
|
|
- expandAdvancedConfig()
|
|
|
- expect(screen.queryByTestId("search-min-score-slider")).not.toBeInTheDocument()
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- describe("Error Handling", () => {
|
|
|
- it("should handle invalid provider gracefully", () => {
|
|
|
- const propsWithInvalidProvider = {
|
|
|
- ...defaultProps,
|
|
|
- codebaseIndexConfig: {
|
|
|
- ...defaultProps.codebaseIndexConfig,
|
|
|
- codebaseIndexEmbedderProvider: "invalid-provider" as any,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- expect(() => render(<CodeIndexSettings {...propsWithInvalidProvider} />)).not.toThrow()
|
|
|
- })
|
|
|
-
|
|
|
- it("should handle missing translation keys gracefully", () => {
|
|
|
- // Mock translation function to return undefined for some keys
|
|
|
- vi.doMock("@src/i18n/TranslationContext", () => ({
|
|
|
- useAppTranslation: () => ({
|
|
|
- t: (key: string) => (key.includes("missing") ? undefined : key),
|
|
|
- }),
|
|
|
- }))
|
|
|
-
|
|
|
- expect(() => render(<CodeIndexSettings {...defaultProps} />)).not.toThrow()
|
|
|
- })
|
|
|
- })
|
|
|
-})
|