Skip to content

Commit

Permalink
Merge pull request #460 from ral-facilities/creating-mulitple-items-#453
Browse files Browse the repository at this point in the history
create mulitple items #453
  • Loading branch information
joshuadkitenge authored Apr 9, 2024
2 parents d3e17dd + 9b34413 commit ae760ee
Show file tree
Hide file tree
Showing 7 changed files with 632 additions and 38 deletions.
106 changes: 106 additions & 0 deletions cypress/e2e/with_mock_data/items.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,112 @@ describe('Items', () => {
});
});

it('adds an item with only mandatory fields (serial number advanced options)', () => {
cy.findByRole('button', { name: 'Add Item' }).click();
cy.findByText('Show advanced options').click();
cy.findByLabelText('Serial number').type('test %s');
cy.findByLabelText('Quantity').type('3');
cy.findByLabelText('Starting value').type('2');

cy.startSnoopingBrowserMockedRequest();

cy.findByRole('button', { name: 'Next' }).click();
cy.findByRole('button', { name: 'Next' }).click();
cy.findByText('Giant laser').click();

cy.findByRole('button', { name: 'Finish' }).click();
cy.findByRole('dialog').should('not.exist');

cy.findBrowserMockedRequests({
method: 'POST',
url: '/v1/items',
}).should(async (postRequests) => {
expect(postRequests.length).eq(3);

for (let i = 0; i < 3; i++) {
expect(JSON.stringify(await postRequests[i].json())).equal(
JSON.stringify({
catalogue_item_id: '1',
system_id: '65328f34a40ff5301575a4e3',
purchase_order_number: null,
is_defective: false,
usage_status: 0,
warranty_end_date: null,
asset_number: null,
serial_number: `test ${i + 2}`,
delivered_date: null,
notes: null,
properties: [
{ name: 'Resolution', value: 12 },
{ name: 'Frame Rate', value: 30 },
{ name: 'Sensor Type', value: 'CMOS' },
{ name: 'Sensor brand', value: null },
{ name: 'Broken', value: true },
{ name: 'Older than five years', value: false },
],
})
);
}
});
});

it('displays error messages for serial number advanced options', () => {
cy.findByRole('button', { name: 'Add Item' }).click();

cy.findByText('Show advanced options').click();

cy.findByLabelText('Starting value').type('10');

cy.findByText('Please enter a quantity value').should('exist');

cy.findByLabelText('Starting value').clear();

cy.findByLabelText('Quantity').type('10a');
cy.findByLabelText('Starting value').type('10a');

cy.findAllByText('Please enter a valid number').should('have.length', 2);

cy.findByLabelText('Quantity').clear();
cy.findByLabelText('Starting value').clear();

cy.findByLabelText('Quantity').type('10.5');
cy.findByLabelText('Starting value').type('10.5');

cy.findByText('Quantity must be an integer').should('exist');
cy.findByText('Starting value must be an integer').should('exist');

cy.findByLabelText('Quantity').clear();
cy.findByLabelText('Starting value').clear();

cy.findByLabelText('Quantity').type('-1');
cy.findByLabelText('Starting value').type('-1');

cy.findByText('Quantity must be greater than 1').should('exist');
cy.findByText('Starting value must be greater than or equal to 0').should(
'exist'
);

cy.findByLabelText('Quantity').clear();
cy.findByLabelText('Starting value').clear();

cy.findByLabelText('Quantity').type('100');
cy.findByLabelText('Starting value').type('2');

cy.findByText(
'Please use %s to specify the location you want to append the number to serial number'
).should('exist');
cy.findByText('Quantity must be less than 100').should('exist');

cy.findByLabelText('Quantity').clear();
cy.findByLabelText('Starting value').clear();

cy.findByLabelText('Serial number').type('test %s');
cy.findByLabelText('Quantity').type('4');
cy.findByLabelText('Starting value').type('2');

cy.findByText('e.g. test 2').should('exist');
});

