From b335b69f6429bb73a72cd8a442f5e52fdf77084b Mon Sep 17 00:00:00 2001 From: Filip Naumovski Date: Mon, 19 Jun 2023 17:23:22 +0200 Subject: [PATCH 01/17] feat: initial inplayer subscription change implementation --- package.json | 2 +- public/locales/en/user.json | 5 + public/locales/es/user.json | 5 + .../OfferSwitch/OfferSwitch.module.scss | 62 ++++++ src/components/OfferSwitch/OfferSwitch.tsx | 52 +++++ src/components/Payment/Payment.module.scss | 17 ++ src/components/Payment/Payment.tsx | 133 +++++++++++-- .../__snapshots__/Payment.test.tsx.snap | 164 ++++----------- .../User/__snapshots__/User.test.tsx.snap | 186 ++++-------------- src/services/inplayer.checkout.service.ts | 1 + src/services/inplayer.subscription.service.ts | 18 +- src/stores/AccountController.ts | 14 +- src/stores/CheckoutController.ts | 10 + src/utils/formatting.ts | 4 +- types/account.d.ts | 2 +- types/checkout.d.ts | 1 + types/subscription.d.ts | 12 +- yarn.lock | 107 +++++++--- 18 files changed, 455 insertions(+), 340 deletions(-) create mode 100644 src/components/OfferSwitch/OfferSwitch.module.scss create mode 100644 src/components/OfferSwitch/OfferSwitch.tsx diff --git a/package.json b/package.json index 481df0d05..4a19ab54b 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "dependencies": { "@adyen/adyen-web": "^5.42.1", "@codeceptjs/allure-legacy": "^1.0.2", - "@inplayer-org/inplayer.js": "^3.13.12", + "@inplayer-org/inplayer.js": "^3.13.13", "classnames": "^2.3.1", "date-fns": "^2.28.0", "dompurify": "^2.3.8", diff --git a/public/locales/en/user.json b/public/locales/en/user.json index 965978194..244d597ce 100644 --- a/public/locales/en/user.json +++ b/public/locales/en/user.json @@ -68,9 +68,13 @@ "annual_subscription": "Annual subscription", "cancel_subscription": "Cancel subscription", "card_number": "Card number", + "change_plan": "", + "change_plan_error": "", "change_subscription": "Change subscription", "complete_subscription": "Complete subscription", + "current_plan": "", "daily_subscription": "Daily subscription", + "downgrade_plan_success": "", "expiry_date": "Expiry date", "granted_subscription": "Granted subscription", "hidden_transactions_one": "One more transaction", @@ -94,6 +98,7 @@ "subscription_expires_on": "This plan will expire on {{date}}", "transactions": "Transactions", "update_payment_details": "Update payment details", + "upgrade_plan_success": "", "weekly_subscription": "Weekly subscription" } } diff --git a/public/locales/es/user.json b/public/locales/es/user.json index 91d9b35e1..63ce9c488 100644 --- a/public/locales/es/user.json +++ b/public/locales/es/user.json @@ -68,9 +68,13 @@ "annual_subscription": "Suscripción anual", "cancel_subscription": "Cancelar suscripción", "card_number": "Número de tarjeta", + "change_plan": "", + "change_plan_error": "", "change_subscription": "Cambiar suscripción", "complete_subscription": "Completar suscripción", + "current_plan": "", "daily_subscription": "Suscripción diaria", + "downgrade_plan_success": "", "expiry_date": "Fecha de vencimiento", "granted_subscription": "Suscripción otorgada", "hidden_transactions_one": "Una transacción más", @@ -95,6 +99,7 @@ "subscription_expires_on": "Este plan expirará el {{date}}", "transactions": "Transacciones", "update_payment_details": "Actualizar detalles de pago", + "upgrade_plan_success": "", "weekly_subscription": "Suscripción semanal" } } diff --git a/src/components/OfferSwitch/OfferSwitch.module.scss b/src/components/OfferSwitch/OfferSwitch.module.scss new file mode 100644 index 000000000..f55259025 --- /dev/null +++ b/src/components/OfferSwitch/OfferSwitch.module.scss @@ -0,0 +1,62 @@ +@use 'src/styles/variables'; +@use 'src/styles/theme'; + +.offerSwitchContainer { + display: flex; + align-items: center; + width: 100%; + height: auto; + padding: 16px; + gap: 25px; + color: variables.$gray-white; + background-color: theme.$panel-bg; + border-radius: 4px; +} + +.activeOfferSwitchContainer { + color: variables.$gray-darker; + background-color: variables.$white; +} + +.offerSwitchInfoContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + height: 100%; + gap: 4px; + font-weight: 600; +} + +.offerSwitchPlanContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 2px; +} + +.currentPlanHeading { + color: variables.$gray; + font-size: 10px; +} + +.activeCurrentPlanHeading { + color: variables.$gray; +} + +.nextBillingDate { + color: variables.$gray; + font-weight: 400; + font-size: 12px; +} + +.price { + margin-left: auto; + font-size: 20px; + line-height: 28px; +} + +.paymentFrequency { + font-size: 12px; +} diff --git a/src/components/OfferSwitch/OfferSwitch.tsx b/src/components/OfferSwitch/OfferSwitch.tsx new file mode 100644 index 000000000..11c33cfc2 --- /dev/null +++ b/src/components/OfferSwitch/OfferSwitch.tsx @@ -0,0 +1,52 @@ +import classNames from 'classnames'; +import { useTranslation } from 'react-i18next'; + +import styles from './OfferSwitch.module.scss'; + +import type { Offer } from '#types/checkout'; +import Checkbox from '#components/Checkbox/Checkbox'; +import { formatLocalizedDate, formatPrice } from '#src/utils/formatting'; +import { useAccountStore } from '#src/stores/AccountStore'; + +interface OfferSwitchProps { + isCurrentOffer: boolean; + offer: Offer; + selected: { + value: boolean; + set: React.Dispatch>; + }; +} + +const OfferSwitch = ({ isCurrentOffer, offer, selected }: OfferSwitchProps) => { + const { t, i18n } = useTranslation('user'); + const { customerPriceInclTax, customerCurrency, period } = offer; + const expiresAt = useAccountStore((state) => state.subscription?.expiresAt); + + return ( +
+ selected.set(offer.offerId)} /> +
+ {isCurrentOffer && ( +
{t('payment.current_plan')}
+ )} +
+
{t(`payment.${period === 'month' ? 'monthly' : 'annual'}_subscription`)}
+ {isCurrentOffer && expiresAt && ( +
+ {t('payment.next_billing_date_on', { date: formatLocalizedDate(new Date(expiresAt * 1000), i18n.language) })} +
+ )} +
+
+
+ {formatPrice(customerPriceInclTax, customerCurrency, undefined)} + { + //todo: i18n + } + /{period === 'month' ? 'month' : 'year'} +
+
+ ); +}; + +export default OfferSwitch; diff --git a/src/components/Payment/Payment.module.scss b/src/components/Payment/Payment.module.scss index a6a4e9797..817efdab8 100644 --- a/src/components/Payment/Payment.module.scss +++ b/src/components/Payment/Payment.module.scss @@ -69,3 +69,20 @@ margin: 0 0 8px; } } + +.changePlanContainer { + display: flex; + flex-direction: column; + gap: 24px; +} + +.changePlanButtons { + display: flex; + flex-direction: row; + width: 100%; + gap: 12px; +} + +.changePlanCancelButton { + margin-left: auto; +} diff --git a/src/components/Payment/Payment.tsx b/src/components/Payment/Payment.tsx index 1c2100b71..26aa60341 100644 --- a/src/components/Payment/Payment.tsx +++ b/src/components/Payment/Payment.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; +import { useMutation } from 'react-query'; import useBreakpoint, { Breakpoint } from '../../hooks/useBreakpoint'; import IconButton from '../IconButton/IconButton'; @@ -18,6 +19,10 @@ import type { PaymentDetail, Subscription, Transaction } from '#types/subscripti import type { AccessModel } from '#types/Config'; import PayPal from '#src/icons/PayPal'; import type { Offer } from '#types/checkout'; +import useOffers from '#src/hooks/useOffers'; +import OfferSwitch from '#components/OfferSwitch/OfferSwitch'; +import { changeSubscription } from '#src/stores/CheckoutController'; +import Alert from '#components/Alert/Alert'; const VISIBLE_TRANSACTIONS = 4; @@ -69,6 +74,41 @@ const Payment = ({ const breakpoint = useBreakpoint(); const isMobile = breakpoint === Breakpoint.xs; + const { offers } = useOffers(); + const hasSelectableOffers = offers.some((offer) => offer.planSwitchEnabled); + + const [isChangingOffer, setIsChangingOffer] = useState(false); + const [selectedOfferId, setSelectedOfferId] = useState(activeSubscription?.accessFeeId ?? null); + const [isUpgradeOffer, setIsUpgradeOffer] = useState(undefined); + + // TODO: debug why offer upgrade works but downgrade doesn't + + useEffect(() => { + if (!isChangingOffer) { + setSelectedOfferId(activeSubscription?.accessFeeId ?? null); + } + }, [activeSubscription, isChangingOffer]); + + useEffect(() => { + if (selectedOfferId && offers) { + setIsUpgradeOffer( + (offers.find((offer) => offer.offerId === selectedOfferId)?.customerPriceInclTax ?? 0) > + (offers.find((offer) => offer.offerId === activeSubscription?.accessFeeId)?.customerPriceInclTax ?? 0), + ); + } + }, [selectedOfferId, offers, activeSubscription]); + + const changeSubscriptionPlan = useMutation(changeSubscription); + + const onChangePlanClick = async () => { + if (selectedOfferId && activeSubscription?.subscriptionId) { + changeSubscriptionPlan.mutate({ + accessFeeId: selectedOfferId.slice(1), + subscriptionId: `${activeSubscription.subscriptionId}`, + }); + } + }; + function onCompleteSubscriptionClick() { navigate(addQueryParam(location, 'u', 'choose-offer')); } @@ -98,43 +138,68 @@ const Payment = ({ } } + const showChangeSubscriptionButton = offerSwitchesAvailable || (hasSelectableOffers && !isChangingOffer && activeSubscription?.status !== 'active_trial'); + return ( <> + { + changeSubscriptionPlan.reset(); + setIsChangingOffer(false); + }} + /> {accessModel === 'SVOD' && (
-

