Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
69a7fd9
fix: remove dead code and deduplicate utilization color logic (#266)
Jayy4rl Jul 25, 2026
cd63178
fix: wire claim backend sync, validate config at startup, fix network…
jajafwangshak86-ops Jul 25, 2026
8fa47ef
fix: remove dead useProduct hook, dynamic footer year, memoize list c…
Ada-Girly881 Jul 25, 2026
3c6ed0f
Fix accessibility, maintainability, and type safety issues (#289)
Junman140 Jul 25, 2026
09f27ff
feat: SEO, PWA, and test coverage improvements (#290)
AbelOsaretin Jul 25, 2026
36dab36
Add test coverage for ClaimStatus, ClaimHistoryTable, PolicyCard, and…
oomokaro1 Jul 25, 2026
390d296
test: add test coverage for WalletButton, ConnectWalletPrompt, NavBar…
trustosaretin Jul 25, 2026
4184412
feat: add test coverage for TriggerConditionBadge, CategoryFilter, Se…
osasfaith Jul 25, 2026
5f4d469
Add test coverage for CopyButton, Toast, ToastContext, ErrorBoundary,…
KidDev88 Jul 25, 2026
e55a6da
added tests (#311)
Ghoost-404 Jul 25, 2026
4ff5e2b
test: add coverage for LoadingSpinner, Logo, NetworkBanner, and Skele…
akintewe Jul 25, 2026
dcb4032
fix(#43): stop hardcoding a wallet list that can diverge from the kit…
Obiajulu-gif Jul 25, 2026
e8dd2e8
fix(#189): give ProgressBar's progress indicator an accessible name (…
Obiajulu-gif Jul 25, 2026
f6b0e26
fix(#190): give SearchBar input an accessible name and hide decorativ…
Obiajulu-gif Jul 25, 2026
a7ceead
fix(#191): announce copy confirmation to assistive tech (#315)
Obiajulu-gif Jul 25, 2026
810d776
test(hooks): add test coverage for hooks (#317)
fortezzalaboratory Jul 25, 2026
7771f2d
test(#319, #320): add comprehensive test coverage for api.ts and cont…
Uchechukwu-Ekezie Jul 25, 2026
62ed382
Fix lint config, request-race guards, and stale doc comment (#318)
DeborahOlaboye Jul 25, 2026
d0a1b2a
test: add test coverage for TransactionLink, WalletAddressDisplay, us…
Levi-Ojukwu Jul 25, 2026
821876e
fix: preserve auth-failure error message in WalletContext disconnect …
adetomiwa21 Jul 25, 2026
24caffd
fix: clamp rainfall key precision and validate length for crop/flight…
adetomiwa21 Jul 25, 2026
2b05f24
fix: prevent colon-containing flight numbers from breaking oracle key…
adetomiwa21 Jul 25, 2026
23e5037
test: cover cancelledRef reset so retried submit failures surface (#220)
adetomiwa21 Jul 25, 2026
f74e2d3
feat: apple-icon PWA, WalletState type, stellar.ts tests (#324 #322 #…
Mawuli-tech Jul 26, 2026
7936e1d
Fix/ci magic numbers test coverage (#329)
Mosas2000 Jul 26, 2026
6faf7f1
Merge branch 'main' into fix/wallet-oracle-claim-error-handling
adetomiwa21 Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NEXT_PUBLIC_API_URL=http://localhost:3001/api/v1
NEXT_PUBLIC_STELLAR_NETWORK=testnet
NEXT_PUBLIC_STELLAR_NETWORK=TESTNET
NEXT_PUBLIC_CONTRACT_ID=
NEXT_PUBLIC_POSTHOG_KEY=your_posthog_project_key_here
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ jobs:

- run: corepack enable
- run: yarn install --immutable
- run: yarn lint
- run: yarn test
- run: yarn build
16 changes: 16 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];

export default eslintConfig;
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@netlify/plugin-nextjs": "^5.15.12",
"@tailwindcss/postcss": "^4.0.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.0",
"@testing-library/react": "^16.3.2",
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^6.0.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-config-next": "^15.0.0",
"jsdom": "^29.1.1",
"postcss": "^8.5.0",
"tailwindcss": "^4.0.0",
Expand Down
81 changes: 81 additions & 0 deletions src/__tests__/BuyPolicyModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { renderToStaticMarkup } from 'react-dom/server';
import { BuyPolicyModal } from '../components/BuyPolicyModal';
import type { Product } from '../types';

vi.mock('@/lib/stellar', () => ({
signTransaction: vi.fn(),
getAddress: vi.fn(),
isConnected: vi.fn(),
}));
vi.mock('@/hooks/useWallet', () => ({
useWallet: vi.fn(() => ({
address: null,
connect: vi.fn(),
connecting: false,
error: null,
})),
}));
vi.mock('@/context/ToastContext', () => ({
useToast: vi.fn(() => ({
show: vi.fn(),
})),
}));

function makeProduct(overrides: Partial<Product> = {}): Product {
return {
id: 'product-1',
name: 'Crop Insurance',
category: 'crop',
description: 'Test product',
coverageMin: '1000000',
coverageMax: '100000000',
premiumRate: 500,
maxDuration: 30,
...overrides,
};
}

describe('BuyPolicyModal', () => {
it('renders crop-specific latitude and longitude inputs', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ category: 'crop' })} onClose={vi.fn()} />);
expect(html).toContain('Latitude');
expect(html).toContain('Longitude');
expect(html).toContain('Month');
expect(html).toContain('Year');
});

it('renders flight-specific inputs for flight products', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ category: 'flight', name: 'Flight Delay' })} onClose={vi.fn()} />);
expect(html).toContain('Flight Number');
expect(html).toContain('Date');
});

it('shows fixed defi oracle key for defi products', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ category: 'defi', name: 'DeFi Cover' })} onClose={vi.fn()} />);
expect(html).toContain('defi');
expect(html).toContain('Oracle Key (Fixed)');
});

