diff --git a/assets/js/components/ClusterDetails/ClusterDetailsPage.jsx b/assets/js/components/ClusterDetails/ClusterDetailsPage.jsx index 2e53a01882..c3c948ad2f 100644 --- a/assets/js/components/ClusterDetails/ClusterDetailsPage.jsx +++ b/assets/js/components/ClusterDetails/ClusterDetailsPage.jsx @@ -2,17 +2,13 @@ import React, { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; -import { - getCluster, - getClusterHosts, - getClusterHostIDs, -} from '@state/selectors/cluster'; +import { getCluster, getClusterHosts } from '@state/selectors/cluster'; import { updateLastExecution, executionRequested, } from '@state/actions/lastExecutions'; import { getLastExecution } from '@state/selectors/lastExecutions'; -import ClusterDetails from './ClusterDetails'; +import HanaClusterDetails from './HanaClusterDetails'; import { getClusterName } from '../ClusterLink'; export function ClusterDetailsPage() { @@ -23,7 +19,6 @@ export function ClusterDetailsPage() { const dispatch = useDispatch(); const lastExecution = useSelector(getLastExecution(clusterID)); - const hosts = useSelector(getClusterHostIDs(clusterID)); useEffect(() => { dispatch(updateLastExecution(clusterID)); }, [dispatch]); @@ -34,33 +29,35 @@ export function ClusterDetailsPage() { return
Loading...
; } - const renderedNodes = cluster.details?.nodes?.map((node) => ({ - ...node, - ...clusterHosts.find(({ hostname }) => hostname === node.name), - })); - const hasSelectedChecks = cluster.selected_checks.length > 0; - return ( - - dispatch( - executionRequested(clusterID, hostList, checks, navigateFunction) - ) - } - navigate={navigate} - /> - ); + switch (cluster.type) { + case 'hana_scale_up': + case 'hana_scale_out': + return ( + + dispatch( + executionRequested(clusterID, hostList, checks, navigateFunction) + ) + } + navigate={navigate} + /> + ); + case 'ascs_ers': + return
ASCS/ERS
; + default: + return
Unknown cluster type
; + } } diff --git a/assets/js/components/ClusterDetails/ClusterDetailsPage.test.jsx b/assets/js/components/ClusterDetails/ClusterDetailsPage.test.jsx new file mode 100644 index 0000000000..6f70c1405f --- /dev/null +++ b/assets/js/components/ClusterDetails/ClusterDetailsPage.test.jsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import { screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { withState, renderWithRouterMatch } from '@lib/test-utils'; + +import { clusterFactory } from '@lib/test-utils/factories'; + +import { ClusterDetailsPage } from './ClusterDetailsPage'; + +describe('ClusterDetails ClusterDetailsPage component', () => { + it.each([ + { type: 'hana_scale_up', label: 'HANA scale-up' }, + { type: 'ascs_ers', label: 'ASCS/ERS' }, + { type: 'unknwon', label: 'Unknown cluster type' }, + ])( + 'should display the $type details based on cluster type', + ({ type, label }) => { + const cluster = clusterFactory.build({ type }); + const initialState = { + clustersList: { clusters: [cluster] }, + hostsList: { hosts: [] }, + lastExecutions: { + [cluster.id]: { data: null, loading: false, error: null }, + }, + }; + + const [statefulClusterDetailsPage, _] = withState( + , + initialState + ); + + renderWithRouterMatch(statefulClusterDetailsPage, { + path: 'clusters/:clusterID', + route: `/clusters/${cluster.id}`, + }); + + expect(screen.getByText(label)).toBeInTheDocument(); + } + ); +}); diff --git a/assets/js/components/ClusterDetails/ClusterDetails.jsx b/assets/js/components/ClusterDetails/HanaClusterDetails.jsx similarity index 95% rename from assets/js/components/ClusterDetails/ClusterDetails.jsx rename to assets/js/components/ClusterDetails/HanaClusterDetails.jsx index db8dcd2189..88e202bb68 100644 --- a/assets/js/components/ClusterDetails/ClusterDetails.jsx +++ b/assets/js/components/ClusterDetails/HanaClusterDetails.jsx @@ -54,7 +54,7 @@ const siteDetailsConfig = { ], }; -function ClusterDetails({ +function HanaClusterDetails({ clusterID, clusterName, selectedChecks, @@ -64,12 +64,16 @@ function ClusterDetails({ cibLastWritten, sid, provider, - clusterNodes, details, lastExecution, onStartExecution, navigate, }) { + const enrichedNodes = details?.nodes?.map((node) => ({ + ...node, + ...hosts.find(({ hostname }) => hostname === node.name), + })); + return (
Back to Clusters @@ -106,7 +110,7 @@ function ClusterDetails({ cssClasses="flex rounded relative ml-0.5 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-gray-400" clusterId={clusterID} disabled={!hasSelectedChecks} - hosts={hosts} + hosts={hosts.map(({ id }) => id)} checks={selectedChecks} onStartExecution={onStartExecution} > @@ -197,7 +201,7 @@ function ClusterDetails({ site === siteName)} + data={enrichedNodes.filter(({ site }) => site === siteName)} /> ))} @@ -207,4 +211,4 @@ function ClusterDetails({ ); } -export default ClusterDetails; +export default HanaClusterDetails; diff --git a/assets/js/components/ClusterDetails/ClusterDetails.stories.jsx b/assets/js/components/ClusterDetails/HanaClusterDetails.stories.jsx similarity index 79% rename from assets/js/components/ClusterDetails/ClusterDetails.stories.jsx rename to assets/js/components/ClusterDetails/HanaClusterDetails.stories.jsx index 1455c394ad..74c9134a23 100644 --- a/assets/js/components/ClusterDetails/ClusterDetails.stories.jsx +++ b/assets/js/components/ClusterDetails/HanaClusterDetails.stories.jsx @@ -7,7 +7,7 @@ import { hostFactory, } from '@lib/test-utils/factories'; -import ClusterDetails from './ClusterDetails'; +import HanaClusterDetails from './HanaClusterDetails'; const { id: clusterID, @@ -34,14 +34,9 @@ const hosts = [ hostFactory.build({ hostname: details.nodes[1].name }), ]; -const clusterNodes = details.nodes.map((node) => ({ - ...node, - ...hosts.find(({ hostname }) => hostname === node.name), -})); - export default { - title: 'ClusterDetails', - components: ClusterDetails, + title: 'HanaClusterDetails', + components: HanaClusterDetails, decorators: [ (Story) => ( @@ -63,12 +58,11 @@ export const Hana = { clusterName, selectedChecks, hasSelectedChecks: true, - hosts: hosts.map(({ id: hostID }) => hostID), + hosts, clusterType, cibLastWritten, sid, provider, - clusterNodes, details, lastExecution, onStartExecution: () => {}, @@ -76,7 +70,7 @@ export const Hana = { }, render: (args) => ( - + ), }; diff --git a/assets/js/components/ClusterLink.jsx b/assets/js/components/ClusterLink.jsx index 0bed888482..7745df8d33 100644 --- a/assets/js/components/ClusterLink.jsx +++ b/assets/js/components/ClusterLink.jsx @@ -3,6 +3,8 @@ import React from 'react'; import { Link } from 'react-router-dom'; import classNames from 'classnames'; +import { CLUSTER_TYPES } from '@lib/model'; + export const getClusterName = (cluster) => cluster?.name || cluster?.id; function ClusterLink({ cluster }) { @@ -11,7 +13,7 @@ function ClusterLink({ cluster }) { 'truncate w-32 inline-block align-middle' ); - if (cluster?.type === 'hana_scale_up' || cluster?.type === 'hana_scale_out') { + if (CLUSTER_TYPES.includes(cluster?.type)) { return ( { + it.each(['hana_scale_up', 'hana_scale_out', 'ascs_ers'])( + 'should render a link if the cluster type is %s', + (type) => { + const cluster = clusterFactory.build({ type }); + + renderWithRouter(); + + const link = screen.getByRole('link'); + + expect(link).toHaveTextContent(cluster.name); + expect(link).toHaveAttribute('href', `/clusters/${cluster.id}`); + } + ); + + it('should show a simple text if the cluster type is unknown', () => { + const cluster = clusterFactory.build({ type: 'unknown' }); + + renderWithRouter(); + + expect(screen.queryByRole('link')).not.toBeInTheDocument(); + expect(screen.getByText(cluster.name)).toBeInTheDocument(); + }); + + it('should show the cluster ID if the name is not available', () => { + const cluster = clusterFactory.build({ name: '' }); + + renderWithRouter(); + + expect(screen.getByText(cluster.id)).toBeInTheDocument(); + }); +}); diff --git a/assets/js/lib/model/index.js b/assets/js/lib/model/index.js index 7775d47144..79a5b8a7b8 100644 --- a/assets/js/lib/model/index.js +++ b/assets/js/lib/model/index.js @@ -12,3 +12,5 @@ export const isValidTargetType = (targetType) => export const UNKNOWN_PROVIDER = 'unknown'; export const VMWARE_PROVIDER = 'vmware'; + +export const CLUSTER_TYPES = ['hana_scale_up', 'hana_scale_out', 'ascs_ers']; diff --git a/assets/js/lib/test-utils/factories/clusters.js b/assets/js/lib/test-utils/factories/clusters.js index 13106ce069..503ec65e96 100644 --- a/assets/js/lib/test-utils/factories/clusters.js +++ b/assets/js/lib/test-utils/factories/clusters.js @@ -10,6 +10,11 @@ const clusterTypeEnum = () => const hanaStatus = () => faker.helpers.arrayElement(['Primary', 'Failed']); +export const sbdDevicesFactory = Factory.define(() => ({ + device: faker.system.filePath(), + status: faker.helpers.arrayElement(['healthy', 'unhealthy']), +})); + export const clusterResourceFactory = Factory.define(() => ({ id: faker.datatype.uuid(), role: faker.animal.bear(), @@ -18,7 +23,7 @@ export const clusterResourceFactory = Factory.define(() => ({ fail_count: faker.datatype.number(), })); -export const clusterDetailsNodesFactory = Factory.define(() => ({ +export const hanaClusterDetailsNodesFactory = Factory.define(() => ({ name: faker.animal.dog(), site: faker.address.city(), virtual_ip: faker.internet.ip(), @@ -33,12 +38,18 @@ export const clusterDetailsNodesFactory = Factory.define(() => ({ resources: clusterResourceFactory.buildList(5), })); -export const sbdDevicesFactory = Factory.define(() => ({ - device: faker.system.filePath(), - status: faker.helpers.arrayElement(['healthy', 'unhealthy']), +export const hanaClusterDetailsFactory = Factory.define(() => ({ + fencing_type: 'external/sbd', + nodes: hanaClusterDetailsNodesFactory.buildList(2), + sbd_devices: sbdDevicesFactory.buildList(3), + secondary_sync_state: 'SOK', + sr_health_state: '4', + stopped_resources: clusterResourceFactory.buildList(2), + system_replication_mode: 'sync', + system_replication_operation_mode: 'logreplay', })); -export const clusterFactory = Factory.define(({ sequence }) => ({ +export const clusterFactory = Factory.define(({ sequence, params }) => ({ id: faker.datatype.uuid(), name: `${faker.name.firstName()}_${sequence}`, sid: faker.random.alphaNumeric(3, { casing: 'upper' }), @@ -49,14 +60,5 @@ export const clusterFactory = Factory.define(({ sequence }) => ({ selected_checks: [], provider: cloudProviderEnum(), cib_last_written: day(faker.date.recent()).format(), - details: { - fencing_type: 'external/sbd', - nodes: clusterDetailsNodesFactory.buildList(2), - sbd_devices: sbdDevicesFactory.buildList(3), - secondary_sync_state: 'SOK', - sr_health_state: '4', - stopped_resources: clusterResourceFactory.buildList(2), - system_replication_mode: 'sync', - system_replication_operation_mode: 'logreplay', - }, + details: hanaClusterDetailsFactory.build(params.details), }));