{t('user:payment.subscription_details')}

+

{isChangingOffer ? t('user:payment.change_plan') : t('user:payment.subscription_details')}

{activeSubscription ? ( -
-

- {getTitle(activeSubscription.period)}
- {activeSubscription.status === 'active' && !isGrantedSubscription - ? t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) }) - : t('user:payment.subscription_expires_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })} - {pendingOffer && ( - {t('user:payment.pending_offer_switch', { title: getTitle(pendingOffer.period) })} - )} -

- {!isGrantedSubscription && ( -

- {formatPrice(activeSubscription.nextPaymentPrice, activeSubscription.nextPaymentCurrency, customer.country)} - /{t(`account:periods.${activeSubscription.period}`)} + {!isChangingOffer && ( +

+

+ {getTitle(activeSubscription.period)}
+ {activeSubscription.status === 'active' && !isGrantedSubscription + ? t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) }) + : t('user:payment.subscription_expires_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })} + {pendingOffer && ( + {t('user:payment.pending_offer_switch', { title: getTitle(pendingOffer.period) })} + )}

- )} -
- {offerSwitchesAvailable && ( + {!isGrantedSubscription && ( +

+ {formatPrice(activeSubscription.nextPaymentPrice, activeSubscription.nextPaymentCurrency, customer.country)} + /{t(`account:periods.${activeSubscription.period}`)} +

+ )} +
+ )} + {showChangeSubscriptionButton && (
+ + )} )}
diff --git a/src/components/Payment/__snapshots__/Payment.test.tsx.snap b/src/components/Payment/__snapshots__/Payment.test.tsx.snap index edf98ba58..daaf6f431 100644 --- a/src/components/Payment/__snapshots__/Payment.test.tsx.snap +++ b/src/components/Payment/__snapshots__/Payment.test.tsx.snap @@ -2,133 +2,45 @@ exports[` > renders and matches snapshot 1`] = `
-
-
-

- user:payment.payment_method -

-
-
-
- -

- •••• •••• •••• 0002 -

-
-
-
- -

- 03/2030 -

-
-
- -

- ****** -

-
-
-
-
-
-
-

- user:payment.transactions -

-
-
+ Unhandled Thrown Error! + +

+ checkout service is not available +

+
+    Error: checkout service is not available
+    at Module.useOffers [as default] (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/hooks/useOffers.ts:19:31)
+    at Payment (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/components/Payment/Payment.tsx:77:22)
+    at renderWithHooks (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:14985:18)
+    at mountIndeterminateComponent (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:17811:13)
+    at beginWork (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:19049:16)
+    at HTMLUnknownElement.callCallback (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:3945:14)
+    at HTMLUnknownElement.callTheUserObjectsOperation (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
+    at innerInvokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:340:25)
+    at invokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:276:3)
+    at HTMLUnknownElementImpl._dispatch (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:223:9)
+  
+

+ 💿 Hey developer 👋 +

+

+ You can provide a way better UX than this when your app throws errors by providing your own  + -

- - Monthly subscription (recurring) - - -
- user:payment.price_payed_with -

-
-

- T712014024 -
- May 5, 2021 -

-
-
-
+ props on  + -

- - Monthly subscription (recurring) - - -
- user:payment.price_payed_with -

-
-

- T177974068 -
- May 5, 2021 -

-
-
-
-

- - Monthly subscription (recurring) - - -
- user:payment.price_payed_with -

-
-

- T996601696 -
- May 5, 2021 -

-
-
-
+ <Route> + +

`; diff --git a/src/pages/User/__snapshots__/User.test.tsx.snap b/src/pages/User/__snapshots__/User.test.tsx.snap index de1b109c9..b52f5d148 100644 --- a/src/pages/User/__snapshots__/User.test.tsx.snap +++ b/src/pages/User/__snapshots__/User.test.tsx.snap @@ -616,160 +616,46 @@ exports[`User Component tests > Payments Page 1`] = `
-
-
-

- user:payment.subscription_details -

-
-
-

- - user:payment.annual_subscription - - -
- user:payment.next_billing_date_on -

-

- - $199.99 - - - / - account:periods.year - -

-
- -
-
+ Unhandled Thrown Error! + +

-
-

- user:payment.payment_method -

-
-
-
- -

- •••• •••• •••• 9888 -

-
-
-
- -

- 03/30 -

-
-
- -

- ****** -

-
-
-
-

-
+
-        
-

- user:payment.transactions -

-
-
+

+ 💿 Hey developer 👋 +

+

+ You can provide a way better UX than this when your app throws errors by providing your own  + -

- - Your Cool Subscription - - -
- user:payment.price_payed_with -

-
-

- 11232 -
- July 16, 2022 -

-
-
-
+ props on  + -

- - Your Cool Subscription - - -
- user:payment.price_payed_with -

-
-

- 11234 -
- November 9, 2022 -

-
-
-
+ <Route> + +

diff --git a/src/services/inplayer.checkout.service.ts b/src/services/inplayer.checkout.service.ts index 85c19dbaa..d5c19344e 100644 --- a/src/services/inplayer.checkout.service.ts +++ b/src/services/inplayer.checkout.service.ts @@ -208,6 +208,7 @@ const formatOffer = (offer: AccessFee): Offer => { active: true, period: offer.access_type.period, freePeriods: offer.trial_period ? 1 : 0, + planSwitchEnabled: offer.item.plan_switch_enabled ?? false, } as Offer; }; diff --git a/src/services/inplayer.subscription.service.ts b/src/services/inplayer.subscription.service.ts index bea33a10c..a55555324 100644 --- a/src/services/inplayer.subscription.service.ts +++ b/src/services/inplayer.subscription.service.ts @@ -1,7 +1,7 @@ import i18next from 'i18next'; import InPlayer, { PurchaseDetails, Card, GetItemAccessV1, SubscriptionDetails as InplayerSubscription } from '@inplayer-org/inplayer.js'; -import type { PaymentDetail, Subscription, Transaction, UpdateSubscription } from '#types/subscription'; +import type { ChangeSubscription, PaymentDetail, Subscription, Transaction, UpdateSubscription } from '#types/subscription'; import type { Config } from '#types/Config'; import type { InPlayerError } from '#types/inplayer'; @@ -17,6 +17,7 @@ interface SubscriptionDetails extends InplayerSubscription { access_type?: { period: string; }; + access_fee_id?: number; } export async function getActiveSubscription({ config }: { config: Config }) { @@ -90,6 +91,18 @@ export const updateSubscription: UpdateSubscription = async ({ offerId, unsubscr } }; +export const changeSubscription: ChangeSubscription = async ({ accessFeeId, subscriptionId }) => { + try { + const response = await InPlayer.Subscription.changeSubscriptionPlan({ access_fee_id: parseInt(accessFeeId), inplayer_token: subscriptionId }); + return { + errors: [], + responseData: { message: response.data.message }, + }; + } catch { + throw new Error('Failed to change subscription'); + } +}; + const formatCardDetails = (card: Card): PaymentDetail => { const { number, exp_month, exp_year, card_name, card_type, account_id } = card; const zeroFillExpMonth = `0${exp_month}`.slice(-2); @@ -136,6 +149,8 @@ const formatActiveSubscription = (subscription: SubscriptionDetails, expiresAt: let status = ''; switch (subscription.action_type) { case 'free-trial': + status = 'active_trial'; + break; case 'recurrent': status = 'active'; break; @@ -152,6 +167,7 @@ const formatActiveSubscription = (subscription: SubscriptionDetails, expiresAt: return { subscriptionId: subscription.subscription_id, offerId: subscription.item_id?.toString(), + accessFeeId: `S${subscription.access_fee_id}`, status, expiresAt, nextPaymentAt: subscription.next_rebill_date, diff --git a/src/stores/AccountController.ts b/src/stores/AccountController.ts index ca536124e..f96d7ccaf 100644 --- a/src/stores/AccountController.ts +++ b/src/stores/AccountController.ts @@ -417,26 +417,28 @@ export async function reloadActiveSubscription({ delay }: { delay: number } = { }); } -export async function exportAccountData() { +export const exportAccountData = async () => { return await useAccount(async () => { return await useService(async ({ accountService }) => { return await accountService.exportAccountData(undefined, true); }); }); -} +}; -export async function getSocialLoginUrls() { +export const getSocialLoginUrls = async () => { return await useService(async ({ accountService, config }) => { return await accountService.getSocialUrls(config); }); -} -export async function deleteAccountData(password: string) { +}; + +export const deleteAccountData = async (password: string) => { return await useAccount(async () => { return await useService(async ({ accountService }) => { return await accountService.deleteAccount({ password }, true); }); }); -} +}; + export const getReceipt = async (transactionId: string) => { return await useAccount(async () => { return await useService(async ({ subscriptionService, sandbox = true }) => { diff --git a/src/stores/CheckoutController.ts b/src/stores/CheckoutController.ts index 1cc918eb6..019fbb935 100644 --- a/src/stores/CheckoutController.ts +++ b/src/stores/CheckoutController.ts @@ -295,6 +295,16 @@ export const switchSubscription = async (toOfferId: string, switchDirection: 'up }); }; +export const changeSubscription = async ({ accessFeeId, subscriptionId }: { accessFeeId: string; subscriptionId: string }) => { + return await useService(async ({ subscriptionService, sandbox = true }) => { + if (!subscriptionService || !('changeSubscription' in subscriptionService)) throw new Error('subscription service is not configured'); + + const { responseData } = await subscriptionService.changeSubscription({ accessFeeId, subscriptionId }, sandbox); + + return responseData; + }); +}; + export const updatePayPalPaymentMethod = async ( successUrl: string, cancelUrl: string, diff --git a/src/utils/formatting.ts b/src/utils/formatting.ts index f37511145..97b873e2d 100644 --- a/src/utils/formatting.ts +++ b/src/utils/formatting.ts @@ -110,8 +110,8 @@ export const buildLegacySeriesUrlFromMediaItem = (media: PlaylistItem, play: boo }); }; -export const formatPrice = (price: number, currency: string, country: string) => { - return new Intl.NumberFormat(country || undefined, { +export const formatPrice = (price: number, currency: string, country?: string) => { + return new Intl.NumberFormat(country || 'en-US', { style: 'currency', currency: currency, }).format(price); diff --git a/types/account.d.ts b/types/account.d.ts index e03e5c119..997eafe3f 100644 --- a/types/account.d.ts +++ b/types/account.d.ts @@ -343,5 +343,5 @@ type ChangePassword = EnvironmentServiceRequest>; type UpdatePersonalShelves = EnvironmentServiceRequest>; type GetLocales = EmptyServiceRequest; -type ExportAccountData = AuthServiceRequest; +type ExportAccountData = EnvironmentServiceRequest; type DeleteAccount = EnvironmentServiceRequest; diff --git a/types/checkout.d.ts b/types/checkout.d.ts index 79c6c9e82..777420b0e 100644 --- a/types/checkout.d.ts +++ b/types/checkout.d.ts @@ -41,6 +41,7 @@ export type Offer = { contentExternalId: number | null; contentExternalData: string | null; contentAgeRestriction: string | null; + planSwitchEnabled?: boolean; }; export type OrderOffer = { diff --git a/types/subscription.d.ts b/types/subscription.d.ts index e2f316d12..19c644f67 100644 --- a/types/subscription.d.ts +++ b/types/subscription.d.ts @@ -1,8 +1,11 @@ +import type { ChangeSubscriptionPlanRequestBody, ChangeSubscriptionPlanResponse } from '@inplayer-org/inplayer.js'; + // Subscription types export type Subscription = { subscriptionId: number | string; offerId: string; - status: 'active' | 'cancelled' | 'expired' | 'terminated'; + accessFeeId?: string; + status: 'active' | 'active_trial' | 'cancelled' | 'expired' | 'terminated'; expiresAt: number; nextPaymentPrice: number; nextPaymentCurrency: string; @@ -118,8 +121,15 @@ export type FetchReceiptPayload = { export type FetchReceiptResponse = string; +type ChangeSubscriptionPayload = { + accessFeeId: string; + subscriptionId: string; +}; + type GetSubscriptions = CleengRequest; type UpdateSubscription = CleengRequest; type GetPaymentDetails = CleengRequest; type GetTransactions = CleengRequest; type FetchReceipt = CleengRequest; + +type ChangeSubscription = EnvironmentServiceRequest; diff --git a/yarn.lock b/yarn.lock index 3fbaea244..01a687774 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,10 +1729,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@inplayer-org/inplayer.js@^3.13.12": - version "3.13.12" - resolved "https://registry.yarnpkg.com/@inplayer-org/inplayer.js/-/inplayer.js-3.13.12.tgz#6a8ff6516ff92fd1a38ef4e2854de512c29ad44b" - integrity sha512-NYmpZINBxu/vR4eTFtT84NfN0TcHH1Tplf+j36lQeiSa6Xj0mN6BlTUpZYXVseuOb9SePvGUbPyjXBCIAhALrg== +"@inplayer-org/inplayer.js@^3.13.13": + version "3.13.13" + resolved "https://registry.yarnpkg.com/@inplayer-org/inplayer.js/-/inplayer.js-3.13.13.tgz#b64dfee35e488037f64608a8264aecc6e85252da" + integrity sha512-LnyriDdasvRy1CUv2Zg0THRs48s+LffhnHO7hEf4K7gOl5DaFmkf16OM1uOBuOjL4xzHGgFR3IfPz7Pay3uzsA== dependencies: aws-iot-device-sdk "^2.2.6" axios "^0.21.2" @@ -3492,7 +3492,7 @@ compare-func@^2.0.0: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^2.0.0, concat-stream@~2.0.0: version "2.0.0" @@ -3599,9 +3599,9 @@ core-js-pure@^3.25.1, core-js-pure@^3.25.3: integrity sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg== core-js@^3.19.2: - version "3.22.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.3.tgz#498c41d997654cb00e81c7a54b44f0ab21ab01d5" - integrity sha512-1t+2a/d2lppW1gkLXx3pKPVGbBdxXAkqztvWb1EJ8oF8O2gIGiytzflNiFEehYwVK/t2ryUsGBoOFFvNx95mbg== + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.0.tgz#4471dd33e366c79d8c0977ed2d940821719db344" + integrity sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ== core-util-is@~1.0.0: version "1.0.3" @@ -3813,14 +3813,14 @@ de-indent@^1.0.2: resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.2.0, debug@^4.3.1: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" -debug@4.3.4, debug@^4.3.2, debug@^4.3.4: +debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4885,7 +4885,7 @@ fs-tree-diff@^2.0.1: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.2" @@ -4917,7 +4917,17 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: +get-intrinsic@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -4998,7 +5008,7 @@ glob-stream@^6.1.0: to-absolute-glob "^2.0.0" unique-stream "^2.0.2" -glob@7.2.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@7.2.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -5021,6 +5031,18 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -5130,7 +5152,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -5411,7 +5438,7 @@ indent-string@^4.0.0: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -5761,7 +5788,7 @@ is-wsl@^2.2.0: isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" @@ -6530,7 +6557,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -"minimatch@2 || 3", minimatch@5.0.1, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: +"minimatch@2 || 3", minimatch@5.0.1, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -6558,12 +6585,12 @@ minimist@1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -minimist@^1.1.0: +minimist@^1.1.0, minimist@^1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== @@ -6853,11 +6880,16 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-inspect@^1.11.0, object-inspect@^1.9.0: +object-inspect@^1.11.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -6916,7 +6948,7 @@ oblivious-set@1.0.0: once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -7129,7 +7161,7 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^2.0.1: version "2.0.1" @@ -7580,9 +7612,9 @@ qj@~2.0.0: integrity sha512-8466vlnAF/piI42tzMBUfhaAWn2yBNPOLSSbA2YBlEh+S8CxBXbAO1AwuDReGKYX6LlsK19wBL9cpXZGlgsXxA== qs@^6.9.4: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== dependencies: side-channel "^1.0.4" @@ -7785,7 +7817,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@3: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -7794,7 +7826,20 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.3.3: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -7807,7 +7852,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.6.0: +readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -9127,7 +9172,7 @@ typedarray-to-buffer@^3.1.5: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@^4.2.4, typescript@^4.3.4: version "4.6.2" @@ -9277,7 +9322,7 @@ use-debounce@^7.0.1: util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@9.0.0, uuid@^9.0: version "9.0.0" @@ -9829,7 +9874,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" From 00eab0917118bc6ca86948a603e4ecbb2e2948c9 Mon Sep 17 00:00:00 2001 From: Filip Naumovski Date: Mon, 19 Jun 2023 17:33:37 +0200 Subject: [PATCH 02/17] chore: fix tests --- .../__snapshots__/Payment.test.tsx.snap | 164 +++++++++++---- src/hooks/useOffers.ts | 14 +- .../User/__snapshots__/User.test.tsx.snap | 186 ++++++++++++++---- 3 files changed, 289 insertions(+), 75 deletions(-) diff --git a/src/components/Payment/__snapshots__/Payment.test.tsx.snap b/src/components/Payment/__snapshots__/Payment.test.tsx.snap index daaf6f431..edf98ba58 100644 --- a/src/components/Payment/__snapshots__/Payment.test.tsx.snap +++ b/src/components/Payment/__snapshots__/Payment.test.tsx.snap @@ -2,45 +2,133 @@ exports[` > renders and matches snapshot 1`] = `
-

