@@ -111,6 +111,38 @@ const WrappedCheckout = () => {
111111}
112112
113113describe ( 'Checkout One Click' , ( ) => {
114+ // Helper to create a BOPIS-only basket (single pickup shipment, no delivery)
115+ const createBopisOnlyBasket = ( ) => {
116+ const basket = JSON . parse ( JSON . stringify ( scapiBasketWithItem ) )
117+ basket . productItems = [
118+ {
119+ itemId : 'item-pickup-1' ,
120+ productId : '701643070725M' ,
121+ quantity : 1 ,
122+ price : 19.18 ,
123+ shipmentId : 'pickup1' ,
124+ inventoryId : 'inventory_m_store_store1'
125+ }
126+ ]
127+ basket . shipments = [
128+ {
129+ shipmentId : 'pickup1' ,
130+ c_fromStoreId : 'store1' ,
131+ shippingMethod : { id : 'PICKUP' , c_storePickupEnabled : true } ,
132+ shippingAddress : {
133+ firstName : 'Store 1' ,
134+ lastName : 'Pickup' ,
135+ address1 : '1 Market St' ,
136+ city : 'San Francisco' ,
137+ postalCode : '94105' ,
138+ stateCode : 'CA' ,
139+ countryCode : 'US'
140+ }
141+ }
142+ ]
143+ return basket
144+ }
145+
114146 // Set up and clean up
115147 beforeEach ( ( ) => {
116148 global . server . use (
@@ -356,6 +388,157 @@ describe('Checkout One Click', () => {
356388 } )
357389 } )
358390
391+ // BOPIS-only checkout tests
392+ describe ( 'BOPIS-only checkout' , ( ) => {
393+ test ( 'can checkout BOPIS-only order as guest shopper' , async ( ) => {
394+ // Mock authorizePasswordlessLogin to fail with 404 (unregistered user)
395+ mockUseAuthHelper . mockRejectedValueOnce ( {
396+ response : { status : 404 }
397+ } )
398+
399+ const bopisOnlyBasket = createBopisOnlyBasket ( )
400+ global . server . use (
401+ rest . get ( '*/baskets' , ( req , res , ctx ) => {
402+ return res (
403+ ctx . json ( {
404+ baskets : [ bopisOnlyBasket ] ,
405+ total : 1
406+ } )
407+ )
408+ } )
409+ )
410+
411+ window . history . pushState ( { } , 'Checkout' , createPathWithDefaults ( '/checkout' ) )
412+ const { user} = renderWithProviders ( < WrappedCheckout history = { history } /> , {
413+ wrapperProps : {
414+ isGuest : true ,
415+ siteAlias : 'uk' ,
416+ appConfig : mockConfig . app
417+ }
418+ } )
419+
420+ // Wait for contact info step
421+ await screen . findByText ( / c o n t a c t i n f o / i)
422+
423+ // Fill email and phone number
424+ const emailInput = await screen . findByLabelText ( / e m a i l / i)
425+ await user . type ( emailInput , 'bopisguest@test.com' )
426+ await user . tab ( )
427+ const phoneInput = screen . queryByLabelText ( / p h o n e / i)
428+ if ( phoneInput ) {
429+ await user . type ( phoneInput , '5551234567' )
430+ }
431+
432+ // Wait for continue button and click
433+ const continueBtn = await screen . findByText ( / c o n t i n u e t o s h i p p i n g a d d r e s s / i)
434+ await user . click ( continueBtn )
435+
436+ // Verify we skip directly to payment
437+ await waitFor (
438+ ( ) => {
439+ const paymentStep = screen . queryByTestId ( 'sf-toggle-card-step-4' )
440+ const paymentHeading = screen . queryByRole ( 'heading' , { name : / p a y m e n t / i} )
441+ expect ( paymentStep || paymentHeading ) . toBeTruthy ( )
442+ } ,
443+ { timeout : 5000 }
444+ )
445+ } )
446+
447+ test ( 'can checkout BOPIS-only order as registered shopper' , async ( ) => {
448+ const bopisOnlyBasket = createBopisOnlyBasket ( )
449+ global . server . use (
450+ rest . get ( '*/baskets' , ( req , res , ctx ) => {
451+ return res (
452+ ctx . json ( {
453+ baskets : [ bopisOnlyBasket ] ,
454+ total : 1
455+ } )
456+ )
457+ } )
458+ )
459+
460+ window . history . pushState ( { } , 'Checkout' , createPathWithDefaults ( '/checkout' ) )
461+ renderWithProviders ( < WrappedCheckout history = { history } /> , {
462+ wrapperProps : {
463+ bypassAuth : true ,
464+ isGuest : false ,
465+ siteAlias : 'uk' ,
466+ locale : { id : 'en-GB' } ,
467+ appConfig : mockConfig . app
468+ }
469+ } )
470+
471+ // Wait for checkout to load - registered user should have email displayed
472+ await waitFor ( ( ) => {
473+ expect ( screen . getByText ( 'customer@test.com' ) ) . toBeInTheDocument ( )
474+ } )
475+
476+ // For BOPIS-only, we should see payment step
477+ await waitFor (
478+ ( ) => {
479+ const paymentStep = screen . queryByTestId ( 'sf-toggle-card-step-4' )
480+ const paymentHeading = screen . queryByRole ( 'heading' , { name : / p a y m e n t / i} )
481+ expect ( paymentStep || paymentHeading ) . toBeTruthy ( )
482+ } ,
483+ { timeout : 5000 }
484+ )
485+ } )
486+
487+ test ( 'BOPIS-only guest shopper editing contact info continues to payment, not pickup address' , async ( ) => {
488+ mockUseAuthHelper . mockRejectedValueOnce ( {
489+ response : { status : 404 }
490+ } )
491+
492+ const bopisOnlyBasket = createBopisOnlyBasket ( )
493+ global . server . use (
494+ rest . get ( '*/baskets' , ( req , res , ctx ) => {
495+ return res (
496+ ctx . json ( {
497+ baskets : [ bopisOnlyBasket ] ,
498+ total : 1
499+ } )
500+ )
501+ } )
502+ )
503+
504+ window . history . pushState ( { } , 'Checkout' , createPathWithDefaults ( '/checkout' ) )
505+ const { user} = renderWithProviders ( < WrappedCheckout history = { history } /> , {
506+ wrapperProps : {
507+ isGuest : true ,
508+ siteAlias : 'uk' ,
509+ appConfig : mockConfig . app
510+ }
511+ } )
512+
513+ // Wait for contact info step
514+ await screen . findByText ( / c o n t a c t i n f o / i)
515+
516+ // Fill email and phone
517+ const emailInput = await screen . findByLabelText ( / e m a i l / i)
518+ await user . type ( emailInput , 'bopisguest@test.com' )
519+ await user . tab ( )
520+
521+ const phoneInput = screen . queryByLabelText ( / p h o n e / i)
522+ if ( phoneInput ) {
523+ await user . type ( phoneInput , '5551234567' )
524+ }
525+
526+ // Wait for continue button and click
527+ const continueBtn = await screen . findByText ( / c o n t i n u e t o s h i p p i n g a d d r e s s / i)
528+ await user . click ( continueBtn )
529+
530+ // Verify we continue to payment
531+ await waitFor (
532+ ( ) => {
533+ const paymentStep = screen . queryByTestId ( 'sf-toggle-card-step-4' )
534+ const paymentHeading = screen . queryByRole ( 'heading' , { name : / p a y m e n t / i} )
535+ expect ( paymentStep || paymentHeading ) . toBeTruthy ( )
536+ } ,
537+ { timeout : 5000 }
538+ )
539+ } )
540+ } )
541+
359542 afterEach ( ( ) => {
360543 jest . resetModules ( )
361544 jest . clearAllMocks ( )
0 commit comments