it('adds an item with only mandatory fields (allowed list of values)', () => {
cy.visit('/catalogue/item/17/items');
cy.findByRole('button', { name: 'Add Item' }).click();
Expand Down
107 changes: 106 additions & 1 deletion src/api/items.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useItem,
useItems,
useMoveItemsToSystem,
useAddItems,
} from './items';
import {
getItemById,
Expand All @@ -15,6 +16,7 @@ import {
} from '../testUtils';
import {
AddItem,
AddItems,
EditItem,
Item,
MoveItemsToSystem,
Expand All @@ -23,7 +25,7 @@ import {
import SystemsJSON from '../mocks/Systems.json';
import { imsApi } from './api';

describe('catalogue items api functions', () => {
describe('items api functions', () => {
afterEach(() => {
vi.clearAllMocks();
});
Expand Down Expand Up @@ -298,4 +300,107 @@ describe('catalogue items api functions', () => {
);
});
});

describe('useAddItems', () => {
let addItems: AddItems;

// Use post spy for testing since response is not actual data in this case
// so can't test the underlying use of editSystem otherwise
let axiosPostSpy;
const { _id, ...item } = getItemById('KvT2Ox7n');
beforeEach(() => {
addItems = {
quantity: 2,
startingValue: 10,
item: {
...item,
serial_number: item.serial_number + '_%s',
},
};

axiosPostSpy = vi.spyOn(imsApi, 'post');
});

afterEach(() => {
vi.clearAllMocks();
});

it('sends requests to add multiple items and returns a successful response for each', async () => {
const { result } = renderHook(() => useAddItems(), {
wrapper: hooksWrapperWithProviders(),
});

expect(result.current.isIdle).toBe(true);

result.current.mutate(addItems);

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

for (
let i = addItems.startingValue;
i < addItems.startingValue + addItems.quantity;
i++
) {
expect(axiosPostSpy).toHaveBeenCalledWith('/v1/items', {
...item,
serial_number: item.serial_number + `_${i}`,
});
}

expect(result.current.data).toEqual([
{
message: 'Successfully created 5YUQDDjKpz2z_10',
name: '5YUQDDjKpz2z_10',
state: 'success',
},
{
message: 'Successfully created 5YUQDDjKpz2z_11',
name: '5YUQDDjKpz2z_11',
state: 'success',
},
]);
});

it('handles failed requests when adding multiple items correctly', async () => {
addItems.item.serial_number = 'Error 500';

const { result } = renderHook(() => useAddItems(), {
wrapper: hooksWrapperWithProviders(),
});

expect(result.current.isIdle).toBe(true);

result.current.mutate(addItems);

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

for (
let i = addItems.startingValue;
i < addItems.startingValue + addItems.quantity;
i++
) {
expect(axiosPostSpy).toHaveBeenCalledWith('/v1/items', {
...item,
serial_number: 'Error 500',
});
}

expect(result.current.data).toEqual([
{
message: 'Something went wrong',
name: 'Error 500',
state: 'error',
},
{
message: 'Something went wrong',
name: 'Error 500',
state: 'error',
},
]);
});
});
});
61 changes: 61 additions & 0 deletions src/api/items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@tanstack/react-query';
import {
AddItem,
AddItems,
EditItem,
ErrorParsing,
Item,
Expand All @@ -30,6 +31,66 @@ export const useAddItem = (): UseMutationResult<Item, AxiosError, AddItem> => {
});
};

export const useAddItems = (): UseMutationResult<
TransferState[],
AxiosError,
AddItems
> => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (addItems: AddItems) => {
const transferStates: TransferState[] = [];
const successfulSerialNumbers: string[] = [];

const promises = [];

for (
let i = addItems.startingValue;
i < addItems.startingValue + addItems.quantity;
i++
) {
const item: AddItem = {
...addItems.item,
serial_number:
addItems.item.serial_number?.replace('%s', String(i)) ?? null,
};

const promise = addItem(item)
.then((result: Item) => {
transferStates.push({
name: result.serial_number ?? '',
message: `Successfully created ${result.serial_number ?? ''}`,
state: 'success',
});
successfulSerialNumbers.push(result.serial_number ?? '');
})
.catch((error) => {
const response = error.response?.data as ErrorParsing;
transferStates.push({
name: item.serial_number ?? '',
message: response.detail,
state: 'error',
});
});

promises.push(promise);
}

await Promise.all(promises);
if (successfulSerialNumbers.length > 0) {
queryClient.invalidateQueries({
queryKey: ['Items', undefined, addItems.item.catalogue_item_id],
});
queryClient.invalidateQueries({
queryKey: ['Items', addItems.item.system_id, undefined],
});
}

return transferStates;
},
});
};

const fetchItems = async (
system_id?: string,
catalogue_item_id?: string
Expand Down
10 changes: 10 additions & 0 deletions src/app.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ export interface AddItem extends ItemDetails {
properties: CatalogueItemProperty[];
}

export interface AddItems {
quantity: number;
startingValue: number;
item: AddItem;
}

export interface Item extends ItemDetails {
properties: CatalogueItemPropertyResponse[];
id: string;
Expand All @@ -281,6 +287,10 @@ export interface CatalogueItemPropertiesErrorsType {
} | null;
}

export interface AdvancedSerialNumberOptionsType {
quantity: string | null;
startingValue: string | null;
}
export interface AllowedValuesListErrorsType {
index: number | null;
errors: { index: number; errorMessage: string }[] | null;
Expand Down
Loading

0 comments on commit ae760ee

Please sign in to comment.