feat: Add data-testid and id attributes to app header components for testing (#5724)

* 📝 (AccountMenu/index.tsx): Add span elements with data-testid and id attributes for menu buttons for better testing and accessibility
📝 (FlowMenu/index.tsx): Add data-testid and id attributes to div elements for menu buttons for better testing and accessibility
📝 (HeaderMenu/index.tsx): Add data-testid and id attributes to DropdownMenuTrigger for user menu button for better testing and accessibility
📝 (ThemeButtons/index.tsx): Add data-testid and id attributes to Button elements for theme change buttons for better testing and accessibility
📝 (flowToolbarComponent/index.tsx): Add data-testid and id attributes to div element for API button modal for better testing and accessibility

* ✨ (FlowMenu/index.tsx): add data-testid attributes to elements for easier testing and debugging
✨ (AppHeaderComponent/index.tsx): add data-testid attributes to header sections for easier testing and debugging
This commit is contained in:
Cristhian Zanforlin Lousa 2025-01-16 13:05:04 -03:00 • committed by GitHub
commit d428fb6cb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 103 additions and 18 deletions

View file

@ -57,7 +57,12 @@ export const AccountMenu = () => {
<HeaderMenuItemsSection> <HeaderMenuItemsSection>
<div className="flex h-[46px] w-full items-center justify-between px-3"> <div className="flex h-[46px] w-full items-center justify-between px-3">
<div className="pl-1 text-xs text-zinc-500"> <div className="pl-1 text-xs text-zinc-500">
Version {version} <span
data-testid="menu_version_button"
id="menu_version_button"
>
Version {version}
</span>
</div> </div>
{!ENABLE_DATASTAX_LANGFLOW && <ThemeButtons />} {!ENABLE_DATASTAX_LANGFLOW && <ThemeButtons />}
</div> </div>
@ -72,25 +77,42 @@ export const AccountMenu = () => {
navigate("/settings"); navigate("/settings");
}} }}
> >
Settings <span
data-testid="menu_settings_button"
id="menu_settings_button"
>
Settings
</span>
</HeaderMenuItemButton> </HeaderMenuItemButton>
)} )}
{!ENABLE_DATASTAX_LANGFLOW && ( {!ENABLE_DATASTAX_LANGFLOW && (
<> <>
{isAdmin && !autoLogin && ( {isAdmin && !autoLogin && (
<HeaderMenuItemButton onClick={() => navigate("/admin")}> <HeaderMenuItemButton onClick={() => navigate("/admin")}>
Admin Page <span
data-testid="menu_admin_button"
id="menu_admin_button"
>
Admin Page
</span>
</HeaderMenuItemButton> </HeaderMenuItemButton>
)} )}
</> </>
)} )}
{ENABLE_DATASTAX_LANGFLOW ? ( {ENABLE_DATASTAX_LANGFLOW ? (
<HeaderMenuItemButton onClick={() => setIsFeedbackOpen(true)}> <HeaderMenuItemButton onClick={() => setIsFeedbackOpen(true)}>
Feedback <span
data-testid="menu_feedback_button"
id="menu_feedback_button"
>
Feedback
</span>
</HeaderMenuItemButton> </HeaderMenuItemButton>
) : ( ) : (
<HeaderMenuItemLink newPage href="https://docs.langflow.org"> <HeaderMenuItemLink newPage href="https://docs.langflow.org">
Docs <span data-testid="menu_docs_button" id="menu_docs_button">
Docs
</span>
</HeaderMenuItemLink> </HeaderMenuItemLink>
)} )}
</HeaderMenuItemsSection> </HeaderMenuItemsSection>
@ -110,14 +132,20 @@ export const AccountMenu = () => {
newPage newPage
href="https://github.com/langflow-ai/langflow/discussions" href="https://github.com/langflow-ai/langflow/discussions"
> >
Share Feedback on Github <span data-testid="menu_github_button" id="menu_github_button">
Share Feedback on Github
</span>
</HeaderMenuItemLink> </HeaderMenuItemLink>
)} )}
<HeaderMenuItemLink newPage href="https://twitter.com/langflow_ai"> <HeaderMenuItemLink newPage href="https://twitter.com/langflow_ai">
Follow Langflow on X <span data-testid="menu_twitter_button" id="menu_twitter_button">
Follow Langflow on X
</span>
</HeaderMenuItemLink> </HeaderMenuItemLink>
<HeaderMenuItemLink newPage href="https://discord.gg/EqksyE2EX9"> <HeaderMenuItemLink newPage href="https://discord.gg/EqksyE2EX9">
Join the Langflow Discord <span data-testid="menu_discord_button" id="menu_discord_button">
Join the Langflow Discord
</span>
</HeaderMenuItemLink> </HeaderMenuItemLink>
</HeaderMenuItemsSection> </HeaderMenuItemsSection>
{ENABLE_DATASTAX_LANGFLOW ? ( {ENABLE_DATASTAX_LANGFLOW ? (

View file

@ -123,7 +123,11 @@ export const MenuBar = ({}: {}): JSX.Element => {
} }
// return savedText; // return savedText;
return ( return (
<div className="shrink-0 text-xs font-medium text-accent-emerald-foreground"> <div
data-testid="menu_status_saved_flow_button"
id="menu_status_saved_flow_button"
className="shrink-0 text-xs font-medium text-accent-emerald-foreground"
>
Saved Saved
</div> </div>
); );
@ -213,8 +217,15 @@ export const MenuBar = ({}: {}): JSX.Element => {
]); ]);
return currentFlow && onFlowPage ? ( return currentFlow && onFlowPage ? (
<div className="flex items-center justify-center gap-2 truncate"> <div
<div className="header-menu-bar hidden w-20 max-w-fit grow justify-end truncate md:flex"> className="flex items-center justify-center gap-2 truncate"
data-testid="menu_bar_wrapper"
>
<div
className="header-menu-bar hidden w-20 max-w-fit grow justify-end truncate md:flex"
data-testid="menu_flow_bar"
id="menu_flow_bar_navigation"
>
{currentFolder?.name && ( {currentFolder?.name && (
<div className="hidden truncate md:flex"> <div className="hidden truncate md:flex">
<div <div
@ -232,12 +243,21 @@ export const MenuBar = ({}: {}): JSX.Element => {
</div> </div>
)} )}
</div> </div>
<div className="hidden w-fit shrink-0 select-none font-normal text-muted-foreground md:flex"> <div
className="hidden w-fit shrink-0 select-none font-normal text-muted-foreground md:flex"
data-testid="menu_bar_separator"
>
/ /
</div> </div>
<div className="overflow-hidden truncate text-sm sm:whitespace-normal"> <div
<div className="header-menu-bar-display-2 truncate"> className="overflow-hidden truncate text-sm sm:whitespace-normal"
data-testid="menu_bar_display"
>
<div
className="header-menu-bar-display-2 truncate"
data-testid="menu_bar_display_wrapper"
>
<div <div
className="header-menu-flow-name-2 truncate" className="header-menu-flow-name-2 truncate"
data-testid="flow-configuration-button" data-testid="flow-configuration-button"
@ -273,6 +293,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
<div <div
className="truncate font-semibold text-primary" className="truncate font-semibold text-primary"
data-testid="flow_name" data-testid="flow_name"
id="flow_name"
onClick={() => { onClick={() => {
setEditingName(true); setEditingName(true);
setFlowName(currentFlow.name); setFlowName(currentFlow.name);
@ -299,6 +320,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
handleAddFlow(); handleAddFlow();
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_new_flow_button"
id="menu_new_flow_button"
> >
<IconComponent name="Plus" className="header-menu-options" /> <IconComponent name="Plus" className="header-menu-options" />
New New
@ -309,6 +332,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
setOpenSettings(true); setOpenSettings(true);
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_edit_flow_button"
id="menu_edit_flow_button"
> >
<IconComponent <IconComponent
name="SquarePen" name="SquarePen"
@ -320,6 +345,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
<DropdownMenuItem <DropdownMenuItem
onClick={handleSave} onClick={handleSave}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_save_flow_button"
id="menu_save_flow_button"
> >
<ToolbarSelectItem <ToolbarSelectItem
value="Save" value="Save"
@ -338,6 +365,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
setOpenLogs(true); setOpenLogs(true);
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_logs_flow_button"
id="menu_logs_flow_button"
> >
<IconComponent <IconComponent
name="ScrollText" name="ScrollText"
@ -361,6 +390,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
}); });
}); });
}} }}
data-testid="menu_import_flow_button"
id="menu_import_flow_button"
> >
<IconComponent name="FileUp" className="header-menu-options" /> <IconComponent name="FileUp" className="header-menu-options" />
Import Import
@ -379,6 +410,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
undo(); undo();
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_undo_flow_button"
id="menu_undo_flow_button"
> >
<ToolbarSelectItem <ToolbarSelectItem
value="Undo" value="Undo"
@ -395,6 +428,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
redo(); redo();
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_redo_flow_button"
id="menu_redo_flow_button"
> >
<ToolbarSelectItem <ToolbarSelectItem
value="Redo" value="Redo"
@ -411,6 +446,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
handleReloadComponents(); handleReloadComponents();
}} }}
className="cursor-pointer" className="cursor-pointer"
data-testid="menu_refresh_flow_button"
id="menu_refresh_flow_button"
> >
<IconComponent <IconComponent
name="RefreshCcw" name="RefreshCcw"

