@@ -465,4 +465,140 @@ describe('Checkout', () => {
465
465
expect ( getByText ( 'August 19, 2025' ) ) . toBeVisible ( ) ;
466
466
} ) ;
467
467
} ) ;
468
+
469
+ it ( 'renders existing payment sources during checkout confirmation' , async ( ) => {
470
+ const { wrapper, fixtures } = await createFixtures ( f => {
471
+ f . withUser ( { email_addresses :
[ '[email protected] ' ] } ) ;
472
+ } ) ;
473
+
474
+ fixtures . clerk . user ?. getPaymentSources . mockResolvedValue ( {
475
+ data : [
476
+ {
477
+ id : 'pm_test_visa' ,
478
+ last4 : '4242' ,
479
+ paymentMethod : 'card' ,
480
+ cardType : 'visa' ,
481
+ isDefault : true ,
482
+ isRemovable : true ,
483
+ status : 'active' ,
484
+ walletType : undefined ,
485
+ remove : jest . fn ( ) ,
486
+ makeDefault : jest . fn ( ) ,
487
+ pathRoot : '/' ,
488
+ reload : jest . fn ( ) ,
489
+ } ,
490
+ {
491
+ id : 'pm_test_mastercard' ,
492
+ last4 : '5555' ,
493
+ paymentMethod : 'card' ,
494
+ cardType : 'mastercard' ,
495
+ isDefault : false ,
496
+ isRemovable : true ,
497
+ status : 'active' ,
498
+ walletType : undefined ,
499
+ remove : jest . fn ( ) ,
500
+ makeDefault : jest . fn ( ) ,
501
+ pathRoot : '/' ,
502
+ reload : jest . fn ( ) ,
503
+ } ,
504
+ ] ,
505
+ total_count : 2 ,
506
+ } ) ;
507
+
508
+ fixtures . clerk . billing . startCheckout . mockResolvedValue ( {
509
+ id : 'chk_trial_2' ,
510
+ status : 'needs_confirmation' ,
511
+ externalClientSecret : 'cs_test_trial_2' ,
512
+ externalGatewayId : 'gw_test' ,
513
+ totals : {
514
+ subtotal : { amount : 1000 , amountFormatted : '10.00' , currency : 'USD' , currencySymbol : '$' } ,
515
+ grandTotal : { amount : 1000 , amountFormatted : '10.00' , currency : 'USD' , currencySymbol : '$' } ,
516
+ taxTotal : { amount : 0 , amountFormatted : '0.00' , currency : 'USD' , currencySymbol : '$' } ,
517
+ credit : { amount : 0 , amountFormatted : '0.00' , currency : 'USD' , currencySymbol : '$' } ,
518
+ pastDue : { amount : 0 , amountFormatted : '0.00' , currency : 'USD' , currencySymbol : '$' } ,
519
+ totalDueNow : { amount : 0 , amountFormatted : '0.00' , currency : 'USD' , currencySymbol : '$' } ,
520
+ } ,
521
+ isImmediatePlanChange : true ,
522
+ planPeriod : 'month' ,
523
+ plan : {
524
+ id : 'plan_trial' ,
525
+ name : 'Pro' ,
526
+ description : 'Pro plan' ,
527
+ features : [ ] ,
528
+ fee : {
529
+ amount : 1000 ,
530
+ amountFormatted : '10.00' ,
531
+ currency : 'USD' ,
532
+ currencySymbol : '$' ,
533
+ } ,
534
+ annualFee : {
535
+ amount : 12000 ,
536
+ amountFormatted : '120.00' ,
537
+ currency : 'USD' ,
538
+ currencySymbol : '$' ,
539
+ } ,
540
+ annualMonthlyFee : {
541
+ amount : 1000 ,
542
+ amountFormatted : '10.00' ,
543
+ currency : 'USD' ,
544
+ currencySymbol : '$' ,
545
+ } ,
546
+ slug : 'pro' ,
547
+ avatarUrl : '' ,
548
+ publiclyVisible : true ,
549
+ isDefault : true ,
550
+ isRecurring : true ,
551
+ hasBaseFee : false ,
552
+ forPayerType : 'user' ,
553
+ freeTrialDays : 7 ,
554
+ freeTrialEnabled : true ,
555
+ } ,
556
+ paymentSource : undefined ,
557
+ confirm : jest . fn ( ) ,
558
+ freeTrialEndsAt : new Date ( '2025-08-19' ) ,
559
+ } as any ) ;
560
+
561
+ const { baseElement, getByText, getByRole, userEvent } = render (
562
+ < Drawer . Root
563
+ open
564
+ onOpenChange = { ( ) => { } }
565
+ >
566
+ < Checkout
567
+ planId = 'plan_with_payment_sources'
568
+ planPeriod = 'month'
569
+ />
570
+ </ Drawer . Root > ,
571
+ { wrapper } ,
572
+ ) ;
573
+
574
+ await waitFor ( async ( ) => {
575
+ // Verify checkout title is displayed
576
+ expect ( getByRole ( 'heading' , { name : 'Checkout' } ) ) . toBeVisible ( ) ;
577
+
578
+ // Verify segmented control for payment method source is rendered
579
+ const paymentMethodsButton = getByText ( 'Payment Methods' ) ;
580
+ expect ( paymentMethodsButton ) . toBeVisible ( ) ;
581
+
582
+ const addPaymentMethodButton = getByText ( 'Add payment method' ) ;
583
+ expect ( addPaymentMethodButton ) . toBeVisible ( ) ;
584
+
585
+ await userEvent . click ( paymentMethodsButton ) ;
586
+ } ) ;
587
+
588
+ await waitFor ( ( ) => {
589
+ const visaPaymentSource = getByText ( 'visa' ) ;
590
+ expect ( visaPaymentSource ) . toBeVisible ( ) ;
591
+
592
+ const last4Digits = getByText ( '⋯ 4242' ) ;
593
+ expect ( last4Digits ) . toBeVisible ( ) ;
594
+
595
+ // Verify the default badge is shown for the first payment source
596
+ const defaultBadge = getByText ( 'Default' ) ;
597
+ expect ( defaultBadge ) . toBeVisible ( ) ;
598
+
599
+ // Verify the hidden input contains the correct payment source id
600
+ const hiddenInput = baseElement . querySelector ( 'input[name="payment_source_id"]' ) ;
601
+ expect ( hiddenInput ) . toHaveAttribute ( 'value' , 'pm_test_visa' ) ;
602
+ } ) ;
603
+ } ) ;
468
604
} ) ;
0 commit comments