Files
stockfill/src/screens/ManageProductsScreen.additional.test.tsx
T
2025-12-02 17:22:09 +10:00

131 lines
4.4 KiB
TypeScript

// src/screens/ManageProductsScreen.additional.test.tsx
import { MemoryRouter } from 'react-router-dom';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import ManageProductsScreen from './ManageProductsScreen';
const mockUseProducts = vi.fn();
const mockUseCategories = vi.fn();
vi.mock('../hooks/dataHooks', () => ({
useProducts: () => mockUseProducts(),
useCategories: () => mockUseCategories(),
}));
// DB mock
const productsDb = {
get: vi.fn(),
put: vi.fn(),
where: vi.fn(), // must return { equals: () => ({ first: async () => ... }) }
filter: vi.fn(),
};
const categoriesDb = {
where: vi.fn(),
get: vi.fn(),
add: vi.fn(),
};
const pickListsDb = {
toArray: vi.fn(),
};
const pickItemsDb = {
add: vi.fn(),
};
const mockDb: any = {
products: productsDb,
categories: categoriesDb,
pickLists: pickListsDb,
pickItems: pickItemsDb,
transaction: vi.fn(async (_mode: string, ...args: any[]) => {
const cb = args[args.length - 1];
if (typeof cb === 'function') return cb();
return undefined;
}),
};
vi.mock('../context/DBProvider', () => ({
useDatabase: () => mockDb,
}));
vi.mock('uuid', () => ({ v4: () => 'new-product-id' }));
beforeEach(() => {
mockUseProducts.mockReset();
mockUseCategories.mockReset();
Object.values(productsDb).forEach((f) => typeof f === 'function' && (f as any).mockReset && (f as any).mockReset());
Object.values(categoriesDb).forEach((f) => typeof f === 'function' && (f as any).mockReset && (f as any).mockReset());
pickListsDb.toArray.mockReset();
pickItemsDb.add.mockReset();
mockDb.transaction.mockReset();
// Default product DB stubs
productsDb.where.mockImplementation(() => ({ equals: () => ({ first: async () => undefined }) }));
productsDb.filter.mockImplementation(() => ({ delete: vi.fn() }));
});
describe('ManageProductsScreen update product category creation branch', () => {
it('creates a new category when updates.category is an unknown name and saves product', async () => {
const existingProduct = {
id: 'prod-2',
name: 'Another Product',
category: 'cat-old',
barcode: '654321',
unit_type: 'unit',
bulk_name: 'pack',
archived: false,
created_at: 0,
updated_at: 0,
};
mockUseProducts.mockReturnValue([existingProduct]);
mockUseCategories.mockReturnValue([]); // no categories available
productsDb.get.mockResolvedValue(existingProduct);
// products.where('barcode').equals(value).first() must exist for uniqueness check
productsDb.where.mockImplementation(() => ({
equals: () => ({
first: async () => undefined,
}),
}));
// categories.where('name').equals(...).first => undefined (no matching category name)
categoriesDb.where.mockImplementation(() => ({ equals: () => ({ first: async () => undefined }) }));
categoriesDb.get.mockResolvedValue(undefined); // isExistingId check -> undefined
categoriesDb.add.mockResolvedValue('new-cat-id');
productsDb.put.mockResolvedValue('prod-2');
productsDb.filter.mockImplementation(() => ({ delete: vi.fn() }));
const user = userEvent.setup();
render(
<MemoryRouter>
<ManageProductsScreen />
</MemoryRouter>,
);
// Open edit for the product
await user.click(screen.getByLabelText(/edit another product/i));
// Find the category field corresponding to that row (value should be 'cat-old')
const categoryInputs = screen.getAllByLabelText(/category/i);
const categoryField = categoryInputs.find((i) => (i as HTMLInputElement).value === 'cat-old') as HTMLInputElement;
expect(categoryField).toBeDefined();
// Change category to a new name that doesn't exist
fireEvent.change(categoryField, { target: { value: 'New Category Name' } });
// Click product-row Save button
const saveButtons = screen.getAllByRole('button', { name: /save product/i });
const productSaveButton = saveButtons.find((b) => b.getAttribute('aria-label') === 'Save product' || b.querySelector('svg') !== null) ?? saveButtons[0];
expect(productSaveButton).toBeDefined();
await user.click(productSaveButton as HTMLElement);
// Wait for categories.add to be called (new category created) and product saved
await waitFor(() => {
expect(categoriesDb.add).toHaveBeenCalled();
expect(productsDb.put).toHaveBeenCalled();
});
});
});