From d53a25679e0ca561f8f37f06863b1111ab4e50c7 Mon Sep 17 00:00:00 2001 From: JamesVictor-O Date: Thu, 24 Sep 2026 01:13:15 +0100 Subject: [PATCH] fix assigned claim, oracle, and storage issues --- src/__tests__/ClaimHistoryTable.test.tsx | 9 ++++++ src/__tests__/OracleDataWidget.test.tsx | 21 ++++++++++++ src/__tests__/storage.test.ts | 30 ++++++++++++++++- src/app/dashboard/page.tsx | 8 ++++- src/components/ClaimHistoryTable.tsx | 28 ++++++++++------ src/components/OracleDataWidget.tsx | 11 +++++-- src/lib/storage.ts | 41 +++++++++++++++++------- 7 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/__tests__/ClaimHistoryTable.test.tsx b/src/__tests__/ClaimHistoryTable.test.tsx index 86342f5..dbc1fe6 100644 --- a/src/__tests__/ClaimHistoryTable.test.tsx +++ b/src/__tests__/ClaimHistoryTable.test.tsx @@ -35,6 +35,15 @@ describe('ClaimHistoryTable', () => { expect(html).toContain('Processing'); }); + it('links each claim row and mobile card to its policy details', () => { + const html = renderToStaticMarkup( + , + ); + + expect(html.match(/href="\/policies\/policy-123"/g)).toHaveLength(4); + expect(html).toContain('aria-label="View policy for claim claim-1"'); + }); + it('displays trigger status correctly', () => { const claims = [ makeClaim({ id: 'claim-1', triggerMet: true }), diff --git a/src/__tests__/OracleDataWidget.test.tsx b/src/__tests__/OracleDataWidget.test.tsx index 0a51efd..4ce263c 100644 --- a/src/__tests__/OracleDataWidget.test.tsx +++ b/src/__tests__/OracleDataWidget.test.tsx @@ -1,5 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { renderToStaticMarkup } from 'react-dom/server'; +import { render } from '@testing-library/react'; import { OracleDataWidget } from '../components/OracleDataWidget'; vi.mock('@/hooks/useOracle', () => ({ @@ -46,6 +47,26 @@ describe('OracleDataWidget', () => { vi.clearAllMocks(); }); + it('does not rerender when its parent rerenders with unchanged props', () => { + mockUseOracleReading.mockReturnValue({ + reading: null, + loading: false, + error: null, + refetch: vi.fn(), + }); + + const { rerender } = render( + , + ); + expect(mockUseOracleReading).toHaveBeenCalledTimes(1); + + rerender(); + expect(mockUseOracleReading).toHaveBeenCalledTimes(1); + + rerender(); + expect(mockUseOracleReading).toHaveBeenCalledTimes(2); + }); + it('shows skeleton when loading', () => { mockUseOracleReading.mockReturnValue({ reading: null, diff --git a/src/__tests__/storage.test.ts b/src/__tests__/storage.test.ts index 181dd6f..4425171 100644 --- a/src/__tests__/storage.test.ts +++ b/src/__tests__/storage.test.ts @@ -12,11 +12,19 @@ const localStorageMock = (() => { Object.defineProperty(window, 'localStorage', { value: localStorageMock }); +const sessionStorageMock = { + getItem: vi.fn<(key: string) => string | null>(() => null), + setItem: vi.fn<(key: string, value: string) => void>(), + removeItem: vi.fn<(key: string) => void>(), +}; + +Object.defineProperty(window, 'sessionStorage', { value: sessionStorageMock }); + describe('storage', () => { beforeEach(() => localStorageMock.clear()); it('sets and gets string values', () => { - storage.set('key', 'value'); + expect(storage.set('key', 'value')).toBe(true); expect(storage.get('key')).toBe('value'); }); @@ -40,4 +48,24 @@ describe('storage', () => { localStorageMock.setItem('bad', 'not-valid-json{'); expect(storage.getJSON('bad')).toBeNull(); }); + + it('returns false instead of throwing when local storage quota is exceeded', () => { + const setItem = vi.spyOn(localStorage, 'setItem').mockImplementation(() => { + throw new DOMException('Storage quota exceeded', 'QuotaExceededError'); + }); + + expect(storage.set('key', 'value')).toBe(false); + expect(() => storage.set('key', 'value')).not.toThrow(); + setItem.mockRestore(); + }); + + it('returns false instead of throwing when session storage quota is exceeded', () => { + sessionStorageMock.setItem.mockImplementation(() => { + throw new DOMException('Storage quota exceeded', 'QuotaExceededError'); + }); + + expect(storage.setSession('token', 'value')).toBe(false); + expect(() => storage.setSession('token', 'value')).not.toThrow(); + sessionStorageMock.setItem.mockReset(); + }); }); diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 1d7a627..bbed3c1 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -182,7 +182,13 @@ export default function DashboardPage() { className="border-b border-white/5 transition-colors hover:bg-white/[0.02]" > - {claim.id.slice(0, 8)}… + + {claim.id.slice(0, 8)}… + {formatDateTime(claim.submittedAt)} diff --git a/src/components/ClaimHistoryTable.tsx b/src/components/ClaimHistoryTable.tsx index cf8274e..9505574 100644 --- a/src/components/ClaimHistoryTable.tsx +++ b/src/components/ClaimHistoryTable.tsx @@ -163,15 +163,21 @@ function ClaimHistoryTableComponent({ claims, className }: ClaimHistoryTableProp {sortedClaims.map((claim) => ( - {claim.id.slice(0, 8)}… + + {claim.id.slice(0, 8)}… + {claim.policyId.slice(0, 8)}… @@ -189,7 +195,7 @@ function ClaimHistoryTableComponent({ claims, className }: ClaimHistoryTableProp {claim.txHash ? ( - + ) : ( — )} @@ -208,19 +214,23 @@ function ClaimHistoryTableComponent({ claims, className }: ClaimHistoryTableProp {sortedClaims.map((claim) => (
- + {claim.id.slice(0, 8)}… - +
Policy {claim.policyId.slice(0, 8)}… @@ -237,7 +247,7 @@ function ClaimHistoryTableComponent({ claims, className }: ClaimHistoryTableProp Tx {claim.txHash ? ( - + ) : ( — )} diff --git a/src/components/OracleDataWidget.tsx b/src/components/OracleDataWidget.tsx index 6d0c1e9..70d0ac1 100644 --- a/src/components/OracleDataWidget.tsx +++ b/src/components/OracleDataWidget.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useCallback } from 'react'; +import { memo, useState, useCallback } from 'react'; import { useOracleReading } from '@/hooks/useOracle'; import { Skeleton } from './Skeleton'; import { formatOracleValue, formatDateTime, formatUtcDateTime } from '@/lib/format'; @@ -11,7 +11,7 @@ interface OracleDataWidgetProps { className?: string; } -export function OracleDataWidget({ oracleKey, className }: OracleDataWidgetProps) { +function OracleDataWidgetComponent({ oracleKey, className }: OracleDataWidgetProps) { const { reading, loading, error, refetch } = useOracleReading(oracleKey); const [refreshing, setRefreshing] = useState(false); @@ -94,3 +94,10 @@ export function OracleDataWidget({ oracleKey, className }: OracleDataWidgetProps
); } + +export const OracleDataWidget = memo( + OracleDataWidgetComponent, + (previous, next) => previous.oracleKey === next.oracleKey && previous.className === next.className, +); + +OracleDataWidget.displayName = 'OracleDataWidget'; diff --git a/src/lib/storage.ts b/src/lib/storage.ts index a36bb4a..e85edb9 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -6,10 +6,24 @@ function get(key: string): string | null { catch { return null; } } -function set(key: string, value: string): void { - if (!isClient) return; - try { localStorage.setItem(key, value); } - catch { /* quota exceeded — ignore */ } +function isQuotaExceededError(error: unknown): boolean { + return error instanceof DOMException && ( + error.name === 'QuotaExceededError' || + error.name === 'NS_ERROR_DOM_QUOTA_REACHED' || + error.code === 22 || + error.code === 1014 + ); +} + +function set(key: string, value: string): boolean { + if (!isClient) return false; + try { + localStorage.setItem(key, value); + return true; + } catch (error) { + if (isQuotaExceededError(error)) return false; + return false; + } } function remove(key: string): void { @@ -25,9 +39,9 @@ function getJSON(key: string): T | null { catch { return null; } } -function setJSON(key: string, value: T): void { - try { set(key, JSON.stringify(value)); } - catch { /* ignore */ } +function setJSON(key: string, value: T): boolean { + try { return set(key, JSON.stringify(value)); } + catch { return false; } } function getSession(key: string): string | null { @@ -36,10 +50,15 @@ function getSession(key: string): string | null { catch { return null; } } -function setSession(key: string, value: string): void { - if (!isClient) return; - try { sessionStorage.setItem(key, value); } - catch { /* quota exceeded — ignore */ } +function setSession(key: string, value: string): boolean { + if (!isClient) return false; + try { + sessionStorage.setItem(key, value); + return true; + } catch (error) { + if (isQuotaExceededError(error)) return false; + return false; + } } function removeSession(key: string): void {