MeshiTrack/packages/web/tests/app/(dashboard)/medicines/CabinetTab.test.tsx

685 lines
23 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
2026-05-19 10:13:40 +09:00
import { SWRConfig } from 'swr';
2026-04-26 18:44:59 +09:00
import type React from 'react';
const {
mockListCabinetItems,
mockGetCabinetSummary,
mockCreateCabinetItem,
mockAdjustCabinetItemQuantity,
mockDeleteCabinetItem,
} = vi.hoisted(() => ({
mockListCabinetItems: vi.fn(),
mockGetCabinetSummary: vi.fn(),
mockCreateCabinetItem: vi.fn(),
mockAdjustCabinetItemQuantity: vi.fn(),
mockDeleteCabinetItem: vi.fn(),
}));
const { mockListMedicines } = vi.hoisted(() => ({ mockListMedicines: vi.fn() }));
vi.mock('@/services/cabinet', () => ({
listCabinetItems: mockListCabinetItems,
getCabinetSummary: mockGetCabinetSummary,
createCabinetItem: mockCreateCabinetItem,
adjustCabinetItemQuantity: mockAdjustCabinetItemQuantity,
deleteCabinetItem: mockDeleteCabinetItem,
}));
vi.mock('@/services/medicines', () => ({ listMedicines: mockListMedicines }));
2026-04-26 18:44:59 +09:00
vi.mock('next/link', () => ({
default: (props: { href?: string; children: React.ReactNode }) => (
<a href={props.href}>{props.children}</a>
),
}));
2026-05-19 11:06:03 +09:00
import { CabinetTab } from '../../../../src/app/(dashboard)/medicines/CabinetTab';
const emptySummary = { data: [] };
const emptyItems = { data: [], pagination: { cursor: null, hasMore: false } };
const emptyMeds = { data: [], pagination: { cursor: null, hasMore: false } };
const cabinetItem = {
_id: 'ci-1',
medicineId: 'med-1',
medicineName: 'Metformin',
quantity: 10,
unit: 'tablet',
status: 'active',
expirationDate: null,
unitPrice: null,
totalPrice: null,
storeId: null,
notes: null,
createdAt: '2026-01-01T00:00:00.000Z',
};
beforeEach(() => {
vi.clearAllMocks();
mockGetCabinetSummary.mockResolvedValue(emptySummary);
mockListCabinetItems.mockResolvedValue(emptyItems);
mockListMedicines.mockResolvedValue(emptyMeds);
});
2026-05-19 10:13:40 +09:00
const wrapper = ({ children }: { children: React.ReactNode }) => (
<SWRConfig value={{ provider: () => new Map(), dedupingInterval: 0 }}>{children}</SWRConfig>
);
describe('CabinetTab', () => {
it('shows empty state when no items', async () => {
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => expect(screen.getByText(/cabinet is empty/i)).toBeInTheDocument());
});
it('shows error when list fails', async () => {
mockGetCabinetSummary.mockRejectedValue(new Error('Server error'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => expect(screen.getByText('Server error')).toBeInTheDocument());
});
it('dismisses error on Dismiss click', async () => {
mockGetCabinetSummary.mockRejectedValue(new Error('Server error'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Server error'));
await userEvent.click(screen.getByText('Dismiss'));
expect(screen.queryByText('Server error')).not.toBeInTheDocument();
});
it('toggles Add to Cabinet form', async () => {
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
expect(screen.getByPlaceholderText('Search medicines...')).toBeInTheDocument();
await userEvent.click(screen.getAllByText('Cancel')[0]);
expect(screen.queryByPlaceholderText('Search medicines...')).not.toBeInTheDocument();
});
it('switches between Summary and All Items views', async () => {
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
expect(mockListCabinetItems).toHaveBeenCalled();
});
it('renders cabinet items in detail view', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
});
it('adjusts item quantity', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
mockAdjustCabinetItemQuantity.mockResolvedValue({ ...cabinetItem, quantity: 11 });
mockGetCabinetSummary.mockResolvedValue(emptySummary);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByTitle('Add 1'));
await userEvent.click(screen.getByTitle('Add 1'));
expect(mockAdjustCabinetItemQuantity).toHaveBeenCalledWith('hh1', 'ci-1', { delta: 1 });
});
it('shows summary view with medicine data', async () => {
mockGetCabinetSummary.mockResolvedValue({
data: [
{
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 30,
unit: 'tablet',
itemCount: 2,
earliestExpiry: null,
},
],
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
expect(screen.getByText(/2 items/)).toBeInTheDocument();
});
it('expands medicine in summary view', async () => {
mockGetCabinetSummary.mockResolvedValue({
data: [
{
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 30,
unit: 'tablet',
itemCount: 1,
earliestExpiry: null,
},
],
});
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Metformin'));
await userEvent.click(screen.getByText('Metformin').closest('button')!);
await waitFor(() =>
expect(mockListCabinetItems).toHaveBeenCalledWith(
'hh1',
expect.objectContaining({ medicineId: 'med-1' }),
),
);
});
it('submits AddToCabinetForm with medicine selection validation', async () => {
mockCreateCabinetItem.mockResolvedValue(cabinetItem);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
// Submit without selecting a medicine - should show error
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
2026-04-26 18:44:59 +09:00
await waitFor(() => expect(screen.getByText('Please select a medicine')).toBeInTheDocument());
});
it('submits AddToCabinetForm successfully', async () => {
mockListMedicines.mockResolvedValue({
data: [
{
_id: 'med-1',
name: 'Metformin',
form: 'tablet',
strength: 500,
strengthUnit: 'mg',
},
],
pagination: { cursor: null, hasMore: false },
});
mockCreateCabinetItem.mockResolvedValue(cabinetItem);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
// Select a medicine (first combobox is the medicine select)
fireEvent.change(screen.getAllByRole('combobox')[0]!, {
target: { value: 'med-1' },
});
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
await waitFor(() =>
2026-04-26 18:44:59 +09:00
expect(mockCreateCabinetItem).toHaveBeenCalledWith(
'hh1',
expect.objectContaining({ medicineId: 'med-1' }),
),
);
});
it('submits AddToCabinetForm with expiration date and notes', async () => {
mockListMedicines.mockResolvedValue({
data: [
{
_id: 'med-1',
name: 'Metformin',
form: 'tablet',
strength: 500,
strengthUnit: 'mg',
},
],
pagination: { cursor: null, hasMore: false },
});
mockCreateCabinetItem.mockResolvedValue(cabinetItem);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
// Select a medicine
fireEvent.change(screen.getAllByRole('combobox')[0]!, {
target: { value: 'med-1' },
});
// Set expiration date (type="date" input, no label association)
const dateInput = document.querySelector('input[type="date"]') as HTMLElement;
if (dateInput) fireEvent.change(dateInput, { target: { value: '2027-12-31' } });
// Set notes
fireEvent.change(screen.getByPlaceholderText('Any notes about this item'), {
target: { value: 'Store in fridge' },
});
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
await waitFor(() =>
expect(mockCreateCabinetItem).toHaveBeenCalledWith(
'hh1',
expect.objectContaining({
medicineId: 'med-1',
notes: 'Store in fridge',
}),
),
);
});
it('changes quantity and unit in AddToCabinetForm', async () => {
mockListMedicines.mockResolvedValue({
data: [
{ _id: 'med-1', name: 'Metformin', form: 'tablet', strength: 500, strengthUnit: 'mg' },
],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
// Search for medicine
fireEvent.change(screen.getByPlaceholderText('Search medicines...'), {
target: { value: 'Met' },
});
// Change quantity
fireEvent.change(screen.getByPlaceholderText('30'), { target: { value: '60' } });
// Change unit
fireEvent.change(screen.getAllByRole('combobox')[1]!, { target: { value: 'capsule' } });
expect(screen.getByPlaceholderText('Search medicines...')).toBeInTheDocument();
});
it('switches from detail view back to summary view', async () => {
mockGetCabinetSummary.mockResolvedValue({
data: [
{
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 10,
unit: 'tablet',
itemCount: 1,
earliestExpiry: null,
},
],
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
// Now in detail view
await waitFor(() => screen.getByText('Summary'));
await userEvent.click(screen.getByText('Summary'));
// Back in summary view — Metformin should show
await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
});
it('shows expiry date for cabinet items', async () => {
mockListCabinetItems.mockResolvedValue({
data: [
{
...cabinetItem,
expirationDate: '2099-12-31T00:00:00.000Z',
},
],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByText('Metformin'));
expect(screen.getByText(/days\)/)).toBeInTheDocument();
});
it('filters cabinet items by status in detail view', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByDisplayValue('All Statuses'));
fireEvent.change(screen.getByDisplayValue('All Statuses'), { target: { value: 'active' } });
await waitFor(() => expect(mockListCabinetItems).toHaveBeenCalledTimes(2));
});
it('takes 1 from cabinet item', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
mockAdjustCabinetItemQuantity.mockResolvedValue({ ...cabinetItem, quantity: 9 });
mockGetCabinetSummary.mockResolvedValue(emptySummary);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByTitle('Take 1'));
await userEvent.click(screen.getByTitle('Take 1'));
expect(mockAdjustCabinetItemQuantity).toHaveBeenCalledWith('hh1', 'ci-1', { delta: -1 });
});
it('collapses expanded medicine when it returns 0 items', async () => {
mockGetCabinetSummary.mockResolvedValue({
data: [
{
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 0,
unit: 'tablet',
itemCount: 0,
earliestExpiry: null,
},
],
});
mockListCabinetItems.mockResolvedValue({
data: [],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Metformin'));
await userEvent.click(screen.getByText('Metformin').closest('button')!);
await waitFor(() =>
expect(mockListCabinetItems).toHaveBeenCalledWith(
'hh1',
expect.objectContaining({ medicineId: 'med-1' }),
),
);
});
it('shows error when createCabinetItem fails', async () => {
mockCreateCabinetItem.mockRejectedValue(new Error('Create failed'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
// Should show 'Please select a medicine' validation first (since no medicine selected)
await waitFor(() => expect(screen.getByText('Please select a medicine')).toBeInTheDocument());
});
it('deletes cabinet item after confirmation', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
mockDeleteCabinetItem.mockResolvedValue({});
vi.spyOn(window, 'confirm').mockReturnValue(true);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByTitle('Delete'));
await userEvent.click(screen.getByTitle('Delete'));
expect(mockDeleteCabinetItem).toHaveBeenCalledWith('hh1', 'ci-1');
});
it('shows error when adjust fails', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
mockAdjustCabinetItemQuantity.mockRejectedValue(new Error('Adjust failed'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByTitle('Add 1'));
await userEvent.click(screen.getByTitle('Add 1'));
await waitFor(() => expect(screen.getByText('Adjust failed')).toBeInTheDocument());
});
it('shows error when delete fails', async () => {
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
mockDeleteCabinetItem.mockRejectedValue(new Error('Delete failed'));
vi.spyOn(window, 'confirm').mockReturnValue(true);
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => screen.getByTitle('Delete'));
await userEvent.click(screen.getByTitle('Delete'));
await waitFor(() => expect(screen.getByText('Delete failed')).toBeInTheDocument());
});
it('cancels AddToCabinetForm with internal Cancel button', async () => {
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
// Click the Cancel button inside the form (not the toggle button)
const cancelButtons = screen.getAllByText('Cancel');
await userEvent.click(cancelButtons[cancelButtons.length - 1]!);
expect(screen.queryByPlaceholderText('Search medicines...')).not.toBeInTheDocument();
});
it('collapses expanded medicine when clicked again', async () => {
const summary = {
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 10,
unit: 'tablet',
itemCount: 1,
earliestExpiry: null,
};
mockGetCabinetSummary.mockResolvedValue({ data: [summary] });
mockListCabinetItems.mockResolvedValue({
data: [cabinetItem],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Metformin'));
2026-05-19 10:13:40 +09:00
// First click expands — handleExpand calls listCabinetItems
await userEvent.click(screen.getByText('Metformin').closest('button')!);
2026-05-19 10:13:40 +09:00
await waitFor(() => expect(mockListCabinetItems).toHaveBeenCalledTimes(1));
const callsAfterExpand = mockListCabinetItems.mock.calls.length;
// Second click collapses — no additional listCabinetItems calls
await userEvent.click(screen.getByText('Metformin').closest('button')!);
await waitFor(() => expect(screen.queryByTitle('Take 1')).not.toBeInTheDocument());
expect(mockListCabinetItems.mock.calls.length).toBe(callsAfterExpand);
});
it('shows empty items when expand fails', async () => {
mockGetCabinetSummary.mockResolvedValue({
data: [
{
medicineId: 'med-1',
medicineName: 'Metformin',
medicineStrength: 500,
medicineStrengthUnit: 'mg',
medicineForm: 'tablet',
totalQuantity: 10,
unit: 'tablet',
itemCount: 1,
earliestExpiry: null,
},
],
});
mockListCabinetItems.mockRejectedValue(new Error('Expand failed'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Metformin'));
await userEvent.click(screen.getByText('Metformin').closest('button')!);
2026-05-19 10:13:40 +09:00
await waitFor(() => expect(mockListCabinetItems).toHaveBeenCalledTimes(1));
// No items should be shown (empty after error)
expect(screen.queryByTitle('Take 1')).not.toBeInTheDocument();
});
it('shows create error when medicine is selected and create fails', async () => {
mockListMedicines.mockResolvedValue({
2026-04-26 18:44:59 +09:00
data: [
{ _id: 'med-1', name: 'Metformin', form: 'tablet', strength: 500, strengthUnit: 'mg' },
],
pagination: { cursor: null, hasMore: false },
});
mockCreateCabinetItem.mockRejectedValue(new Error('Create failed'));
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
await waitFor(() => expect(screen.getByText('Create failed')).toBeInTheDocument());
});
it('shows item notes in detail view', async () => {
mockListCabinetItems.mockResolvedValue({
data: [{ ...cabinetItem, notes: 'Store in fridge' }],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('All Items'));
await userEvent.click(screen.getByText('All Items'));
await waitFor(() => expect(screen.getByText('Store in fridge')).toBeInTheDocument());
});
it('waits for medicines to load then selects medicine in form', async () => {
mockListMedicines.mockResolvedValue({
2026-04-26 18:44:59 +09:00
data: [
{ _id: 'med-1', name: 'Metformin', form: 'tablet', strength: 500, strengthUnit: 'mg' },
],
pagination: { cursor: null, hasMore: false },
});
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await userEvent.click(screen.getByText('Add to Cabinet'));
// Wait for medicine options to load (covers medicines.map callback)
await waitFor(() =>
expect(
(screen.getAllByRole('combobox')[0] as HTMLSelectElement).options.length,
).toBeGreaterThan(1),
);
// Select a valid medicine (true branch: medForm is set → defaultUnitForForm called)
fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });
// Select empty value (false branch: selectedMed undefined → DosageUnit.TABLET fallback)
fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: '' } });
expect(screen.getByPlaceholderText('Search medicines...')).toBeInTheDocument();
});
it('shows fallback error when non-Error is thrown during create', async () => {
mockListMedicines.mockResolvedValue({
2026-04-26 18:44:59 +09:00
data: [
{ _id: 'med-1', name: 'Metformin', form: 'tablet', strength: 500, strengthUnit: 'mg' },
],
pagination: { cursor: null, hasMore: false },
});
mockCreateCabinetItem.mockRejectedValue('unexpected');
2026-05-19 10:13:40 +09:00
render(<CabinetTab householdId="hh1" />, { wrapper });
await waitFor(() => screen.getByText('Add to Cabinet'));
await userEvent.click(screen.getByText('Add to Cabinet'));
await waitFor(() => screen.getByPlaceholderText('Search medicines...'));
fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });
fireEvent.submit(screen.getByPlaceholderText('Search medicines...').closest('form')!);
await waitFor(() => expect(screen.getByText('Failed to add item')).toBeInTheDocument());
});
});