Skip to content

Commit

Permalink
Merge pull request #77 from ral-facilities/delete-a-catalogue-item-#8
Browse files Browse the repository at this point in the history
Delete a catalogue item #8
  • Loading branch information
joshuadkitenge authored Oct 20, 2023
2 parents fc1464e + 2b16406 commit 775ddc3
Show file tree
Hide file tree
Showing 13 changed files with 461 additions and 38 deletions.
13 changes: 4 additions & 9 deletions cypress/e2e/catalogue/catalogueCategory.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,10 @@ describe('Catalogue Category', () => {
});

it('when root has no data it displays no catagories error message', () => {
cy.window().then(async (window) => {
// Reference global instances set in "src/mocks/browser.js".

const { worker, rest } = window.msw;
worker.use(
rest.get('/v1/catalogue-categories/', (req, res, ctx) => {
return res(ctx.status(200), ctx.json([]));
})
);
cy.editEndpointResponse({
url: '/v1/catalogue-categories/',
data: [],
statusCode: 200,
});
cy.findByText(
'There are no catalogue categories. Please add a category using the plus icon in the top left of your screen'
Expand Down
43 changes: 42 additions & 1 deletion cypress/e2e/catalogue/catalogueItems.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe('Catalogue Items', () => {
cy.findByText('Cameras 4').should('exist');
});

it.only('navigates to the landing page, toggles the properties and navigates back to the table view', () => {
it('navigates to the landing page, toggles the properties and navigates back to the table view', () => {
cy.findByText('Cameras 1').click();
cy.findByText(
'High-resolution cameras for beam characterization. 1'
Expand Down Expand Up @@ -181,6 +181,47 @@ describe('Catalogue Items', () => {

cy.findByText('Motion').should('exist');
});

it('displays error message when user tries to delete a catalogue item that has children elements', () => {
cy.visit(
'/inventory-management-system/catalogue/beam-characterization/energy-meters'
);
cy.findByRole('button', {
name: 'Delete Energy Meters 27 catalogue item',
}).click();

cy.findByRole('button', { name: 'Continue' }).click();

cy.findByRole('dialog')
.should('be.visible')
.within(() => {
cy.contains(
'Catalogue category has children elements and cannot be deleted, please delete the children elements first'
);
});
});

it('delete a catalogue item', () => {
cy.visit(
'/inventory-management-system/catalogue/beam-characterization/energy-meters'
);
cy.findByRole('button', {
name: 'Delete Energy Meters 26 catalogue item',
}).click();

cy.startSnoopingBrowserMockedRequest();

cy.findByRole('button', { name: 'Continue' }).click();

cy.findBrowserMockedRequests({
method: 'DELETE',
url: '/v1/catalogue-items/:id',
}).should((patchRequests) => {
expect(patchRequests.length).equal(1);
const request = patchRequests[0];
expect(request.url.toString()).to.contain('89');
});
});
it('checks the href property of the manufacturer link', () => {
// Find the element containing the link
const row = cy.findByRole('row', { name: 'Cameras 1 row' });
Expand Down
28 changes: 25 additions & 3 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ Cypress.Commands.add('clearMocks', () => {
mockedRequests = [];
});

Cypress.Commands.add('editEndpointResponse', ({ url, data, statusCode }) => {
cy.window().then((window) => {
const { worker, rest } = window.msw;

worker.use(
rest.get(url, (req, res, ctx) => {
return res(ctx.status(statusCode), ctx.json(data));
})
);
});
});

Cypress.Commands.add('startSnoopingBrowserMockedRequest', () => {
cy.window().then((window) => {
const worker = window?.msw?.worker;
Expand Down Expand Up @@ -91,12 +103,12 @@ declare global {
* Clear all mocks
* @example cy.clearMocks()
*/
clearMocks(): Chainable<JQuery<HTMLElement>>
clearMocks(): Chainable<JQuery<HTMLElement>>;
/**
* Use before findBrowserMockedRequests for checking specific requests were sent
* @example cy.startSnoopingBrowserMockedRequest()
*/
startSnoopingBrowserMockedRequest(): Chainable<JQuery<HTMLElement>>
startSnoopingBrowserMockedRequest(): Chainable<JQuery<HTMLElement>>;
/**
* Returns a request that was recorded after 'startSnoopingBrowserMockedRequest' was called
*
Expand All @@ -113,7 +125,17 @@ declare global {
);
});
*/
findBrowserMockedRequests({method, url}: any): Chainable<unknown>
editEndpointResponse({ url, data, statusCode }: any): Chainable<unknown>;
/**
* Edits the response of the endpoint request
*
* @example cy.editEndpointResponse({
url: '/v1/catalogue-categories/',
data: [],
statusCode: 200,
});
*/
findBrowserMockedRequests({ method, url }: any): Chainable<unknown>;
}
}
}
31 changes: 30 additions & 1 deletion src/api/catalogueItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
useAddCatalogueItem,
useCatalogueItem,
useCatalogueItems,
useDeleteCatalogueItem,
} from './catalogueItem';
import { catalogueItemData, hooksWrapperWithProviders } from '../setupTests';
import { AddCatalogueItem } from '../app.types';
import { AddCatalogueItem, CatalogueItem } from '../app.types';

describe('catalogue items api functions', () => {
afterEach(() => {
Expand Down Expand Up @@ -105,4 +106,32 @@ describe('catalogue items api functions', () => {
'sends axios request to fetch catalogue item and throws an appropriate error on failure'
);
});

describe('useDeleteCatalogueItem', () => {
let mockDataView: CatalogueItem;
beforeEach(() => {
mockDataView = {
name: 'test',
id: '1',
catalogue_category_id: '3',
description: '',
properties: [],
};
});
it('posts a request to delete a catalogue Item and returns successful response', async () => {
const { result } = renderHook(() => useDeleteCatalogueItem(), {
wrapper: hooksWrapperWithProviders(),
});
expect(result.current.isIdle).toBe(true);
result.current.mutate(mockDataView);
await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});
expect(result.current.data).toEqual('');
});

it.todo(
'sends axios request to delete a catalogue Item and throws an appropriate error on failure'
);
});
});
36 changes: 35 additions & 1 deletion src/api/catalogueItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const useCatalogueItem = (
catalogueCategoryId: string
): UseQueryResult<CatalogueItem, AxiosError> => {
return useQuery<CatalogueItem, AxiosError>(
['CatalogueItems', catalogueCategoryId],
['CatalogueItem', catalogueCategoryId],
(params) => {
return fetchCatalogueItem(catalogueCategoryId);
},
Expand All @@ -117,3 +117,37 @@ export const useCatalogueItem = (
}
);
};

const deleteCatalogueItem = async (
catalogueItem: CatalogueItem
): Promise<void> => {
let apiUrl: string;
apiUrl = '';
const settingsResult = await settings;
if (settingsResult) {
apiUrl = settingsResult['apiUrl'];
}
return axios
.delete(`${apiUrl}/v1/catalogue-items/${catalogueItem.id}`, {})
.then((response) => response.data);
};

export const useDeleteCatalogueItem = (): UseMutationResult<
void,
AxiosError,
CatalogueItem
> => {
const queryClient = useQueryClient();
return useMutation(
(catalogueItem: CatalogueItem) => deleteCatalogueItem(catalogueItem),
{
onError: (error) => {
console.log('Got error ' + error.message);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['CatalogueItems'] });
queryClient.removeQueries({ queryKey: ['CatalogueItem'] });
},
}
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ describe('Catalogue Items Landing Page', () => {
).toBeInTheDocument();
});
});

it('shows the loading indicator', async () => {
createView('/inventory-management-system/catalogue/items/1');

await waitFor(() => {
expect(screen.getByRole('progressbar')).toBeInTheDocument();
});
});
it('toggles the manufacturer so it is either visible or hidden', async () => {
createView('/inventory-management-system/catalogue/items/1');
await waitFor(() => {
Expand Down
40 changes: 26 additions & 14 deletions src/catalogue/items/catalogueItemsLandingPage.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import { Box, Button, Collapse, Link as MuiLink } from '@mui/material';
import {
Box,
Button,
Collapse,
LinearProgress,
Link as MuiLink,
} from '@mui/material';
import { useCatalogueCategoryById } from '../../api/catalogueCategory';

function CatalogueItemsLandingPage() {
Expand Down Expand Up @@ -213,19 +219,25 @@ function CatalogueItemsLandingPage() {
</Box>
</Grid>
)}
{!catalogueItemIdData && !catalogueItemIdDataLoading && (
<Box
sx={{
width: '100%',
justifyContent: 'center',
marginTop: '8px',
}}
>
<Typography sx={{ fontWeight: 'bold' }}>No result found</Typography>
<Typography>
This item doesn't exist. Please click the Home button to navigate to
the catalogue home
</Typography>
{!catalogueItemIdDataLoading ? (
!catalogueItemIdData && (
<Box
sx={{
width: '100%',
justifyContent: 'center',
marginTop: '8px',
}}
>
<Typography sx={{ fontWeight: 'bold' }}>No result found</Typography>
<Typography>
This item doesn't exist. Please click the Home button to navigate
to the catalogue home
</Typography>
</Box>
)
) : (
<Box sx={{ width: '100%' }}>
<LinearProgress />
</Box>
)}
</Grid>
Expand Down
28 changes: 27 additions & 1 deletion src/catalogue/items/catalogueItemsTable.component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,35 @@ describe('Catalogue Items Table', () => {
});
});

it('navigates to the manufacturer url', async () => {
it('opens the delete catalogue item dialog and can delete an item', async () => {
createView();

await waitFor(() => {
expect(
screen.getByLabelText(
'Catalogue item description: Precision energy meters for accurate measurements. 26'
)
).toBeInTheDocument();
});

const deleteButton = screen.getByRole('button', {
name: 'Delete Energy Meters 26 catalogue item',
});

await user.click(deleteButton);

await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});

const continueButton = screen.getByRole('button', { name: 'Continue' });
await user.click(continueButton);
await waitFor(() => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
it('navigates to the manufacturer url', async () => {
createView();
await waitFor(() => {
expect(
screen.getByRole('row', { name: 'Energy Meters 26 row' })
Expand Down
20 changes: 19 additions & 1 deletion src/catalogue/items/catalogueItemsTable.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import {
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
import { CatalogueCategory } from '../../app.types';
import { CatalogueItem, CatalogueCategory } from '../../app.types';
import { useCatalogueItems } from '../../api/catalogueItem';
import { Link } from 'react-router-dom';
import DeleteCatalogueItemsDialog from './deleteCatalogueItemDialog.component';

export interface CatalogueItemsTableProps {
parentInfo: CatalogueCategory;
Expand All @@ -36,6 +37,13 @@ const CatalogueItemsTable = (props: CatalogueItemsTableProps) => {
const theme = useTheme();

const [hoveredRow, setHoveredRow] = React.useState<number | null>(null);

const [deleteItemDialogOpen, setDeleteItemDialogOpen] =
React.useState<boolean>(false);

const [selectedCatalogueItem, setSelectedCatalogueItem] = React.useState<
CatalogueItem | undefined
>(undefined);
return (
<TableContainer style={{ height: tableHeight }}>
<Table stickyHeader>
Expand Down Expand Up @@ -150,6 +158,10 @@ const CatalogueItemsTable = (props: CatalogueItemsTableProps) => {
<IconButton
size="small"
aria-label={`Delete ${item.name} catalogue item`}
onClick={() => {
setDeleteItemDialogOpen(true);
setSelectedCatalogueItem(item);
}}
>
<DeleteIcon />
</IconButton>
Expand Down Expand Up @@ -272,6 +284,12 @@ const CatalogueItemsTable = (props: CatalogueItemsTableProps) => {
</Typography>
</Box>
)}
<DeleteCatalogueItemsDialog
open={deleteItemDialogOpen}
onClose={() => setDeleteItemDialogOpen(false)}
catalogueItem={selectedCatalogueItem}
onChangeCatalogueItem={setSelectedCatalogueItem}
/>
</TableContainer>
);
};
Expand Down
Loading

0 comments on commit 775ddc3

Please sign in to comment.