Skip to content

Commit

Permalink
Update based on feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
xlisachan committed Jan 14, 2025
1 parent b50376a commit 3e26266
Show file tree
Hide file tree
Showing 15 changed files with 398 additions and 359 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default async function UpgradeLayout({
l10n={l10n}
interval={cart.interval}
invoice={cart.upcomingInvoicePreview}
currentPrice={cart.eligibleSourcePrice}
currentPrice={cart.upgradeFromPrice}
currentPurchaseDetails={currentPurchaseDetails}
purchaseDetails={purchaseDetails}
/>
Expand Down
136 changes: 89 additions & 47 deletions libs/payments/cart/src/lib/cart.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ import { CartManager } from './cart.manager';
import { CartService } from './cart.service';
import { CheckoutService } from './checkout.service';
import {
CartEligibilityMismatchError,
CartError,
CartInvalidCurrencyError,
CartInvalidPromoCodeError,
CartInvalidStateForActionError,
CartStateProcessingError,
CartSubscriptionNotFoundError,
CartSuccessMissingRequired,
CartUpgradeMissingRequired,
CartUpgradeNotValid,
} from './cart.error';
import { CurrencyManager } from '@fxa/payments/currency';
Expand Down Expand Up @@ -687,7 +689,6 @@ describe('CartService', () => {
it('returns cart and upcomingInvoicePreview', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
Expand All @@ -698,9 +699,6 @@ describe('CartService', () => {
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});
jest
.spyOn(invoiceManager, 'previewUpcoming')
.mockResolvedValue(mockInvoicePreview);
Expand All @@ -724,7 +722,6 @@ describe('CartService', () => {
currency: mockCart.currency,
customer: mockCustomer,
taxAddress: mockCart.taxAddress,
isUpgrade: false,
});
});

Expand All @@ -734,7 +731,6 @@ describe('CartService', () => {
);
const mockCart = ResultCartFactory({
stripeSubscriptionId: mockSubscription.id,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
Expand All @@ -761,9 +757,6 @@ describe('CartService', () => {
jest
.spyOn(paymentMethodManager, 'retrieve')
.mockResolvedValue(mockPaymentMethod);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});

const result = await cartService.getCart(mockCart.id);
expect(result).toEqual({
Expand Down Expand Up @@ -793,7 +786,6 @@ describe('CartService', () => {
currency: mockCart.currency,
customer: mockCustomer,
taxAddress: mockCart.taxAddress,
isUpgrade: false,
});
expect(invoiceManager.preview).toHaveBeenCalledWith(
mockSubscription.latest_invoice
Expand All @@ -803,7 +795,6 @@ describe('CartService', () => {
it('returns cart and upcomingInvoicePreview if customer is undefined', async () => {
const mockCart = ResultCartFactory({
stripeCustomerId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
Expand All @@ -816,9 +807,6 @@ describe('CartService', () => {
jest
.spyOn(invoiceManager, 'previewUpcoming')
.mockResolvedValue(mockInvoicePreview);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});

const result = await cartService.getCart(mockCart.id);
expect(result).toEqual({
Expand All @@ -837,7 +825,6 @@ describe('CartService', () => {
currency: mockCart.currency,
customer: undefined,
taxAddress: mockCart.taxAddress,
isUpgrade: false,
});
});

Expand All @@ -854,7 +841,6 @@ describe('CartService', () => {
const mockCart = ResultCartFactory({
uid: mockUid,
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
Expand All @@ -871,9 +857,6 @@ describe('CartService', () => {
jest
.spyOn(accountManager, 'getAccounts')
.mockResolvedValue([mockAccount]);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});

const result = await cartService.getCart(mockCart.id);
expect(accountManager.getAccounts).toHaveBeenCalledWith([mockUid]);
Expand All @@ -892,7 +875,6 @@ describe('CartService', () => {
const mockCart = ResultCartFactory({
uid: mockUid,
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
Expand All @@ -909,9 +891,6 @@ describe('CartService', () => {
jest
.spyOn(accountManager, 'getAccounts')
.mockResolvedValue([mockAccount]);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});

const result = await cartService.getCart(mockCart.id);
expect(accountManager.getAccounts).toHaveBeenCalledWith([mockUid]);
Expand All @@ -921,7 +900,6 @@ describe('CartService', () => {
it('has metricsOptedOut set to false if the cart has no associated account', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
Expand All @@ -936,23 +914,25 @@ describe('CartService', () => {
.spyOn(invoiceManager, 'previewUpcoming')
.mockResolvedValue(mockInvoicePreview);
jest.spyOn(accountManager, 'getAccounts').mockResolvedValue([]);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});

const result = await cartService.getCart(mockCart.id);
expect(accountManager.getAccounts).not.toHaveBeenCalled();
expect(result.metricsOptedOut).toBeFalsy();
});
});

describe('getUpgradeCart', () => {
it('returns cart with current plan and offering id', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.UPGRADE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
const mockInvoicePreview = InvoicePreviewFactory({
oneTimeCharge: 4500,
proratedAmount: -500,
});
const mockCurrentPrice = StripePriceFactory();
const mockCurrentOffering = PageContentOfferingTransformedFactory();

Expand All @@ -961,27 +941,29 @@ describe('CartService', () => {
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest
.spyOn(invoiceManager, 'previewUpcoming')
.mockResolvedValue(mockInvoicePreview);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.UPGRADE,
eligibleSourcePrice: mockCurrentPrice,
fromOfferingConfigId: mockCurrentOffering.apiIdentifier,
upgradeFromPrice: mockCurrentPrice,
});
jest
.spyOn(invoiceManager, 'previewUpcomingForUpgrade')
.mockResolvedValue(mockInvoicePreview);

const result = await cartService.getCart(mockCart.id);
const result = await cartService.getUpgradeCart(mockCart.id);
expect(result).toEqual({
...mockCart,
upcomingInvoicePreview: mockInvoicePreview,
metricsOptedOut: false,
eligibilityStatus: CartEligibilityStatus.UPGRADE,
eligibleSourcePrice: {
fromOfferingConfigId: mockCurrentOffering.apiIdentifier,
oneTimeCharge: 4500,
proratedAmount: -500,
upgradeFromPrice: {
currency: mockCurrentPrice.currency,
interval: mockCurrentPrice.recurring?.interval,
listAmount: mockCurrentPrice.unit_amount,
},
fromOfferingConfigId: mockCurrentOffering.apiIdentifier,
});

expect(cartManager.fetchCartById).toHaveBeenCalledWith(mockCart.id);
Expand All @@ -991,44 +973,104 @@ describe('CartService', () => {
expect(customerManager.retrieve).toHaveBeenCalledWith(
mockCart.stripeCustomerId
);
expect(invoiceManager.previewUpcoming).toHaveBeenCalledWith({
expect(invoiceManager.previewUpcomingForUpgrade).toHaveBeenCalledWith({
priceId: mockPrice.id,
currency: mockCart.currency,
customer: mockCustomer,
taxAddress: mockCart.taxAddress,
isUpgrade: true,
sourcePrice: mockCurrentPrice,
upgradeFromPrice: mockCurrentPrice,
});
});

it('throws error if eligibility is upgrade, but no current plan', async () => {
it('throws error if eligibility status is not upgrade for getUpgradeCart', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.CREATE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory({
oneTimeCharge: 4500,
proratedAmount: -500,
});

jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockCart);
jest
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.CREATE,
});
jest
.spyOn(invoiceManager, 'previewUpcomingForUpgrade')
.mockResolvedValue(mockInvoicePreview);

await expect(
cartService.getUpgradeCart(mockCart.id)
).rejects.toThrowError(CartEligibilityMismatchError);
});

it('throws error if upgrade is missing offering id or price from current plan', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.UPGRADE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory();
const mockCurrentPrice = StripePriceFactory();
const mockCurrentOffering = PageContentOfferingTransformedFactory();
const mockInvoicePreview = InvoicePreviewFactory({
oneTimeCharge: 4500,
proratedAmount: -500,
});

jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockCart);
jest
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.UPGRADE,
});
jest
.spyOn(invoiceManager, 'previewUpcoming')
.spyOn(invoiceManager, 'previewUpcomingForUpgrade')
.mockResolvedValue(mockInvoicePreview);

await expect(
cartService.getUpgradeCart(mockCart.id)
).rejects.toThrowError(CartUpgradeNotValid);
});

it('throws error if upgrade is missing oneTimeCharge or proratedAmount from invoice', async () => {
const mockCart = ResultCartFactory({
stripeSubscriptionId: null,
eligibilityStatus: CartEligibilityStatus.UPGRADE,
});
const mockCustomer = StripeResponseFactory(StripeCustomerFactory());
const mockPrice = StripePriceFactory();
const mockInvoicePreview = InvoicePreviewFactory({
oneTimeCharge: undefined,
proratedAmount: undefined,
});
const mockCurrentPrice = StripePriceFactory();
const mockCurrentOffering = PageContentOfferingTransformedFactory();

jest.spyOn(cartManager, 'fetchCartById').mockResolvedValue(mockCart);
jest
.spyOn(productConfigurationManager, 'retrieveStripePrice')
.mockResolvedValue(mockPrice);
jest.spyOn(customerManager, 'retrieve').mockResolvedValue(mockCustomer);
jest.spyOn(eligibilityService, 'checkEligibility').mockResolvedValue({
subscriptionEligibilityResult: EligibilityStatus.UPGRADE,
fromOfferingConfigId: mockCurrentOffering.apiIdentifier,
eligibleSourcePrice: { ...mockCurrentPrice, recurring: null },
upgradeFromPrice: mockCurrentPrice,
});
jest
.spyOn(invoiceManager, 'previewUpcomingForUpgrade')
.mockResolvedValue(mockInvoicePreview);

await expect(cartService.getCart(mockCart.id)).rejects.toThrowError(
CartUpgradeNotValid
);
await expect(
cartService.getUpgradeCart(mockCart.id)
).rejects.toThrowError(CartUpgradeMissingRequired);
});
});

Expand Down
Loading

0 comments on commit 3e26266

Please sign in to comment.