-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Duplicate current catalog view in a new route * Implement new catalog container * Add tests to the catalog container * Include process as global variable * Show error message if the checks catalog is empty * Extract check item to individual component
- Loading branch information
Showing
9 changed files
with
316 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React from 'react'; | ||
|
||
import NotificationBox from '@components/NotificationBox'; | ||
import LoadingBox from '@components/LoadingBox'; | ||
|
||
import { groupBy } from '@lib/lists'; | ||
|
||
import { EOS_ERROR } from 'eos-icons-react'; | ||
|
||
import CheckItem from './CheckItem'; | ||
|
||
const CatalogContainer = ({ | ||
onRefresh = () => {}, | ||
catalogData = [], | ||
catalogError = null, | ||
loading = false, | ||
}) => { | ||
if (loading) { | ||
return <LoadingBox text="Loading checks catalog..." />; | ||
} | ||
|
||
if (catalogError) { | ||
return ( | ||
<NotificationBox | ||
icon={<EOS_ERROR className="m-auto" color="red" size="xl" />} | ||
text={catalogError} | ||
buttonText="Try again" | ||
buttonOnClick={onRefresh} | ||
/> | ||
); | ||
} | ||
|
||
if (catalogData.length === 0) { | ||
return ( | ||
<NotificationBox | ||
icon={<EOS_ERROR className="m-auto" color="red" size="xl" />} | ||
text="Checks catalog is empty." | ||
buttonText="Try again" | ||
buttonOnClick={onRefresh} | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<div> | ||
{Object.entries(groupBy(catalogData, 'group')).map( | ||
([group, checks], idx) => ( | ||
<div | ||
key={idx} | ||
className="check-group bg-white shadow overflow-hidden sm:rounded-md mb-8" | ||
> | ||
<div className="bg-white px-4 py-5 border-b border-gray-200 sm:px-6"> | ||
<h3 className="text-lg leading-6 font-medium text-gray-900"> | ||
{group} | ||
</h3> | ||
</div> | ||
<ul role="list" className="divide-y divide-gray-200"> | ||
{checks.map((check) => ( | ||
<CheckItem | ||
key={check.id} | ||
checkID={check.id} | ||
premium={check.premium} | ||
description={check.description} | ||
remediation={check.remediation} | ||
/> | ||
))} | ||
</ul> | ||
</div> | ||
) | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default CatalogContainer; |
57 changes: 57 additions & 0 deletions
57
assets/js/components/ChecksCatalog/CatalogContainer.test.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React from 'react'; | ||
|
||
import { screen, within } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
|
||
import { faker } from '@faker-js/faker'; | ||
import { renderWithRouter } from '@lib/test-utils'; | ||
import { catalogCheckFactory } from '@lib/test-utils/factories'; | ||
|
||
import CatalogContainer from './CatalogContainer'; | ||
|
||
describe('ChecksCatalog CatalogContainer component', () => { | ||
it('should render the notification box', () => { | ||
renderWithRouter(<CatalogContainer catalogError={'some error'} />); | ||
|
||
expect(screen.getByText('some error')).toBeVisible(); | ||
expect(screen.getByRole('button')).toHaveTextContent('Try again'); | ||
}); | ||
|
||
it('should render the loading box', () => { | ||
renderWithRouter(<CatalogContainer loading={true} />); | ||
|
||
expect(screen.getByText('Loading checks catalog...')).toBeVisible(); | ||
}); | ||
|
||
it('should render an error message if the checks catalog is empty', () => { | ||
renderWithRouter(<CatalogContainer catalogData={[]} />); | ||
|
||
expect(screen.getByText('Checks catalog is empty.')).toBeVisible(); | ||
expect(screen.getByRole('button')).toHaveTextContent('Try again'); | ||
}); | ||
|
||
it('should render the checks catalog', () => { | ||
const groupName1 = faker.animal.cat(); | ||
const groupName2 = faker.animal.cat(); | ||
const group1 = catalogCheckFactory.buildList(5, { group: groupName1 }); | ||
const group2 = catalogCheckFactory.buildList(5, { group: groupName2 }); | ||
const catalog = group1.concat(group2); | ||
|
||
renderWithRouter( | ||
<CatalogContainer | ||
loading={false} | ||
catalogError={null} | ||
catalogData={catalog} | ||
/> | ||
); | ||
|
||
const groups = screen.getAllByRole('list'); | ||
expect(groups.length).toBe(2); | ||
|
||
for (let group of groups) { | ||
let { getAllByRole } = within(group); | ||
let checks = getAllByRole('listitem'); | ||
expect(checks.length).toBe(5); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import React from 'react'; | ||
|
||
import { Disclosure, Transition } from '@headlessui/react'; | ||
|
||
import ReactMarkdown from 'react-markdown'; | ||
import remarkGfm from 'remark-gfm'; | ||
|
||
const CheckItem = ({ checkID, premium = false, description, remediation }) => { | ||
return ( | ||
<li> | ||
<Disclosure> | ||
<Disclosure.Button | ||
as="div" | ||
className="flex justify-between w-full cursor-pointer hover:bg-gray-100" | ||
> | ||
<div className="check-row px-4 py-4 sm:px-6"> | ||
<div className="flex items-center"> | ||
<p className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> | ||
{checkID} | ||
</p> | ||
{premium > 0 && ( | ||
<p className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> | ||
Premium | ||
</p> | ||
)} | ||
</div> | ||
<div className="mt-2 sm:flex sm:justify-between"> | ||
<div className="sm:flex"> | ||
<ReactMarkdown | ||
className="markdown text-sm" | ||
remarkPlugins={[remarkGfm]} | ||
> | ||
{description} | ||
</ReactMarkdown> | ||
</div> | ||
</div> | ||
</div> | ||
</Disclosure.Button> | ||
<Transition | ||
enter="transition duration-100 ease-out" | ||
enterFrom="transform opacity-0" | ||
enterTo="transform opacity-100" | ||
leave="transition duration-100 ease-out" | ||
leaveFrom="transform opacity-100" | ||
leaveTo="transform opacity-0" | ||
> | ||
<Disclosure.Panel className="check-panel border-none"> | ||
<div className="px-8 py-4 sm:px-8"> | ||
<div className="px-4 py-4 sm:px-4 bg-slate-100 rounded"> | ||
<ReactMarkdown className="markdown" remarkPlugins={[remarkGfm]}> | ||
{remediation} | ||
</ReactMarkdown> | ||
</div> | ||
</div> | ||
</Disclosure.Panel> | ||
</Transition> | ||
</Disclosure> | ||
</li> | ||
); | ||
}; | ||
|
||
export default CheckItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import React from 'react'; | ||
|
||
import { screen } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import { renderWithRouter } from '@lib/test-utils'; | ||
import { catalogCheckFactory } from '@lib/test-utils/factories'; | ||
|
||
import CheckItem from './CheckItem'; | ||
|
||
describe('ChecksCatalog CheckItem component', () => { | ||
it('should show check information', () => { | ||
const check = catalogCheckFactory.build(); | ||
|
||
renderWithRouter( | ||
<CheckItem | ||
key={check.id} | ||
checkID={check.id} | ||
description={check.description} | ||
remediation={check.remediation} | ||
/> | ||
); | ||
|
||
expect(screen.getByText(check.id)).toBeVisible(); | ||
expect(screen.getByText(check.description)).toBeVisible(); | ||
}); | ||
|
||
it('should show premium badge if the check is premium', () => { | ||
const check = catalogCheckFactory.build(); | ||
|
||
renderWithRouter( | ||
<CheckItem | ||
key={check.id} | ||
checkID={check.id} | ||
premium={true} | ||
description={check.description} | ||
remediation={check.remediation} | ||
/> | ||
); | ||
|
||
expect(screen.getByText('Premium')).toBeVisible(); | ||
}); | ||
|
||
it('should show check remediation when the row is clicked', () => { | ||
const check = catalogCheckFactory.build(); | ||
|
||
renderWithRouter( | ||
<CheckItem | ||
key={check.id} | ||
checkID={check.id} | ||
description={check.description} | ||
remediation={check.remediation} | ||
/> | ||
); | ||
|
||
const checks = screen.getAllByRole('listitem'); | ||
const checkDiv = checks[0].querySelector('div'); | ||
|
||
expect(screen.queryByText(check.remediation)).not.toBeInTheDocument(); | ||
userEvent.click(checkDiv); | ||
expect(screen.getByText(check.remediation)).toBeVisible(); | ||
userEvent.click(checkDiv); | ||
expect(screen.queryByText(check.remediation)).not.toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
|
||
import axios from 'axios'; | ||
|
||
import CatalogContainer from './CatalogContainer'; | ||
|
||
const wandaURL = process.env.WANDA_URL; | ||
|
||
export const ChecksCatalogNew = () => { | ||
const [catalogError, setError] = useState(null); | ||
const [loading, setLoaded] = useState(true); | ||
const [catalogData, setCatalog] = useState([]); | ||
|
||
useEffect(() => { | ||
getCatalog(); | ||
}, []); | ||
|
||
const getCatalog = () => { | ||
setLoaded(true); | ||
axios | ||
.get(`${wandaURL}/api/checks/catalog`) | ||
.then((catalog) => { | ||
setCatalog(catalog.data.items); | ||
}) | ||
.catch((error) => { | ||
setError(error.message); | ||
}) | ||
.finally(() => { | ||
setLoaded(false); | ||
}); | ||
}; | ||
|
||
return ( | ||
<CatalogContainer | ||
onRefresh={() => getCatalog()} | ||
catalogData={catalogData} | ||
catalogError={catalogError} | ||
loading={loading} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
import ChecksCatalog from './ChecksCatalog'; | ||
|
||
export { ChecksCatalogNew } from './ChecksCatalogNew'; | ||
|
||
export default ChecksCatalog; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters