@@ -291,4 +291,167 @@ describe("TransactionPanel", () => {
291291
292292 expect ( await screen . findByText ( "Transaction submitted" ) ) . toBeInTheDocument ( ) ;
293293 } ) ;
294+
295+ // ── Asset selector (#178) ─────────────────────────────────────────────────
296+ describe ( "asset selector" , ( ) => {
297+ const balances = [
298+ { asset : "XLM" , balance : "100.0000000" , assetType : "native" as const } ,
299+ {
300+ asset : "USDC" ,
301+ balance : "50.0000000" ,
302+ assetType : "credit_alphanum4" as const ,
303+ assetCode : "USDC" ,
304+ assetIssuer : "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN" ,
305+ } ,
306+ ] ;
307+
308+ it ( "populates the asset selector with the correct asset codes from context balances" , ( ) => {
309+ vi . mocked ( useSorokit ) . mockReturnValue ( {
310+ address : "GABC" ,
311+ isConnected : true ,
312+ balances,
313+ } as unknown as ReturnType < typeof useSorokit > ) ;
314+
315+ render ( < TransactionPanel /> ) ;
316+
317+ const select = screen . getByLabelText ( "Asset" ) as HTMLSelectElement ;
318+ const optionValues = Array . from ( select . options ) . map ( ( o ) => o . value ) ;
319+ expect ( optionValues ) . toEqual ( [ "XLM" , "USDC" ] ) ;
320+ } ) ;
321+
322+ it ( "updates the submitted asset when USDC is selected" , async ( ) => {
323+ const mockSubmit = vi
324+ . fn ( )
325+ . mockResolvedValue ( { data : { hash : "h1" , ledger : 1 } , error : null } ) ;
326+ mockGetClient ( mockSubmit ) ;
327+ vi . mocked ( useSorokit ) . mockReturnValue ( {
328+ address : "GABC" ,
329+ isConnected : true ,
330+ balances,
331+ } as unknown as ReturnType < typeof useSorokit > ) ;
332+
333+ render ( < TransactionPanel /> ) ;
334+
335+ const select = screen . getByLabelText ( "Asset" ) ;
336+ fireEvent . change ( select , { target : { value : "USDC" } } ) ;
337+ expect ( select ) . toHaveValue ( "USDC" ) ;
338+ expect ( screen . getByLabelText ( "Amount (USDC)" ) ) . toBeInTheDocument ( ) ;
339+
340+ const validDest = VALID_DEST ;
341+ fireEvent . change ( screen . getByLabelText ( "Destination Address" ) , {
342+ target : { value : validDest } ,
343+ } ) ;
344+ fireEvent . change ( screen . getByLabelText ( "Amount (USDC)" ) , {
345+ target : { value : "10" } ,
346+ } ) ;
347+
348+ await reviewAndConfirm ( ) ;
349+
350+ await screen . findByText ( "Transaction submitted" ) ;
351+ expect ( mockSubmit ) . toHaveBeenCalledWith (
352+ expect . objectContaining ( { asset : "USDC" } ) ,
353+ ) ;
354+ } ) ;
355+
356+ it ( "disables the asset selector when no balances are loaded" , ( ) => {
357+ vi . mocked ( useSorokit ) . mockReturnValue ( {
358+ address : "GABC" ,
359+ isConnected : true ,
360+ balances : [ ] ,
361+ } as unknown as ReturnType < typeof useSorokit > ) ;
362+
363+ render ( < TransactionPanel /> ) ;
364+
365+ const select = screen . getByLabelText ( "Asset" ) ;
366+ expect ( select ) . toBeDisabled ( ) ;
367+ expect ( select ) . toHaveValue ( "XLM" ) ;
368+ } ) ;
369+
370+ it ( "includes the asset's issuer in the submitted payload for a non-native asset (#565)" , async ( ) => {
371+ const mockSubmit = vi
372+ . fn ( )
373+ . mockResolvedValue ( { data : { hash : "h1" , ledger : 1 } , error : null } ) ;
374+ mockGetClient ( mockSubmit ) ;
375+ vi . mocked ( useSorokit ) . mockReturnValue ( {
376+ address : "GABC" ,
377+ isConnected : true ,
378+ balances,
379+ // getClient() here returns the object mockGetClient() just wired up
380+ // above; TransactionPanel reads `client` from context, not from a
381+ // direct getClient() call, so the mocked client has to be threaded
382+ // through explicitly for the submission to actually run.
383+ client : getClient ( ) ,
384+ } as unknown as ReturnType < typeof useSorokit > ) ;
385+
386+ render ( < TransactionPanel /> ) ;
387+
388+ fireEvent . change ( screen . getByLabelText ( "Asset" ) , {
389+ target : { value : "USDC" } ,
390+ } ) ;
391+ const validDest = VALID_DEST ;
392+ fireEvent . change ( screen . getByLabelText ( "Destination Address" ) , {
393+ target : { value : validDest } ,
394+ } ) ;
395+ fireEvent . change ( screen . getByLabelText ( "Amount (USDC)" ) , {
396+ target : { value : "10" } ,
397+ } ) ;
398+
399+ await reviewAndConfirm ( ) ;
400+
401+ await screen . findByText ( "Transaction submitted" ) ;
402+ expect ( mockSubmit ) . toHaveBeenCalledWith (
403+ expect . objectContaining ( {
404+ asset : "USDC" ,
405+ assetIssuer : "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN" ,
406+ } ) ,
407+ ) ;
408+ } ) ;
409+
410+ it ( "omits assetIssuer for the native XLM asset (#565)" , async ( ) => {
411+ const mockSubmit = vi
412+ . fn ( )
413+ . mockResolvedValue ( { data : { hash : "h1" , ledger : 1 } , error : null } ) ;
414+ mockGetClient ( mockSubmit ) ;
415+ vi . mocked ( useSorokit ) . mockReturnValue ( {
416+ address : "GABC" ,
417+ isConnected : true ,
418+ balances,
419+ client : getClient ( ) ,
420+ } as unknown as ReturnType < typeof useSorokit > ) ;
421+
422+ render ( < TransactionPanel /> ) ;
423+
424+ const validDest = VALID_DEST ;
425+ fireEvent . change ( screen . getByLabelText ( "Destination Address" ) , {
426+ target : { value : validDest } ,
427+ } ) ;
428+ fireEvent . change ( screen . getByLabelText ( "Amount (XLM)" ) , {
429+ target : { value : "10" } ,
430+ } ) ;
431+
432+ await reviewAndConfirm ( ) ;
433+
434+ await screen . findByText ( "Transaction submitted" ) ;
435+ expect ( mockSubmit ) . toHaveBeenCalledWith (
436+ expect . objectContaining ( { asset : "XLM" , assetIssuer : undefined } ) ,
437+ ) ;
438+ } ) ;
439+
440+ it ( "shows the selected asset's balance as a hint near the amount input (#565)" , ( ) => {
441+ vi . mocked ( useSorokit ) . mockReturnValue ( {
442+ address : "GABC" ,
443+ isConnected : true ,
444+ balances,
445+ } as unknown as ReturnType < typeof useSorokit > ) ;
446+
447+ render ( < TransactionPanel /> ) ;
448+
449+ expect ( screen . getByText ( "Balance: 100.0000000 XLM" ) ) . toBeInTheDocument ( ) ;
450+
451+ fireEvent . change ( screen . getByLabelText ( "Asset" ) , {
452+ target : { value : "USDC" } ,
453+ } ) ;
454+ expect ( screen . getByText ( "Balance: 50.0000000 USDC" ) ) . toBeInTheDocument ( ) ;
455+ } ) ;
456+ } ) ;
294457} ) ;
0 commit comments