View file

@ -15,7 +15,11 @@ export const HeaderMenu = ({ children }) => (
); );
export const HeaderMenuToggle = ({ children }) => ( export const HeaderMenuToggle = ({ children }) => (
<DropdownMenuTrigger className="group inline-flex w-full items-center justify-center gap-1 rounded-md pr-0"> <DropdownMenuTrigger
className="group inline-flex w-full items-center justify-center gap-1 rounded-md pr-0"
data-testid="user_menu_button"
id="user_menu_button"
>
<div className="flex items-center gap-1 rounded-lg px-2 py-1.5 group-hover:bg-muted"> <div className="flex items-center gap-1 rounded-lg px-2 py-1.5 group-hover:bg-muted">
{children} {children}
<ChevronsUpDown <ChevronsUpDown

View file

@ -57,6 +57,8 @@ export const ThemeButtons = () => {
: "text-foreground hover:bg-amber-400 hover:text-background" : "text-foreground hover:bg-amber-400 hover:text-background"
}`} }`}
onClick={() => handleThemeChange("light")} onClick={() => handleThemeChange("light")}
data-testid="menu_light_button"
id="menu_light_button"
> >
<ForwardedIconComponent name="Sun" className="w-4" /> <ForwardedIconComponent name="Sun" className="w-4" />
</Button> </Button>
@ -70,6 +72,8 @@ export const ThemeButtons = () => {
: "text-foreground hover:bg-purple-400 hover:text-background" : "text-foreground hover:bg-purple-400 hover:text-background"
}`} }`}
onClick={() => handleThemeChange("dark")} onClick={() => handleThemeChange("dark")}
data-testid="menu_dark_button"
id="menu_dark_button"
> >
<ForwardedIconComponent name="Moon" className="w-4" /> <ForwardedIconComponent name="Moon" className="w-4" />
</Button> </Button>
@ -83,6 +87,8 @@ export const ThemeButtons = () => {
: "hover:bg-foreground hover:text-background" : "hover:bg-foreground hover:text-background"
}`} }`}
onClick={() => handleThemeChange("system")} onClick={() => handleThemeChange("system")}
data-testid="menu_system_button"
id="menu_system_button"
> >
<ForwardedIconComponent name="Monitor" className="w-4" /> <ForwardedIconComponent name="Monitor" className="w-4" />
</Button> </Button>

