From 3079b3aa298819718a47d31d61815f047a4fd0d5 Mon Sep 17 00:00:00 2001 From: jadonamite Date: Wed, 29 Jul 2026 21:58:39 +0100 Subject: [PATCH] fix: repair unclosed blocks left by merges in FeeEstimator and SorobanPanel Both files carried two interleaved versions of the same code from botched conflict resolutions, leaving unclosed blocks. They failed to parse, so `tsc -b` and `npm run build` aborted and every test file importing them could not run. FeeEstimator: - `load()` had two copies of the onFeeLoad guard, the first unclosed. - Props declared both `compact` and `variant`; the render body contained a `compact` branch wrapping an orphaned `variant` branch plus a duplicate header. Kept the `compact` API the existing tests exercise and dropped the untested, uncalled `variant` duplicate. SorobanPanel: - `buildCurlCommand` was defined twice, the first copy unclosed. Kept the version the tests assert against (soroban-rpc.example.com/invoke). - The `else` branch of `doInvoke` was left unclosed by a trailing fragment of the older single-call implementation. - The submit button's label ternary had a `?` where a `:` belonged. - Dropped the now-unused `cn` import and a redundant `state === "loading"` check that TypeScript proved unreachable via `canInvoke`. SorobanPanel.test.tsx: - A truncated test body left `describe("simulate mode")` unclosed. Completed the textarea-growth test to match its name and removed a dead constant. Restores 5 test files and 57 previously unrunnable tests. --- src/components/FeeEstimator.tsx | 53 +--------------------------- src/components/SorobanPanel.test.tsx | 19 ++++++++-- src/components/SorobanPanel.tsx | 45 +++-------------------- 3 files changed, 21 insertions(+), 96 deletions(-) diff --git a/src/components/FeeEstimator.tsx b/src/components/FeeEstimator.tsx index e39dd99..d16f628 100644 --- a/src/components/FeeEstimator.tsx +++ b/src/components/FeeEstimator.tsx @@ -18,7 +18,6 @@ interface FeeEstimatorProps { /** Compact single-line display variant. */ compact?: boolean; /** Callback fired when fee data loads successfully. */ - variant?: "default" | "compact"; onFeeLoad?: (fee: FeeData) => void; } @@ -26,7 +25,6 @@ export function FeeEstimator({ className, refreshInterval = 0, compact, - variant = "default", onFeeLoad, }: FeeEstimatorProps) { const [fee, setFee] = useState(null); @@ -45,8 +43,6 @@ export function FeeEstimator({ setError(null); if (data && onFeeLoad) { onFeeLoad(data); - if (data) { - onFeeLoad?.(data); } } finally { setLoading(false); @@ -144,54 +140,7 @@ export function FeeEstimator({ } /> - - - -
- {loading && !fee ? ( - variant === "compact" ? ( -
- ) : ( -
-
-
-
- ) - ) : error ? ( -

{error}

- ) : fee ? ( - variant === "compact" ? ( -

Fee: ~{fee.recommended} stroops

- ) : ( -
- -
- parseInt(fee.baseFee, 10) * 2 - } - /> -
- ) - ) : null} + ) : null}
)} diff --git a/src/components/SorobanPanel.test.tsx b/src/components/SorobanPanel.test.tsx index 6e1c23c..a5df23b 100644 --- a/src/components/SorobanPanel.test.tsx +++ b/src/components/SorobanPanel.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, act } from "@testing-library/react"; +import { act,fireEvent, render, screen } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { useSorokit } from "@/context/useSorokit"; @@ -123,7 +123,20 @@ describe("SorobanPanel", () => { it("grows the argument textarea as lines are added and remains user-resizable", () => { render( {}} />); - const textarea = screen.getByLabelText("Arguments (JSON array)"); + const textarea = screen.getByLabelText( + "Arguments (JSON array)", + ) as HTMLTextAreaElement; + + expect(textarea.rows).toBe(3); + expect(textarea.className).toContain("resize-y"); + + fireEvent.input(textarea, { + target: { value: "[\n1,\n2,\n3,\n4\n]" }, + }); + + expect(textarea.rows).toBe(6); + }); + describe("simulate mode", () => { it("renders Simulate badge and subtitle", () => { render( {}} mode="simulate" />); @@ -149,6 +162,7 @@ describe("SorobanPanel", () => { fireEvent.click(screen.getByRole("button", { name: /simulate/i })); expect(await screen.findByText("Simulation Result", { selector: "span" })).toBeInTheDocument(); }); + }); it("updates the textarea height style dynamically on input", () => { render( {}} />); @@ -162,7 +176,6 @@ describe("SorobanPanel", () => { // ── Contract ID history (#205) ────────────────────────────────────────── describe("contract ID history", () => { - const HISTORY_KEY = "sorokit-soroban-contract-history"; it("shows Simulating… label while loading", async () => { let resolveSimulate: (v: { data: unknown; error: null }) => void = () => {}; mockSimulateContract.mockReturnValueOnce(new Promise((resolve) => { resolveSimulate = resolve; })); diff --git a/src/components/SorobanPanel.tsx b/src/components/SorobanPanel.tsx index 0f69b2b..efe13fc 100644 --- a/src/components/SorobanPanel.tsx +++ b/src/components/SorobanPanel.tsx @@ -3,13 +3,11 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; import { Input } from "@/components/ui/Input"; - -import { cn } from "@/lib/utils"; - -import { ContractInteractionDebugger } from "./ContractInteractionDebugger"; import { useSorokit } from "@/context/useSorokit"; import { getClient } from "@/lib/client"; +import { ContractInteractionDebugger } from "./ContractInteractionDebugger"; + type State = "idle" | "loading" | "success" | "error"; interface SorobanPanelProps { @@ -76,27 +74,6 @@ function addContractToHistory(contractId: string, current: string[]): string[] { return next; } -function buildCurlCommand( - contractId: string, - method: string, - args: unknown[], -): string { - const rpcBody = JSON.stringify( - { - jsonrpc: "2.0", - id: 1, - method: "simulateTransaction", - params: { - transaction: `AAAAAgAAAAB7AAAAAAAAAAAAAAABAAAAAeJ0ZXN0AAAAAAAAAQAAABAAAAABAAAAHAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAKAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=`, - }, - }, - null, - 2, - ); - return `curl -X POST \\ - -H "Content-Type: application/json" \\ - -d '${rpcBody}' \\ - https://soroban-testnet.stellar.org`; function buildCurlCommand(contractId: string, method: string, args: unknown[]): string { const body = JSON.stringify({ contractId, method, args }, null, 2); return `curl -X POST https://soroban-rpc.example.com/invoke \\\n -H "Content-Type: application/json" \\\n -d '${body}'`; @@ -194,20 +171,6 @@ export function SorobanPanel({ setContractHistory((prev) => addContractToHistory(contractId.trim(), prev), ); - const invoke = mode === "simulate" - ? getClient().soroban.simulateContract - : getClient().soroban.invokeContract; - const { data, error: err } = await invoke({ - contractId: contractId.trim(), - method: method.trim(), - args: parsedArgs, - sourceAccount: address ?? undefined, - }); - if (signal.aborted) return; - if (err) { - setError(err); - setState("error"); - return; } } catch (e) { if (!signal.aborted) { @@ -477,7 +440,8 @@ export function SorobanPanel({