Skip to content

Commit

Permalink
Add test for the NutritionDiaryEntryForm component
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandgeider committed Jul 29, 2023
1 parent 3c72c8f commit a014a4f
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 29 deletions.
34 changes: 6 additions & 28 deletions src/components/Nutrition/widgets/IngredientAutocompleter.test.tsx
Original file line number Diff line number Diff line change
@@ -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 () => {
Expand Down Expand Up @@ -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]);
});
});
Original file line number Diff line number Diff line change
@@ -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(
<QueryClientProvider client={queryClient}>
<NutritionDiaryEntryForm planId={123} closeFn={closeFnMock} />
</QueryClientProvider>
);
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(
<QueryClientProvider client={queryClient}>
<NutritionDiaryEntryForm planId={123} mealId={456} closeFn={closeFnMock} />
</QueryClientProvider>
);
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(
<QueryClientProvider client={queryClient}>
<NutritionDiaryEntryForm planId={123} entry={TEST_DIARY_ENTRY_1} closeFn={closeFnMock} />
</QueryClientProvider>
);
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(
<QueryClientProvider client={queryClient}>
<NutritionDiaryEntryForm planId={123} mealId={456} entry={TEST_DIARY_ENTRY_1} closeFn={closeFnMock} />
</QueryClientProvider>
);
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,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
22 changes: 22 additions & 0 deletions src/tests/api/ingredientSearch.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
];

0 comments on commit a014a4f

Please sign in to comment.