View file

@ -49,7 +49,10 @@ export default function AppHeader(): JSX.Element {
return ( return (
<div className="flex h-[62px] w-full items-center justify-between gap-2 border-b px-5 py-2.5 dark:bg-background"> <div className="flex h-[62px] w-full items-center justify-between gap-2 border-b px-5 py-2.5 dark:bg-background">
{/* Left Section */} {/* Left Section */}
<div className={`flex items-center gap-2`}> <div
className={`flex items-center gap-2`}
data-testid="header_left_section_wrapper"
>
<Button <Button
unstyled unstyled
onClick={() => navigate("/")} onClick={() => navigate("/")}
@ -78,7 +81,10 @@ export default function AppHeader(): JSX.Element {
</div> </div>
{/* Right Section */} {/* Right Section */}
<div className={`flex items-center gap-2`}> <div
className={`flex items-center gap-2`}
data-testid="header_right_section_wrapper"
>
{!ENABLE_DATASTAX_LANGFLOW && ( {!ENABLE_DATASTAX_LANGFLOW && (
<> <>
<Button <Button

View file

@ -134,7 +134,11 @@ export default function FlowToolbar(): JSX.Element {
</div> </div>
{ENABLE_API && ( {ENABLE_API && (
<> <>
<div className="flex cursor-pointer items-center gap-2"> <div
className="flex cursor-pointer items-center gap-2"
data-testid="api_button_modal"
id="api_button_modal"
>
{currentFlow && currentFlow.data && ( {currentFlow && currentFlow.data && (
<ApiModal <ApiModal
flow={currentFlow} flow={currentFlow}