diff --git a/package.json b/package.json index fe83fa310..95c502729 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,7 @@ "node_modules/(?!(@wagmi|wagmi|viem|@viem|@walletconnect|@justaname\\.id|@zerodev|permissionless)/)" ], "moduleNameMapper": { - "\\.(svg|png|jpg|jpeg|gif|webp)$": "jest-transform-stub", + "\\.(svg|png|jpg|jpeg|gif|webp)$": "/src/utils/__mocks__/static-image.ts", "^@/config/wagmi\\.config$": "/src/utils/__mocks__/wagmi-config.ts", "^wagmi/chains$": "/src/utils/__mocks__/wagmi.ts", "^@justaname\\.id/react$": "/src/utils/__mocks__/justaname.ts", diff --git a/src/app/[...recipient]/error.tsx b/src/app/[...recipient]/error.tsx index b9e261f5f..e2157ebba 100644 --- a/src/app/[...recipient]/error.tsx +++ b/src/app/[...recipient]/error.tsx @@ -1,14 +1,12 @@ 'use client' import { useEffect } from 'react' -import { useRouter } from 'next/navigation' import { useModalsContext } from '@/context/ModalsContext' import { Button } from '@/components/0_Bruddle/Button' import { Card } from '@/components/0_Bruddle/Card' import { recoverFromChunkError } from '@/utils/chunk-error-recovery' export default function PaymentError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { - const router = useRouter() const { setIsSupportModalOpen } = useModalsContext() useEffect(() => { diff --git a/src/components/Badges/__tests__/badge.utils.test.ts b/src/components/Badges/__tests__/badge.utils.test.ts new file mode 100644 index 000000000..a0e6c3f35 --- /dev/null +++ b/src/components/Badges/__tests__/badge.utils.test.ts @@ -0,0 +1,16 @@ +import { BADGES, getBadgeIcon } from '../badge.utils' + +describe('getBadgeIcon', () => { + it('returns the badge path for known codes', () => { + expect(getBadgeIcon('WAITLIST_SKIP')).toBe(BADGES.WAITLIST_SKIP.path) + }) + + it('falls back to a string URL for unknown codes (raw consumers)', () => { + // Unknown codes happen in prod when the FE BADGES map drops a code the BE + // still awards (the recurring badge-registry silent-drop incident). The + // fallback must unwrap StaticImageData.src — never leak the object. + expect(typeof getBadgeIcon('NOT_A_REAL_BADGE')).toBe('string') + expect(getBadgeIcon('NOT_A_REAL_BADGE')).toBeTruthy() + expect(getBadgeIcon(undefined)).toBe(getBadgeIcon('NOT_A_REAL_BADGE')) + }) +}) diff --git a/src/components/Badges/badge.utils.ts b/src/components/Badges/badge.utils.ts index e8fcaa698..ac830689a 100644 --- a/src/components/Badges/badge.utils.ts +++ b/src/components/Badges/badge.utils.ts @@ -195,8 +195,10 @@ export const BADGES: Record = { * list. Used by /dev/share-builder + /dev/debug for iteration. */ export const BADGE_CODES: readonly string[] = Object.keys(BADGES) -export function getBadgeIcon(code?: string) { - return (code && BADGES[code]?.path) || PEANUTMAN_LOGO +export function getBadgeIcon(code?: string): string { + // .src: the svg import is StaticImageData (typed `any` by the module shim, so the + // annotation alone can't enforce this) — raw consumers need a string URL. + return (code && BADGES[code]?.path) || PEANUTMAN_LOGO.src } // returns the public-facing description for a badge code (third-person perspective) diff --git a/src/components/Jobs/index.tsx b/src/components/Jobs/index.tsx index 6407a3f8d..6282063ae 100644 --- a/src/components/Jobs/index.tsx +++ b/src/components/Jobs/index.tsx @@ -4,7 +4,7 @@ export function Careers() { return (
- +
{'<'} Hey there! Want to work at Peanut?
diff --git a/src/components/Profile/views/UnlockedRegions.view.tsx b/src/components/Profile/views/UnlockedRegions.view.tsx index f0e99017d..99e12708f 100644 --- a/src/components/Profile/views/UnlockedRegions.view.tsx +++ b/src/components/Profile/views/UnlockedRegions.view.tsx @@ -117,7 +117,10 @@ const UnlockedRegions = () => { !!selectedRegion && clickedRegionProvider !== null && isSumsubApproved && - (providerRejectionForRegion.state === 'fixable' || providerRejectionForRegion.state === 'blocked') + // Any non-happy state has a dedicated rendering in the ActionModal below. + // Derive from !== 'happy' so a new ProviderRejectionState member can't + // silently miss this gate again (restart-identity did exactly that). + providerRejectionForRegion.state !== 'happy' const modalVariant = hasProviderRejectionForRegion ? ('provider_rejection' as const) : baseModalVariant const handleFinalKycSuccess = useCallback(() => { diff --git a/src/utils/__mocks__/static-image.ts b/src/utils/__mocks__/static-image.ts new file mode 100644 index 000000000..364252b7e --- /dev/null +++ b/src/utils/__mocks__/static-image.ts @@ -0,0 +1,13 @@ +// Jest stand-in for Next.js static asset imports (svg/png/jpg/gif/webp). +// Next yields StaticImageData ({ src, width, height, ... }), but the previous +// jest-transform-stub flattened imports to a bare string — so any code reading +// `.src` (the prod pattern, e.g. getBadgeIcon's fallback) tested against +// fiction. Mirror the real shape so tests and prod agree. +const staticImageStub = { + src: '/test-file-stub', + height: 1, + width: 1, + blurDataURL: '/test-file-stub', +} + +export default staticImageStub