Skip to content

Commit

Permalink
fix(RHINENG-14893): Correct empty state SystemDetails (#2312)
Browse files Browse the repository at this point in the history
- Refactor: Remove GQL implementation
- Correct empty state usage on System details page
- Handle number of API calls depending on Page
  • Loading branch information
LightOfHeaven1994 authored Jan 23, 2025
1 parent a019538 commit fb2602c
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 363 deletions.
72 changes: 6 additions & 66 deletions src/SmartComponents/SystemDetails/Details.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import React from 'react';
import propTypes from 'prop-types';
import { Bullseye } from '@patternfly/react-core';
import ComplianceEmptyState from 'PresentationalComponents/ComplianceEmptyState';
import { useQuery } from '@apollo/client';
import { Spinner } from '@redhat-cloud-services/frontend-components/Spinner';
import './compliance.scss';
import { ErrorCard } from 'PresentationalComponents';
import useAPIV2FeatureFlag from '../../Utilities/hooks/useAPIV2FeatureFlag';
import SystemPoliciesAndRules from './SystemPoliciesAndRules';
import { SYSTEM_QUERY } from './constants';
import useTestResults from './useTestResults';
import SystemPoliciesAndRulesRest from './SystemPoliciesAndRulesRest';
import SystemPoliciesAndRules from './SystemPoliciesAndRules';
import EmptyState from './EmptyState';

export const DetailsRest = ({ inventoryId, hidePassed, ...props }) => {
export const Details = ({ inventoryId, hidePassed, system, ...props }) => {
const { testResults, testResultsLoading } = useTestResults(inventoryId);

return (
Expand All @@ -23,9 +18,9 @@ export const DetailsRest = ({ inventoryId, hidePassed, ...props }) => {
</Bullseye>
) : testResults.length === 0 ? (
// we render no policy cards nor rules table if there are no reporting policies
<ComplianceEmptyState title="No policies are reporting for this system" />
<EmptyState inventoryId={inventoryId} system={system} />
) : (
<SystemPoliciesAndRulesRest
<SystemPoliciesAndRules
{...props}
systemId={inventoryId}
reportTestResults={testResults}
Expand All @@ -36,65 +31,10 @@ export const DetailsRest = ({ inventoryId, hidePassed, ...props }) => {
);
};

DetailsRest.propTypes = {
inventoryId: propTypes.string,
hidePassed: propTypes.bool,
};

export const DetailsGraphQL = ({ inventoryId, hidePassed, ...props }) => {
const { data, error, loading } = useQuery(SYSTEM_QUERY, {
variables: { systemId: inventoryId },
fetchPolicy: 'no-cache',
});
const is404 = error?.networkError?.statusCode === 404;

if (loading) {
return <Spinner />;
}

if (error && !is404) {
// network errors other than 404 are unexpected
return <ErrorCard />;
}

return (
<div className="ins-c-compliance__scope">
{!data?.system || is404 ? (
<ComplianceEmptyState title="No policies are reporting for this system" />
) : (
<SystemPoliciesAndRules
{...props}
hidePassed={hidePassed}
data={data}
loading={loading}
/>
)}
</div>
);
};

DetailsGraphQL.propTypes = {
inventoryId: propTypes.string,
hidePassed: propTypes.bool,
};

export const Details = (props) => {
const apiV2Enabled = useAPIV2FeatureFlag();

return apiV2Enabled === undefined ? (
<Bullseye>
<Spinner />
</Bullseye>
) : apiV2Enabled ? (
<DetailsRest {...props} />
) : (
<DetailsGraphQL {...props} />
);
};

Details.propTypes = {
inventoryId: propTypes.string,
hidePassed: propTypes.bool,
system: propTypes.object,
};

export default Details;
33 changes: 26 additions & 7 deletions src/SmartComponents/SystemDetails/EmptyState.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,39 @@ import propTypes from 'prop-types';
import { NotConnected } from '@redhat-cloud-services/frontend-components/NotConnected';
import NoPoliciesState from './NoPoliciesState';
import NoReportsState from './NoReportsState';
import useSystem from 'Utilities/hooks/api/useSystem';
import { Bullseye, Spinner } from '@patternfly/react-core';

const EmptyState = ({ system }) => {
if (!system?.insightsId) {
const EmptyState = ({ inventoryId, system }) => {
// request system data in case Inventory details Compliance opened
const { data: { data } = {} } = useSystem({
params: [inventoryId],
skip: system,
});
const policiesCount = system?.policies.length || data?.policies.length;
const insightsId = system?.insights_id || data?.insights_id;

if (!system && !data) {
return (
<Bullseye>
<Spinner />
</Bullseye>
);
}

if (!insightsId) {
return <NotConnected />;
}

if (policiesCount === 0) {
return <NoPoliciesState />;
} else {
if (!system?.hasPolicy) {
return <NoPoliciesState system={system} />;
} else if (system?.hasPolicy && system?.testResultProfiles?.length === 0) {
return <NoReportsState system={system} />;
}
return <NoReportsState policiesCount={policiesCount} />;
}
};

EmptyState.propTypes = {
inventoryId: propTypes.string,
system: propTypes.object,
};

Expand Down
109 changes: 109 additions & 0 deletions src/SmartComponents/SystemDetails/EmptyState.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import TestWrapper from '@/Utilities/TestWrapper';

import EmptyState from './EmptyState.js';
import useSystem from 'Utilities/hooks/api/useSystem';

jest.mock('Utilities/hooks/api/useSystem', () => jest.fn());

describe('EmptyState for systemDetails', () => {
it('expect to render loading state while waiting for data', () => {
useSystem.mockImplementation(() => ({
data: { data: undefined },
error: undefined,
loading: undefined,
}));
render(
<TestWrapper>
<EmptyState inventoryId={'123'} />
</TestWrapper>
);

expect(screen.getByLabelText('Contents')).toHaveAttribute(
'aria-valuetext',
'Loading...'
);
});

it('expect to render NotConnected component', () => {
useSystem.mockImplementation(() => ({
data: { data: { display_name: 'foo', policies: [], insights_id: '' } },
error: undefined,
loading: undefined,
}));
render(
<TestWrapper>
<EmptyState inventoryId={'123'} />
</TestWrapper>
);

expect(
screen.getByText('This system isn’t connected to Insights yet')
).toBeInTheDocument();
});

it('expect to render NoPoliciesState component', () => {
useSystem.mockImplementation(() => ({
data: { data: { display_name: 'foo', policies: [], insights_id: '123' } },
error: undefined,
loading: undefined,
}));
render(
<TestWrapper>
<EmptyState inventoryId={'123'} />
</TestWrapper>
);

expect(
screen.getByText(
'This system is not part of any SCAP policies defined within Compliance.'
)
).toBeInTheDocument();
});

it('expect to render NoReportsState component', () => {
useSystem.mockImplementation(() => ({
data: {
data: { display_name: 'foo', policies: [{}, {}], insights_id: '123' },
},
error: undefined,
loading: undefined,
}));
render(
<TestWrapper>
<EmptyState inventoryId={'123'} />
</TestWrapper>
);

expect(screen.getByText('No results reported')).toBeInTheDocument();
expect(
screen.getByText(
`This system is part of 2 policies, but has not returned any results.`
)
).toBeInTheDocument();
});

it('expect to render NoReportsState component', () => {
render(
<TestWrapper>
<EmptyState
inventoryId={'123'}
system={{ insights_id: '123', policies: [{}] }}
/>
</TestWrapper>
);

expect(useSystem).toHaveBeenCalledWith({
params: ['123'],
skip: { insights_id: '123', policies: [{}] }, // Ensure correct 'skip' value
});

expect(screen.getByText('No results reported')).toBeInTheDocument();
expect(
screen.getByText(
`This system is part of 1 policy, but has not returned any results.`
)
).toBeInTheDocument();
});
});
12 changes: 5 additions & 7 deletions src/SmartComponents/SystemDetails/NoReportsState.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
EmptyStateIcon,
EmptyStateHeader,
} from '@patternfly/react-core';
import { pluralize } from '@patternfly/react-core';

const NoReportsState = ({ system }) => (
const NoReportsState = ({ policiesCount }) => (
<Bullseye>
<EmptyState
style={{
Expand All @@ -31,9 +32,8 @@ const NoReportsState = ({ system }) => (
headingLevel="h1"
/>
<EmptyStateBody>
This system is part of {system?.policies?.length}
{system?.policies?.length > 1 ? ' policies' : ' policy'}, but has not
returned any results.
This system is part of {pluralize(policiesCount, 'policy', 'policies')},
but has not returned any results.
</EmptyStateBody>
<EmptyStateBody>
Reports are returned when the system checks into Insights. By default,
Expand All @@ -44,9 +44,7 @@ const NoReportsState = ({ system }) => (
);

NoReportsState.propTypes = {
system: propTypes.shape({
policies: propTypes.array,
}),
policiesCount: propTypes.number,
};

export default NoReportsState;
4 changes: 2 additions & 2 deletions src/SmartComponents/SystemDetails/NoReportsState.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import NoReportsState from './NoReportsState';

describe('NoReportsState', () => {
it('with a system having policies', () => {
render(<NoReportsState system={{ policies: [{}] }} />);
render(<NoReportsState policiesCount={1} />);

expect(
screen.getByText(
Expand All @@ -15,7 +15,7 @@ describe('NoReportsState', () => {
});

it('with a system having multiple policies', () => {
render(<NoReportsState system={{ policies: [{}, {}] }} />);
render(<NoReportsState policiesCount={2} />);

expect(
screen.getByText(
Expand Down
Loading

0 comments on commit fb2602c

Please sign in to comment.