From 43e49b0bcc13a8fe8865b118ff8f347e8274802b Mon Sep 17 00:00:00 2001
From: Stefan Poindl <55439476+thwbh@users.noreply.github.com>
Date: Sat, 6 Dec 2025 15:19:11 +0100
Subject: [PATCH 1/4] fix stale weight update message (#183)
* fix stale weight update message
* clean up unused props in WeightScore
---
src/lib/component/weight/WeightScore.svelte | 53 ++---
src/lib/component/weight/WeightScore.test.ts | 208 ++++++-------------
src/routes/(app)/+page.svelte | 11 +-
3 files changed, 82 insertions(+), 190 deletions(-)
diff --git a/src/lib/component/weight/WeightScore.svelte b/src/lib/component/weight/WeightScore.svelte
index 21a37775..cea0b63a 100644
--- a/src/lib/component/weight/WeightScore.svelte
+++ b/src/lib/component/weight/WeightScore.svelte
@@ -1,27 +1,17 @@
Current Weight
- {#if (weightTracker && 'id' in weightTracker) || lastWeightTracker}
- {#if weightTracker && 'id' in weightTracker}
-
- {:else if lastWeightTracker}
-
- {/if}
- {:else}
- -
- {/if}
+
kg
@@ -57,24 +43,15 @@
- {#if weightTracker && 'id' in weightTracker}
+ {#if lastEntryDayDiff === 0}
Last update: Today.
- {:else if lastWeightTracker}
- {@const lastEntryDayDiff = differenceInDays(
- new Date(),
- parseStringAsDate(lastWeightTracker.added)
- )}
- {#if lastEntryDayDiff > 2}
-
- Last update was {lastEntryDayDiff} days ago!
- {:else}
-
- Last update: {lastEntryDayDiff} days ago.
- {/if}
+ {:else if lastEntryDayDiff > 2}
+
+ Last update was {lastEntryDayDiff} days ago!
{:else}
-
- Nothing tracked yet.
+
+ Last update: {lastEntryDayDiff} days ago.
{/if}
diff --git a/src/lib/component/weight/WeightScore.test.ts b/src/lib/component/weight/WeightScore.test.ts
index 1c2110ac..e99dae0c 100644
--- a/src/lib/component/weight/WeightScore.test.ts
+++ b/src/lib/component/weight/WeightScore.test.ts
@@ -1,8 +1,42 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { render, screen, waitFor } from '@testing-library/svelte';
-import userEvent from '@testing-library/user-event';
+import { describe, it, expect, vi } from 'vitest';
+import { render, screen } from '@testing-library/svelte';
import type { WeightTarget, WeightTracker, NewWeightTracker } from '$lib/api/gen';
import WeightScore from './WeightScore.svelte';
+import { getDateAsStr } from '$lib/date';
+import { subDays } from 'date-fns';
+
+// Mock NumberFlow as a simple component that renders the number
+// For Svelte 5, components need $$render method for SSR and proper client-side mounting
+vi.mock('@number-flow/svelte', () => {
+ const NumberFlowMock = function (anchor: any, props: any) {
+ const value = props?.value ?? 0;
+ const textNode = document.createTextNode(String(value));
+
+ // Insert the text node
+ if (anchor && anchor.parentNode) {
+ anchor.parentNode.insertBefore(textNode, anchor);
+ }
+
+ return {
+ p: (newProps: any) => {
+ // update
+ if (newProps.value !== undefined) {
+ textNode.textContent = String(newProps.value);
+ }
+ },
+ d: () => {
+ // destroy
+ if (textNode.parentNode) {
+ textNode.parentNode.removeChild(textNode);
+ }
+ }
+ };
+ };
+
+ return {
+ default: NumberFlowMock
+ };
+});
describe('WeightScore', () => {
const mockWeightTarget: WeightTarget = {
@@ -17,12 +51,14 @@ describe('WeightScore', () => {
const mockWeightTracker: WeightTracker = {
id: 1,
added: '2024-01-15',
+ time: '08:30:00',
amount: 82
};
- const mockLastWeightTracker: WeightTracker = {
+ const mockWeightTrackerToday: WeightTracker = {
id: 1,
- added: '2024-01-10',
+ added: getDateAsStr(new Date()),
+ time: '12:00:00',
amount: 83
};
@@ -36,27 +72,7 @@ describe('WeightScore', () => {
});
// Check for kg unit and Last update message
- expect(container.textContent).toContain('kg');
- expect(container.textContent).toContain('Last update: Today');
- });
-
- it('should display last weight when only lastWeightTracker provided', () => {
- const newEntry: NewWeightTracker = {
- added: '2024-01-20',
- amount: 81
- };
-
- const { container } = render(WeightScore, {
- props: {
- weightTracker: newEntry,
- lastWeightTracker: mockLastWeightTracker,
- weightTarget: mockWeightTarget
- }
- });
-
- // Check for kg unit and last update message
- expect(container.textContent).toContain('kg');
- expect(container.textContent).toMatch(/Last update/i);
+ expect(container.textContent).toMatch(/82 kg/i);
});
it('should show dash when no weight data', () => {
@@ -77,26 +93,10 @@ describe('WeightScore', () => {
});
describe('Status Messages', () => {
- it('should show "Nothing tracked yet" when no data', () => {
- const newEntry: NewWeightTracker = {
- added: '2024-01-20',
- amount: 81
- };
-
- render(WeightScore, {
- props: {
- weightTracker: newEntry,
- weightTarget: mockWeightTarget
- }
- });
-
- expect(screen.getByText(/Nothing tracked yet/i)).toBeTruthy();
- });
-
it('should show "Last update: Today" for current day entry', () => {
render(WeightScore, {
props: {
- weightTracker: mockWeightTracker,
+ weightTracker: mockWeightTrackerToday,
weightTarget: mockWeightTarget
}
});
@@ -106,20 +106,16 @@ describe('WeightScore', () => {
it('should show days ago for old entries (warning < 2 days)', () => {
// Create a tracker from 1 day ago
- const yesterday = new Date();
- yesterday.setDate(yesterday.getDate() - 1);
- const yesterdayStr = yesterday.toISOString().split('T')[0];
-
const oldTracker: WeightTracker = {
id: 1,
- added: yesterdayStr,
+ added: getDateAsStr(subDays(new Date(), 1)),
+ time: '08:30:00',
amount: 83
};
const { container } = render(WeightScore, {
props: {
- weightTracker: { added: '2024-01-20', amount: 81 } as NewWeightTracker,
- lastWeightTracker: oldTracker,
+ weightTracker: oldTracker,
weightTarget: mockWeightTarget
}
});
@@ -130,20 +126,16 @@ describe('WeightScore', () => {
it('should show critical warning for very old entries (> 2 days)', () => {
// Create a tracker from 5 days ago
- const fiveDaysAgo = new Date();
- fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 5);
- const oldDate = fiveDaysAgo.toISOString().split('T')[0];
-
const veryOldTracker: WeightTracker = {
id: 1,
- added: oldDate,
+ added: getDateAsStr(subDays(new Date(), 5)),
+ time: '08:30:00',
amount: 83
};
const { container } = render(WeightScore, {
props: {
- weightTracker: { added: '2024-01-20', amount: 81 } as NewWeightTracker,
- lastWeightTracker: veryOldTracker,
+ weightTracker: veryOldTracker,
weightTarget: mockWeightTarget
}
});
@@ -157,7 +149,7 @@ describe('WeightScore', () => {
it('should show success state with ShieldCheck for current day', () => {
render(WeightScore, {
props: {
- weightTracker: mockWeightTracker,
+ weightTracker: mockWeightTrackerToday,
weightTarget: mockWeightTarget
}
});
@@ -168,20 +160,16 @@ describe('WeightScore', () => {
it('should show warning state for entries 1-2 days old', () => {
// Create a tracker from 1 day ago
- const yesterday = new Date();
- yesterday.setDate(yesterday.getDate() - 1);
- const yesterdayStr = yesterday.toISOString().split('T')[0];
-
const oldTracker: WeightTracker = {
id: 1,
- added: yesterdayStr,
+ added: getDateAsStr(subDays(new Date(), 1)),
+ time: '08:30:00',
amount: 83
};
const { container } = render(WeightScore, {
props: {
- weightTracker: { added: '2024-01-20', amount: 81 } as NewWeightTracker,
- lastWeightTracker: oldTracker,
+ weightTracker: oldTracker,
weightTarget: mockWeightTarget
}
});
@@ -192,20 +180,16 @@ describe('WeightScore', () => {
it('should show error state for entries older than 2 days', () => {
// Create a tracker from 5 days ago
- const fiveDaysAgo = new Date();
- fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 5);
- const oldDate = fiveDaysAgo.toISOString().split('T')[0];
-
const veryOldTracker: WeightTracker = {
id: 1,
- added: oldDate,
+ added: getDateAsStr(subDays(new Date(), 5)),
+ time: '08:30:00',
amount: 83
};
const { container } = render(WeightScore, {
props: {
- weightTracker: { added: '2024-01-20', amount: 81 } as NewWeightTracker,
- lastWeightTracker: veryOldTracker,
+ weightTracker: veryOldTracker,
weightTarget: mockWeightTarget
}
});
@@ -213,18 +197,6 @@ describe('WeightScore', () => {
// ShieldWarning (error color) should show with "days ago!" (with exclamation)
expect(container.textContent).toMatch(/Last update was.*5.*days ago!/i);
});
-
- it('should show neutral state with Shield icon when no data tracked', () => {
- render(WeightScore, {
- props: {
- weightTracker: { added: '2024-01-20', amount: 81 } as NewWeightTracker,
- weightTarget: mockWeightTarget
- }
- });
-
- // Shield icon shows with "Nothing tracked yet" text
- expect(screen.getByText(/Nothing tracked yet/i)).toBeTruthy();
- });
});
describe('Progress Information', () => {
@@ -269,64 +241,12 @@ describe('WeightScore', () => {
});
});
- describe('Callbacks', () => {
- it('should call onAdd when provided', async () => {
- const onaddMock = vi.fn().mockResolvedValue({
- id: 2,
- added: '2024-01-20',
- amount: 80
- });
-
- render(WeightScore, {
- props: {
- weightTracker: mockWeightTracker,
- weightTarget: mockWeightTarget,
- onAdd: onaddMock
- }
- });
-
- // Component should render without calling onadd immediately
- expect(onaddMock).not.toHaveBeenCalled();
- });
-
- it('should work without onAdd callback', () => {
- expect(() => {
- render(WeightScore, {
- props: {
- weightTracker: mockWeightTracker,
- weightTarget: mockWeightTarget
- }
- });
- }).not.toThrow();
- });
-
- it('should work without onEdit callback', () => {
- expect(() => {
- render(WeightScore, {
- props: {
- weightTracker: mockWeightTracker,
- weightTarget: mockWeightTarget
- }
- });
- }).not.toThrow();
- });
- });
-
describe('Edge Cases', () => {
- it('should handle missing weightTracker', () => {
- const { container } = render(WeightScore, {
- props: {
- weightTarget: mockWeightTarget
- }
- });
-
- expect(container).toBeTruthy();
- });
-
it('should handle very large weight values', () => {
const heavyTracker: WeightTracker = {
id: 1,
- added: '2024-01-15',
+ added: getDateAsStr(new Date()),
+ time: '08:30:00',
amount: 300
};
@@ -338,14 +258,15 @@ describe('WeightScore', () => {
});
// Component should render without error
- expect(container.textContent).toContain('kg');
+ expect(container.textContent).toMatch(/300 kg/i);
expect(container.textContent).toContain('Last update: Today');
});
it('should handle minimum weight values', () => {
const lightTracker: WeightTracker = {
id: 1,
- added: '2024-01-15',
+ added: getDateAsStr(new Date()),
+ time: '08:30:00',
amount: 30
};
@@ -356,14 +277,15 @@ describe('WeightScore', () => {
}
});
- expect(container.textContent).toContain('kg');
+ expect(container.textContent).toMatch(/30 kg/i);
expect(container.textContent).toContain('Last update: Today');
});
it('should handle fractional weight values', () => {
const fractionalTracker: WeightTracker = {
id: 1,
- added: '2024-01-15',
+ added: getDateAsStr(new Date()),
+ time: '08:30:00',
amount: 82.5
};
@@ -374,7 +296,7 @@ describe('WeightScore', () => {
}
});
- expect(container.textContent).toContain('kg');
+ expect(container.textContent).toMatch(/82.5 kg/i);
expect(container.textContent).toContain('Last update: Today');
});
});
diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte
index fd25c962..4b191b80 100644
--- a/src/routes/(app)/+page.svelte
+++ b/src/routes/(app)/+page.svelte
@@ -32,7 +32,7 @@
let index: number = $state(0);
let intake: Array = $state(dashboard.intakeTodayList);
- let lastWeightTracker = $state(dashboard.weightMonthList[0]);
+ let lastWeightTracker: WeightTracker = $state(dashboard.weightMonthList[0]);
const weightTarget: WeightTarget = dashboard.weightTarget;
const intakeTarget: IntakeTarget = dashboard.intakeTarget;
@@ -118,14 +118,7 @@
- createWeightTrackerEntry({ newEntry: entry })}
- onEdit={(id, entry: WeightTracker) =>
- updateWeightTrackerEntry({ trackerId: id, updatedEntry: entry })}
- />
+