feat: add breaking change update modal, refactor dismissed updates (#7882)

* fix: add optional method property to OutputFieldType

* feat: Enhance GenericNode with breaking change detection

- Added state management for breaking changes in GenericNode.
- Updated useCheckCodeValidity hook to evaluate breaking changes based on outputs and template keys.
- Improved node status color logic to reflect breaking changes and outdated code.
- Enhanced UI feedback for users with appropriate alerts and dismiss options.

* refactor: Improve breaking change handling in useCheckCodeValidity hook

- Simplified logic for detecting breaking changes and outdated code.
- Updated state management to ensure accurate status updates based on user inputs and templates.
- Enhanced readability by consolidating related checks into a single conditional structure.

* Fix outdated check

* Componentized breaking change

* Updated design of update handle on node

* Added small-update to modal sizes

* updated duplicate flow hook to duplicate just a flow

* Added update component modal with updating for single component

* Added new duplicateFlow on dropdown on main page

* use new update code modal on generic node

* delete check code validity

* add new check code vaildity util function

* removed unused sets from update node code

* Make componentsToUpdate contain breaking info

* Make Generic Node use Components to Update

* Change border in Node Status

* Stop propagation on node update

* Update update all components to have changes from figma

* updated flow store type and added components to update

* Update update component modal

* added icon on outdatedNodes

* Added id filtering on update components

* Added table with components to update

* Update styling

* Update update component modal to use table component

* Updated styles

* filter map

* Update select to not allow selecting texts on backup flow

* Update cursor for label

* Update text of backup flow

* Try to update selection

* Fix selection of components on opening modal

* Insert Update button on node toolbar if dismissed

* Added new parameters of node toolbar

* Added new types of node toolbar

* Removed update button from node status

* Updated shadcn theme

* Added dismiss by node, added dismissing to local storage, added correct update display

* Clarified update warnings in the UpdateComponentModal to better inform users about potential breaking changes and the need to reconnect components.

* Refactored update component visibility logic in GenericNode to use a memoized value for improved performance and readability.

* Updated test for outdated components to reflect changes in button selectors and improved visibility assertions for update notifications.

* Simplified visibility assertion in outdated components test to check for a more concise update message.

* Fixed edges not coming back after undoing

* Fixed breaking change check to not be checked if code is the same

* Fixed imports

* removed unused functions

* updated icon color

* updated test id

* updated for function to foreach

* updated data testid

* updated outdated flow

* removed flowToCanvas that caused bug when going from main page to flow page

* [autofix.ci] apply automated fixes

* Fixed outdated actions test

* fixed timeouts

* Added check for Backup

---------

Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lucas Oliveira 2025-05-09 09:30:32 -03:00 committed by GitHub
commit 79e35834b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1160 additions and 419 deletions

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,9 @@ import { expect, test } from "@playwright/test";
import { readFileSync } from "fs";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test("user must be able to update outdated components", async ({ page }) => {
test("user must be able to update outdated components by update all button", async ({
page,
}) => {
await awaitBootstrapTest(page);
await page.locator("span").filter({ hasText: "Close" }).first().click();
@ -27,22 +29,152 @@ test("user must be able to update outdated components", async ({ page }) => {
dataTransfer,
});
await page.waitForTimeout(1000);
await page.waitForSelector("data-testid=list-card", {
timeout: 3000,
});
await page.getByTestId("list-card").first().click();
await expect(page.getByText("components are ready to update")).toBeVisible({
await expect(page.getByText("Updates are available for 5")).toBeVisible({
timeout: 30000,
});
let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count();
expect(outdatedComponents).toBeGreaterThan(0);
let outdatedComponents = await page.getByTestId("update-button").count();
expect(outdatedComponents).toBe(1);
await page.getByText("Update All", { exact: true }).click();
let outdatedBreakingComponents = await page
.getByTestId("review-button")
.count();
expect(outdatedBreakingComponents).toBe(4);
await expect(page.getByTestId("icon-AlertTriangle")).toHaveCount(0, {
expect(await page.getByTestId("update-all-button")).toHaveText("Review All");
await page.getByTestId("update-all-button").click();
expect(
await page.locator('input[data-ref="eInput"]').nth(2).isChecked(),
).toBe(false);
expect(
await page.locator('input[data-ref="eInput"]').nth(3).isChecked(),
).toBe(false);
expect(
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
).toBe(true);
expect(
await page.locator('input[data-ref="eInput"]').nth(5).isChecked(),
).toBe(false);
expect(
await page.locator('input[data-ref="eInput"]').nth(6).isChecked(),
).toBe(false);
await page
.getByRole("checkbox", { name: "Column with Header Selection" })
.check();
expect(await page.getByTestId("backup-flow-checkbox").isChecked()).toBe(true);
await page.getByTestId("backup-flow-checkbox").click();
await page.getByRole("button", { name: "Update Components" }).click();
await expect(page.getByTestId("update-button")).toHaveCount(0, {
timeout: 5000,
});
await expect(page.getByTestId("review-button")).toHaveCount(0, {
timeout: 5000,
});
});
test("user must be able to update outdated components by each outdated component", async ({
page,
}) => {
await awaitBootstrapTest(page);
await page.locator("span").filter({ hasText: "Close" }).first().click();
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read your file into a buffer.
const jsonContent = readFileSync("tests/assets/outdated_flow.json", "utf-8");
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "outdated_flow.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
// Now dispatch
await page.getByTestId("cards-wrapper").dispatchEvent("drop", {
dataTransfer,
});
await page.waitForTimeout(1000);
await page.waitForSelector("data-testid=list-card", {
timeout: 3000,
});
await page.getByTestId("list-card").first().click();
await expect(page.getByText("Updates are available for 5")).toBeVisible({
timeout: 30000,
});
let outdatedComponents = await page.getByTestId("update-button").count();
expect(outdatedComponents).toBe(1);
let outdatedBreakingComponents = await page
.getByTestId("review-button")
.count();
expect(outdatedBreakingComponents).toBe(4);
expect(await page.getByTestId("update-all-button")).toHaveText("Review All");
await page.getByTestId("review-button").first().click();
await page.waitForSelector("button[data-testid='backup-flow-checkbox']", {
timeout: 30000,
});
expect(await page.getByTestId("backup-flow-checkbox").isChecked()).toBe(true);
await page.getByRole("button", { name: "Update Component" }).click();
await expect(page.getByTestId("update-button")).toHaveCount(1, {
timeout: 5000,
});
await expect(page.getByTestId("review-button")).toHaveCount(3, {
timeout: 5000,
});
await expect(page.getByText("Updates are available for 4")).toBeVisible({
timeout: 30000,
});
expect(await page.getByTestId("update-all-button")).toHaveText("Review All");
await page.getByTestId("update-button").first().click();
await expect(page.getByTestId("update-button")).toHaveCount(0, {
timeout: 5000,
});
await expect(page.getByTestId("review-button")).toHaveCount(3, {
timeout: 5000,
});
await awaitBootstrapTest(page, { skipModal: true });
await expect(page.getByText("Backup").count()).toBeGreaterThan(0);
});