diff --git a/app/react/sidebar/AzureSidebar/AzureSidebar.tsx b/app/react/sidebar/AzureSidebar/AzureSidebar.tsx index 64db4f224..0adcec5b0 100644 --- a/app/react/sidebar/AzureSidebar/AzureSidebar.tsx +++ b/app/react/sidebar/AzureSidebar/AzureSidebar.tsx @@ -12,12 +12,17 @@ interface Props { export function AzureSidebar({ environmentId }: Props) { return ( <> - <DashboardLink environmentId={environmentId} platformPath="azure" /> + <DashboardLink + environmentId={environmentId} + platformPath="azure" + data-cy="azureSidebar-dashboard" + /> <SidebarItem to="azure.containerinstances" params={{ endpointId: environmentId }} icon={Box} label="Container instances" + data-cy="azureSidebar-containerInstances" /> </> ); diff --git a/app/react/sidebar/DockerSidebar.tsx b/app/react/sidebar/DockerSidebar.tsx index 1eba959ea..bf4ad0333 100644 --- a/app/react/sidebar/DockerSidebar.tsx +++ b/app/react/sidebar/DockerSidebar.tsx @@ -58,27 +58,35 @@ export function DockerSidebar({ environmentId, environment }: Props) { label: 'Swarm', icon: Trello, to: 'docker.swarm', + dataCy: 'portainerSidebar-swarm', } : { label: 'Host', icon: Trello, to: 'docker.host', + dataCy: 'portainerSidebar-host', }; return ( <> - <DashboardLink environmentId={environmentId} platformPath="docker" /> + <DashboardLink + environmentId={environmentId} + platformPath="docker" + data-cy="dockerSidebar-dashboard" + /> <SidebarItem label="App Templates" icon={Edit} to="docker.templates" params={{ endpointId: environmentId }} + data-cy="portainerSidebar-appTemplates" > <SidebarItem label="Custom Templates" to="docker.templates.custom" params={{ endpointId: environmentId }} + data-cy="dockerSidebar-customTemplates" /> </SidebarItem> @@ -88,6 +96,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Layers} label="Stacks" + data-cy="dockerSidebar-stacks" /> )} @@ -97,6 +106,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Shuffle} label="Services" + data-cy="dockerSidebar-services" /> )} @@ -105,6 +115,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Box} label="Containers" + data-cy="dockerSidebar-containers" /> <SidebarItem @@ -112,6 +123,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={List} label="Images" + data-cy="dockerSidebar-images" /> <SidebarItem @@ -119,9 +131,14 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Share2} label="Networks" + data-cy="dockerSidebar-networks" /> - <VolumesLink environmentId={environmentId} platformPath="docker" /> + <VolumesLink + environmentId={environmentId} + platformPath="docker" + data-cy="dockerSidebar-volumes" + /> {apiVersion >= 1.3 && isSwarmManager && ( <SidebarItem @@ -129,6 +146,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Clipboard} label="Configs" + data-cy="dockerSidebar-configs" /> )} @@ -138,6 +156,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Lock} label="Secrets" + data-cy="dockerSidebar-secrets" /> )} @@ -147,6 +166,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { params={{ endpointId: environmentId }} icon={Clock} label="Events" + data-cy="dockerSidebar-events" /> )} @@ -155,6 +175,7 @@ export function DockerSidebar({ environmentId, environment }: Props) { icon={setupSubMenuProps.icon} to={setupSubMenuProps.to} params={{ endpointId: environmentId }} + data-cy={setupSubMenuProps.dataCy} > <Authorized authorizations="PortainerEndpointUpdateSettings" diff --git a/app/react/sidebar/EdgeComputeSidebar.tsx b/app/react/sidebar/EdgeComputeSidebar.tsx index 9f6eeb3c7..8d545f4a3 100644 --- a/app/react/sidebar/EdgeComputeSidebar.tsx +++ b/app/react/sidebar/EdgeComputeSidebar.tsx @@ -6,10 +6,30 @@ import { SidebarSection } from './SidebarSection'; export function EdgeComputeSidebar() { return ( <SidebarSection title="Edge compute"> - <SidebarItem to="edge.devices" label="Edge Devices" icon={Box} /> - <SidebarItem to="edge.groups" label="Edge Groups" icon={Grid} /> - <SidebarItem to="edge.stacks" label="Edge Stacks" icon={Layers} /> - <SidebarItem to="edge.jobs" label="Edge Jobs" icon={Clock} /> + <SidebarItem + to="edge.devices" + label="Edge Devices" + icon={Box} + data-cy="portainerSidebar-edgeDevices" + /> + <SidebarItem + to="edge.groups" + label="Edge Groups" + icon={Grid} + data-cy="portainerSidebar-edgeGroups" + /> + <SidebarItem + to="edge.stacks" + label="Edge Stacks" + icon={Layers} + data-cy="portainerSidebar-edgeStacks" + /> + <SidebarItem + to="edge.jobs" + label="Edge Jobs" + icon={Clock} + data-cy="portainerSidebar-edgeJobs" + /> </SidebarSection> ); } diff --git a/app/react/sidebar/KubernetesSidebar/KubernetesSidebar.tsx b/app/react/sidebar/KubernetesSidebar/KubernetesSidebar.tsx index 1b58ecfb9..92838e4c2 100644 --- a/app/react/sidebar/KubernetesSidebar/KubernetesSidebar.tsx +++ b/app/react/sidebar/KubernetesSidebar/KubernetesSidebar.tsx @@ -21,13 +21,18 @@ export function KubernetesSidebar({ environmentId }: Props) { <> {isOpen && <KubectlShellButton environmentId={environmentId} />} - <DashboardLink environmentId={environmentId} platformPath="kubernetes" /> + <DashboardLink + environmentId={environmentId} + platformPath="kubernetes" + data-cy="k8sSidebar-dashboard" + /> <SidebarItem to="kubernetes.templates.custom" params={{ endpointId: environmentId }} icon={Edit} label="Custom Templates" + data-cy="k8sSidebar-customTemplates" /> <SidebarItem @@ -35,6 +40,7 @@ export function KubernetesSidebar({ environmentId }: Props) { params={{ endpointId: environmentId }} icon={Layers} label="Namespaces" + data-cy="k8sSidebar-namespaces" /> <Authorized authorizations="HelmInstallChart"> @@ -43,6 +49,7 @@ export function KubernetesSidebar({ environmentId }: Props) { params={{ endpointId: environmentId }} icon={Loader} label="Helm" + data-cy="k8sSidebar-helm" /> </Authorized> @@ -51,6 +58,7 @@ export function KubernetesSidebar({ environmentId }: Props) { params={{ endpointId: environmentId }} icon={Box} label="Applications" + data-cy="k8sSidebar-applications" /> <SidebarItem @@ -58,21 +66,28 @@ export function KubernetesSidebar({ environmentId }: Props) { params={{ endpointId: environmentId }} icon={Lock} label="ConfigMaps & Secrets" + data-cy="k8sSidebar-configurations" /> - <VolumesLink environmentId={environmentId} platformPath="kubernetes" /> + <VolumesLink + environmentId={environmentId} + platformPath="kubernetes" + data-cy="k8sSidebar-volumes" + /> <SidebarItem label="Cluster" to="kubernetes.cluster" icon={Server} params={{ endpointId: environmentId }} + data-cy="k8sSidebar-cluster" > <Authorized authorizations="K8sClusterSetupRW" adminOnlyCE> <SidebarItem to="portainer.k8sendpoint.kubernetesConfig" params={{ id: environmentId }} label="Setup" + data-cy="k8sSidebar-setup" /> </Authorized> @@ -81,6 +96,7 @@ export function KubernetesSidebar({ environmentId }: Props) { to="portainer.k8sendpoint.securityConstraint" params={{ id: environmentId }} label="Security constraints" + data-cy="k8sSidebar-securityConstraints" /> </Authorized> @@ -88,6 +104,7 @@ export function KubernetesSidebar({ environmentId }: Props) { to="kubernetes.registries" params={{ endpointId: environmentId }} label="Registries" + data-cy="k8sSidebar-registries" /> </SidebarItem> </> diff --git a/app/react/sidebar/SettingsSidebar.tsx b/app/react/sidebar/SettingsSidebar.tsx index 6e80b83f3..5caa599f9 100644 --- a/app/react/sidebar/SettingsSidebar.tsx +++ b/app/react/sidebar/SettingsSidebar.tsx @@ -27,10 +27,25 @@ export function SettingsSidebar({ isAdmin }: Props) { return ( <SidebarSection title="Settings"> {showUsersSection && ( - <SidebarItem to="portainer.users" label="Users" icon={Users}> - <SidebarItem to="portainer.teams" label="Teams" /> + <SidebarItem + to="portainer.users" + label="Users" + icon={Users} + data-cy="portainerSidebar-users" + > + <SidebarItem + to="portainer.teams" + label="Teams" + data-cy="portainerSidebar-teams" + /> - {isAdmin && <SidebarItem to="portainer.roles" label="Roles" />} + {isAdmin && ( + <SidebarItem + to="portainer.roles" + label="Roles" + data-cy="portainerSidebar-roles" + /> + )} </SidebarItem> )} {isAdmin && ( @@ -40,15 +55,25 @@ export function SettingsSidebar({ isAdmin }: Props) { to="portainer.endpoints" icon={HardDrive} openOnPaths={['portainer.wizard.endpoints']} + data-cy="portainerSidebar-environments" > - <SidebarItem to="portainer.groups" label="Groups" /> - <SidebarItem to="portainer.tags" label="Tags" /> + <SidebarItem + to="portainer.groups" + label="Groups" + data-cy="portainerSidebar-environmentGroups" + /> + <SidebarItem + to="portainer.tags" + label="Tags" + data-cy="portainerSidebar-environmentTags" + /> </SidebarItem> <SidebarItem label="Registries" to="portainer.registries" icon={Radio} + data-cy="portainerSidebar-registries" /> {process.env.PORTAINER_EDITION !== 'CE' && ( @@ -56,6 +81,7 @@ export function SettingsSidebar({ isAdmin }: Props) { to="portainer.licenses" label="Licenses" icon={Award} + data-cy="portainerSidebar-licenses" /> )} @@ -63,22 +89,38 @@ export function SettingsSidebar({ isAdmin }: Props) { label="Authentication logs" to="portainer.authLogs" icon={FileText} + data-cy="portainerSidebar-authLogs" > - <SidebarItem to="portainer.activityLogs" label="Activity Logs" /> + <SidebarItem + to="portainer.activityLogs" + label="Activity Logs" + data-cy="portainerSidebar-activityLogs" + /> </SidebarItem> - <SidebarItem to="portainer.settings" label="Settings" icon={Settings}> + <SidebarItem + to="portainer.settings" + label="Settings" + icon={Settings} + data-cy="portainerSidebar-settings" + > {!window.ddExtension && ( <SidebarItem to="portainer.settings.authentication" label="Authentication" + data-cy="portainerSidebar-authentication" /> )} - <SidebarItem to="portainer.settings.cloud" label="Cloud" /> + <SidebarItem + to="portainer.settings.cloud" + label="Cloud" + data-cy="portainerSidebar-cloud" + /> <SidebarItem to="portainer.settings.edgeCompute" label="Edge Compute" + data-cy="portainerSidebar-edgeCompute" /> <SidebarItem.Wrapper label="Help / About"> @@ -90,7 +132,7 @@ export function SettingsSidebar({ isAdmin }: Props) { } target="_blank" rel="noreferrer" - className="px-3 rounded flex h-full items-center" + className="px-3 rounded flex h-8 items-center" > Help / About </a> diff --git a/app/react/sidebar/Sidebar.tsx b/app/react/sidebar/Sidebar.tsx index 07bb634ae..3902c83d6 100644 --- a/app/react/sidebar/Sidebar.tsx +++ b/app/react/sidebar/Sidebar.tsx @@ -41,7 +41,12 @@ export function Sidebar() { {/* negative margin + padding -> scrollbar won't hide the content */} <div className="mt-6 overflow-y-auto flex-1 -mr-4 pr-4"> <ul className="space-y-9"> - <SidebarItem to="portainer.home" icon={Home} label="Home" /> + <SidebarItem + to="portainer.home" + icon={Home} + label="Home" + data-cy="portainerSidebar-home" + /> <EnvironmentSidebar /> diff --git a/app/react/sidebar/SidebarItem/Head.tsx b/app/react/sidebar/SidebarItem/Head.tsx index 98b58beaa..bf2a2ee08 100644 --- a/app/react/sidebar/SidebarItem/Head.tsx +++ b/app/react/sidebar/SidebarItem/Head.tsx @@ -6,12 +6,17 @@ import { import clsx from 'clsx'; import { ComponentProps } from 'react'; +import { AutomationTestingProps } from '@/types'; + import { Link } from '@@/Link'; import { IconProps, Icon } from '@@/Icon'; import { useSidebarState } from '../useSidebarState'; -interface Props extends IconProps, ComponentProps<typeof Link> { +interface Props + extends IconProps, + ComponentProps<typeof Link>, + AutomationTestingProps { label: string; ignorePaths?: string[]; } @@ -23,6 +28,7 @@ export function Head({ label, icon, ignorePaths = [], + 'data-cy': dataCy, }: Props) { const { isOpen } = useSidebarState(); const anchorProps = useSrefActive( @@ -43,6 +49,7 @@ export function Head({ 'text-inherit no-underline hover:no-underline hover:text-inherit focus:no-underline focus:text-inherit w-full flex-1 rounded-md', { 'px-3': isOpen } )} + data-cy={dataCy} > <div className={clsx('flex items-center h-8 space-x-4 text-sm', { diff --git a/app/react/sidebar/SidebarItem/SidebarItem.tsx b/app/react/sidebar/SidebarItem/SidebarItem.tsx index 3ea03690d..4b2c6cb06 100644 --- a/app/react/sidebar/SidebarItem/SidebarItem.tsx +++ b/app/react/sidebar/SidebarItem/SidebarItem.tsx @@ -1,12 +1,14 @@ import { ReactNode } from 'react'; import { Icon } from 'react-feather'; +import { AutomationTestingProps } from '@/types'; + import { Wrapper } from './Wrapper'; import { Menu } from './Menu'; import { Head } from './Head'; import { getPathsForChildren } from './utils'; -interface Props { +interface Props extends AutomationTestingProps { icon?: Icon; to: string; params?: object; @@ -22,6 +24,7 @@ export function SidebarItem({ params, label, openOnPaths = [], + 'data-cy': dataCy, }: Props) { const childrenPath = getPathsForChildren(children); const head = ( @@ -31,6 +34,7 @@ export function SidebarItem({ params={params} label={label} ignorePaths={childrenPath} + data-cy={dataCy} /> ); diff --git a/app/react/sidebar/items/DashboardLink.tsx b/app/react/sidebar/items/DashboardLink.tsx index 9ed05be9e..9e295941f 100644 --- a/app/react/sidebar/items/DashboardLink.tsx +++ b/app/react/sidebar/items/DashboardLink.tsx @@ -1,21 +1,27 @@ import { Layout } from 'react-feather'; import { EnvironmentId } from '@/portainer/environments/types'; +import { AutomationTestingProps } from '@/types'; import { SidebarItem } from '../SidebarItem'; -interface Props { +interface Props extends AutomationTestingProps { environmentId: EnvironmentId; platformPath: string; } -export function DashboardLink({ environmentId, platformPath }: Props) { +export function DashboardLink({ + environmentId, + platformPath, + 'data-cy': dataCy, +}: Props) { return ( <SidebarItem to={`${platformPath}.dashboard`} params={{ endpointId: environmentId }} icon={Layout} label="Dashboard" + data-cy={dataCy} /> ); } diff --git a/app/react/sidebar/items/VolumesLink.tsx b/app/react/sidebar/items/VolumesLink.tsx index 7d59d9499..131ada783 100644 --- a/app/react/sidebar/items/VolumesLink.tsx +++ b/app/react/sidebar/items/VolumesLink.tsx @@ -1,21 +1,27 @@ import { Database } from 'react-feather'; import { EnvironmentId } from '@/portainer/environments/types'; +import { AutomationTestingProps } from '@/types'; import { SidebarItem } from '../SidebarItem'; -interface Props { +interface Props extends AutomationTestingProps { environmentId: EnvironmentId; platformPath: string; } -export function VolumesLink({ environmentId, platformPath }: Props) { +export function VolumesLink({ + environmentId, + platformPath, + 'data-cy': dataCy, +}: Props) { return ( <SidebarItem to={`${platformPath}.volumes`} params={{ endpointId: environmentId }} icon={Database} label="Volumes" + data-cy={dataCy} /> ); }