- Unhandled Thrown Error! -

-

- checkout service is not available -

-
-    Error: checkout service is not available
-    at Module.useOffers [as default] (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/hooks/useOffers.ts:19:31)
-    at Payment (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/components/Payment/Payment.tsx:77:22)
-    at renderWithHooks (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:14985:18)
-    at mountIndeterminateComponent (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:17811:13)
-    at beginWork (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:19049:16)
-    at HTMLUnknownElement.callCallback (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:3945:14)
-    at HTMLUnknownElement.callTheUserObjectsOperation (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
-    at innerInvokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:340:25)
-    at invokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:276:3)
-    at HTMLUnknownElementImpl._dispatch (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:223:9)
-  
-

- 💿 Hey developer 👋 -

-

- You can provide a way better UX than this when your app throws errors by providing your own  - +

+

+ user:payment.payment_method +

+
+
+
+ +

+ •••• •••• •••• 0002 +

+
+
+
+ +

+ 03/2030 +

+
+
+ +

+ ****** +

+
+
+
+
+
+
+

+ user:payment.transactions +

+
+
- errorElement - - props on  - + + Monthly subscription (recurring) + + +
+ user:payment.price_payed_with +

+
+

+ T712014024 +
+ May 5, 2021 +

+
+
+
- <Route> - -

