@@ -4,8 +4,13 @@ import { createScriptLoader } from '@bigcommerce/script-loader';
44import { Observable , of } from 'rxjs' ;
55
66import { CheckoutRequestSender , CheckoutStore , createCheckoutStore } from '../../../checkout' ;
7- import { InvalidArgumentError , MissingDataError } from '../../../common/error/errors' ;
7+ import {
8+ InvalidArgumentError ,
9+ MissingDataError ,
10+ NotInitializedError ,
11+ } from '../../../common/error/errors' ;
812import { getGuestCustomer } from '../../../customer/customers.mock' ;
13+ import { getAddressFormFields } from '../../../form/form.mock' ;
914import {
1015 LoadPaymentMethodAction ,
1116 PaymentMethod ,
@@ -15,13 +20,17 @@ import {
1520} from '../../../payment' ;
1621import { getStripeUPE } from '../../../payment/payment-methods.mock' ;
1722import {
23+ StripeElement ,
1824 StripeHostWindow ,
1925 StripeScriptLoader ,
26+ StripeShippingEvent ,
2027 StripeUPEClient ,
2128} from '../../../payment/strategies/stripe-upe' ;
2229import {
2330 getShippingStripeUPEJsMock ,
2431 getShippingStripeUPEJsMockWithAnElementCreated ,
32+ getShippingStripeUPEJsOnMock ,
33+ getStripeUPEInitializeOptionsMockWithStyles ,
2534 getStripeUPEShippingInitializeOptionsMock ,
2635} from '../../../shipping/strategies/stripe-upe/stripe-upe-shipping.mock' ;
2736import ConsignmentActionCreator from '../../consignment-action-creator' ;
@@ -45,6 +54,28 @@ describe('StripeUPEShippingStrategy', () => {
4554 let paymentMethodMock : PaymentMethod ;
4655 let paymentMethodActionCreator : PaymentMethodActionCreator ;
4756
57+ const stripeShippingEvent = ( complete = true ) : StripeShippingEvent => {
58+ return {
59+ complete,
60+ elementType : '' ,
61+ empty : false ,
62+ isNewAddress : true ,
63+ phoneFieldRequired : true ,
64+ value : {
65+ address : {
66+ city : 'Lorem' ,
67+ country : 'US' ,
68+ line1 : 'ok' ,
69+ line2 : 'ok' ,
70+ postal_code : '44910' ,
71+ state : 'TX' ,
72+ } ,
73+ name : 'Alan' ,
74+ phone : '+523333333333' ,
75+ } ,
76+ } ;
77+ } ;
78+
4879 beforeEach ( ( ) => {
4980 store = createCheckoutStore ( ) ;
5081 paymentMethodMock = { ...getStripeUPE ( ) , clientToken : 'myToken' } ;
@@ -137,6 +168,94 @@ describe('StripeUPEShippingStrategy', () => {
137168 expect ( stripeUPEJsMock . elements ) . toHaveBeenCalledTimes ( 1 ) ;
138169 } ) ;
139170
171+ it ( 'does not load stripeUPE if initialization options are not provided' , ( ) => {
172+ const promise = strategy . initialize ( { methodId : 'stripeupe' } ) ;
173+
174+ expect ( promise ) . rejects . toThrow ( NotInitializedError ) ;
175+ } ) ;
176+
177+ it ( 'does not load stripeUPE if UPE options are not provided' , ( ) => {
178+ const promise = strategy . initialize ( {
179+ methodId : 'stripeupe' ,
180+ stripeupe : {
181+ methodId : '' ,
182+ gatewayId : '' ,
183+ onChangeShipping : jest . fn ( ) ,
184+ availableCountries : 'US,MX' ,
185+ getStripeState : jest . fn ( ) ,
186+ } ,
187+ } ) ;
188+
189+ expect ( promise ) . rejects . toThrow ( InvalidArgumentError ) ;
190+ } ) ;
191+
192+ it ( 'does not load stripeUPE when styles is provided' , async ( ) => {
193+ const testColor = '#123456' ;
194+ const style = {
195+ labelText : testColor ,
196+ fieldText : testColor ,
197+ fieldPlaceholderText : testColor ,
198+ fieldErrorText : testColor ,
199+ fieldBackground : testColor ,
200+ fieldInnerShadow : testColor ,
201+ fieldBorder : testColor ,
202+ } ;
203+
204+ await expect (
205+ strategy . initialize ( getStripeUPEInitializeOptionsMockWithStyles ( style ) ) ,
206+ ) . resolves . toBe ( store . getState ( ) ) ;
207+ expect ( stripeUPEJsMock . elements ) . toHaveBeenNthCalledWith ( 1 , {
208+ clientSecret : 'clientToken' ,
209+ appearance : {
210+ rules : {
211+ '.Input' : {
212+ borderColor : testColor ,
213+ boxShadow : testColor ,
214+ color : testColor ,
215+ } ,
216+ } ,
217+ variables : {
218+ borderRadius : '4px' ,
219+ colorBackground : testColor ,
220+ colorDanger : testColor ,
221+ colorPrimary : testColor ,
222+ colorText : testColor ,
223+ colorTextPlaceholder : testColor ,
224+ colorTextSecondary : testColor ,
225+ spacingUnit : '4px' ,
226+ } ,
227+ } ,
228+ } ) ;
229+ } ) ;
230+
231+ it ( 'loads a single instance of StripeUPEClient without last name and phone fields' , async ( ) => {
232+ jest . spyOn ( store . getState ( ) . shippingAddress , 'getShippingAddress' ) . mockReturnValue (
233+ getShippingAddress ( ) ,
234+ ) ;
235+
236+ jest . spyOn ( store . getState ( ) . form , 'getShippingAddressFields' ) . mockReturnValue ( [
237+ {
238+ id : 'field_7' ,
239+ name : 'phone' ,
240+ custom : false ,
241+ label : 'Phone Number' ,
242+ required : false ,
243+ default : '' ,
244+ } ,
245+ ] ) ;
246+ jest . spyOn ( store . getState ( ) . shippingAddress , 'getShippingAddress' ) . mockReturnValue ( {
247+ ...getShippingAddress ( ) ,
248+ lastName : '' ,
249+ } ) ;
250+
251+ await expect ( strategy . initialize ( shippingInitialization ) ) . resolves . toBe (
252+ store . getState ( ) ,
253+ ) ;
254+
255+ expect ( stripeScriptLoader . getStripeClient ) . toHaveBeenCalledTimes ( 1 ) ;
256+ expect ( stripeUPEJsMock . elements ) . toHaveBeenCalledTimes ( 1 ) ;
257+ } ) ;
258+
140259 it ( 'returns an error when methodId is not present' , ( ) => {
141260 const promise = strategy . initialize ( {
142261 ...getStripeUPEShippingInitializeOptionsMock ( ) ,
@@ -156,6 +275,105 @@ describe('StripeUPEShippingStrategy', () => {
156275
157276 expect ( promise ) . rejects . toBeInstanceOf ( MissingDataError ) ;
158277 } ) ;
278+
279+ it ( 'triggers onChange event callback and mounts component' , async ( ) => {
280+ const stripeMockElement : StripeElement = {
281+ destroy : jest . fn ( ) ,
282+ mount : jest . fn ( ) ,
283+ unmount : jest . fn ( ) ,
284+ on : jest . fn ( ( _ , callback ) => callback ( stripeShippingEvent ( true ) ) ) ,
285+ } ;
286+ const stripeUPEJsMockWithElement = getShippingStripeUPEJsOnMock ( stripeMockElement ) ;
287+
288+ jest . spyOn ( store . getState ( ) . shippingAddress , 'getShippingAddress' ) . mockReturnValue (
289+ getShippingAddress ( ) ,
290+ ) ;
291+ jest . spyOn ( stripeScriptLoader , 'getStripeClient' ) . mockResolvedValueOnce (
292+ stripeUPEJsMockWithElement ,
293+ ) ;
294+ jest . useFakeTimers ( ) ;
295+
296+ await expect ( strategy . initialize ( shippingInitialization ) ) . resolves . toBe (
297+ store . getState ( ) ,
298+ ) ;
299+
300+ jest . runAllTimers ( ) ;
301+
302+ expect ( stripeScriptLoader . getStripeClient ) . toHaveBeenCalledTimes ( 1 ) ;
303+ expect ( stripeMockElement . on ) . toHaveBeenCalledTimes ( 1 ) ;
304+ expect ( stripeMockElement . mount ) . toHaveBeenCalledWith ( expect . any ( String ) ) ;
305+ expect ( shippingInitialization . stripeupe ?. onChangeShipping ) . toHaveBeenCalledTimes ( 1 ) ;
306+ } ) ;
307+
308+ it ( 'triggers onChange event callback and mounts component when event is not completed' , async ( ) => {
309+ const stripeMockElement : StripeElement = {
310+ destroy : jest . fn ( ) ,
311+ mount : jest . fn ( ) ,
312+ unmount : jest . fn ( ) ,
313+ on : jest . fn ( ( _ , callback ) => callback ( stripeShippingEvent ( false ) ) ) ,
314+ } ;
315+ const stripeUPEJsMockWithElement = getShippingStripeUPEJsOnMock ( stripeMockElement ) ;
316+
317+ jest . spyOn ( store . getState ( ) . form , 'getShippingAddressFields' ) . mockReturnValue (
318+ getAddressFormFields ( ) ,
319+ ) ;
320+ jest . spyOn ( store . getState ( ) . shippingAddress , 'getShippingAddress' ) . mockReturnValue ( {
321+ ...getShippingAddress ( ) ,
322+ countryCode : '' ,
323+ } ) ;
324+ jest . spyOn ( stripeScriptLoader , 'getStripeClient' ) . mockResolvedValueOnce (
325+ stripeUPEJsMockWithElement ,
326+ ) ;
327+ jest . useFakeTimers ( ) ;
328+
329+ await expect ( strategy . initialize ( shippingInitialization ) ) . resolves . toBe (
330+ store . getState ( ) ,
331+ ) ;
332+
333+ jest . runAllTimers ( ) ;
334+
335+ expect ( stripeScriptLoader . getStripeClient ) . toHaveBeenCalledTimes ( 1 ) ;
336+ expect ( stripeMockElement . on ) . toHaveBeenCalledTimes ( 1 ) ;
337+ expect ( stripeMockElement . mount ) . toHaveBeenCalledWith ( expect . any ( String ) ) ;
338+ expect ( shippingInitialization . stripeupe ?. onChangeShipping ) . toHaveBeenCalledTimes ( 1 ) ;
339+ } ) ;
340+
341+ it ( 'triggers onChange event callback and throws error if event data is missing' , async ( ) => {
342+ const missingShippingEvent = ( ) : StripeShippingEvent => {
343+ return {
344+ complete : false ,
345+ elementType : '' ,
346+ empty : false ,
347+ phoneFieldRequired : false ,
348+ value : {
349+ address : {
350+ city : '' ,
351+ country : '' ,
352+ line1 : '' ,
353+ line2 : '' ,
354+ postal_code : '' ,
355+ state : '' ,
356+ } ,
357+ name : '' ,
358+ phone : '' ,
359+ } ,
360+ } ;
361+ } ;
362+ const stripeMockElement : StripeElement = {
363+ destroy : jest . fn ( ) ,
364+ mount : jest . fn ( ) ,
365+ unmount : jest . fn ( ) ,
366+ on : jest . fn ( ( _ , callback ) => callback ( missingShippingEvent ) ) ,
367+ } ;
368+
369+ jest . spyOn ( stripeScriptLoader , 'getStripeClient' ) . mockResolvedValueOnce (
370+ getShippingStripeUPEJsOnMock ( stripeMockElement ) ,
371+ ) ;
372+
373+ const promise = strategy . initialize ( shippingInitialization ) ;
374+
375+ await expect ( promise ) . rejects . toBeInstanceOf ( MissingDataError ) ;
376+ } ) ;
159377 } ) ;
160378
161379 describe ( '#deinitialize()' , ( ) => {
0 commit comments