From 09c70eeee42d537c6064d7dda2d8d4bae7e2408b Mon Sep 17 00:00:00 2001 From: mbeaulne Date: Wed, 26 Aug 2026 17:17:02 -0400 Subject: [PATCH] Removes public component library flag and enabled the feature --- .../shared/Dialogs/ComponentDetailsDialog.tsx | 18 +- .../Dialogs/ComponentDuplicateDialog.test.tsx | 1 - .../TaskNode/TaskNodeCard/TaskNodeCard.tsx | 21 +- .../FlowSidebar/components/ComponentItem.tsx | 9 +- .../FlowSidebar/components/SearchFilter.tsx | 98 ------ .../FlowSidebar/components/SearchInput.tsx | 40 --- .../components/SearchResults.test.tsx | 99 ------ .../FlowSidebar/components/SearchResults.tsx | 116 ------- .../ReactFlow/FlowSidebar/components/index.ts | 2 - .../FlowSidebar/sections/GraphComponents.tsx | 51 +-- src/flags.ts | 7 - src/providers/AnalyticsProvider.test.tsx | 6 +- .../ComponentLibraryProvider.tsx | 22 -- .../v2/shared/nodes/TaskNode/TaskNode.tsx | 7 +- src/types/componentLibrary.ts | 16 +- src/utils/constants.ts | 2 - tests/e2e/componentlib.spec.ts | 301 ------------------ tests/e2e/helpers.ts | 6 - tests/e2e/published-componentlib.spec.ts | 38 ++- .../e2e/published-componentlifecycle.spec.ts | 3 - 20 files changed, 56 insertions(+), 807 deletions(-) delete mode 100644 src/components/shared/ReactFlow/FlowSidebar/components/SearchFilter.tsx delete mode 100644 src/components/shared/ReactFlow/FlowSidebar/components/SearchInput.tsx delete mode 100644 src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.test.tsx delete mode 100644 src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.tsx delete mode 100644 tests/e2e/componentlib.spec.ts diff --git a/src/components/shared/Dialogs/ComponentDetailsDialog.tsx b/src/components/shared/Dialogs/ComponentDetailsDialog.tsx index a0588bad19..84ffb422f2 100644 --- a/src/components/shared/Dialogs/ComponentDetailsDialog.tsx +++ b/src/components/shared/Dialogs/ComponentDetailsDialog.tsx @@ -22,7 +22,6 @@ import { ComponentFavoriteToggle } from "../FavoriteComponentToggle"; import { InfoBox } from "../InfoBox"; import { PublishComponent } from "../ManageComponent/PublishComponent"; import { PublishedComponentDetails } from "../ManageComponent/PublishedComponentDetails"; -import { useFlagValue } from "../Settings/useFlags"; import { withSuspenseWrapper } from "../SuspenseWrapper"; import { TaskDetails, TaskImplementation, TaskIO } from "../TaskDetails"; import TaskActions from "../TaskDetails/Actions"; @@ -65,10 +64,6 @@ const ComponentDetailsDialogContentSkeleton = () => { const ComponentDetailsDialogContent = withSuspenseWrapper( ({ component, displayName, readOnly }: ComponentDetailsProps) => { - const remoteComponentLibrarySearchEnabled = useFlagValue( - "remote-component-library-search", - ); - const componentRef = useHydrateComponentReference(component); if (!componentRef) { @@ -81,8 +76,7 @@ const ComponentDetailsDialogContent = withSuspenseWrapper( const componentSpec = componentRef.spec; - const hasPublishSection = - remoteComponentLibrarySearchEnabled && component.owned; + const hasPublishSection = component.owned; return ( <> @@ -124,12 +118,10 @@ const ComponentDetailsDialogContent = withSuspenseWrapper(
- {remoteComponentLibrarySearchEnabled && ( - - )} + { const navigate = useNavigate(); - const isRemoteComponentLibrarySearchEnabled = useFlagValue( - "remote-component-library-search", - ); const inputAggregatorEnabled = useFlagValue("input-aggregator"); const { registerNode } = useNodesOverlay(); @@ -260,16 +257,12 @@ const TaskNodeCard = () => { )} - {isRemoteComponentLibrarySearchEnabled ? ( - - {digestMarkup} - - ) : ( - digestMarkup - )} + + {digestMarkup} + {isSubgraphNode && subgraphExecutionStats && ( @@ -298,7 +291,7 @@ const TaskNodeCard = () => { onBackgroundClick={handleOutputSectionClick} />
- {isRemoteComponentLibrarySearchEnabled && updateOverlayDialogOpen ? ( + {updateOverlayDialogOpen ? ( { - const isRemoteComponentLibrarySearchEnabled = useFlagValue( - "remote-component-library-search", - ); - const shouldShowOutdatedBadge = - isRemoteComponentLibrarySearchEnabled && showOutdatedBadge; - const popoverRef = useRef(null); // TODO: respect selected node as a starting point @@ -252,7 +245,7 @@ const ComponentMarkup = ({ wrap="nowrap" blockAlign="start" > - {shouldShowOutdatedBadge ? ( + {showOutdatedBadge ? ( { - const handleCheckboxChange = (filter: string, checked: boolean) => { - if (checked) { - onFiltersChange([...activeFilters, filter]); - } else { - onFiltersChange(activeFilters.filter((f) => f !== filter)); - } - }; - - return ( - - -
- - {activeFilters.length > 0 && !disableCounter && ( - - {activeFilters.length} - - )} -
-
- -
- - Filter Search - -
- {availableFilters.map((filter) => { - if (filter === ComponentSearchFilter.EXACTMATCH) return; - - return ( -
- - handleCheckboxChange(filter, !!checked) - } - className="hover:cursor-pointer" - /> - -
- ); - })} -
-
-
- - handleCheckboxChange( - ComponentSearchFilter.EXACTMATCH, - !!checked, - ) - } - className="hover:cursor-pointer" - /> - -
-
-
-
- ); -}; - -export default SearchFilter; diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/SearchInput.tsx b/src/components/shared/ReactFlow/FlowSidebar/components/SearchInput.tsx deleted file mode 100644 index 7485b44099..0000000000 --- a/src/components/shared/ReactFlow/FlowSidebar/components/SearchInput.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Search } from "lucide-react"; - -import { Input } from "@/components/ui/input"; -import { type SearchInputProps } from "@/types/componentLibrary"; -import { COMPONENT_SEARCH_FILTERS } from "@/utils/constants"; - -import SearchFilter from "./SearchFilter"; - -const SearchInput = ({ - value, - activeFilters, - onChange, - onFiltersChange, -}: SearchInputProps) => { - return ( -
-
-
- -
- -
- -
- ); -}; - -export default SearchInput; diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.test.tsx b/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.test.tsx deleted file mode 100644 index cfc1e00f1b..0000000000 --- a/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import type { ReactNode } from "react"; -import { describe, expect, it, vi } from "vitest"; - -import type { SearchResult } from "@/types/componentLibrary"; -import { ComponentSearchFilter } from "@/utils/constants"; - -import SearchResults from "./SearchResults"; - -vi.mock("@/components/shared/Dialogs", () => ({ - ComponentDetailsDialog: () => , -})); - -vi.mock("@/components/shared/FavoriteComponentToggle", () => ({ - ComponentFavoriteToggle: () => , -})); - -vi.mock( - "@/components/shared/ManageComponent/hooks/useOutdatedComponents", - () => ({ - useOutdatedComponents: () => ({ data: [] }), - }), -); - -vi.mock("@/components/shared/Settings/useFlags", () => ({ - useFlagValue: () => false, -})); - -vi.mock("@/providers/ComponentLibraryProvider/ForcedSearchProvider", () => ({ - useForcedSearchContext: () => ({ - currentSearchFilter: { - searchTerm: "scrape", - filters: [ComponentSearchFilter.NAME], - }, - }), -})); - -vi.mock("../../NodesOverlay/NodesOverlayProvider", () => ({ - useNodesOverlay: () => ({ - notifyNode: vi.fn(), - getNodeIdsByDigest: vi.fn(() => []), - fitNodeIntoView: vi.fn(), - }), -})); - -vi.mock("./ComponentHoverPopover", () => ({ - ComponentHoverPopover: ({ children }: { children: ReactNode }) => ( - <>{children} - ), -})); - -describe("SearchResults", () => { - it("shows publisher metadata for v1 component search results", () => { - const searchResult: SearchResult = { - components: { - standard: [ - { - digest: "published-digest", - name: "Scrape V2", - published_by: "pipeline-components@shopify.com", - }, - ], - user: [], - used: [], - }, - }; - - render( - , - ); - - expect(screen.getByText("Scrape V2")).toBeInTheDocument(); - expect( - screen.getByText("Published by pipeline-components@shopify.com"), - ).toBeInTheDocument(); - }); - - it("shows digest metadata when a v1 component search result has no publisher", () => { - const searchResult: SearchResult = { - components: { - standard: [ - { - digest: "sha256:abc123", - name: "Upload to GCS", - }, - ], - user: [], - used: [], - }, - }; - - render( - , - ); - - expect(screen.getByText("Upload to GCS")).toBeInTheDocument(); - expect(screen.getByText("Digest sha256:abc123")).toBeInTheDocument(); - }); -}); diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.tsx b/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.tsx deleted file mode 100644 index fa696e8d84..0000000000 --- a/src/components/shared/ReactFlow/FlowSidebar/components/SearchResults.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { useCallback } from "react"; - -import { Button } from "@/components/ui/button"; -import { useForcedSearchContext } from "@/providers/ComponentLibraryProvider/ForcedSearchProvider"; -import type { SearchResult } from "@/types/componentLibrary"; -import { ComponentSearchFilter } from "@/utils/constants"; - -import { ComponentMarkup } from "./ComponentItem"; - -interface SearchResultsProps { - searchResult: SearchResult; - onFiltersChange: (filters: string[]) => void; -} - -const SearchResults = ({ - searchResult, - onFiltersChange, -}: SearchResultsProps) => { - const { currentSearchFilter } = useForcedSearchContext(); - - const matchedComponents = searchResult.components.standard; - - const matchedUserComponents = searchResult.components.user; - - const handleNameFilterClick = useCallback(() => { - if (!currentSearchFilter.filters.includes(ComponentSearchFilter.NAME)) { - onFiltersChange([ - ...currentSearchFilter.filters, - ComponentSearchFilter.NAME, - ]); - } - }, [currentSearchFilter.filters, onFiltersChange]); - - const filtersWithoutExactMatch = - currentSearchFilter.filters?.filter( - (f) => f !== ComponentSearchFilter.EXACTMATCH, - ) ?? []; - - const exactMatchFilter = currentSearchFilter.filters?.includes( - ComponentSearchFilter.EXACTMATCH, - ); - - if (filtersWithoutExactMatch.length === 0) { - return ( -
- No search filters set.{" "} - -
- ); - } - - const hasResults = - matchedComponents.length > 0 || matchedUserComponents.length > 0; - if (!hasResults) { - return ( -
- No component {filtersWithoutExactMatch.join(" or ")}{" "} - {exactMatchFilter ? "exactly matches" : "contains"} “ - {currentSearchFilter.searchTerm} - ” -
- ); - } - - const totalResults = matchedComponents.length + matchedUserComponents.length; - - return ( -
-
- Search Results ({totalResults}) -
-
- {matchedUserComponents.length > 0 && ( - <> - {/* User component section header if both types exist */} - {matchedComponents.length > 0 && ( -
- User Components -
- )} - {/* User component results */} - {matchedUserComponents.map((component, index) => ( - - ))} - - )} - - {matchedComponents.length > 0 && ( - <> - {/* Library component section header if both types exist */} - {matchedUserComponents.length > 0 && ( -
- Library Components -
- )} - {/* Library component results */} - {matchedComponents.map((component, index) => ( - - ))} - - )} -
-
- ); -}; - -export default SearchResults; diff --git a/src/components/shared/ReactFlow/FlowSidebar/components/index.ts b/src/components/shared/ReactFlow/FlowSidebar/components/index.ts index 8879d72e85..1417375cb6 100644 --- a/src/components/shared/ReactFlow/FlowSidebar/components/index.ts +++ b/src/components/shared/ReactFlow/FlowSidebar/components/index.ts @@ -1,5 +1,3 @@ export { default as FolderItem } from "./FolderItem"; export { default as ImportComponent } from "./ImportComponent"; export { EmptyState, ErrorState, LoadingState } from "./LibraryStates"; -export { default as SearchInput } from "./SearchInput"; -export { default as SearchResults } from "./SearchResults"; diff --git a/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx b/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx index ff4287644e..914be2db9d 100644 --- a/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx +++ b/src/components/shared/ReactFlow/FlowSidebar/sections/GraphComponents.tsx @@ -1,4 +1,4 @@ -import { type ChangeEvent, useCallback, useMemo } from "react"; +import { useMemo } from "react"; import { ManageLibrariesDialog } from "@/components/shared/GitHubLibrary/ManageLibrariesDialog"; import { useFlagValue } from "@/components/shared/Settings/useFlags"; @@ -7,7 +7,6 @@ import { BlockStack, InlineStack } from "@/components/ui/layout"; import { Separator } from "@/components/ui/separator"; import { Text } from "@/components/ui/typography"; import { useComponentLibrary } from "@/providers/ComponentLibraryProvider"; -import { useForcedSearchContext } from "@/providers/ComponentLibraryProvider/ForcedSearchProvider"; import type { UIComponentFolder } from "@/types/componentLibrary"; import { @@ -16,8 +15,6 @@ import { FolderItem, ImportComponent, LoadingState, - SearchInput, - SearchResults, } from "../components"; import { ComponentItemFromUrl, @@ -39,9 +36,6 @@ interface GraphComponentsProps { const GraphComponents = ({ showSectionHeader = false, }: GraphComponentsProps) => { - const remoteComponentLibrarySearchEnabled = useFlagValue( - "remote-component-library-search", - ); const githubComponentLibraryEnabled = useFlagValue( "github-component-library", ); @@ -49,7 +43,6 @@ const GraphComponents = ({ const { getComponentLibrary, existingComponentLibraries } = useComponentLibrary(); - const { updateSearchFilter, currentSearchFilter } = useForcedSearchContext(); const { componentLibrary, usedComponentsFolder, @@ -57,38 +50,13 @@ const GraphComponents = ({ favoritesFolder, isLoading, error, - searchResult, } = useComponentLibrary(); - const handleSearchChange = (e: ChangeEvent) => { - updateSearchFilter({ - searchTerm: e.target.value, - }); - }; - - const handleFiltersChange = useCallback( - (filters: string[]) => { - updateSearchFilter({ - filters, - }); - }, - [updateSearchFilter], - ); - const memoizedContent = useMemo(() => { if (isLoading) return ; if (error) return ; if (!componentLibrary) return ; - if (!remoteComponentLibrarySearchEnabled && searchResult) { - return ( - - ); - } - const hasUsedComponents = usedComponentsFolder?.components && usedComponentsFolder.components.length > 0; @@ -102,7 +70,7 @@ const GraphComponents = ({ return ( - {remoteComponentLibrarySearchEnabled && } + {memoizedContent} - ) : ( - <> - - - {memoizedContent} - ); const content = ( diff --git a/src/flags.ts b/src/flags.ts index 2d7a932197..48ed753f9d 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -1,13 +1,6 @@ import type { ConfigFlags } from "@/types/configuration"; export const ExistingFlags: ConfigFlags = { - ["remote-component-library-search"]: { - name: "Published Components Library", - description: "Enable the Published Components Library feature.", - default: true, - category: "beta", - }, - ["github-component-library"]: { name: "GitHub Component Library", description: diff --git a/src/providers/AnalyticsProvider.test.tsx b/src/providers/AnalyticsProvider.test.tsx index d1fe748dc7..56beb6a3fa 100644 --- a/src/providers/AnalyticsProvider.test.tsx +++ b/src/providers/AnalyticsProvider.test.tsx @@ -220,7 +220,7 @@ describe("AnalyticsProvider", () => { it("includes resolved effective flag values in session.tab.start metadata", async () => { mockGetUser.mockResolvedValue({ id: "user-1" }); mockIsFlagEnabled.mockImplementation( - (key: string) => key === "remote-component-library-search", + (key: string) => key === "github-component-library", ); const { events, cleanup } = captureEvents(); renderHook(() => useAnalytics(), { wrapper: makeWrapper() }); @@ -232,8 +232,8 @@ describe("AnalyticsProvider", () => { const { flags } = events.find( (e) => e.detail.actionType === "session.tab.start", )!.detail.metadata as Record>; - expect(flags["remote-component-library-search"]).toBe(true); - expect(flags["github-component-library"]).toBe(false); + expect(flags["github-component-library"]).toBe(true); + expect(flags["input-aggregator"]).toBe(false); cleanup(); }); diff --git a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx index 780ea0611d..b65872ed7e 100644 --- a/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx +++ b/src/providers/ComponentLibraryProvider/ComponentLibraryProvider.tsx @@ -64,7 +64,6 @@ import { isFavoriteComponent, populateComponentRefs, } from "./componentLibrary"; -import { useForcedSearchContext } from "./ForcedSearchProvider"; import { createLibraryObject } from "./libraries/factory"; import { PublishedComponentsLibrary } from "./libraries/publishedComponentsLibrary"; import { ensureLibraryFactoriesRegistered } from "./libraries/setup"; @@ -81,7 +80,6 @@ type ComponentLibraryContextType = { isLoading: boolean; error: Error | null; existingComponentLibraries: StoredLibrary[] | undefined; - searchResult: SearchResult | null; searchComponentLibrary: ( search: string, @@ -171,7 +169,6 @@ export const ComponentLibraryProvider = ({ children: ReactNode; }) => { const { graphSpec } = useComponentSpec(); - const { currentSearchFilter } = useForcedSearchContext(); const queryClient = useQueryClient(); const { track } = useAnalytics(); @@ -588,23 +585,6 @@ export const ComponentLibraryProvider = ({ dispatchEvent(new CustomEvent("tangle.library.duplicateDialogClosed")); }, []); - const [searchResult, setSearchResult] = useState(null); - - useEffect(() => { - let cancelled = false; - searchComponentLibrary( - currentSearchFilter.searchTerm, - currentSearchFilter.filters, - ).then((result) => { - if (!cancelled) { - setSearchResult(result); - } - }); - return () => { - cancelled = true; - }; - }, [currentSearchFilter, searchComponentLibrary]); - useEffect(() => { if (!rawComponentLibrary) { setComponentLibrary(undefined); @@ -641,7 +621,6 @@ export const ComponentLibraryProvider = ({ favoritesFolder, isLoading, error, - searchResult, existingComponentLibraries, searchComponentLibrary, getComponentLibrary, @@ -657,7 +636,6 @@ export const ComponentLibraryProvider = ({ favoritesFolder, isLoading, error, - searchResult, existingComponentLibraries, searchComponentLibrary, getComponentLibrary, diff --git a/src/routes/v2/shared/nodes/TaskNode/TaskNode.tsx b/src/routes/v2/shared/nodes/TaskNode/TaskNode.tsx index 88a3b8233c..257885e2c0 100644 --- a/src/routes/v2/shared/nodes/TaskNode/TaskNode.tsx +++ b/src/routes/v2/shared/nodes/TaskNode/TaskNode.tsx @@ -244,9 +244,6 @@ export const TaskNode = observer(function TaskNode({ const { getEdges, setEdges } = useReactFlow(); const showContent = useIsDetailedView(); const inputAggregatorEnabled = useFlagValue("input-aggregator"); - const publishedComponentBadgeEnabled = useFlagValue( - "remote-component-library-search", - ); const spec = useSpec(); const task = spec?.tasks.find((t) => t.$id === entityId); @@ -333,9 +330,7 @@ export const TaskNode = observer(function TaskNode({ conditionReferenceLabel ?? describeConditionSource(task.isEnabled) ?? CONDITION_LITERAL_LABELS[toConditionLiteral(task.isEnabled)], - componentRef: publishedComponentBadgeEnabled - ? task.resolvedComponentRef - : undefined, + componentRef: task.resolvedComponentRef, publishedComponentBadgeReadOnly, isAggregator, outputType: resolveAggregatorOutputType(task), diff --git a/src/types/componentLibrary.ts b/src/types/componentLibrary.ts index 9e76651a29..64841f3730 100644 --- a/src/types/componentLibrary.ts +++ b/src/types/componentLibrary.ts @@ -1,4 +1,4 @@ -import type { ChangeEvent, ReactElement } from "react"; +import type { ReactElement } from "react"; import type { ComponentReference } from "@/utils/componentSpec"; @@ -24,20 +24,6 @@ export type UIComponentFolder = { isUserFolder?: boolean; }; -export type SearchInputProps = { - value: string; - activeFilters: string[]; - onChange: (e: ChangeEvent) => void; - onFiltersChange: (filters: string[]) => void; -}; - -export type SearchFilterProps = { - availableFilters: string[]; - activeFilters: string[]; - disableCounter?: boolean; - onFiltersChange: (filters: string[]) => void; -}; - export type SearchResult = { components: { standard: ComponentReference[]; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d2cbe36dac..c89110d9ef 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -69,8 +69,6 @@ export enum ComponentSearchFilter { EXACTMATCH = "Exact Match", } -export const COMPONENT_SEARCH_FILTERS = Object.values(ComponentSearchFilter); - export const DEFAULT_FILTERS = [ComponentSearchFilter.NAME]; export const AUTOSAVE_DEBOUNCE_TIME_MS = 300; diff --git a/tests/e2e/componentlib.spec.ts b/tests/e2e/componentlib.spec.ts deleted file mode 100644 index 33153e44be..0000000000 --- a/tests/e2e/componentlib.spec.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { expect, type Page, test } from "@playwright/test"; - -import { - assertSearchState, - createNewPipeline, - dropComponentFromLibraryOnCanvas, - locateComponentInFolder, - locateFlowCanvas, - locateFolderByName, - openComponentLibFolder, - removeComponentFromCanvas, - setBetaFlag, -} from "./helpers"; - -/** - * Due to the time it takes to load the library, the tests are run in serial - * and one page is used for all the tests. - * - * So every test must clean up after itself - */ -test.describe.configure({ mode: "serial" }); - -test.describe("Component Library", () => { - let page: Page; - - test.beforeAll(async ({ browser }) => { - page = await browser.newPage(); - - await setBetaFlag(page, "remote-component-library-search", false); - await createNewPipeline(page); - - await expect(page.locator("[data-testid='search-input']")).toBeVisible(); - await locateFolderByName(page, "Standard library"); - }); - - test.afterAll(async () => { - await page.close(); - }); - - test("standard library successfully loads", async () => { - await openComponentLibFolder(page, "Standard library"); - - const standardLibraryFolders = [ - "Quick start", - "Basics", - "Datasets", - "Data manipulation", - "Upload/Download", - "ML frameworks", - "ML Metrics", - "Converters", - ]; - - // expect to see all the folders - for (const folder of standardLibraryFolders) { - const folderContainer = await locateFolderByName(page, folder); - await expect(folderContainer).toBeVisible(); - } - }); - - test("folder can be expanded and collapsed", async () => { - // ensure no components are visible in the folder before it is expanded - const inputsOutputsFolder = await locateFolderByName( - page, - "Inputs & Outputs", - ); - await expect(inputsOutputsFolder.getByRole("button")).toHaveAttribute( - "aria-expanded", - "false", - ); - const inputsOutputsFolderContent = inputsOutputsFolder.locator("li"); - - (await inputsOutputsFolderContent.all()).forEach(async (component) => { - await expect(component).toBeHidden(); - }); - - // expand the folder - await openComponentLibFolder(page, "Inputs & Outputs"); - - await expect(inputsOutputsFolder.getByRole("button")).toHaveAttribute( - "aria-expanded", - "true", - ); - - // expect only two components in the folder - const components = inputsOutputsFolder.locator("li"); - await expect(components).toHaveCount(2); - - await inputsOutputsFolder.getByRole("button").click(); - - await expect(inputsOutputsFolder.getByRole("button")).toHaveAttribute( - "aria-expanded", - "false", - ); - }); - - test("user can navigate deep into the nested folders", async () => { - // navigate to the nested folder - await openComponentLibFolder(page, "Standard library"); - - const topFolder = await openComponentLibFolder(page, "ML frameworks"); - const topFolderContent = topFolder.locator("[data-folder-name]"); - await expect(await topFolderContent).toHaveCount(6); - - const nestedFolder = await openComponentLibFolder(page, "XGBoost"); - - const nestedFolderContent = nestedFolder.getByTestId("component-item"); - await expect(await nestedFolderContent).toHaveCount(4); - }); - - test("components can be added and removed from favorites folder", async () => { - // add component to the favorites by clicking the star icon - await openComponentLibFolder(page, "Standard library"); - - const quickStartFolder = await openComponentLibFolder(page, "Quick start"); - const chicagoTaxiTripsDataset = await locateComponentInFolder( - quickStartFolder, - "Chicago Taxi Trips dataset", - ); - await chicagoTaxiTripsDataset.getByTestId("favorite-star").click(); - - // expect the component to be in the favorites folder - const favoritesFolder = await openComponentLibFolder( - page, - "Favorite Components", - ); - await expect(favoritesFolder.locator("li")).toHaveCount(1); - - // unstar the component - await chicagoTaxiTripsDataset.getByTestId("favorite-star").click(); - - await expect(favoritesFolder.locator("li")).toHaveCount(0); - }); - - test("component details can be opened as a dialog", async () => { - await openComponentLibFolder(page, "Standard library"); - const quickStartFolder = await openComponentLibFolder(page, "Quick start"); - const chicagoTaxiTripsDataset = await locateComponentInFolder( - quickStartFolder, - "Chicago Taxi Trips dataset", - ); - - await chicagoTaxiTripsDataset.getByTestId("info-icon-button").click(); - - const dialog = page.getByTestId("component-details-dialog"); - const dialogHeader = dialog.locator('[data-slot="dialog-header"]'); - await expect(dialogHeader).toBeVisible(); - - await expect(dialogHeader).toHaveText("Chicago Taxi Trips dataset"); - - await page.locator('button[data-slot="dialog-close"]').click(); - - await expect(dialogHeader).toBeHidden(); - }); - - test("components can be dragged to the canvas and appear in the used in pipeline folder", async () => { - await openComponentLibFolder(page, "Standard library"); - await dropComponentFromLibraryOnCanvas( - page, - "Quick start", - "Chicago Taxi Trips dataset", - ); - - const usedOnCanvasFolder = await openComponentLibFolder( - page, - "Used in Pipeline", - ); - await expect(usedOnCanvasFolder.locator("li")).toHaveCount(1); - - // remove the component from the canvas - await removeComponentFromCanvas(page, "Chicago Taxi Trips dataset"); - await expect(usedOnCanvasFolder.locator("li")).toHaveCount(0); - }); - - test("library can be searched", async () => { - await page.getByTestId("search-input").fill("GCS"); - - const searchResultsHeader = page.getByTestId("search-results-header"); - await expect(searchResultsHeader).toBeVisible(); - await expect(searchResultsHeader).toHaveText("Search Results (3)"); - - const componentItem = page.getByTestId("component-item"); - await expect(componentItem).toHaveCount(3); - - await page.getByTestId("search-input").clear(); - - await expect(searchResultsHeader).toBeHidden(); - }); - - test("search results can be highlighted on input pin click", async () => { - await openComponentLibFolder(page, "Standard library"); - await openComponentLibFolder(page, "Data manipulation"); - await openComponentLibFolder(page, "CSV"); - - const node = await dropComponentFromLibraryOnCanvas( - page, - "CSV", - "Select columns using Pandas on CSV data", - ); - await expect( - node, - "Dropped component should be on the canvas before clicking its pin", - ).toBeVisible(); - - const inputHandle = page.getByTestId("input-handle-table"); - await expect(inputHandle).toBeVisible(); - await inputHandle.click(); - - const outputConnection = page.getByTestId( - "output-connection-transformed_table", - ); - const inputConnection = page.getByTestId("input-connection-table"); - await expect(outputConnection).toHaveAttribute("data-highlighted", "true"); - - // assert highlighting - await expect(outputConnection).toHaveAttribute("data-highlighted", "true"); - await expect(inputConnection).toHaveAttribute("data-highlighted", "false"); - await expect(inputConnection).toHaveAttribute("data-selected", "true"); - - await assertSearchState(page, { - searchTerm: "CSV", - searchFilterCount: "2", - searchResultsCount: "*", - }); - - // reset highlighting after clicking on the canvas - await locateFlowCanvas(page).click(); - - // resets selection after clicking on the canvas - await expect(inputConnection).toHaveAttribute("data-highlighted", "false"); - await expect(outputConnection).toHaveAttribute("data-highlighted", "false"); - - // search should be reset - await assertSearchState(page, { - searchTerm: "", - }); - - // remove the component from the canvas - await removeComponentFromCanvas( - page, - "Select columns using Pandas on CSV data", - ); - }); - - test("search results can be highlighted on output pin click", async () => { - await openComponentLibFolder(page, "Standard library"); - await openComponentLibFolder(page, "Data manipulation"); - await openComponentLibFolder(page, "CSV"); - - const node = await dropComponentFromLibraryOnCanvas( - page, - "CSV", - "Select columns using Pandas on CSV data", - ); - await expect( - node, - "Dropped component should be on the canvas before clicking its pin", - ).toBeVisible(); - - const outputHandle = page.getByTestId("output-handle-transformed_table"); - await expect(outputHandle).toBeVisible(); - await outputHandle.click(); - - const outputConnection = page.getByTestId( - "output-connection-transformed_table", - ); - const inputConnection = page.getByTestId("input-connection-table"); - await expect(outputConnection).toHaveAttribute("data-selected", "true"); - - // assert highlighting - await expect(outputConnection).toHaveAttribute("data-highlighted", "false"); - await expect(outputConnection).toHaveAttribute("data-selected", "true"); - await expect(inputConnection).toHaveAttribute("data-highlighted", "true"); - - // assert search inputs - await assertSearchState(page, { - searchTerm: "CSV", - searchFilterCount: "2", - // todo: this can be painful to maintain, find a better way to do this - searchResultsCount: "*", - }); - - // reset highlighting after clicking on the canvas - await locateFlowCanvas(page).click(); - - // resets selection after clicking on the canvas - await expect(inputConnection).toHaveAttribute("data-highlighted", "false"); - await expect(outputConnection).toHaveAttribute("data-selected", "false"); - - // search should be reset - await assertSearchState(page, { - searchTerm: "", - }); - - // remove the component from the canvas - await removeComponentFromCanvas( - page, - "Select columns using Pandas on CSV data", - ); - }); -}); diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts index 66a25fc752..05fb4df550 100644 --- a/tests/e2e/helpers.ts +++ b/tests/e2e/helpers.ts @@ -310,12 +310,6 @@ async function navigateToSettings( ).toBeVisible(); } -/** - * Toggles a beta feature flag via the settings UI. - * @param page - Playwright page object - * @param flagKey - The flag key as defined in flags.ts (e.g. "remote-component-library-search") - * @param enabled - Whether the flag should be enabled or disabled - */ export async function setBetaFlag( page: Page, flagKey: string, diff --git a/tests/e2e/published-componentlib.spec.ts b/tests/e2e/published-componentlib.spec.ts index 26c3957ef2..3bf12003eb 100644 --- a/tests/e2e/published-componentlib.spec.ts +++ b/tests/e2e/published-componentlib.spec.ts @@ -10,7 +10,6 @@ import { locateFolderByName, openComponentLibFolder, removeComponentFromCanvas, - setBetaFlag, } from "./helpers"; /** @@ -28,8 +27,6 @@ test.describe("Published Component Library", () => { page = await browser.newPage(); await createNewPipeline(page); - await setBetaFlag(page, "remote-component-library-search", true); - await page.goBack(); await expect(page.locator("[data-testid='search-input']")).toBeVisible(); }); @@ -59,6 +56,41 @@ test.describe("Published Component Library", () => { } }); + test("folder can be expanded and collapsed", async () => { + const folder = await locateFolderByName(page, "Inputs & Outputs"); + await expect(folder.getByRole("button")).toHaveAttribute( + "aria-expanded", + "false", + ); + + await openComponentLibFolder(page, "Inputs & Outputs"); + + await expect(folder.getByRole("button")).toHaveAttribute( + "aria-expanded", + "true", + ); + await expect(folder.locator("li")).toHaveCount(2); + + await folder.getByRole("button").click(); + await expect(folder.getByRole("button")).toHaveAttribute( + "aria-expanded", + "false", + ); + }); + + test("user can navigate nested folders", async () => { + await openComponentLibFolder(page, "Standard library"); + + const frameworksFolder = await openComponentLibFolder( + page, + "ML frameworks", + ); + await expect(frameworksFolder.locator("[data-folder-name]")).toHaveCount(6); + + const xgboostFolder = await openComponentLibFolder(page, "XGBoost"); + await expect(xgboostFolder.getByTestId("component-item")).toHaveCount(4); + }); + test("library can be searched", async () => { // search for a component await searchForComponent(page, "GCS"); diff --git a/tests/e2e/published-componentlifecycle.spec.ts b/tests/e2e/published-componentlifecycle.spec.ts index 1ea2159cf1..df60ab6350 100644 --- a/tests/e2e/published-componentlifecycle.spec.ts +++ b/tests/e2e/published-componentlifecycle.spec.ts @@ -6,7 +6,6 @@ import { locateComponentInFolder, locateFolderByName, openComponentLibFolder, - setBetaFlag, } from "./helpers"; /** @@ -25,8 +24,6 @@ test.describe("Published Component Library - Lifecycle", () => { test.beforeAll(async ({ browser }) => { page = await browser.newPage(); - await setBetaFlag(page, "remote-component-library-search", true); - await createNewPipeline(page); await expect(page.locator("[data-testid='search-input']")).toBeVisible();