+

+ + Monthly subscription (recurring) + + +
+ user:payment.price_payed_with +

+
+

+ T177974068 +
+ May 5, 2021 +

+
+
+
+

+ + Monthly subscription (recurring) + + +
+ user:payment.price_payed_with +

+
+

+ T996601696 +
+ May 5, 2021 +

+
+
+
`; diff --git a/src/hooks/useOffers.ts b/src/hooks/useOffers.ts index b4ba8153b..63678923d 100644 --- a/src/hooks/useOffers.ts +++ b/src/hooks/useOffers.ts @@ -16,7 +16,19 @@ const useOffers = () => { const { clientOffers, sandbox } = useClientIntegration(); const checkoutService: CheckoutService = useService(({ checkoutService }) => checkoutService); - if (!checkoutService) throw new Error('checkout service is not available'); + if (!checkoutService) { + return { + hasTVODOffers: false, + hasMultipleOfferTypes: false, + isLoading: false, + hasPremierOffer: false, + defaultOfferId: '', + offerType: 'svod' as OfferType, + setOfferType: () => null, + offers: [], + offersDict: {}, + }; + } const { requestedMediaOffers } = useCheckoutStore(({ requestedMediaOffers }) => ({ requestedMediaOffers }), shallow); const hasTvodOffer = (requestedMediaOffers || []).some((offer) => offer.offerId); diff --git a/src/pages/User/__snapshots__/User.test.tsx.snap b/src/pages/User/__snapshots__/User.test.tsx.snap index b52f5d148..de1b109c9 100644 --- a/src/pages/User/__snapshots__/User.test.tsx.snap +++ b/src/pages/User/__snapshots__/User.test.tsx.snap @@ -616,46 +616,160 @@ exports[`User Component tests > Payments Page 1`] = `
-

