From a014a4f34e380ed24ace9fb3813db1665eaef230 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sat, 29 Jul 2023 23:26:19 +0200 Subject: [PATCH] Add test for the NutritionDiaryEntryForm component --- .../widgets/IngredientAutocompleter.test.tsx | 34 +--- .../forms/NutritionDiaryEntryForm.test.tsx | 168 ++++++++++++++++++ .../widgets/forms/NutritionDiaryEntryForm.tsx | 1 - src/tests/api/ingredientSearch.ts | 22 +++ 4 files changed, 196 insertions(+), 29 deletions(-) create mode 100644 src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx create mode 100644 src/tests/api/ingredientSearch.ts diff --git a/src/components/Nutrition/widgets/IngredientAutocompleter.test.tsx b/src/components/Nutrition/widgets/IngredientAutocompleter.test.tsx index 71df9fd0..1d8748bf 100644 --- a/src/components/Nutrition/widgets/IngredientAutocompleter.test.tsx +++ b/src/components/Nutrition/widgets/IngredientAutocompleter.test.tsx @@ -1,40 +1,18 @@ import { act, render, screen, within } from '@testing-library/react'; -import { searchIngredient } from 'services'; -import { IngredientAutocompleter } from 'components/Nutrition/widgets/IngredientAutcompleter'; import userEvent from "@testing-library/user-event"; +import { IngredientAutocompleter } from 'components/Nutrition/widgets/IngredientAutcompleter'; +import { searchIngredient } from 'services'; +import { INGREDIENT_SEARCH } from "tests/api/ingredientSearch"; jest.mock("services"); -const mockCallback = jest.fn(); describe("Test the IngredientAutocompleter component", () => { // Arrange - const response = [ - { - "value": "Baguette with cheese", - "data": { - "id": 1234, - "name": "Baguette with cheese", - "category": "Desserts", - "image": null, - "image_thumbnail": null - } - }, - { - "value": "Blue cheese", - "data": { - "id": 4321, - "name": "Blue cheese", - "category": "Beverages", - "image": null, - "image_thumbnail": null - } - } - ]; - + const mockCallback = jest.fn(); beforeEach(() => { // @ts-ignore - searchIngredient.mockImplementation(() => Promise.resolve(response)); + searchIngredient.mockImplementation(() => Promise.resolve(INGREDIENT_SEARCH)); }); test('renders correct results', async () => { @@ -78,6 +56,6 @@ describe("Test the IngredientAutocompleter component", () => { await user.keyboard('{ArrowDown}{Enter}'); // Assert - expect(mockCallback).lastCalledWith(response[0]); + expect(mockCallback).lastCalledWith(INGREDIENT_SEARCH[0]); }); }); diff --git a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx new file mode 100644 index 00000000..4f3890f1 --- /dev/null +++ b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx @@ -0,0 +1,168 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { act, render, screen, within } from '@testing-library/react'; +import userEvent from "@testing-library/user-event"; +import { UserEvent } from "@testing-library/user-event/setup/setup"; + +import { useAddDiaryEntryQuery, useEditDiaryEntryQuery } from "components/Nutrition/queries"; +import { NutritionDiaryEntryForm } from "components/Nutrition/widgets/forms/NutritionDiaryEntryForm"; +import React from 'react'; +import { searchIngredient } from "services"; +import { INGREDIENT_SEARCH } from "tests/api/ingredientSearch"; +import { TEST_DIARY_ENTRY_1 } from "tests/nutritionDiaryTestdata"; + +jest.mock('components/Nutrition/queries'); +jest.mock('services'); + +async function fillInEntry(user: UserEvent) { + const autocomplete = screen.getByTestId('autocomplete'); + const input = within(autocomplete).getByRole('combobox'); + await user.click(autocomplete); + await user.type(input, 'Bagu'); + + // There's a bounce period of 200ms between the input and the search + await act(async () => { + await new Promise((r) => setTimeout(r, 250)); + }); + + // Select first result + await user.click(input); + await user.keyboard('{ArrowDown}{Enter}'); + + const amountInput = screen.getByLabelText('amount'); + await user.clear(amountInput); + await user.type(amountInput, '120'); + const submitButton = screen.getByRole('button', { name: 'submit' }); + await user.click(submitButton); +} + +describe('Test the NutritionDiaryEntryForm component', () => { + const queryClient = new QueryClient(); + let mutateAddMock = jest.fn(); + let mutateEditMock = jest.fn(); + let closeFnMock = jest.fn(); + + beforeEach(() => { + mutateAddMock = jest.fn(); + mutateEditMock = jest.fn(); + closeFnMock = jest.fn(); + + // @ts-ignore + useEditDiaryEntryQuery.mockImplementation(() => ({ mutate: mutateEditMock })); + + // @ts-ignore + useAddDiaryEntryQuery.mockImplementation(() => ({ mutate: mutateAddMock })); + + // @ts-ignore + searchIngredient.mockImplementation(() => Promise.resolve(INGREDIENT_SEARCH)); + }); + + + test('A new entry should be added - no meal', async () => { + // Arrange + const user = userEvent.setup(); + + // Act + render( + + + + ); + await fillInEntry(user); + + // Assert + expect(screen.getByDisplayValue('Baguette with cheese')).toBeInTheDocument(); + expect(screen.getByDisplayValue('120')).toBeInTheDocument(); + expect(mutateEditMock).not.toHaveBeenCalled(); + expect(closeFnMock).toBeCalled(); + expect(mutateAddMock).toHaveBeenCalledWith({ + amount: "120", + datetime: expect.anything(), + ingredient: 1234, + meal: undefined, + plan: 123, + // eslint-disable-next-line camelcase + weight_unit: null, + }); + }); + test('A new entry should be added - passing meal ID', async () => { + // Arrange + const user = userEvent.setup(); + + // Act + render( + + + + ); + await fillInEntry(user); + + // Assert + expect(screen.getByDisplayValue('Baguette with cheese')).toBeInTheDocument(); + expect(screen.getByDisplayValue('120')).toBeInTheDocument(); + expect(mutateEditMock).not.toHaveBeenCalled(); + expect(closeFnMock).toBeCalled(); + expect(mutateAddMock).toHaveBeenCalledWith({ + amount: "120", + datetime: expect.anything(), + ingredient: 1234, + meal: 456, + plan: 123, + // eslint-disable-next-line camelcase + weight_unit: null, + }); + }); + + test('An existing diary entry should be edited', async () => { + // Arrange + const user = userEvent.setup(); + + // Act + render( + + + + ); + await fillInEntry(user); + + // Assert + expect(mutateAddMock).not.toHaveBeenCalled(); + expect(closeFnMock).toBeCalled(); + expect(mutateEditMock).toHaveBeenCalledWith({ + id: 42, + amount: "120", + datetime: expect.anything(), + ingredient: 1234, + meal: undefined, + plan: 123, + // eslint-disable-next-line camelcase + weight_unit: null, + }); + }); + + test('An existing diary entry should be edited - passing a meal Id', async () => { + // Arrange + const user = userEvent.setup(); + + // Act + render( + + + + ); + await fillInEntry(user); + + // Assert + expect(mutateAddMock).not.toHaveBeenCalled(); + expect(closeFnMock).toBeCalled(); + expect(mutateEditMock).toHaveBeenCalledWith({ + id: 42, + amount: "120", + datetime: expect.anything(), + ingredient: 1234, + meal: 456, + plan: 123, + // eslint-disable-next-line camelcase + weight_unit: null, + }); + }); +}); \ No newline at end of file diff --git a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx index 7dc09e56..0ae4832a 100644 --- a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx +++ b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx @@ -2,7 +2,6 @@ import { Button, Stack, TextField } from "@mui/material"; import { DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterLuxon } from "@mui/x-date-pickers/AdapterLuxon"; import { DiaryEntry } from "components/Nutrition/models/diaryEntry"; - import { useAddDiaryEntryQuery, useEditDiaryEntryQuery } from "components/Nutrition/queries"; import { IngredientAutocompleter } from "components/Nutrition/widgets/IngredientAutcompleter"; import { Form, Formik } from "formik"; diff --git a/src/tests/api/ingredientSearch.ts b/src/tests/api/ingredientSearch.ts new file mode 100644 index 00000000..92536a68 --- /dev/null +++ b/src/tests/api/ingredientSearch.ts @@ -0,0 +1,22 @@ +export const INGREDIENT_SEARCH = [ + { + "value": "Baguette with cheese", + "data": { + "id": 1234, + "name": "Baguette with cheese", + "category": "Desserts", + "image": null, + "image_thumbnail": null + } + }, + { + "value": "Blue cheese", + "data": { + "id": 4321, + "name": "Blue cheese", + "category": "Beverages", + "image": null, + "image_thumbnail": null + } + } +]; \ No newline at end of file