diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index be74ae6bb4..b971e73834 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -107,6 +107,7 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({ awsSecretKey: z.string().optional(), awsSessionToken: z.string().optional(), awsRegion: z.string().optional(), + awsCustomRegion: z.string().optional(), awsUseCrossRegionInference: z.boolean().optional(), awsUsePromptCache: z.boolean().optional(), awsProfile: z.string().optional(), diff --git a/packages/types/src/providers/bedrock.ts b/packages/types/src/providers/bedrock.ts index 58e860dd94..be686ce844 100644 --- a/packages/types/src/providers/bedrock.ts +++ b/packages/types/src/providers/bedrock.ts @@ -405,4 +405,10 @@ export const BEDROCK_REGIONS = [ { value: "sa-east-1", label: "sa-east-1" }, { value: "us-gov-east-1", label: "us-gov-east-1" }, { value: "us-gov-west-1", label: "us-gov-west-1" }, -].sort((a, b) => a.value.localeCompare(b.value)) + { value: "custom", label: "Custom region..." }, +].sort((a, b) => { + // Keep "Custom region..." at the end + if (a.value === "custom") return 1 + if (b.value === "custom") return -1 + return a.value.localeCompare(b.value) +}) diff --git a/webview-ui/src/components/settings/providers/Bedrock.tsx b/webview-ui/src/components/settings/providers/Bedrock.tsx index 1839298f9b..86bb225111 100644 --- a/webview-ui/src/components/settings/providers/Bedrock.tsx +++ b/webview-ui/src/components/settings/providers/Bedrock.tsx @@ -18,12 +18,18 @@ type BedrockProps = { export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: BedrockProps) => { const { t } = useAppTranslation() const [awsEndpointSelected, setAwsEndpointSelected] = useState(!!apiConfiguration?.awsBedrockEndpointEnabled) + const [customRegionSelected, setCustomRegionSelected] = useState(apiConfiguration?.awsRegion === "custom") // Update the endpoint enabled state when the configuration changes useEffect(() => { setAwsEndpointSelected(!!apiConfiguration?.awsBedrockEndpointEnabled) }, [apiConfiguration?.awsBedrockEndpointEnabled]) + // Update the custom region state when the configuration changes + useEffect(() => { + setCustomRegionSelected(apiConfiguration?.awsRegion === "custom") + }, [apiConfiguration?.awsRegion]) + const handleInputChange = useCallback( ( field: K, @@ -89,7 +95,14 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo + {customRegionSelected && ( + <> + +
+ {t("settings:providers.awsCustomRegion.examples")} +
• us-west-3
+
• eu-central-3
+
• ap-southeast-3
+
+ + )} diff --git a/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx index b5bb16e975..80c1c4fd90 100644 --- a/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx @@ -425,4 +425,215 @@ describe("Bedrock Component", () => { expect(screen.getByTestId("vpc-endpoint-input")).toHaveValue("https://updated-endpoint.aws.com") }) }) + + // Test Scenario 6: Custom Region Tests + describe("Custom Region", () => { + it("should show custom region input when 'Custom region...' is selected", () => { + const apiConfiguration: Partial = { + awsRegion: "", + awsUseProfile: true, + } + + render( + , + ) + + // Custom region input should not be visible initially + expect(screen.queryByTestId("custom-region-input")).not.toBeInTheDocument() + + // Mock the Select component to simulate selecting "custom" + // Since we're mocking the Select component, we need to simulate the onValueChange call + const selectComponent = screen.getByRole("combobox", { hidden: true }) + if (selectComponent) { + // Simulate selecting "custom" region + const onValueChange = (selectComponent as any).onValueChange || (() => {}) + onValueChange("custom") + } + + // Verify that setApiConfigurationField was called with "custom" + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsRegion", "custom") + }) + + it("should hide custom region input when switching from custom to standard region", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "us-west-3", + awsUseProfile: true, + } + + render( + , + ) + + // Custom region input should be visible initially + expect(screen.getByTestId("custom-region-input")).toBeInTheDocument() + expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3") + + // Mock selecting a standard region + const selectComponent = screen.getByRole("combobox", { hidden: true }) + if (selectComponent) { + const onValueChange = (selectComponent as any).onValueChange || (() => {}) + onValueChange("us-east-1") + } + + // Verify that both awsRegion and awsCustomRegion were updated + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsRegion", "us-east-1") + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "") + }) + + it("should handle custom region input changes", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "", + awsUseProfile: true, + } + + render( + , + ) + + // Find the custom region input field + const customRegionInput = screen.getByTestId("custom-region-input") + expect(customRegionInput).toBeInTheDocument() + + // Enter a custom region + fireEvent.change(customRegionInput, { target: { value: "us-west-3" } }) + + // Verify the configuration field was updated + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "us-west-3") + }) + + it("should display example regions when custom region is selected", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "us-west-3", + awsUseProfile: true, + } + + render( + , + ) + + // Check that the custom region input is visible + expect(screen.getByTestId("custom-region-input")).toBeInTheDocument() + + // Check for the example regions section + expect(screen.getByText("settings:providers.awsCustomRegion.examples")).toBeInTheDocument() + expect(screen.getByText("• us-west-3")).toBeInTheDocument() + expect(screen.getByText("• eu-central-3")).toBeInTheDocument() + expect(screen.getByText("• ap-southeast-3")).toBeInTheDocument() + }) + + it("should preserve custom region value when toggling between custom and standard regions", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "us-west-3", + awsUseProfile: true, + } + + const { rerender } = render( + , + ) + + // Initial state: custom region selected with value + expect(screen.getByTestId("custom-region-input")).toBeInTheDocument() + expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3") + + // Switch to standard region + const updatedConfig: Partial = { + awsRegion: "us-east-1", + awsCustomRegion: "", // This would be cleared by the component logic + awsUseProfile: true, + } + + rerender( + , + ) + + // Custom region input should be hidden + expect(screen.queryByTestId("custom-region-input")).not.toBeInTheDocument() + + // Switch back to custom region with preserved value + const backToCustomConfig: Partial = { + awsRegion: "custom", + awsCustomRegion: "us-west-3", // Value preserved in parent state + awsUseProfile: true, + } + + rerender( + , + ) + + // Custom region input should be visible again with preserved value + expect(screen.getByTestId("custom-region-input")).toBeInTheDocument() + expect(screen.getByTestId("custom-region-input")).toHaveValue("us-west-3") + }) + + it("should handle empty custom region input", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "us-west-3", + awsUseProfile: true, + } + + render( + , + ) + + // Find the custom region input field + const customRegionInput = screen.getByTestId("custom-region-input") + + // Clear the field + fireEvent.change(customRegionInput, { target: { value: "" } }) + + // Verify the configuration field was updated with empty string + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("awsCustomRegion", "") + }) + + it("should initialize with correct state when custom region is pre-selected", () => { + const apiConfiguration: Partial = { + awsRegion: "custom", + awsCustomRegion: "eu-central-3", + awsUseProfile: true, + } + + render( + , + ) + + // Verify custom region input is visible and has correct value + expect(screen.getByTestId("custom-region-input")).toBeInTheDocument() + expect(screen.getByTestId("custom-region-input")).toHaveValue("eu-central-3") + + // Verify examples are shown + expect(screen.getByText("settings:providers.awsCustomRegion.examples")).toBeInTheDocument() + }) + }) })