diff --git a/e2e/farm.spec.ts b/e2e/farm.spec.ts index 4feef21..0fbdee6 100644 --- a/e2e/farm.spec.ts +++ b/e2e/farm.spec.ts @@ -1,12 +1,20 @@ import { type Page } from '@playwright/test'; import { Networks, TransactionBuilder } from '@stellar/stellar-sdk'; -import { test, expect, TEST_PUBLIC_KEY, TEST_ADDRESS_DISPLAY } from './mocks/freighter'; +import { test, expect, TEST_ADDRESS_DISPLAY } from './mocks/freighter'; // Pre-computed XDR constants (generated with @stellar/stellar-sdk) // Pools ScVal XDR: scvVec([scvMap({ id: 'pool-xlm', contract_address: '...', asset_code: 'XLM', ... })]) const POOLS_XDR = 'AAAAEAAAAAEAAAABAAAAEQAAAAEAAAAKAAAADwAAAAJpZAAAAAAADgAAAAhwb29sLXhsbQAAAA8AAAAQY29udHJhY3RfYWRkcmVzcwAAAA4AAAA4Q0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUQyS00AAAAPAAAACmFzc2V0X2NvZGUAAAAAAA4AAAADWExNAAAAAA8AAAAJaXNfbmF0aXZlAAAAAAAAAAAAAAEAAAAPAAAACmRhaWx5X3JhdGUAAAAAAAoAAAAAAAAAAAAAAAAAAYagAAAADwAAAA9taW5fbG9ja19wZXJpb2QAAAAABQAAAAAACTqAAAAADwAAAAx0b3RhbF9sb2NrZWQAAAAKAAAAAAAAAAAAAAAXSHboAAAAAA8AAAALdG90YWxfdXNlcnMAAAAAAwAAAAUAAAAPAAAACWlzX2FjdGl2ZQAAAAAAAAAAAAABAAAADwAAAApjcmVhdGVkX2F0AAAAAAAFAAAAAAAAAAA='; +// User position ScVal XDRs — locked (10 XLM at FIXED_NOW_MS-60s) and empty. +// locked_at / unlockable_at are epoch MILLISECONDS (FarmPosition contract: +// unlockAvailableAt = lockedAt + lockPeriodSeconds * 1000). +const POSITION_XDR = + 'AAAAEQAAAAEAAAAFAAAADgAAAAZhbW91bnQAAAAAAAUAAAAABfXhAAAAAA4AAAAHY3JlZGl0cwAAAAAFAAAAAAAAAAAAAAAOAAAACWlzX2xvY2tlZAAAAAAAAAAAAAABAAAADgAAAAlsb2NrZWRfYXQAAAAAAAAFAAABl3Qf8aAAAAAOAAAADXVubG9ja2FibGVfYXQAAAAAAAAFAAABl5gsdaA='; +const EMPTY_POSITION_XDR = + 'AAAAEQAAAAEAAAAFAAAADgAAAAZhbW91bnQAAAAAAAUAAAAAAAAAAAAAAA4AAAAHY3JlZGl0cwAAAAAFAAAAAAAAAAAAAAAOAAAACWlzX2xvY2tlZAAAAAAAAAAAAAAAAAAADgAAAAlsb2NrZWRfYXQAAAAAAAAFAAAAAAAAAAAAAAAOAAAADXVubG9ja2FibGVfYXQAAAAAAAAFAAAAAAAAAAA='; + // Account LedgerEntry XDR for getLedgerEntries mock response const ACCOUNT_XDR = 'AAAAAAAAAAA2Ien4u6Ar2/msLbY4G0lyInC8QbRR+8jvZwBJ4mqxggAAABdIdugAAAAAAEmWAtIAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA'; @@ -27,6 +35,8 @@ const SUCCESS_META_XDR = 'AAAAAAAAAAA='; const FIXED_NOW_MS = 1_750_000_000_000; const CONNECT_WALLET_BUTTON_NAME = /connect (freighter|wallet)/i; +type PositionState = 'empty' | 'locked'; + function getSimulatedFunctionName(transactionXdr?: string): string | null { if (!transactionXdr) return null; @@ -49,9 +59,18 @@ function getSimulatedFunctionName(transactionXdr?: string): string | null { } // ── RPC fetch mock ────────────────────────────────────────────────────────── - -async function mockSorobanRpc(page: Page): Promise { +// +// Seeds pool + position data at the network layer (issue #475) so farm.spec +// no longer depends on `window.__queryClient`. get_pools / get_user_position +// return pre-computed XDRs; sendTransaction flips the mock position state so +// the post-mutation QueryClient invalidation refetches the updated value. + +async function mockSorobanRpc( + page: Page, + options: { initialPosition?: PositionState } = {}, +): Promise { let submittedTransactionXdr = ''; + let positionState: PositionState = options.initialPosition ?? 'empty'; await page.route('**/horizon-testnet.stellar.org/accounts/**', async (route) => { await route.fulfill({ @@ -72,6 +91,28 @@ async function mockSorobanRpc(page: Page): Promise { let result: unknown; switch (body.method) { + case 'getHealth': + result = { status: 'OK', latestLedger: 100 }; + break; + + case 'getLatestLedger': + // Intentionally missing headerXdr/metadataXdr: parseRawLatestLedger + // throws, getCreditVelocity / useSorobanEvents catch → "0" / abort + // polling. Avoids a real-network continue() while staying hermetic. + result = { sequence: 100, id: 'b'.repeat(64) }; + break; + + case 'getEvents': + result = { + latestLedger: 100, + oldestLedger: 1, + latestLedgerCloseTime: '0', + oldestLedgerCloseTime: '0', + cursor: '', + events: [], + }; + break; + case 'getLedgerEntries': result = { entries: [ @@ -85,7 +126,7 @@ async function mockSorobanRpc(page: Page): Promise { }; break; - case 'simulateTransaction': + case 'simulateTransaction': { const functionName = getSimulatedFunctionName(body.params?.transaction); const auth = functionName === 'lock_assets' @@ -93,22 +134,33 @@ async function mockSorobanRpc(page: Page): Promise { : functionName === 'unlock_assets' ? [UNLOCK_ASSETS_AUTH_XDR] : []; - // Works for get_pools, get_user_position, and unlock_assets alike. - // get_user_position parsing ignores a Vec retval and returns null (no position), - // so positions come exclusively from the QueryClient seed in tests. + // Function-specific retval: get_pools → POOLS_XDR, + // get_user_position → POSITION_XDR / EMPTY_POSITION_XDR, + // lock/unlock (and anything else) → scvVoid. + let retvalXdr = 'AAAAAQ=='; + if (functionName === 'get_pools') retvalXdr = POOLS_XDR; + else if (functionName === 'get_user_position') { + retvalXdr = positionState === 'locked' ? POSITION_XDR : EMPTY_POSITION_XDR; + } result = { id: String(body.id), transactionData: SOROBAN_DATA_XDR, - results: [{ xdr: 'AAAAAQ==', auth }], // scvVoid + results: [{ xdr: retvalXdr, auth }], minResourceFee: '100', events: [], cost: { cpuInsns: '1000', memBytes: '1000' }, latestLedger: 100, }; break; + } - case 'sendTransaction': + case 'sendTransaction': { submittedTransactionXdr = body.params?.transaction ?? ''; + const sentFn = getSimulatedFunctionName(submittedTransactionXdr); + // Flip mock position state BEFORE getTransaction SUCCESS so the + // mutation onSuccess → invalidateQueries refetch sees the new value. + if (sentFn === 'lock_assets') positionState = 'locked'; + else if (sentFn === 'unlock_assets') positionState = 'empty'; result = { hash: 'a'.repeat(64), status: 'PENDING', @@ -116,6 +168,7 @@ async function mockSorobanRpc(page: Page): Promise { latestLedgerCloseTime: '0', }; break; + } case 'getTransaction': result = { @@ -145,52 +198,6 @@ async function mockSorobanRpc(page: Page): Promise { }); } -// ── QueryClient seed helpers ──────────────────────────────────────────────── - -const MOCK_POOL = { - id: 'pool-xlm', - contractAddress: 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM', - asset: { code: 'XLM', isNative: true }, - dailyRate: '0.0000001', - minLockPeriod: 604800, // 7 days in seconds - totalLocked: '10.0000000', - totalUsers: 5, - isActive: true, - createdAt: 0, -}; - -function makeMockPosition(lockedAtMs: number) { - return { - user: TEST_PUBLIC_KEY, - poolId: 'pool-xlm', - amount: '10.0000000', - lockedAt: lockedAtMs, - credits: '0', - isLocked: true, - unlockableAt: lockedAtMs + 604_800_000, - }; -} - -async function seedPools(page: Page): Promise { - await page.evaluate((pool) => { - const qc = (window as any).__queryClient; - if (qc) qc.setQueryData(['pools'], [pool]); - }, MOCK_POOL); -} - -async function seedPosition(page: Page, lockedAtMs: number): Promise { - const pos = makeMockPosition(lockedAtMs); - await page.evaluate( - ({ pool, position, pubKey }) => { - const qc = (window as any).__queryClient; - if (!qc) return; - qc.setQueryData(['pools'], [pool]); - qc.setQueryData(['userPosition', 'all', pubKey], [{ pool, position }]); - }, - { pool: MOCK_POOL, position: pos, pubKey: TEST_PUBLIC_KEY }, - ); -} - async function connectWallet(page: Page): Promise { await page.getByRole('button', { name: CONNECT_WALLET_BUTTON_NAME }).first().click(); await page.waitForFunction( @@ -220,15 +227,14 @@ test.describe('Farm E2E', () => { }); test('2 · deposit — modal accepts 10 XLM and submits', async ({ page }) => { - await mockSorobanRpc(page); + // Start empty; sendTransaction of lock_assets flips the mock position → + // locked so the post-success QueryClient invalidation refetches POSITION_XDR. + await mockSorobanRpc(page, { initialPosition: 'empty' }); await page.goto('/farm'); await page.waitForLoadState('networkidle'); await connectWallet(page); - // Seed pool data so the Farm pools section renders a row - await seedPools(page); - - // Wait for pool row with Deposit button + // Wait for pool row with Deposit button (seeded via get_pools XDR mock) const depositBtn = page.getByRole('button', { name: /^\+ deposit$/i }).first(); await expect(depositBtn).toBeVisible({ timeout: 8_000 }); await depositBtn.click(); @@ -248,16 +254,15 @@ test.describe('Farm E2E', () => { // Chakra renders a spinner; just ensure the button still exists }); - // After the 1.5 s stub delay, seed a position so "My earnings" shows 10 XLM + // Give the lock flow time to submit + flip mock position + invalidate. await page.waitForTimeout(1_800); - await seedPosition(page, FIXED_NOW_MS - 60_000); // My earnings row now shows the staked amount await expect(page.getByText('10.0000000').last()).toBeVisible({ timeout: 5_000 }); }); test('3 · countdown visible — Unlock button is disabled before lock period', async ({ page }) => { - await mockSorobanRpc(page); + await mockSorobanRpc(page, { initialPosition: 'locked' }); // Set the clock to a fixed point so the countdown is always non-zero await page.clock.setFixedTime(new Date(FIXED_NOW_MS)); @@ -266,9 +271,6 @@ test.describe('Farm E2E', () => { await page.waitForLoadState('networkidle'); await connectWallet(page); - // Seed a position locked 1 minute ago (7-day lock period → ~7 days remaining) - await seedPosition(page, FIXED_NOW_MS - 60_000); - // Countdown label is visible and contains time-remaining text (e.g. "6d …"). // Both the pool row (FarmPoolRow) and the earnings row (EarningRow) render // this position's countdown independently, so two elements legitimately @@ -283,21 +285,23 @@ test.describe('Farm E2E', () => { }); test('4 · unlock available — fast-forward 8 days enables Unlock', async ({ page }) => { - await mockSorobanRpc(page); + await mockSorobanRpc(page, { initialPosition: 'locked' }); await page.clock.setFixedTime(new Date(FIXED_NOW_MS)); await page.goto('/farm'); await page.waitForLoadState('networkidle'); await connectWallet(page); - await seedPosition(page, FIXED_NOW_MS - 60_000); // Fast-forward 8 days (past the 7-day lock period) const eightDaysMs = 8 * 24 * 60 * 60 * 1_000; await page.clock.setFixedTime(new Date(FIXED_NOW_MS + eightDaysMs)); - // The useCountdown hook re-evaluates on each render - await page.reload(); + // The useCountdown hook re-evaluates on each render; trigger a navigation + // back to /farm so the hook picks up the new Date.now() + await page.goto('/farm'); + await page.waitForLoadState('networkidle'); + await connectWallet(page); // Unlock button should now be enabled const unlockBtn = page.getByRole('button', { name: /^unlock$/i }).first(); @@ -309,7 +313,9 @@ test.describe('Farm E2E', () => { }); test('5 · unlock — fill modal, sign, submit; stake drops to 0', async ({ page }) => { - await mockSorobanRpc(page); + // Start locked; sendTransaction of unlock_assets flips the mock position → + // empty so the post-success QueryClient invalidation refetches EMPTY_POSITION_XDR. + await mockSorobanRpc(page, { initialPosition: 'locked' }); // Start with clock 8 days in the future so Unlock is immediately available const eightDaysMs = 8 * 24 * 60 * 60 * 1_000; @@ -318,7 +324,6 @@ test.describe('Farm E2E', () => { await page.goto('/farm'); await page.waitForLoadState('networkidle'); await connectWallet(page); - await seedPosition(page, FIXED_NOW_MS - 60_000); // Unlock button should be enabled const unlockBtn = page.getByRole('button', { name: /^unlock$/i }).first(); @@ -343,26 +348,7 @@ test.describe('Farm E2E', () => { page.getByText(/unlock (confirmed|submitted)/i).first(), ).toBeVisible({ timeout: 15_000 }); - // After success, update cache to show 0 stake - await page.evaluate( - ({ pool, pubKey }) => { - const qc = (window as any).__queryClient; - if (!qc) return; - const emptyPos = { - user: pubKey, - poolId: 'pool-xlm', - amount: '0.0000000', - lockedAt: 0, - credits: '0', - isLocked: false, - unlockableAt: 0, - }; - qc.setQueryData(['userPosition', 'all', pubKey], [{ pool, position: emptyPos }]); - }, - { pool: MOCK_POOL, pubKey: TEST_PUBLIC_KEY }, - ); - - // "My earnings" now shows 0.0000000 + // "My earnings" now shows 0.0000000 after the position flip + refetch await expect(page.getByText('0.0000000').last()).toBeVisible({ timeout: 5_000 }); }); }); diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index fb1223b..5e35d6b 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -34,6 +34,14 @@ export default defineConfig({ 'http://localhost:3000/__mock-leaderboard-api', NEXT_PUBLIC_MIN_LOCK_PERIOD_SECONDS: '604800', NEXT_PUBLIC_E2E: 'true', + // Network-level seeding for farm.spec (issue #475): getFactoryPools / + // getUserPosition short-circuit unless a factory contract and a funded + // simulation account are configured. Both are satisfied by the RPC + // mocks in e2e/farm.spec.ts (POOLS_XDR / POSITION_XDR / ACCOUNT_XDR). + NEXT_PUBLIC_FACTORY_CONTRACT_ID: + 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM', + NEXT_PUBLIC_SIMULATION_ACCOUNT: + 'GA3CD2PYXOQCXW7ZVQW3MOA3JFZCE4F4IG2FD66I55TQASPCNKYYEFRN', }, }, }); diff --git a/src/app/loading.tsx b/src/app/loading.tsx index bf480df..9336337 100644 --- a/src/app/loading.tsx +++ b/src/app/loading.tsx @@ -3,7 +3,15 @@ import { usePathname } from "next/navigation"; import { Box, Flex, Spinner, Text, type BoxProps } from "@chakra-ui/react"; -function SkeletonBar({ w = "100%", h = "16px", delay = 0 }: { w?: BoxProps["w"]; h?: BoxProps["h"]; delay?: number }) { +function SkeletonBar({ + w = "100%", + h = "16px", + delay = 0, +}: { + w?: string | { base: string; md: string }; + h?: string; + delay?: number; +}) { return ( Layout Component', () => { it('matches baseline style design snapshot tests', () => { vi.spyOn(hooks, 'usePlatformStats').mockReturnValue({ data: { - // getPlatformStats returns totalValueLocked already formatted via - // toLocaleString (currency USD), so the component must not re-format it. - tvl: '$42,300,000', + // Production-shaped values: getPlatformStats formats TVL with + // toLocaleString({ style: 'currency' }), and getCreditVelocity returns + // a raw credit-unit count that formatCredits scales for display. + tvl: '$3,500', activePools: 12, totalFarmers: 15450, - creditVelocity: '8500000000000' + creditVelocity: '8500000000000', }, isLoading: false, isError: false, diff --git a/src/components/PlatformStats/PlatformStats.tsx b/src/components/PlatformStats/PlatformStats.tsx index a3b3d51..f19c146 100644 --- a/src/components/PlatformStats/PlatformStats.tsx +++ b/src/components/PlatformStats/PlatformStats.tsx @@ -35,7 +35,7 @@ export const PlatformStats: React.FC = ({ initialData }) => Total Value Locked - {stats ? stats.tvl : '—'} + {stats ? stats.tvl : '$0'} diff --git a/src/components/PlatformStats/__snapshots__/PlatformStats.test.tsx.snap b/src/components/PlatformStats/__snapshots__/PlatformStats.test.tsx.snap index 27e1cad..50e1703 100644 --- a/src/components/PlatformStats/__snapshots__/PlatformStats.test.tsx.snap +++ b/src/components/PlatformStats/__snapshots__/PlatformStats.test.tsx.snap @@ -23,7 +23,7 @@ exports[` Layout Component > matches baseline style design snap
- $42,300,000 + $3,500
diff --git a/src/components/UnlockModal/UnlockModal.test.tsx b/src/components/UnlockModal/UnlockModal.test.tsx index 23f5c09..be1c7da 100644 --- a/src/components/UnlockModal/UnlockModal.test.tsx +++ b/src/components/UnlockModal/UnlockModal.test.tsx @@ -4,7 +4,7 @@ import UnlockModal from './UnlockModal'; import { useFarmStore, type FarmStore } from '@/store/farmStore'; import { useStellarWallet } from '@/context/StellarWalletContext'; import { useUnlockAssetsFeePreview } from '@/hooks/useSorobanQuery'; -import { unlockAssets } from '@/lib/soroban'; +import { unlockAssets, type UnlockAssetsCallbacks } from '@/lib/soroban'; import { useCountdown } from '@/hooks/useCountdown'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ChakraProvider } from '@chakra-ui/react'; @@ -202,14 +202,15 @@ describe('UnlockModal', () => { }); it('handles successful unlock flow', async () => { - vi.mocked(unlockAssets).mockImplementation(async (args) => { - const onStep = args.onStep as ((s: string) => void) | undefined; - const onHash = args.onHash as ((h: string) => void) | undefined; - onStep?.('simulating'); - onStep?.('signing'); - onStep?.('submitting'); - onHash?.('test-hash-abc'); - return { success: true, hash: 'test-hash-abc', status: 'SUCCESS' } as Awaited>; + vi.mocked(unlockAssets).mockImplementation(async (args: { + onStep?: UnlockAssetsCallbacks['onStep']; + onHash?: UnlockAssetsCallbacks['onHash']; + }) => { + args.onStep?.('simulating'); + args.onStep?.('signing'); + args.onStep?.('submitting'); + args.onHash?.('test-hash-abc'); + return { success: true, hash: 'test-hash-abc', status: 'SUCCESS' }; }); renderWithProviders(createElement(UnlockModal)); @@ -230,9 +231,10 @@ describe('UnlockModal', () => { }); it('handles unlock error flow — result.success === false', async () => { - vi.mocked(unlockAssets).mockImplementation(async (args) => { - const onStep = args.onStep as ((s: string) => void) | undefined; - onStep?.('simulating'); + vi.mocked(unlockAssets).mockImplementation(async (args: { + onStep?: UnlockAssetsCallbacks['onStep']; + }) => { + args.onStep?.('simulating'); return { success: false, status: 'FAILED', diff --git a/src/context/index.tsx b/src/context/index.tsx index 53dbd01..cfcadbc 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -21,10 +21,9 @@ function ContextProvider({ children }: { children: ReactNode }) { }); useEffect(() => { - // Expose the QueryClient only in non-production builds. E2E tests run - // against `next dev` (NODE_ENV=development), so the dev-only guard is - // sufficient — never expose it in production, even when NEXT_PUBLIC_E2E - // is set (#471). + // Dev-only escape hatch for local debugging and Playwright specs that run + // `next dev` (NODE_ENV=development). Never expose the QueryClient in a + // production bundle — not even when NEXT_PUBLIC_E2E is set (issue #471). if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') { window.__queryClient = queryClient; } diff --git a/src/hooks/useLeaderboard.test.ts b/src/hooks/useLeaderboard.test.ts index 6a1f7c2..e315f8f 100644 --- a/src/hooks/useLeaderboard.test.ts +++ b/src/hooks/useLeaderboard.test.ts @@ -22,7 +22,7 @@ const mockStakeEntries: LeaderboardEntry[] = [ { address: "addr-stake", totalCredits: 10, totalStake: 999, boostUtilization: 0.9 }, ]; -let fetchLeaderboardMock: ReturnType Promise>>; +let fetchLeaderboardMock: ReturnType unknown>>; vi.mock("./useLeaderboard", async (importOriginal) => { const actual = await importOriginal(); diff --git a/src/hooks/useLockFlow.ts b/src/hooks/useLockFlow.ts index e816161..0e792e0 100644 --- a/src/hooks/useLockFlow.ts +++ b/src/hooks/useLockFlow.ts @@ -48,6 +48,8 @@ export function useLockFlow({ const [error, setError] = useState(null); // Use refs to avoid recreating execute callback when step or walletApi change during execution + const walletApiRef = useRef(walletApi); + walletApiRef.current = walletApi; // step changes throughout the flow (idle -> simulating -> signing -> // submitting -> success/error), so depending on it directly recreated // `execute` mid-flow -- a caller holding an earlier reference to `execute` diff --git a/src/hooks/useSorobanQuery.test.ts b/src/hooks/useSorobanQuery.test.ts index 48f776c..3004379 100644 --- a/src/hooks/useSorobanQuery.test.ts +++ b/src/hooks/useSorobanQuery.test.ts @@ -280,7 +280,7 @@ describe("useSetBoost (#92)", () => { const { useToast } = await import("@chakra-ui/react"); toastMock = vi.fn(); - vi.mocked(useToast).mockReturnValue(toastMock as unknown as ReturnType); + vi.mocked(useToast).mockReturnValue(toastMock as never); }); it("calls sorobanService.setBoost with correct arguments", async () => { @@ -453,7 +453,7 @@ describe("useUnlockAssets (#138)", () => { const { useToast } = await import("@chakra-ui/react"); toastMock = vi.fn(); - vi.mocked(useToast).mockReturnValue(toastMock as unknown as ReturnType); + vi.mocked(useToast).mockReturnValue(toastMock as never); }); it("invalidates the stellarBalance cache on success, matching useLockAssets", async () => { diff --git a/src/lib/soroban.history.test.ts b/src/lib/soroban.history.test.ts index b9ead4e..187dd70 100644 --- a/src/lib/soroban.history.test.ts +++ b/src/lib/soroban.history.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from 'vitest'; import { xdr, nativeToScVal, StrKey, Address } from '@stellar/stellar-sdk'; import { getUserTransactionHistory, getAllEvents } from './soroban'; // Generate valid-format Stellar G-addresses from fixed 32-byte seeds. -const USER_KEY = StrKey.encodeEd25519PublicKey(Buffer.alloc(32, 0)); +const USER_KEY = StrKey.encodeEd25519PublicKey(Buffer.alloc(32)); const OTHER_KEY = StrKey.encodeEd25519PublicKey(Buffer.alloc(32, 1)); const POOL_ID = 'CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4'; const TX_HASH_1 = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'; diff --git a/src/lib/soroban.network-guard.test.ts b/src/lib/soroban.network-guard.test.ts index 027776e..964ff67 100644 --- a/src/lib/soroban.network-guard.test.ts +++ b/src/lib/soroban.network-guard.test.ts @@ -161,7 +161,7 @@ describe("SorobanService network passphrase guard", () => { expect(rpcServer.sendTransaction).not.toHaveBeenCalled(); }); - it("blocks signing when wallet network mismatches in unlockAssets", async () => { + it("throws FreighterError when wallet network mismatches in unlockAssets", async () => { const { service, rpcServer } = makeService(); rpcServer.simulateTransaction.mockResolvedValue({ result: { auth: [makeAuthEntry("unlock_assets")] }, @@ -188,7 +188,7 @@ describe("SorobanService network passphrase guard", () => { expect(rpcServer.sendTransaction).not.toHaveBeenCalled(); }); - it("blocks signing when wallet network mismatches in setBoost", async () => { + it("throws FreighterError when wallet network mismatches in setBoost", async () => { const { service, rpcServer } = makeService(); rpcServer.simulateTransaction.mockResolvedValue({ result: { auth: [makeAuthEntry("set_boost")] },