it('shows manual oracle key input for disaster products', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ category: 'disaster', name: 'Natural Disaster' })} onClose={vi.fn()} />);
expect(html).toContain('Oracle Key');
expect(html).toContain('Max 32 chars');
});

it('shows manual oracle key input for health products', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ category: 'health', name: 'Health' })} onClose={vi.fn()} />);
expect(html).toContain('Oracle Key');
expect(html).toContain('Max 32 chars');
});

it('renders the configure step with coverage and duration inputs', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct()} onClose={vi.fn()} />);
expect(html).toContain('Coverage Amount (USDC)');
expect(html).toContain('Duration (days');
});

it('displays the product name in the modal title', () => {
const html = renderToStaticMarkup(<BuyPolicyModal product={makeProduct({ name: 'Test Product' })} onClose={vi.fn()} />);
expect(html).toContain('Buy — Test Product');
});
});
58 changes: 58 additions & 0 deletions src/__tests__/CategoryFilter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { describe, it, expect, vi } from 'vitest';
import { renderToStaticMarkup } from 'react-dom/server';
import { CategoryFilter } from '../components/CategoryFilter';

describe('CategoryFilter', () => {
it('renders all category buttons including "All products"', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} />);
expect(html).toContain('All products');
const buttonCount = (html.match(/<button/g) || []).length;
expect(buttonCount).toBe(6);
});

it('sets aria-pressed true only on the selected button', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="crop" onChange={onChange} />);
const pressedCount = (html.match(/aria-pressed="true"/g) || []).length;
expect(pressedCount).toBe(1);
expect(html).toContain('Crop Insurance');
});

it('sets aria-pressed false on non-selected buttons', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="crop" onChange={onChange} />);
const falseCount = (html.match(/aria-pressed="false"/g) || []).length;
expect(falseCount).toBe(5);
});

it('renders Flight button with correct label', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} />);
expect(html).toContain('Flight Delay');
});

it('renders Disaster button with correct label', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} />);
expect(html).toContain('Disaster');
});

it('renders Health button with correct label', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} />);
expect(html).toContain('Health');
});

it('renders DeFi button with correct label', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} />);
expect(html).toContain('DeFi Cover');
});

it('applies custom className', () => {
const onChange = vi.fn();
const html = renderToStaticMarkup(<CategoryFilter value="all" onChange={onChange} className="custom" />);
expect(html).toContain('custom');
});
});
86 changes: 86 additions & 0 deletions src/__tests__/ClaimHistoryTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { renderToStaticMarkup } from 'react-dom/server';
import { ClaimHistoryTable } from '../components/ClaimHistoryTable';
import type { Claim } from '../types';

function makeClaim(overrides: Partial<Claim> = {}): Claim {
return {
id: 'claim-1',
policyId: 'policy-1',
claimant: 'GWALLET',
triggerMet: true,
status: 'Paid',
submittedAt: 1_720_000_000,
processedAt: null,
payoutAmount: '1000000',
...overrides,
};
}

describe('ClaimHistoryTable', () => {
it('renders empty state when no claims', () => {
const html = renderToStaticMarkup(<ClaimHistoryTable claims={[]} />);
expect(html).toContain('No claims yet');
});

it('renders claims in a table', () => {
const claims = [
makeClaim({ id: 'claim-1', status: 'Paid' }),
makeClaim({ id: 'claim-2', status: 'Processing' }),
];

const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('claim-1');
expect(html).toContain('claim-2');
expect(html).toContain('Paid');
expect(html).toContain('Processing');
});

it('displays trigger status correctly', () => {
const claims = [
makeClaim({ id: 'claim-1', triggerMet: true }),
makeClaim({ id: 'claim-2', triggerMet: false }),
];

const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('Met');
expect(html).toContain('Not met');
});

it('displays payout amount correctly', () => {
const claims = [
makeClaim({ id: 'claim-1', payoutAmount: '1000000' }),
makeClaim({ id: 'claim-2', payoutAmount: undefined }),
];

const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('0.10');
expect(html).toContain('—');
});

it('displays transaction link for paid claims with txHash', () => {
const claims = [
makeClaim({ id: 'claim-1', status: 'Paid', txHash: 'tx-abc123' }),
];

const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('tx-abc123');
});

it('displays dash for claims without txHash', () => {
const claims = [
makeClaim({ id: 'claim-1', status: 'Paid', txHash: undefined }),
];

const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('—');
});

it('renders sort buttons for sortable columns', () => {
const claims = [makeClaim()];
const html = renderToStaticMarkup(<ClaimHistoryTable claims={claims} />);
expect(html).toContain('Trigger');
expect(html).toContain('Payout');
expect(html).toContain('Submitted');
expect(html).toContain('Status');
});
});
Loading