From df985fcece04dac43561e02b2b35909817a11757 Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 16 Jun 2025 23:14:59 -0700 Subject: [PATCH 01/11] Start converting webview-ui tests to vitest --- webview-ui/jest.config.cjs | 2 +- webview-ui/package.json | 5 +- ...View.spec.tsx => MarketplaceView.test.tsx} | 0 ...{ModesView.test.tsx => ModesView.spec.tsx} | 48 +++++++---- ...ger.test.tsx => ApiConfigManager.spec.tsx} | 18 ++--- ...piOptions.test.tsx => ApiOptions.spec.tsx} | 34 ++++---- ...tsx => ContextManagementSettings.spec.tsx} | 81 +++++++++++-------- ...elPicker.test.tsx => ModelPicker.spec.tsx} | 12 +-- ...l.test.tsx => TemperatureControl.spec.tsx} | 45 ++++++++--- ...udget.test.tsx => ThinkingBudget.spec.tsx} | 14 ++-- ...ble.spec.tsx => OpenAICompatible.test.tsx} | 0 ...Model.test.ts => useSelectedModel.spec.ts} | 13 ++- ...est.tsx => ExtensionStateContext.spec.tsx} | 4 +- ...ion.test.ts => command-validation.spec.ts} | 2 +- .../{format.test.ts => format.spec.ts} | 2 +- ...odel-utils.test.ts => model-utils.spec.ts} | 2 +- webview-ui/vitest.config.ts | 19 +++++ webview-ui/vitest.setup.ts | 24 ++++++ 18 files changed, 208 insertions(+), 117 deletions(-) rename webview-ui/src/components/marketplace/__tests__/{MarketplaceView.spec.tsx => MarketplaceView.test.tsx} (100%) rename webview-ui/src/components/modes/__tests__/{ModesView.test.tsx => ModesView.spec.tsx} (80%) rename webview-ui/src/components/settings/__tests__/{ApiConfigManager.test.tsx => ApiConfigManager.spec.tsx} (96%) rename webview-ui/src/components/settings/__tests__/{ApiOptions.test.tsx => ApiOptions.spec.tsx} (94%) rename webview-ui/src/components/settings/__tests__/{ContextManagementSettings.test.tsx => ContextManagementSettings.spec.tsx} (91%) rename webview-ui/src/components/settings/__tests__/{ModelPicker.test.tsx => ModelPicker.spec.tsx} (93%) rename webview-ui/src/components/settings/__tests__/{TemperatureControl.test.tsx => TemperatureControl.spec.tsx} (69%) rename webview-ui/src/components/settings/__tests__/{ThinkingBudget.test.tsx => ThinkingBudget.spec.tsx} (91%) rename webview-ui/src/components/settings/providers/__tests__/{OpenAICompatible.spec.tsx => OpenAICompatible.test.tsx} (100%) rename webview-ui/src/components/ui/hooks/__tests__/{useSelectedModel.test.ts => useSelectedModel.spec.ts} (97%) rename webview-ui/src/context/__tests__/{ExtensionStateContext.test.tsx => ExtensionStateContext.spec.tsx} (98%) rename webview-ui/src/utils/__tests__/{command-validation.test.ts => command-validation.spec.ts} (99%) rename webview-ui/src/utils/__tests__/{format.test.ts => format.spec.ts} (97%) rename webview-ui/src/utils/__tests__/{model-utils.test.ts => model-utils.spec.ts} (96%) create mode 100644 webview-ui/vitest.config.ts create mode 100644 webview-ui/vitest.setup.ts diff --git a/webview-ui/jest.config.cjs b/webview-ui/jest.config.cjs index 367bbff3c7..f11b69a401 100644 --- a/webview-ui/jest.config.cjs +++ b/webview-ui/jest.config.cjs @@ -5,7 +5,7 @@ module.exports = { injectGlobals: true, moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], transform: { "^.+\\.(ts|tsx)$": ["ts-jest", { tsconfig: { jsx: "react-jsx", module: "ESNext" } }] }, - testMatch: ["/src/**/__tests__/**/*.{js,jsx,ts,tsx}", "/src/**/*.{spec,test}.{js,jsx,ts,tsx}"], + testMatch: ["/src/**/*.test.{js,jsx,ts,tsx}"], setupFilesAfterEnv: ["/src/setupTests.tsx"], moduleNameMapper: { "\\.(css|less|scss|sass)$": "identity-obj-proxy", diff --git a/webview-ui/package.json b/webview-ui/package.json index ee7b2e01c4..f7536de930 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -6,7 +6,8 @@ "lint": "eslint src --ext=ts,tsx --max-warnings=0", "check-types": "tsc", "pretest": "turbo run bundle --cwd ..", - "test": "jest -w=40%", + "test": "jest -w=40% && vitest run", + "test:vitest": "vitest run", "format": "prettier --write src", "dev": "vite", "build": "tsc -b && vite build", @@ -89,10 +90,12 @@ "@types/testing-library__jest-dom": "^5.14.5", "@types/vscode-webview": "^1.57.5", "@vitejs/plugin-react": "^4.3.4", + "@vitest/ui": "^3.2.3", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jest-simple-dot-reporter": "^1.0.5", + "jsdom": "^26.0.0", "ts-jest": "^29.2.5", "typescript": "5.8.3", "vite": "6.3.5", diff --git a/webview-ui/src/components/marketplace/__tests__/MarketplaceView.spec.tsx b/webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx similarity index 100% rename from webview-ui/src/components/marketplace/__tests__/MarketplaceView.spec.tsx rename to webview-ui/src/components/marketplace/__tests__/MarketplaceView.test.tsx diff --git a/webview-ui/src/components/modes/__tests__/ModesView.test.tsx b/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx similarity index 80% rename from webview-ui/src/components/modes/__tests__/ModesView.test.tsx rename to webview-ui/src/components/modes/__tests__/ModesView.spec.tsx index ea21c72410..7fd6bb25b1 100644 --- a/webview-ui/src/components/modes/__tests__/ModesView.test.tsx +++ b/webview-ui/src/components/modes/__tests__/ModesView.spec.tsx @@ -1,4 +1,4 @@ -// npx jest src/components/prompts/__tests__/PromptsView.test.tsx +// npx vitest src/components/modes/__tests__/ModesView.spec.tsx import { render, screen, fireEvent, waitFor } from "@testing-library/react" import ModesView from "../ModesView" @@ -6,9 +6,9 @@ import { ExtensionStateContext } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" // Mock vscode API -jest.mock("@src/utils/vscode", () => ({ +vitest.mock("@src/utils/vscode", () => ({ vscode: { - postMessage: jest.fn(), + postMessage: vitest.fn(), }, })) @@ -19,17 +19,17 @@ const mockExtensionState = { { id: "config2", name: "Config 2" }, ], enhancementApiConfigId: "", - setEnhancementApiConfigId: jest.fn(), + setEnhancementApiConfigId: vitest.fn(), mode: "code", customModes: [], customSupportPrompts: [], currentApiConfigName: "", customInstructions: "Initial instructions", - setCustomInstructions: jest.fn(), + setCustomInstructions: vitest.fn(), } const renderPromptsView = (props = {}) => { - const mockOnDone = jest.fn() + const mockOnDone = vitest.fn() return render( @@ -45,11 +45,11 @@ class MockResizeObserver { global.ResizeObserver = MockResizeObserver -Element.prototype.scrollIntoView = jest.fn() +Element.prototype.scrollIntoView = vitest.fn() describe("PromptsView", () => { beforeEach(() => { - jest.clearAllMocks() + vitest.clearAllMocks() }) it("displays the current mode name in the select trigger", () => { @@ -105,10 +105,18 @@ describe("PromptsView", () => { // Get the textarea const textarea = await waitFor(() => screen.getByTestId("code-prompt-textarea")) - fireEvent.change(textarea, { - target: { value: "New prompt value" }, + + // Simulate VSCode TextArea change event + const changeEvent = new CustomEvent("change", { + detail: { + target: { + value: "New prompt value", + }, + }, }) + fireEvent(textarea, changeEvent) + expect(vscode.postMessage).toHaveBeenCalledWith({ type: "updatePrompt", promptMode: "code", @@ -128,7 +136,7 @@ describe("PromptsView", () => { const { unmount } = render( - + , ) @@ -151,7 +159,7 @@ describe("PromptsView", () => { render( - + , ) @@ -160,7 +168,7 @@ describe("PromptsView", () => { }) it("handles clearing custom instructions correctly", async () => { - const setCustomInstructions = jest.fn() + const setCustomInstructions = vitest.fn() renderPromptsView({ ...mockExtensionState, customInstructions: "Initial instructions", @@ -168,10 +176,20 @@ describe("PromptsView", () => { }) const textarea = screen.getByTestId("global-custom-instructions-textarea") - fireEvent.change(textarea, { - target: { value: "" }, + + // Simulate VSCode TextArea change event with empty value + // We need to simulate both the CustomEvent format and regular event format + // since the component handles both + Object.defineProperty(textarea, "value", { + writable: true, + value: "", }) + const changeEvent = new Event("change", { bubbles: true }) + fireEvent(textarea, changeEvent) + + // The component calls setCustomInstructions with value || undefined + // Since empty string is falsy, it should be undefined expect(setCustomInstructions).toHaveBeenCalledWith(undefined) expect(vscode.postMessage).toHaveBeenCalledWith({ type: "customInstructions", diff --git a/webview-ui/src/components/settings/__tests__/ApiConfigManager.test.tsx b/webview-ui/src/components/settings/__tests__/ApiConfigManager.spec.tsx similarity index 96% rename from webview-ui/src/components/settings/__tests__/ApiConfigManager.test.tsx rename to webview-ui/src/components/settings/__tests__/ApiConfigManager.spec.tsx index 37cbdabcda..553f60c79e 100644 --- a/webview-ui/src/components/settings/__tests__/ApiConfigManager.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiConfigManager.spec.tsx @@ -1,11 +1,11 @@ -// npx jest src/components/settings/__tests__/ApiConfigManager.test.tsx +// npx vitest src/components/settings/__tests__/ApiConfigManager.spec.tsx import { render, screen, fireEvent, within } from "@testing-library/react" import ApiConfigManager from "../ApiConfigManager" // Mock VSCode components -jest.mock("@vscode/webview-ui-toolkit/react", () => ({ +vitest.mock("@vscode/webview-ui-toolkit/react", () => ({ VSCodeTextField: ({ value, onInput, placeholder, onKeyDown, "data-testid": dataTestId }: any) => ( ({ ), })) -jest.mock("@/components/ui", () => ({ - ...jest.requireActual("@/components/ui"), +vitest.mock("@/components/ui", () => ({ + ...vitest.importActual("@/components/ui"), Dialog: ({ children, open }: any) => (
{children} @@ -91,10 +91,10 @@ jest.mock("@/components/ui", () => ({ })) describe("ApiConfigManager", () => { - const mockOnSelectConfig = jest.fn() - const mockOnDeleteConfig = jest.fn() - const mockOnRenameConfig = jest.fn() - const mockOnUpsertConfig = jest.fn() + const mockOnSelectConfig = vitest.fn() + const mockOnDeleteConfig = vitest.fn() + const mockOnRenameConfig = vitest.fn() + const mockOnUpsertConfig = vitest.fn() const defaultProps = { currentApiConfigName: "Default Config", @@ -109,7 +109,7 @@ describe("ApiConfigManager", () => { } beforeEach(() => { - jest.clearAllMocks() + vitest.clearAllMocks() }) const getRenameForm = () => screen.getByTestId("rename-form") diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.spec.tsx similarity index 94% rename from webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx rename to webview-ui/src/components/settings/__tests__/ApiOptions.spec.tsx index 17421d3960..31cc2ec82f 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.spec.tsx @@ -1,4 +1,4 @@ -// npx jest src/components/settings/__tests__/ApiOptions.test.tsx +// npx vitest src/components/settings/__tests__/ApiOptions.spec.tsx import { render, screen, fireEvent } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" @@ -10,7 +10,7 @@ import { ExtensionStateContextProvider } from "@src/context/ExtensionStateContex import ApiOptions, { ApiOptionsProps } from "../ApiOptions" // Mock VSCode components -jest.mock("@vscode/webview-ui-toolkit/react", () => ({ +vi.mock("@vscode/webview-ui-toolkit/react", () => ({ VSCodeTextField: ({ children, value, onBlur }: any) => (
{children} @@ -24,7 +24,7 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({ })) // Mock other components -jest.mock("vscrui", () => ({ +vi.mock("vscrui", () => ({ Checkbox: ({ children, checked, onChange }: any) => (