mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-27 05:37:16 +08:00
fix: Prevent crash on invalid replacement components (#10110)
* ✨ (NodeLegacyComponent): Improve rendering logic to only display components with valid replacements 🐛 (use-get-replacement-components): Update data access to handle potential undefined values 📝 (general-bugs-frontend-crashing-on-invalid-replace.spec.ts): Add test case for handling undefined replacements in custom components ♻️ (local_db.py): Remove unnecessary replacement values from LocalDBComponent * ✅ (fileUploadComponent.spec.ts): update test for file types visibility to use async/await syntax for better readability and maintainability * ✨ (userSettings.spec.ts): refactor tests to use a helper function awaitBootstrapTest for code reusability and to skip modal popups before interacting with the page. * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) --------- Co-authored-by: Carlos Coelho <80289056+carlosrcoelho@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
67960cf044
commit
4dd5bf3009
@ -23,6 +23,7 @@ export default function NodeLegacyComponent({
|
||||
};
|
||||
|
||||
const foundComponents = useGetReplacementComponents(replacement);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -48,20 +49,27 @@ export default function NodeLegacyComponent({
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-mmd text-muted-foreground w-full">
|
||||
{replacement && Array.isArray(replacement) && replacement.length > 0 ? (
|
||||
{replacement &&
|
||||
Array.isArray(replacement) &&
|
||||
replacement.length > 0 &&
|
||||
foundComponents.some((component) => component) ? (
|
||||
<span className="block items-center">
|
||||
Use{" "}
|
||||
{foundComponents.map((component, index) => (
|
||||
<>
|
||||
{index > 0 && ", "}
|
||||
<Button
|
||||
variant="link"
|
||||
className=" !text-accent-pink-foreground !text-mmd !inline-block"
|
||||
size={null}
|
||||
onClick={() => handleFilterComponent(replacement[index])}
|
||||
>
|
||||
<span>{component}</span>
|
||||
</Button>
|
||||
{component && (
|
||||
<>
|
||||
{index > 0 && ", "}
|
||||
<Button
|
||||
variant="link"
|
||||
className=" !text-accent-pink-foreground !text-mmd !inline-block"
|
||||
size={null}
|
||||
onClick={() => handleFilterComponent(replacement[index])}
|
||||
>
|
||||
<span>{component}</span>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
.
|
||||
|
||||
@ -11,7 +11,7 @@ export const useGetReplacementComponents = (replacement?: string[]) => {
|
||||
return (
|
||||
categoryName &&
|
||||
componentName &&
|
||||
data[categoryName][componentName].display_name
|
||||
data[categoryName]?.[componentName]?.display_name
|
||||
);
|
||||
})
|
||||
: [];
|
||||
|
||||
@ -14,7 +14,9 @@ test(
|
||||
{ tag: ["@release"] },
|
||||
|
||||
async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, {
|
||||
skipModal: true,
|
||||
});
|
||||
await page.waitForSelector('[data-testid="mainpage_title"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
@ -42,7 +44,9 @@ test(
|
||||
const randomName2 = Math.random().toString(36).substring(2);
|
||||
const randomName3 = Math.random().toString(36).substring(2);
|
||||
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, {
|
||||
skipModal: true,
|
||||
});
|
||||
await page.getByTestId("user-profile-settings").click();
|
||||
await page.getByText("Settings").click();
|
||||
await page.getByText("Global Variables").click();
|
||||
@ -134,7 +138,9 @@ test(
|
||||
);
|
||||
|
||||
test("should see shortcuts", { tag: ["@release"] }, async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, {
|
||||
skipModal: true,
|
||||
});
|
||||
await page.waitForSelector('[data-testid="mainpage_title"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
@ -172,7 +178,9 @@ test(
|
||||
"should interact with API Keys",
|
||||
{ tag: ["@release", "@api"] },
|
||||
async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, {
|
||||
skipModal: true,
|
||||
});
|
||||
await page.getByTestId("user-profile-settings").click();
|
||||
await page.getByText("Settings").click();
|
||||
await page.getByText("Langflow API").first().click();
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
import { expect, test } from "../../fixtures";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test(
|
||||
"user must be able to use a component with undefined replacement",
|
||||
{
|
||||
tag: ["@release"],
|
||||
},
|
||||
async ({ page }) => {
|
||||
await awaitBootstrapTest(page);
|
||||
|
||||
await page.waitForSelector('[data-testid="blank-flow"]', {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page.getByTestId("blank-flow").click();
|
||||
|
||||
await page.getByTestId("sidebar-custom-component-button").click();
|
||||
|
||||
await page.getByTestId("title-Custom Component").click();
|
||||
|
||||
await page.getByTestId("code-button-modal").last().click();
|
||||
|
||||
const problematicCode = `
|
||||
# from lfx.field_typing import Data
|
||||
from lfx.custom.custom_component.component import Component
|
||||
from lfx.io import MessageTextInput, Output
|
||||
from lfx.schema.data import Data
|
||||
|
||||
|
||||
class CustomComponent(Component):
|
||||
display_name = "Custom Component"
|
||||
description = "Use as a template to create your own component."
|
||||
documentation: str = "https://docs.langflow.org/components-custom-components"
|
||||
icon = "code"
|
||||
name = "CustomComponent"
|
||||
replacement = ["knowledgebases.KnowledgeRetrieval", "knowledgebases.KnowledgeIngestion", "THISISNOTEXISTING.COMPONENT"] # This line was causing the crash
|
||||
inputs = [
|
||||
MessageTextInput(
|
||||
name="input_value",
|
||||
display_name="Input Value",
|
||||
info="This is a custom component Input",
|
||||
value="Hello, World!",
|
||||
tool_mode=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Output", name="output", method="build_output"),
|
||||
]
|
||||
|
||||
def build_output(self) -> Data:
|
||||
data = Data(value=self.input_value)
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
`;
|
||||
|
||||
await page.locator(".ace_content").click();
|
||||
await page.keyboard.press(`ControlOrMeta+A`);
|
||||
await page.locator("textarea").fill(problematicCode);
|
||||
|
||||
await page.getByText("Check & Save").last().click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector("text=No direct replacement", {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
const numberOfDirectReplacementText = await page
|
||||
.getByText("No direct replacement")
|
||||
.count();
|
||||
expect(numberOfDirectReplacementText).toBe(1);
|
||||
},
|
||||
);
|
||||
@ -22,7 +22,6 @@ class LocalDBComponent(LCVectorStoreComponent):
|
||||
name = "LocalDB"
|
||||
icon = "database"
|
||||
legacy = True
|
||||
replacement = ["knowledgebases.KnowledgeRetrieval", "knowledgebases.KnowledgeIngestion"]
|
||||
|
||||
inputs = [
|
||||
TabInput(
|
||||
|
||||
Reference in New Issue
Block a user