fix: Raw input value leaks into Global Variables dropdown list (#12660)

* Variable input shown in dropdown

* added testcases

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Olayinka Adelakun <olayinkaadelakun@Olayinkas-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
olayinkaadelakun
2026-04-13 13:39:49 -04:00
committed by GitHub
parent 349e78c758
commit 7e786a728f
2 changed files with 117 additions and 9 deletions

View File

@ -2,6 +2,7 @@ import { render, waitFor } from "@testing-library/react";
import InputGlobalComponent from "..";
const mockUseGetGlobalVariables = jest.fn();
const mockInputComponent = jest.fn().mockReturnValue(null);
jest.mock("@/controllers/API/queries/variables", () => ({
useGetGlobalVariables: () => mockUseGetGlobalVariables(),
@ -32,7 +33,10 @@ jest.mock(
"@/components/core/parameterRenderComponent/components/inputComponent",
() => ({
__esModule: true,
default: () => null,
default: (props: Record<string, unknown>) => {
mockInputComponent(props);
return null;
},
}),
);
@ -104,4 +108,112 @@ describe("InputGlobalComponent", () => {
expect(handleOnNewValue).not.toHaveBeenCalled();
});
});
describe("options passed to InputComponent", () => {
const configuredVariables = [
{ name: "MY_API_KEY" },
{ name: "ANOTHER_VAR" },
];
beforeEach(() => {
mockUseGetGlobalVariables.mockReturnValue({
data: configuredVariables,
isFetchedAfterMount: true,
isFetching: false,
isSuccess: true,
});
});
const getRenderedOptions = (): string[] =>
mockInputComponent.mock.calls[mockInputComponent.mock.calls.length - 1][0]
.options as string[];
it("does not add typed camelCase text to the dropdown", () => {
render(
<InputGlobalComponent
id="test"
value="invalidKey"
display_name="API Key"
handleOnNewValue={handleOnNewValue}
load_from_db={false}
password={true}
editNode={false}
disabled={false}
/>,
);
expect(getRenderedOptions()).not.toContain("invalidKey");
expect(getRenderedOptions()).toEqual(["MY_API_KEY", "ANOTHER_VAR"]);
});
it("does not add SCREAMING_SNAKE_CASE typed text to the dropdown", () => {
render(
<InputGlobalComponent
id="test"
value="OPENAI_API_KEY"
display_name="API Key"
handleOnNewValue={handleOnNewValue}
load_from_db={false}
password={true}
editNode={false}
disabled={false}
/>,
);
expect(getRenderedOptions()).not.toContain("OPENAI_API_KEY");
expect(getRenderedOptions()).toEqual(["MY_API_KEY", "ANOTHER_VAR"]);
});
it("shows only configured global variables when the field is not a password field", () => {
render(
<InputGlobalComponent
id="test"
value="SOME_TYPED_VALUE"
display_name="Some Field"
handleOnNewValue={handleOnNewValue}
load_from_db={false}
password={false}
editNode={false}
disabled={false}
/>,
);
expect(getRenderedOptions()).toEqual(["MY_API_KEY", "ANOTHER_VAR"]);
});
it("temporarily includes an orphaned variable reference while it is being cleared", () => {
render(
<InputGlobalComponent
id="test"
value="DELETED_VAR"
display_name="API Key"
handleOnNewValue={handleOnNewValue}
load_from_db={true}
password={false}
editNode={false}
disabled={false}
/>,
);
expect(getRenderedOptions()).toContain("DELETED_VAR");
});
it("does not duplicate a variable that already exists in the configured list", () => {
render(
<InputGlobalComponent
id="test"
value="MY_API_KEY"
display_name="API Key"
handleOnNewValue={handleOnNewValue}
load_from_db={true}
password={false}
editNode={false}
disabled={false}
/>,
);
const options = getRenderedOptions();
expect(options.filter((o) => o === "MY_API_KEY")).toHaveLength(1);
});
});
});

View File

@ -1,7 +1,6 @@
import { useEffect } from "react";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import GeneralDeleteConfirmationModal from "@/shared/components/delete-confirmation-modal";
import { looksLikeVariableName } from "../../../../../utils/reactflowUtils";
import { cn } from "../../../../../utils/utils";
import ForwardedIconComponent from "../../../../common/genericIconComponent";
import { CommandItem } from "../../../../ui/command";
@ -143,14 +142,11 @@ export default function InputGlobalComponent({
let variableOptions = typedGlobalVariables.map((variable) => variable.name);
const isEnvVarName =
password && currentValue && looksLikeVariableName(currentValue);
if (
(loadFromDb &&
currentValue &&
!valueExists &&
!variableOptions.includes(currentValue)) ||
(isEnvVarName && !variableOptions.includes(currentValue))
loadFromDb &&
currentValue &&
!valueExists &&
!variableOptions.includes(currentValue)
) {
variableOptions = [...variableOptions, currentValue];
}