- Unhandled Thrown Error! -

-

+
+

+ user:payment.subscription_details +

+
+
+

+ + user:payment.annual_subscription + + +
+ user:payment.next_billing_date_on +

+

+ + $199.99 + + + / + account:periods.year + +

+
+ +

+
- checkout service is not available - -
+          

+ user:payment.payment_method +

+
+
+
+ +

+ •••• •••• •••• 9888 +

+
+
+
+ +

+ 03/30 +

+
+
+ +

+ ****** +

+
+
+
+ +
- Error: checkout service is not available - at Module.useOffers [as default] (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/hooks/useOffers.ts:19:31) - at Payment (/Users/filip.naumovski/Documents/Projects/ott-web-app/src/components/Payment/Payment.tsx:77:22) - at renderWithHooks (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:14985:18) - at mountIndeterminateComponent (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:17811:13) - at beginWork (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:19049:16) - at HTMLUnknownElement.callCallback (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/react-dom/cjs/react-dom.development.js:3945:14) - at HTMLUnknownElement.callTheUserObjectsOperation (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30) - at innerInvokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:340:25) - at invokeEventListeners (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:276:3) - at HTMLUnknownElementImpl._dispatch (/Users/filip.naumovski/Documents/Projects/ott-web-app/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:223:9) - -

