@@ -268,5 +268,155 @@ describe('MonthlyPlan', () => {
268268 mp . setNewPaymentMethod ( request ) ;
269269 expect ( mp . plan . btdata . last4 ) . to . equal ( 'unknown' ) ;
270270 } ) ;
271+
272+ it ( 'preserves nextBillingDate and lastBillingDate through the merge' , ( ) => {
273+ const mp = new MonthlyPlan ( makePlan ( ) ) ;
274+
275+ const request = new PaymentMethodRequest ( {
276+ paymentMethodInfo : {
277+ description : 'New card' ,
278+ nonce : 'nonce_789' ,
279+ type : 'CreditCard' ,
280+ details : {
281+ cardType : 'Amex' ,
282+ lastFour : '9999' ,
283+ } ,
284+ } ,
285+ donorContactInfo : { } ,
286+ paymentProvider : PaymentProvider . CreditCard ,
287+ } ) ;
288+
289+ mp . setNewPaymentMethod ( request ) ;
290+
291+ expect ( mp . plan . btdata . nextBillingDate . date ) . to . equal (
292+ '2024-08-15 00:00:00' ,
293+ ) ;
294+ expect ( mp . plan . btdata . lastBillingDate . date ) . to . equal (
295+ '2024-07-15 00:00:00' ,
296+ ) ;
297+ } ) ;
298+
299+ it ( 'preserves status field through the merge' , ( ) => {
300+ const mp = new MonthlyPlan ( makePlan ( ) ) ;
301+
302+ const request = new PaymentMethodRequest ( {
303+ paymentMethodInfo : {
304+ description : 'New card' ,
305+ nonce : 'nonce_abc' ,
306+ type : 'CreditCard' ,
307+ details : {
308+ cardType : 'Visa' ,
309+ lastFour : '1111' ,
310+ } ,
311+ } ,
312+ donorContactInfo : { } ,
313+ paymentProvider : PaymentProvider . CreditCard ,
314+ } ) ;
315+
316+ mp . setNewPaymentMethod ( request ) ;
317+ expect ( mp . plan . btdata . status ) . to . equal ( 'Active' ) ;
318+ expect ( mp . plan . btdata . paymentMethodType ) . to . equal ( 'CreditCard' ) ;
319+ } ) ;
320+
321+ it ( 'stores old_btData as a snapshot when called twice' , ( ) => {
322+ const mp = new MonthlyPlan ( makePlan ( ) ) ;
323+
324+ const firstRequest = new PaymentMethodRequest ( {
325+ paymentMethodInfo : {
326+ description : 'First update' ,
327+ nonce : 'nonce_1' ,
328+ type : 'CreditCard' ,
329+ details : {
330+ cardType : 'Mastercard' ,
331+ lastFour : '2222' ,
332+ expirationMonth : '03' ,
333+ expirationYear : '2027' ,
334+ } ,
335+ } ,
336+ donorContactInfo : { } ,
337+ paymentProvider : PaymentProvider . CreditCard ,
338+ } ) ;
339+
340+ mp . setNewPaymentMethod ( firstRequest ) ;
341+ const afterFirstUpdate = mp . plan . btdata ;
342+
343+ const secondRequest = new PaymentMethodRequest ( {
344+ paymentMethodInfo : {
345+ description : 'Second update' ,
346+ nonce : 'nonce_2' ,
347+ type : 'CreditCard' ,
348+ details : {
349+ cardType : 'Amex' ,
350+ lastFour : '3333' ,
351+ expirationMonth : '11' ,
352+ expirationYear : '2029' ,
353+ } ,
354+ } ,
355+ donorContactInfo : { } ,
356+ paymentProvider : PaymentProvider . CreditCard ,
357+ } ) ;
358+
359+ mp . setNewPaymentMethod ( secondRequest ) ;
360+
361+ // old_btData should be the intermediate state, not the original
362+ expect ( mp . plan . old_btData ) . to . equal ( afterFirstUpdate ) ;
363+ expect ( mp . plan . btdata . cardType ) . to . equal ( 'Amex' ) ;
364+ expect ( mp . plan . btdata . last4 ) . to . equal ( '3333' ) ;
365+ } ) ;
366+
367+ it ( 'handles PayPal details with paypalEmail on original btdata' , ( ) => {
368+ const btdata = makeBtData ( {
369+ paymentMethodType : 'PayPal' ,
370+ paypalEmail : 'donor@example.com' ,
371+ last4 : null ,
372+ cardType : null ,
373+ } ) ;
374+ const mp = new MonthlyPlan ( makePlan ( { btdata } ) ) ;
375+
376+ const request = new PaymentMethodRequest ( {
377+ paymentMethodInfo : {
378+ description : 'New PayPal' ,
379+ nonce : 'nonce_pp' ,
380+ type : 'PayPal' ,
381+ details : {
382+ description : 'new-donor@example.com' ,
383+ } ,
384+ } ,
385+ donorContactInfo : { } ,
386+ paymentProvider : PaymentProvider . CreditCard ,
387+ } ) ;
388+
389+ mp . setNewPaymentMethod ( request ) ;
390+
391+ // paypalEmail from original btdata is preserved via spread
392+ expect ( mp . plan . btdata . paypalEmail ) . to . equal ( 'donor@example.com' ) ;
393+ expect ( mp . plan . btdata . last4 ) . to . equal ( 'unknown' ) ;
394+ } ) ;
395+
396+ it ( 'handles Venmo details with venmoUsername on original btdata' , ( ) => {
397+ const btdata = makeBtData ( {
398+ paymentMethodType : 'Venmo' ,
399+ venmoUsername : '@donor' ,
400+ last4 : null ,
401+ cardType : null ,
402+ } ) ;
403+ const mp = new MonthlyPlan ( makePlan ( { btdata } ) ) ;
404+
405+ const request = new PaymentMethodRequest ( {
406+ paymentMethodInfo : {
407+ description : 'New Venmo' ,
408+ nonce : 'nonce_venmo' ,
409+ type : 'Venmo' ,
410+ details : { } ,
411+ } ,
412+ donorContactInfo : { } ,
413+ paymentProvider : PaymentProvider . CreditCard ,
414+ } ) ;
415+
416+ mp . setNewPaymentMethod ( request ) ;
417+
418+ expect ( mp . plan . btdata . venmoUsername ) . to . equal ( '@donor' ) ;
419+ expect ( mp . plan . btdata . last4 ) . to . equal ( 'unknown' ) ;
420+ } ) ;
271421 } ) ;
272422} ) ;
0 commit comments