diff --git a/apps/standalone/src/app/components/AppLayout/AppToolbar.tsx b/apps/standalone/src/app/components/AppLayout/AppToolbar.tsx index 57c9dace4..3b7095cb1 100644 --- a/apps/standalone/src/app/components/AppLayout/AppToolbar.tsx +++ b/apps/standalone/src/app/components/AppLayout/AppToolbar.tsx @@ -112,6 +112,7 @@ const AppToolbar = () => { toggle={(toggleRef) => ( setHelpDropdownOpen(!helpDropdownOpen)} @@ -122,6 +123,7 @@ const AppToolbar = () => { popperProps={{ position: 'right' }} > { navigate(ROUTE.COMMAND_LINE_TOOLS); }} diff --git a/libs/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx b/libs/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx index bcec1bbed..3d806638b 100644 --- a/libs/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx +++ b/libs/ui-components/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx @@ -172,6 +172,7 @@ const DeviceDetailsPage = ({ children, hideTerminal }: DeviceDetailsPageProps) = {hasEditPermissions && ( navigate({ route: ROUTE.DEVICE_EDIT, postfix: deviceId })} {...editActionProps} > diff --git a/libs/ui-components/src/components/Device/DevicesPage/EnrolledDeviceTableRow.tsx b/libs/ui-components/src/components/Device/DevicesPage/EnrolledDeviceTableRow.tsx index 4a5ecc4a6..705481dab 100644 --- a/libs/ui-components/src/components/Device/DevicesPage/EnrolledDeviceTableRow.tsx +++ b/libs/ui-components/src/components/Device/DevicesPage/EnrolledDeviceTableRow.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { ActionsColumn, OnSelect, Td, Tr } from '@patternfly/react-table'; +import { ActionsColumn, IAction, OnSelect, Td, Tr } from '@patternfly/react-table'; import { Device } from '@flightctl/types'; import DeviceFleet from '../DeviceDetails/DeviceFleet'; @@ -53,6 +53,42 @@ const EnrolledDeviceTableRow = ({ const columnIds = React.useMemo(() => deviceColumns.map(({ id }) => id), [deviceColumns]); + const actionItems: IAction[] = [ + ...(canEdit + ? [ + { + title: t('Edit device configurations'), + 'data-testid': 'device-row-menu-edit-configurations', + onClick: () => navigate({ route: ROUTE.DEVICE_EDIT, postfix: deviceName }), + ...editActionProps, + } as IAction, + ] + : []), + { + title: t('View device details'), + 'data-testid': 'device-row-menu-view-details', + onClick: () => navigate({ route: ROUTE.DEVICE_DETAILS, postfix: deviceName }), + } as IAction, + ...(canResume && resumeAction + ? [ + resumeAction({ + resourceId: deviceName, + resourceName: deviceAlias, + disabledReason: resumeDisabledReason, + }), + ] + : []), + ...(canDecommission && decommissionAction + ? [ + decommissionAction({ + resourceId: deviceName, + resourceName: deviceAlias, + disabledReason: decommissionDisabledReason, + }), + ] + : []), + ]; + return ( )} @@ -99,42 +135,8 @@ const EnrolledDeviceTableRow = ({ )} {!hideActions && ( - - navigate({ route: ROUTE.DEVICE_EDIT, postfix: deviceName }), - ...editActionProps, - }, - ] - : []), - { - title: t('View device details'), - onClick: () => navigate({ route: ROUTE.DEVICE_DETAILS, postfix: deviceName }), - }, - ...(canResume && resumeAction - ? [ - resumeAction({ - resourceId: deviceName, - resourceName: deviceAlias, - disabledReason: resumeDisabledReason, - }), - ] - : []), - ...(canDecommission && decommissionAction - ? [ - decommissionAction({ - resourceId: deviceName, - resourceName: deviceAlias, - disabledReason: decommissionDisabledReason, - }), - ] - : []), - ]} - /> + + )} diff --git a/libs/ui-components/src/components/Fleet/CreateFleet/steps/GeneralInfoStep.tsx b/libs/ui-components/src/components/Fleet/CreateFleet/steps/GeneralInfoStep.tsx index 26ace609e..547290372 100644 --- a/libs/ui-components/src/components/Fleet/CreateFleet/steps/GeneralInfoStep.tsx +++ b/libs/ui-components/src/components/Fleet/CreateFleet/steps/GeneralInfoStep.tsx @@ -35,7 +35,7 @@ const GeneralInfoStep = ({ isEdit, isReadOnly }: GeneralInfoStepProps) => { validations={getDnsSubdomainValidations(t)} /> - + diff --git a/libs/ui-components/src/components/Fleet/FleetDetails/FleetDetailsPage.tsx b/libs/ui-components/src/components/Fleet/FleetDetails/FleetDetailsPage.tsx index 5d973157a..03a36d03e 100644 --- a/libs/ui-components/src/components/Fleet/FleetDetails/FleetDetailsPage.tsx +++ b/libs/ui-components/src/components/Fleet/FleetDetails/FleetDetailsPage.tsx @@ -48,6 +48,7 @@ const FleetDetailPage = () => { loading={isLoading} error={error} id={fleetId} + titleDataTestId="fleet-details-title" resourceLink={ROUTE.FLEETS} resourceType="Fleets" resourceTypeLabel={t('Fleets')} @@ -64,12 +65,18 @@ const FleetDetailPage = () => { {isManaged && ( - navigate({ route: ROUTE.FLEET_EDIT, postfix: fleetId })}> + navigate({ route: ROUTE.FLEET_EDIT, postfix: fleetId })} + > {t('View fleet configurations')} )} {canEdit && !isManaged && ( - navigate({ route: ROUTE.FLEET_EDIT, postfix: fleetId })}> + navigate({ route: ROUTE.FLEET_EDIT, postfix: fleetId })} + > {t('Edit fleet configurations')} )} diff --git a/libs/ui-components/src/components/Fleet/FleetRow.tsx b/libs/ui-components/src/components/Fleet/FleetRow.tsx index 1be2e234c..23f47b9f4 100644 --- a/libs/ui-components/src/components/Fleet/FleetRow.tsx +++ b/libs/ui-components/src/components/Fleet/FleetRow.tsx @@ -32,14 +32,16 @@ const useFleetActions = (fleetName: string, isManaged: boolean, canEdit: boolean actions.push({ title: t('View fleet details'), + 'data-testid': 'fleet-row-menu-view-details', onClick: () => navigate({ route: ROUTE.FLEET_DETAILS, postfix: fleetName }), - }); + } as IAction); // If users can't edit, the wizard will be in read-only mode actions.push({ title: isManaged || !canEdit ? t('View fleet configurations') : t('Edit fleet configurations'), + 'data-testid': isManaged || !canEdit ? 'fleet-row-menu-view-configurations' : 'fleet-row-menu-edit-configurations', onClick: () => navigate({ route: ROUTE.FLEET_EDIT, postfix: fleetName }), - }); + } as IAction); return actions; }; @@ -65,6 +67,7 @@ const FleetRow: React.FC = ({ if (canDelete) { actions.push({ title: t('Delete fleet'), + 'data-testid': 'fleet-row-menu-delete-fleet', onClick: onDeleteClick, tooltipProps: isManaged ? { @@ -74,7 +77,7 @@ const FleetRow: React.FC = ({ } : undefined, isAriaDisabled: isManaged, - }); + } as IAction); } return ( @@ -90,7 +93,7 @@ const FleetRow: React.FC = ({ /> - + {fleet.spec.template.spec.os?.image || '-'} @@ -101,11 +104,11 @@ const FleetRow: React.FC = ({ error={fleetRolloutError} /> - + {!hideActions && ( - + )} diff --git a/libs/ui-components/src/components/Fleet/FleetsPage.tsx b/libs/ui-components/src/components/Fleet/FleetsPage.tsx index 3cbe9d0e3..a192db877 100644 --- a/libs/ui-components/src/components/Fleet/FleetsPage.tsx +++ b/libs/ui-components/src/components/Fleet/FleetsPage.tsx @@ -54,14 +54,14 @@ const FleetPageActions = ({ createText }: { createText?: string }) => { {canCreateFleet && ( - )} {canImportFleet && ( - @@ -144,7 +144,12 @@ const FleetTable = () => { )} {canDelete && ( - diff --git a/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx b/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx index 6dcec1c24..592f75522 100644 --- a/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx +++ b/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx @@ -422,6 +422,7 @@ const CreateRepositoryFormContent = ({ isEdit, onClose, options, children }: Cre {showResourceSyncs && canCreateRS && ( navigate({ route: ROUTE.REPO_EDIT, postfix: repository.metadata.name }), - 'data-testid': `repository-dropdown-edit-${rowIndex}`, + 'data-testid': 'repository-row-menu-edit-repository', } as IAction); } if (canDelete) { actions.push({ title: t('Delete repository'), onClick: () => setDeleteModalRepoId(repository.metadata.name), - 'data-testid': `repository-dropdown-delete-${rowIndex}`, + 'data-testid': 'repository-row-menu-delete-repository', } as IAction); } return ( @@ -133,11 +134,7 @@ const RepositoryTableRow = ({ }} /> - + {getRepoTypeLabel(t, repository.spec.type)} {getRepoUrlOrRegistry(repository.spec) || '-'} @@ -146,7 +143,7 @@ const RepositoryTableRow = ({ {getLastTransitionTimeText(repository, t).text} {!!actions.length && ( - + )} diff --git a/libs/ui-components/src/components/Table/Table.tsx b/libs/ui-components/src/components/Table/Table.tsx index c10c3512c..f48bf9936 100644 --- a/libs/ui-components/src/components/Table/Table.tsx +++ b/libs/ui-components/src/components/Table/Table.tsx @@ -84,6 +84,7 @@ const Table: TableFC = ({ {!emptyData && onSelectAll && ( onSelectAll(isSelecting), isSelected: !!isAllSelected, diff --git a/libs/ui-components/src/components/form/LabelsField.tsx b/libs/ui-components/src/components/form/LabelsField.tsx index ebb7f053f..628155d47 100644 --- a/libs/ui-components/src/components/form/LabelsField.tsx +++ b/libs/ui-components/src/components/form/LabelsField.tsx @@ -15,12 +15,21 @@ type LabelsFieldProps = { addButtonText?: string; helperText?: React.ReactNode; onChangeCallback?: (newLabels: FlightCtlLabel[], hasErrors: boolean) => void; + /** Applied to the PatternFly `LabelGroup` root for E2E selectors (e.g. fleet wizard fleet labels). */ + labelGroupTestId?: string; }; const maxWidthDefaultLabel = '18ch'; // Can fit more chars as it doesn't have a "Close" button const maxWidthNonDefaultLabel = '16ch'; // Can fit less chars due to the "Close" button -const LabelsField = ({ name, onChangeCallback, addButtonText, helperText, isLoading }: LabelsFieldProps) => { +const LabelsField = ({ + name, + onChangeCallback, + addButtonText, + helperText, + isLoading, + labelGroupTestId, +}: LabelsFieldProps) => { const [{ value: labels }, meta, { setValue: setLabels }] = useField(name); const updateLabels = async (newLabels: FlightCtlLabel[]) => { const errors = await setLabels(newLabels, true); @@ -68,6 +77,7 @@ const LabelsField = ({ name, onChangeCallback, addButtonText, helperText, isLoad = ({ onClose, fl -