- 💿 Hey developer 👋 -

-

- You can provide a way better UX than this when your app throws errors by providing your own  - +

+ user:payment.transactions +

+
+
- errorElement - - props on  - + + Your Cool Subscription + + +
+ user:payment.price_payed_with +

+
+

+ 11232 +
+ July 16, 2022 +

+
+
+
- <Route> - -

+

+ + Your Cool Subscription + + +
+ user:payment.price_payed_with +

+
+

+ 11234 +
+ November 9, 2022 +

+
+
+ From 2faf833fe05c529a162c733b5a77325d2614817c Mon Sep 17 00:00:00 2001 From: Filip Naumovski Date: Tue, 20 Jun 2023 12:18:32 +0200 Subject: [PATCH 03/17] chore: add translations --- public/locales/en/user.json | 10 +++++----- src/components/Payment/Payment.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/public/locales/en/user.json b/public/locales/en/user.json index 9553cba89..2ed5d8866 100644 --- a/public/locales/en/user.json +++ b/public/locales/en/user.json @@ -69,13 +69,13 @@ "annual_subscription": "Annual subscription", "cancel_subscription": "Cancel subscription", "card_number": "Card number", - "change_plan": "", - "change_plan_error": "", + "change_plan": "Change Plan", + "change_plan_error": "There was a problem saving your subscription plan change.", "change_subscription": "Change subscription", "complete_subscription": "Complete subscription", - "current_plan": "", + "current_plan": "CURRENT PLAN", "daily_subscription": "Daily subscription", - "downgrade_plan_success": "", + "downgrade_plan_success": "You've successfully changed the subscription plan. Your new plan will start after the end of the current cycle ( {{date}} ).", "expiry_date": "Expiry date", "granted_subscription": "Granted subscription", "hidden_transactions_one": "One more transaction", @@ -99,7 +99,7 @@ "subscription_expires_on": "This plan will expire on {{date}}", "transactions": "Transactions", "update_payment_details": "Update payment details", - "upgrade_plan_success": "", + "upgrade_plan_success": "You've successfully changed the subscription plan. You can enjoy your additional benefits immediately.", "weekly_subscription": "Weekly subscription" } } diff --git a/src/components/Payment/Payment.tsx b/src/components/Payment/Payment.tsx index adc47ab33..5c62d349f 100644 --- a/src/components/Payment/Payment.tsx +++ b/src/components/Payment/Payment.tsx @@ -146,12 +146,14 @@ const Payment = ({ return ( <> Date: Tue, 20 Jun 2023 14:12:11 +0200 Subject: [PATCH 04/17] chore: add disabled check to save button --- public/locales/en/user.json | 2 +- src/components/Payment/Payment.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/locales/en/user.json b/public/locales/en/user.json index 2ed5d8866..e09a8e353 100644 --- a/public/locales/en/user.json +++ b/public/locales/en/user.json @@ -75,7 +75,7 @@ "complete_subscription": "Complete subscription", "current_plan": "CURRENT PLAN", "daily_subscription": "Daily subscription", - "downgrade_plan_success": "You've successfully changed the subscription plan. Your new plan will start after the end of the current cycle ( {{date}} ).", + "downgrade_plan_success": "You've successfully changed the subscription plan. Your new plan will start after the end of the current cycle ({{date}}).", "expiry_date": "Expiry date", "granted_subscription": "Granted subscription", "hidden_transactions_one": "One more transaction", diff --git a/src/components/Payment/Payment.tsx b/src/components/Payment/Payment.tsx index 5c62d349f..b4f7080df 100644 --- a/src/components/Payment/Payment.tsx +++ b/src/components/Payment/Payment.tsx @@ -229,7 +229,11 @@ const Payment = ({ /> ))}
-
-
Date: Thu, 6 Jul 2023 15:01:20 +0200 Subject: [PATCH 08/17] chore: fix tests --- src/components/Payment/Payment.tsx | 15 ++++++++--- .../User/__snapshots__/User.test.tsx.snap | 8 ++++++ test-e2e/tests/payments/coupons_test.ts | 4 +-- test-e2e/tests/payments/subscription_test.ts | 4 +-- test-e2e/utils/payments.ts | 27 ++++++++++++++----- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/components/Payment/Payment.tsx b/src/components/Payment/Payment.tsx index bd0e1f201..9ec5186c2 100644 --- a/src/components/Payment/Payment.tsx +++ b/src/components/Payment/Payment.tsx @@ -77,7 +77,6 @@ const Payment = ({ const isMobile = breakpoint === Breakpoint.xs; const { offers } = useOffers(); - const hasSelectableOffers = offers.some((offer) => offer.planSwitchEnabled); const [isChangingOffer, setIsChangingOffer] = useState(false); const [selectedOfferId, setSelectedOfferId] = useState(activeSubscription?.accessFeeId ?? null); @@ -89,6 +88,10 @@ const Payment = ({ } }, [activeSubscription, isChangingOffer]); + useEffect(() => { + setIsChangingOffer(false); + }, [activeSubscription?.status, activeSubscription?.pendingSwitchId]); + useEffect(() => { if (selectedOfferId && offers) { setIsUpgradeOffer( @@ -162,7 +165,7 @@ const Payment = ({ } } - const showChangeSubscriptionButton = offerSwitchesAvailable || (hasSelectableOffers && !isChangingOffer && activeSubscription?.status !== 'active_trial'); + const showChangeSubscriptionButton = offerSwitchesAvailable || (!isChangingOffer && !canRenewSubscription); return ( <> @@ -233,7 +236,12 @@ const Payment = ({ !isGrantedSubscription && !isChangingOffer && canRenewSubscription ? ( -
diff --git a/src/pages/User/__snapshots__/User.test.tsx.snap b/src/pages/User/__snapshots__/User.test.tsx.snap index 57a82bea3..aa57ff3f5 100644 --- a/src/pages/User/__snapshots__/User.test.tsx.snap +++ b/src/pages/User/__snapshots__/User.test.tsx.snap @@ -650,6 +650,14 @@ exports[`User Component tests > Payments Page 1`] = `

+
{ couponLoginContext = await I.registerOrLogin(couponLoginContext); - cancelPlan(I, addYear(today), props.canRenewSubscription); + cancelPlan(I, addYear(today), props.canRenewSubscription, providerName); }); Scenario(`I can renew a free subscription - ${providerName}`, async ({ I }) => { diff --git a/test-e2e/tests/payments/subscription_test.ts b/test-e2e/tests/payments/subscription_test.ts index b39077f93..282b735cf 100644 --- a/test-e2e/tests/payments/subscription_test.ts +++ b/test-e2e/tests/payments/subscription_test.ts @@ -170,7 +170,7 @@ function runTestSuite(props: ProviderProps, providerName: string) { props.fieldWrapper, ); - await finishAndCheckSubscription(I, addYear(today), today, props.yearlyOffer.price); + await finishAndCheckSubscription(I, addYear(today), today, props.yearlyOffer.price, providerName); I.seeAll(cardInfo); }); @@ -178,7 +178,7 @@ function runTestSuite(props: ProviderProps, providerName: string) { Scenario(`I can cancel my subscription - ${providerName}`, async ({ I }) => { paidLoginContext = await I.registerOrLogin(paidLoginContext); - cancelPlan(I, addYear(today), props.canRenewSubscription); + cancelPlan(I, addYear(today), props.canRenewSubscription, providerName); // Still see payment info I.seeAll(cardInfo); diff --git a/test-e2e/utils/payments.ts b/test-e2e/utils/payments.ts index 13b14978f..f1f0e7c52 100644 --- a/test-e2e/utils/payments.ts +++ b/test-e2e/utils/payments.ts @@ -35,7 +35,7 @@ export function formatDate(date: Date) { return new Intl.DateTimeFormat('en-US', { day: 'numeric', month: 'long', year: 'numeric' }).format(date); } -export async function finishAndCheckSubscription(I: CodeceptJS.I, billingDate: Date, today: Date, yearlyPrice: string) { +export async function finishAndCheckSubscription(I: CodeceptJS.I, billingDate: Date, today: Date, yearlyPrice: string, providerName?: string) { I.click('Continue'); I.waitForLoaderDone(longTimeout); I.wait(2); @@ -63,18 +63,30 @@ export async function finishAndCheckSubscription(I: CodeceptJS.I, billingDate: D I.see(yearlyPrice); I.see('/year'); I.see('Next billing date is on ' + formatDate(billingDate)); - I.see('Cancel subscription'); - I.waitForElement('[class*="transactionItem"]'); + if (providerName?.includes('JW')) { + I.waitForElement('[data-testid="change-subscription-button"]', 10); + I.click('[data-testid="change-subscription-button"]'); + } + + I.waitForElement('[data-testid="cancel-subscription-button"]', 10); + + I.waitForElement('[class*="transactionItem"]', 10); I.see(formatDate(today)); } -export function cancelPlan(I: CodeceptJS.I, expirationDate: Date, canRenewSubscription: boolean) { +export function cancelPlan(I: CodeceptJS.I, expirationDate: Date, canRenewSubscription: boolean, providerName?: string) { I.amOnPage(constants.paymentsUrl); I.waitForLoaderDone(); - I.click('Cancel subscription'); + if (providerName?.includes('JW')) { + I.waitForElement('[data-testid="change-subscription-button"]', 10); + I.click('[data-testid="change-subscription-button"]'); + } + + I.waitForElement('[data-testid="cancel-subscription-button"]', 10); + I.click('[data-testid="cancel-subscription-button"]'); I.see('We are sorry to see you go.'); I.see('You will be unsubscribed from your current plan by clicking the unsubscribe button below.'); I.see('Unsubscribe'); @@ -83,7 +95,8 @@ export function cancelPlan(I: CodeceptJS.I, expirationDate: Date, canRenewSubscr I.dontSee('This plan will expire'); - I.click('Cancel subscription'); + I.waitForElement('[data-testid="cancel-subscription-button"]', 10); + I.click('[data-testid="cancel-subscription-button"]'); I.click('Unsubscribe'); I.waitForLoaderDone(); I.see('Miss you already.'); @@ -122,7 +135,7 @@ export function renewPlan(I: CodeceptJS.I, billingDate: Date, yearlyPrice: strin I.see('Annual subscription'); I.see('Next billing date is on'); - I.see('Cancel subscription'); + I.waitForElement('[data-testid="cancel-subscription-button"]', 10); } export function overrideIP(I: CodeceptJS.I) { From be09ba2b3cb8ae6688e7673f8216e31b02e38bf0 Mon Sep 17 00:00:00 2001 From: Filip Naumovski Date: Thu, 6 Jul 2023 16:29:42 +0200 Subject: [PATCH 09/17] fix: disable change subscription button when subscription is cancelled and can't renew --- src/components/Payment/Payment.tsx | 1 + src/pages/User/__snapshots__/User.test.tsx.snap | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/Payment/Payment.tsx b/src/components/Payment/Payment.tsx index 9ec5186c2..266956c80 100644 --- a/src/components/Payment/Payment.tsx +++ b/src/components/Payment/Payment.tsx @@ -220,6 +220,7 @@ const Payment = ({