From 114e31ba58e0b44bc7d7e80c8e1caf7ebd3b0bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Bregar?= Date: Wed, 6 May 2026 22:40:09 +0200 Subject: [PATCH] Extend dashboard tests: integrate `listStockEvents` mock, update stock transition handling, and enhance recent changes validation --- .../features/dashboard/dashboard-page.test.js | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/tests/features/dashboard/dashboard-page.test.js b/tests/features/dashboard/dashboard-page.test.js index 9b6ff2a..0b99368 100644 --- a/tests/features/dashboard/dashboard-page.test.js +++ b/tests/features/dashboard/dashboard-page.test.js @@ -1,11 +1,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; const listKitchenChangesMock = vi.fn(); +const listStockEventsMock = vi.fn(); const getStockEntryMock = vi.fn(); const fetchLocationsMock = vi.fn(); vi.mock('../../../src/api/stock.js', () => ({ listKitchenChanges: (...args) => listKitchenChangesMock(...args), + listStockEvents: (...args) => listStockEventsMock(...args), getStockEntry: (...args) => getStockEntryMock(...args), })); @@ -18,6 +20,7 @@ const { dashboardPageData, renderDashboardPage } = await import('../../../src/fe describe('features/dashboard/dashboard-page', () => { beforeEach(() => { listKitchenChangesMock.mockReset(); + listStockEventsMock.mockReset(); getStockEntryMock.mockReset(); fetchLocationsMock.mockReset(); }); @@ -26,10 +29,11 @@ describe('features/dashboard/dashboard-page', () => { const html = renderDashboardPage(); expect(html).toContain('Recent changes'); expect(html).toContain('x-data="dashboardPage()"'); - expect(html).toContain('Saved means the backend created or updated a record.'); + expect(html).toContain('Latest item and stock updates, including used and inactive stock.'); + expect(html).toContain('recent-change-list'); }); - it('loads recent changes on init and renders item-focused state lines', async () => { + it('loads recent changes on init and renders item-focused details', async () => { listKitchenChangesMock.mockResolvedValueOnce({ since: null, nextCursor: null, @@ -62,16 +66,20 @@ describe('features/dashboard/dashboard-page', () => { await data.init(); - expect(listKitchenChangesMock).toHaveBeenCalledWith(store, { limit: 10 }); + expect(listKitchenChangesMock).toHaveBeenCalledWith(store, { limit: 200 }); expect(data.recentChanges).toHaveLength(1); expect(data.changesState.error).toBe(''); - expect(data.changeHeadline(data.recentChanges[0])).toBe('Item saved: Rice'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Quantity: 3 kg'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Location: Pantry / Shelf A'); + expect(data.changeHeadline(data.recentChanges[0])).toBe('Rice'); + expect(data.changeKindLabel(data.recentChanges[0])).toBe('Updated'); + expect(data.changeSubtitle(data.recentChanges[0])).toBe('Item details were updated.'); + expect(data.changeDetails(data.recentChanges[0])).toEqual(expect.arrayContaining([ + { label: 'Quantity', value: '3 kg' }, + { label: 'Location', value: 'Pantry / Shelf A' }, + ])); expect(getStockEntryMock).not.toHaveBeenCalled(); }); - it('resolves stock event item context via item lookup when needed', async () => { + it('resolves stock event item context and renders stock transitions', async () => { listKitchenChangesMock.mockResolvedValueOnce({ since: null, nextCursor: null, @@ -80,6 +88,7 @@ describe('features/dashboard/dashboard-page', () => { action: 'upsert', timestamp: '2026-04-10T10:00:00Z', stock: { + id: 11, item_uuid_b64: 'item-uuid-1', quantity: 0.5, uom_symbol: 'kg', @@ -88,6 +97,10 @@ describe('features/dashboard/dashboard-page', () => { }, }], }); + listStockEventsMock.mockResolvedValueOnce([ + { id: 11, quantity: 0.5, uom_symbol: 'kg', level: 'some' }, + { id: 10, quantity: 1, uom_symbol: 'kg', level: 'good' }, + ]); getStockEntryMock.mockResolvedValueOnce({ uuid_b64: 'item-uuid-1', name: 'Flour', @@ -106,11 +119,23 @@ describe('features/dashboard/dashboard-page', () => { await data.refreshChanges(); - expect(data.changeHeadline(data.recentChanges[0])).toBe('Stock saved: Flour'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Quantity: 0.5 kg'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Level: Some'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Location: Pantry / Bin 2'); + expect(data.changeHeadline(data.recentChanges[0])).toBe('Flour'); + expect(data.changeKindLabel(data.recentChanges[0])).toBe('Stock changed'); + expect(data.changeStockTransition(data.recentChanges[0])).toEqual({ + previous: '1 kg · Good', + current: '0.5 kg · Some', + }); + expect(data.changeDetails(data.recentChanges[0])).toEqual(expect.arrayContaining([ + { label: 'Level', value: 'Some' }, + { label: 'Location', value: 'Pantry / Bin 2' }, + ])); expect(getStockEntryMock).toHaveBeenCalledWith(expect.anything(), 'item-uuid-1'); + expect(listStockEventsMock).toHaveBeenCalledWith(expect.anything(), 'item-uuid-1', { + allowInactive: true, + limit: 50, + orderBy: 'id', + orderDir: 'desc', + }); }); it('retries stock event item lookup with allowInactive after 404', async () => { @@ -122,12 +147,16 @@ describe('features/dashboard/dashboard-page', () => { action: 'upsert', timestamp: '2026-04-10T10:00:00Z', stock: { + id: 12, item_uuid_b64: 'item-uuid-2', quantity: 1, uom_symbol: 'pcs', }, }], }); + listStockEventsMock.mockResolvedValueOnce([ + { id: 12, quantity: 1, uom_symbol: 'pcs' }, + ]); getStockEntryMock .mockRejectedValueOnce(Object.assign(new Error('Not found'), { status: 404 })) .mockResolvedValueOnce({ @@ -153,8 +182,12 @@ describe('features/dashboard/dashboard-page', () => { 'item-uuid-2', { allowInactive: true }, ); - expect(data.changeHeadline(data.recentChanges[0])).toBe('Stock saved: Archived pasta'); - expect(data.changeStateLine(data.recentChanges[0])).toContain('Quantity: 1 pcs'); + expect(data.changeHeadline(data.recentChanges[0])).toBe('Archived pasta'); + expect(data.changeKindLabel(data.recentChanges[0])).toBe('New item'); + expect(data.changeStockTransition(data.recentChanges[0])).toEqual({ + previous: 'Initial stock', + current: '1 pcs', + }); }); it('keeps empty state when API returns no changes', async () => {