CUB-196: fix future timestamps, negative battery clamp, boundary tests

Grimm review fixes:
- formatRelativeTime: guard future timestamps (clock skew) → 'unknown'
- battery display: clamp negative values to 0%
- formatTimeLeft: floor fractional seconds
- Tests: +4 (future timestamp, negative battery, 15%/50% boundaries)
This commit is contained in:
2026-05-21 14:07:57 +00:00
parent 08d5ceb792
commit db4663380b
2 changed files with 32 additions and 2 deletions
+29
View File
@@ -131,4 +131,33 @@ describe('CameraCard', () => {
) )
expect(screen.getByText('unknown')).toBeInTheDocument() expect(screen.getByText('unknown')).toBeInTheDocument()
}) })
it('shows "unknown" when last_seen is in the future', () => {
const future = new Date(Date.now() + 86400000).toISOString() // +1 day
render(<CameraCard camera={makeCamera({ last_seen: future })} />)
expect(screen.getByText('unknown')).toBeInTheDocument()
})
// ── Edge cases ──────────────────────────────────────────────────────────
it('clamps negative battery_pct to 0%', () => {
render(<CameraCard camera={makeCamera({ battery_pct: -5 })} />)
expect(screen.getByText('0%')).toBeInTheDocument()
})
it('shows exact boundary: 15% battery → yellow bar', () => {
const { container } = render(
<CameraCard camera={makeCamera({ battery_pct: 15 })} />,
)
const bar = container.querySelector('[role="progressbar"] div')
expect(bar?.className).toContain('bg-rig-warning')
})
it('shows exact boundary: 50% battery → green bar', () => {
const { container } = render(
<CameraCard camera={makeCamera({ battery_pct: 50 })} />,
)
const bar = container.querySelector('[role="progressbar"] div')
expect(bar?.className).toContain('bg-rig-success')
})
}) })
+3 -2
View File
@@ -8,6 +8,7 @@ function formatRelativeTime(iso: string): string {
if (isNaN(then)) return 'unknown' if (isNaN(then)) return 'unknown'
const diffSec = Math.floor((Date.now() - then) / 1000) const diffSec = Math.floor((Date.now() - then) / 1000)
if (diffSec < 0) return 'unknown'
if (diffSec < 10) return 'just now' if (diffSec < 10) return 'just now'
if (diffSec < 60) return `${diffSec}s ago` if (diffSec < 60) return `${diffSec}s ago`
@@ -32,7 +33,7 @@ function batteryColor(pct: number | null): { bar: string; text: string } {
function formatTimeLeft(sec: number): string { function formatTimeLeft(sec: number): string {
if (sec <= 0 || !isFinite(sec)) return '--' if (sec <= 0 || !isFinite(sec)) return '--'
const m = Math.floor(sec / 60) const m = Math.floor(sec / 60)
const s = sec % 60 const s = Math.floor(sec % 60)
return `${m}m ${s}s left` return `${m}m ${s}s left`
} }
@@ -138,7 +139,7 @@ export default function CameraCard({ camera }: CameraCardProps) {
Battery Battery
</span> </span>
<span className={`font-mono text-xs ${batt.text}`}> <span className={`font-mono text-xs ${batt.text}`}>
{battery_pct !== null ? `${battery_pct}%` : 'N/A'} {battery_pct !== null ? `${Math.max(0, battery_pct)}%` : 'N/A'}
</span> </span>
</div> </div>
<div <div