SupplierStats.test.tsx 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. /**
  2. * Tests for the "By Supplier" dashboard widget (#2988).
  3. */
  4. import { describe, it, expect, vi, beforeEach } from 'vitest';
  5. import { render, screen } from '@testing-library/react';
  6. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  7. import { SupplierStats } from '../../components/SupplierStats';
  8. import { api } from '../../api/client';
  9. vi.mock('../../api/client', () => ({
  10. api: { getSupplierStats: vi.fn() },
  11. }));
  12. const rows = [
  13. { supplier_id: 1, supplier_name: 'Filament24', spool_count: 2, remaining_g: 1400, consumed_g: 600, cost: 12.5 },
  14. ];
  15. function renderWidget(props: { dateFrom?: string; dateTo?: string } = {}) {
  16. const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  17. return render(
  18. <QueryClientProvider client={client}>
  19. <SupplierStats currency="€" {...props} />
  20. </QueryClientProvider>,
  21. );
  22. }
  23. describe('SupplierStats', () => {
  24. beforeEach(() => {
  25. vi.clearAllMocks();
  26. vi.mocked(api.getSupplierStats).mockResolvedValue(rows);
  27. });
  28. it('passes the dashboard timeframe to the endpoint', async () => {
  29. renderWidget({ dateFrom: '2026-08-01', dateTo: '2026-08-31' });
  30. expect(await screen.findByText('Filament24')).toBeInTheDocument();
  31. expect(api.getSupplierStats).toHaveBeenCalledWith('2026-08-01', '2026-08-31');
  32. });
  33. it('refetches when the timeframe changes', async () => {
  34. const { rerender } = renderWidget({ dateFrom: '2026-08-01' });
  35. await screen.findByText('Filament24');
  36. const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  37. rerender(
  38. <QueryClientProvider client={client}>
  39. <SupplierStats currency="€" dateFrom="2026-01-01" />
  40. </QueryClientProvider>,
  41. );
  42. await screen.findByText('Filament24');
  43. // The range is part of the query key, so a new window is a new read.
  44. expect(vi.mocked(api.getSupplierStats).mock.calls).toContainEqual(['2026-01-01', undefined]);
  45. });
  46. it('reports a failed request as an error, not as "no suppliers yet"', async () => {
  47. // A 403 from a missing inventory:read or a 500 used to render the empty
  48. // state, telling the user they had not assigned suppliers they had.
  49. vi.mocked(api.getSupplierStats).mockRejectedValue(new Error('boom'));
  50. renderWidget();
  51. expect(await screen.findByText(/Could not load supplier statistics/i)).toBeInTheDocument();
  52. expect(screen.queryByText(/No purchases recorded yet/i)).not.toBeInTheDocument();
  53. });
  54. it('still shows the empty state when the inventory really is empty', async () => {
  55. vi.mocked(api.getSupplierStats).mockResolvedValue([]);
  56. renderWidget();
  57. expect(await screen.findByText(/No purchases recorded yet/i)).toBeInTheDocument();
  58. });
  59. });