@lookiero/checkout 10.0.0-beta.4 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fake-dependencies/@lookiero/payments-front/index.d.ts +6 -8
- package/dist/fake-dependencies/@lookiero/payments-front/index.js +4 -7
- package/dist/index.d.ts +4 -3
- package/dist/src/ExpoRoot.js +13 -17
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.d.ts +1 -1
- package/dist/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.js +0 -2
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.d.ts +1 -1
- package/dist/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.js +1 -2
- package/dist/src/infrastructure/tracking/tracking.d.ts +2 -2
- package/dist/src/infrastructure/tracking/useTrackCheckout.d.ts +17 -10
- package/dist/src/infrastructure/tracking/useTrackCheckout.js +12 -27
- package/dist/src/infrastructure/ui/Root.d.ts +9 -7
- package/dist/src/infrastructure/ui/Root.js +3 -2
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.d.ts +2 -3
- package/dist/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.js +26 -17
- package/dist/src/infrastructure/ui/hooks/useStaticInfo.d.ts +3 -0
- package/dist/src/infrastructure/ui/hooks/useStaticInfo.js +7 -2
- package/dist/src/infrastructure/ui/i18n/i18n.d.ts +0 -1
- package/dist/src/infrastructure/ui/i18n/i18n.js +0 -1
- package/dist/src/infrastructure/ui/routing/CheckoutMiddleware.js +12 -1
- package/dist/src/infrastructure/ui/routing/Routing.d.ts +7 -5
- package/dist/src/infrastructure/ui/routing/Routing.js +11 -3
- package/dist/src/infrastructure/ui/routing/routes.d.ts +1 -0
- package/dist/src/infrastructure/ui/routing/routes.js +1 -0
- package/dist/src/infrastructure/ui/views/App.js +6 -5
- package/dist/src/infrastructure/ui/views/checkout/Checkout.d.ts +2 -7
- package/dist/src/infrastructure/ui/views/checkout/Checkout.js +9 -20
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.d.ts +0 -3
- package/dist/src/infrastructure/ui/views/checkout/Checkout.style.js +0 -3
- package/dist/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.js +1 -3
- package/dist/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.js +7 -7
- package/dist/src/infrastructure/ui/views/summary/Summary.js +1 -2
- package/dist/src/projection/customer/customer.d.ts +0 -2
- package/dist/src/projection/order/order.d.ts +1 -1
- package/dist/src/projection/subscription/subscription.d.ts +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/fake-dependencies/@lookiero/payments-front/index.tsx +9 -32
- package/index.ts +4 -10
- package/package.json +4 -3
- package/src/ExpoRoot.tsx +37 -42
- package/src/infrastructure/domain/checkoutBooking/react/useBlockCheckoutBooking.ts +1 -4
- package/src/infrastructure/projection/pricing/react/useViewPricingByCheckoutId.ts +2 -3
- package/src/infrastructure/tracking/tracking.ts +2 -2
- package/src/infrastructure/tracking/useTrackCheckout.ts +56 -66
- package/src/infrastructure/ui/Root.tsx +14 -10
- package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.test.tsx +6 -0
- package/src/infrastructure/ui/components/templates/header/itemHeader/ItemHeader.tsx +0 -1
- package/src/infrastructure/ui/hooks/usePaymentInstrumentEvents.ts +60 -18
- package/src/infrastructure/ui/hooks/useStaticInfo.test.tsx +6 -1
- package/src/infrastructure/ui/hooks/useStaticInfo.tsx +13 -2
- package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +297 -0
- package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +169 -0
- package/src/infrastructure/ui/i18n/i18n.ts +0 -1
- package/src/infrastructure/ui/routing/CheckoutMiddleware.test.tsx +9 -1
- package/src/infrastructure/ui/routing/CheckoutMiddleware.tsx +15 -1
- package/src/infrastructure/ui/routing/Routing.tsx +29 -15
- package/src/infrastructure/ui/routing/routes.ts +1 -0
- package/src/infrastructure/ui/views/App.tsx +13 -5
- package/src/infrastructure/ui/views/checkout/Checkout.style.ts +0 -3
- package/src/infrastructure/ui/views/checkout/Checkout.test.tsx +43 -51
- package/src/infrastructure/ui/views/checkout/Checkout.tsx +13 -51
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.test.tsx +134 -0
- package/src/infrastructure/ui/views/checkout/components/checkoutPaymentModal/CheckoutPaymentModal.tsx +124 -0
- package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx +8 -8
- package/src/infrastructure/ui/views/return/components/returnQuestionsForm/ReturnQuestionsForm.test.tsx +6 -0
- package/src/infrastructure/ui/views/summary/Summary.tsx +1 -1
- package/src/infrastructure/ui/views/summaryTabs/SummaryTabs.test.tsx +4 -1
- package/src/projection/customer/customer.ts +0 -2
- package/src/projection/order/order.ts +1 -1
- package/src/projection/subscription/subscription.ts +1 -1
- package/dist/public/public/assets/adaptive-icon.png +0 -0
- package/dist/public/public/assets/favicon.png +0 -0
- package/dist/public/public/assets/icon.png +0 -0
- package/dist/public/public/assets/splash.png +0 -0
- package/dist/public/public/images/not-found.png +0 -0
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.d.ts +0 -26
- package/dist/src/infrastructure/ui/hooks/useCheckoutFlow.js +0 -127
- package/dist/src/infrastructure/ui/routing/useBasePath.d.ts +0 -8
- package/dist/src/infrastructure/ui/routing/useBasePath.js +0 -9
- package/src/infrastructure/ui/hooks/useCheckoutFlow.test.tsx +0 -302
- package/src/infrastructure/ui/hooks/useCheckoutFlow.tsx +0 -203
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { fireEvent } from "@testing-library/react-native";
|
|
2
|
-
import React
|
|
3
|
-
import { View } from "react-native";
|
|
2
|
+
import React from "react";
|
|
4
3
|
import { QueryStatus } from "@lookiero/messaging-react";
|
|
5
4
|
import { Country } from "@lookiero/sty-psp-locale";
|
|
6
5
|
import { Segment } from "@lookiero/sty-psp-segment";
|
|
7
6
|
import { DummyLayout } from "@lookiero/sty-psp-ui";
|
|
8
7
|
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
9
|
-
import { OrderProjection } from "../../../../projection/order/order";
|
|
10
|
-
import { SubscriptionProjection } from "../../../../projection/subscription/subscription";
|
|
11
8
|
import { checkout } from "../../../projection/checkout/checkout.mock";
|
|
12
9
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
13
10
|
import { pricing } from "../../../projection/pricing/pricing.mock";
|
|
@@ -17,9 +14,7 @@ import { Routes } from "../../routing/routes";
|
|
|
17
14
|
import { render } from "../../test/render";
|
|
18
15
|
import { Checkout } from "./Checkout";
|
|
19
16
|
|
|
20
|
-
const getAuthToken = () => Promise.resolve("token");
|
|
21
17
|
const customerId = "a8fff6d7-708c-41a7-b42a-58c5706d33df";
|
|
22
|
-
const basePath = "/checkout";
|
|
23
18
|
const country = Country.ES;
|
|
24
19
|
const segment = Segment.WOMEN;
|
|
25
20
|
const mockCheckout = checkout({
|
|
@@ -31,29 +26,49 @@ const mockCheckout = checkout({
|
|
|
31
26
|
{ status: CheckoutItemStatus.REPLACED },
|
|
32
27
|
],
|
|
33
28
|
});
|
|
34
|
-
const order: OrderProjection = {
|
|
35
|
-
orderNumber: 12345,
|
|
36
|
-
isFirstOrder: false,
|
|
37
|
-
coupon: null,
|
|
38
|
-
};
|
|
39
|
-
const subscription: SubscriptionProjection = "o";
|
|
40
29
|
const mockUseRedirect = jest.fn(() => ({ returnUrl: "https://web2.dev.aws.lookiero.es/user/" }));
|
|
41
30
|
|
|
42
|
-
const mockOnSuccess = jest.fn();
|
|
43
|
-
const mockCheckoutFlow = jest.fn();
|
|
44
|
-
const paymentFlowTestId = "payment-flow";
|
|
45
|
-
const mockPaymentFlowComponent: ReactNode = <View testID={paymentFlowTestId}>PaymentFlow</View>;
|
|
46
|
-
jest.mock("../../hooks/useCheckoutFlow", () => ({
|
|
47
|
-
useCheckoutFlow: () => [mockCheckoutFlow, QueryStatus.SUCCESS, mockPaymentFlowComponent],
|
|
48
|
-
}));
|
|
49
|
-
|
|
50
31
|
jest.mock("../../hooks/useStaticInfo", () => ({
|
|
51
|
-
useStaticInfo: () => ({
|
|
32
|
+
useStaticInfo: () => ({
|
|
33
|
+
customer: { customerId, country, segment },
|
|
34
|
+
basePath: "",
|
|
35
|
+
}),
|
|
52
36
|
}));
|
|
53
|
-
|
|
54
|
-
jest.mock("./components/paymentInstrument/PaymentInstrument", () => ({ PaymentInstrument: () => null }));
|
|
37
|
+
|
|
55
38
|
jest.mock("../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId");
|
|
56
39
|
jest.mock("../../../projection/pricing/react/useViewPricingByCheckoutId");
|
|
40
|
+
jest.mock("../../hooks/usePaymentInstrumentEvents");
|
|
41
|
+
|
|
42
|
+
const mockStartLegacyBoxCheckout = jest.fn();
|
|
43
|
+
jest.mock("@lookiero/payments-front", () => {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
|
+
const { useImperativeHandle, forwardRef } = require("react");
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
CheckoutStatus: {
|
|
49
|
+
REJECTED: "REJECTED",
|
|
50
|
+
ERROR: "ERROR",
|
|
51
|
+
FULFILLED: "FULFILLED",
|
|
52
|
+
},
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention, react/display-name
|
|
56
|
+
PaymentFlow: forwardRef((params, ref) => {
|
|
57
|
+
useImperativeHandle(ref, () => ({
|
|
58
|
+
startLegacyBoxCheckout: mockStartLegacyBoxCheckout,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}),
|
|
63
|
+
PaymentMethod: {
|
|
64
|
+
["GOOGLE_PAY"]: "google_pay",
|
|
65
|
+
},
|
|
66
|
+
Section: {
|
|
67
|
+
["BOX_CHECKOUT"]: "box-checkout",
|
|
68
|
+
},
|
|
69
|
+
PaymentInstrumentSelect: jest.fn(() => <></>),
|
|
70
|
+
};
|
|
71
|
+
});
|
|
57
72
|
|
|
58
73
|
const mockTrackPressContinue = jest.fn();
|
|
59
74
|
jest.mock("../../../tracking/useTrackPressContinue", () => ({
|
|
@@ -72,25 +87,13 @@ jest.mock("react-router-native", () => ({
|
|
|
72
87
|
useNavigate: () => mockUseNavigate,
|
|
73
88
|
}));
|
|
74
89
|
|
|
75
|
-
beforeEach(() => {
|
|
76
|
-
mockCheckoutFlow.mockClear();
|
|
77
|
-
mockTrackPressContinue.mockClear();
|
|
78
|
-
mockTrackPressBack.mockClear();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
90
|
describe("Checkout view", () => {
|
|
82
91
|
it("renders correctly", async () => {
|
|
83
92
|
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
84
93
|
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([pricing(), QueryStatus.SUCCESS]);
|
|
94
|
+
|
|
85
95
|
const { findByText, getAllByTestId, getByText, getByTestId } = render(
|
|
86
|
-
<Checkout
|
|
87
|
-
getAuthToken={getAuthToken}
|
|
88
|
-
layout={DummyLayout}
|
|
89
|
-
order={order}
|
|
90
|
-
subscription={subscription}
|
|
91
|
-
useRedirect={mockUseRedirect}
|
|
92
|
-
onCheckoutFlowSuccess={mockOnSuccess}
|
|
93
|
-
/>,
|
|
96
|
+
<Checkout layout={DummyLayout} useRedirect={mockUseRedirect} />,
|
|
94
97
|
);
|
|
95
98
|
|
|
96
99
|
expect(await findByText(I18nMessages.CHECKOUT_TITLE)).toBeTruthy();
|
|
@@ -111,17 +114,15 @@ describe("Checkout view", () => {
|
|
|
111
114
|
expect(getByText(I18nMessages.SUMMARY_FEE)).toBeTruthy();
|
|
112
115
|
expect(getByText("-€10.00")).toBeTruthy();
|
|
113
116
|
|
|
114
|
-
expect(getByTestId(paymentFlowTestId)).toBeTruthy();
|
|
115
|
-
|
|
116
117
|
expect(getByText(I18nMessages.CHECKOUT_PAY_BUTTON)).toBeTruthy();
|
|
117
118
|
fireEvent.press(getByText(I18nMessages.CHECKOUT_PAY_BUTTON));
|
|
118
119
|
expect(mockTrackPressContinue).toHaveBeenCalled();
|
|
119
|
-
expect(
|
|
120
|
+
expect(mockUseNavigate).toHaveBeenCalledWith(`/${Routes.CHECKOUT}/${Routes.CHECKOUT_PAYMENT}`, { replace: true });
|
|
120
121
|
|
|
121
122
|
expect(getByTestId("checkout-header")).toBeTruthy();
|
|
122
123
|
fireEvent.press(getByTestId("arrow-left-button-icon"));
|
|
123
124
|
expect(mockTrackPressBack).toHaveBeenCalled();
|
|
124
|
-
expect(mockUseNavigate).toHaveBeenCalledWith(
|
|
125
|
+
expect(mockUseNavigate).toHaveBeenCalledWith(`/${Routes.SUMMARY}`);
|
|
125
126
|
});
|
|
126
127
|
|
|
127
128
|
it("does not render a delivery banner", async () => {
|
|
@@ -137,16 +138,7 @@ describe("Checkout view", () => {
|
|
|
137
138
|
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
138
139
|
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([pricing(), QueryStatus.SUCCESS]);
|
|
139
140
|
|
|
140
|
-
const { findByText, queryByText } = render(
|
|
141
|
-
<Checkout
|
|
142
|
-
getAuthToken={getAuthToken}
|
|
143
|
-
layout={DummyLayout}
|
|
144
|
-
order={order}
|
|
145
|
-
subscription={subscription}
|
|
146
|
-
useRedirect={mockUseRedirect}
|
|
147
|
-
onCheckoutFlowSuccess={mockOnSuccess}
|
|
148
|
-
/>,
|
|
149
|
-
);
|
|
141
|
+
const { findByText, queryByText } = render(<Checkout layout={DummyLayout} useRedirect={mockUseRedirect} />);
|
|
150
142
|
|
|
151
143
|
expect(await findByText(I18nMessages.CHECKOUT_TITLE)).toBeTruthy();
|
|
152
144
|
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import React, { FC, useCallback, useMemo, useState } from "react";
|
|
1
|
+
import React, { FC, ReactNode, useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { LayoutRectangle, Platform, ScrollView, View } from "react-native";
|
|
3
3
|
import { useNavigate } from "react-router-native";
|
|
4
4
|
import { Box, Button, Layout as AuroraLayout, Spinner, Text, useDevice } from "@lookiero/aurora";
|
|
5
5
|
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
6
6
|
import { QueryStatus } from "@lookiero/messaging-react";
|
|
7
|
-
import { Country } from "@lookiero/sty-psp-locale";
|
|
8
7
|
import { Layout as UiLayout, Sticky } from "@lookiero/sty-psp-ui";
|
|
9
8
|
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
10
|
-
import { OrderProjection } from "../../../../projection/order/order";
|
|
11
|
-
import { SubscriptionProjection } from "../../../../projection/subscription/subscription";
|
|
12
9
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
13
10
|
import { useViewPricingByCheckoutId } from "../../../projection/pricing/react/useViewPricingByCheckoutId";
|
|
14
11
|
import { TrackingPage } from "../../../tracking/tracking";
|
|
@@ -17,7 +14,6 @@ import { useTrackPressBack } from "../../../tracking/useTrackPressBack";
|
|
|
17
14
|
import { useTrackPressContinue } from "../../../tracking/useTrackPressContinue";
|
|
18
15
|
import { Body } from "../../components/layouts/body/Body";
|
|
19
16
|
import { CheckoutHeader } from "../../components/templates/header/checkoutHeader/CheckoutHeader";
|
|
20
|
-
import { useCheckoutFlow } from "../../hooks/useCheckoutFlow";
|
|
21
17
|
import { useStaticInfo } from "../../hooks/useStaticInfo";
|
|
22
18
|
import { DOMAIN, I18nMessages } from "../../i18n/i18n";
|
|
23
19
|
import { Routes } from "../../routing/routes";
|
|
@@ -28,22 +24,12 @@ import { DeliveryBanner } from "./components/deliveryBanner/DeliveryBanner";
|
|
|
28
24
|
import { PaymentInstrument } from "./components/paymentInstrument/PaymentInstrument";
|
|
29
25
|
|
|
30
26
|
interface CheckoutProps {
|
|
27
|
+
readonly children?: ReactNode;
|
|
31
28
|
readonly layout: UiLayout;
|
|
32
|
-
readonly order: OrderProjection;
|
|
33
|
-
readonly subscription: SubscriptionProjection;
|
|
34
|
-
readonly getAuthToken: () => Promise<string>;
|
|
35
|
-
readonly onCheckoutFlowSuccess: () => void;
|
|
36
29
|
readonly useRedirect: () => Record<string, string>;
|
|
37
30
|
}
|
|
38
31
|
|
|
39
|
-
const Checkout: FC<CheckoutProps> = ({
|
|
40
|
-
layout: Layout,
|
|
41
|
-
order,
|
|
42
|
-
subscription,
|
|
43
|
-
getAuthToken,
|
|
44
|
-
useRedirect,
|
|
45
|
-
onCheckoutFlowSuccess,
|
|
46
|
-
}) => {
|
|
32
|
+
const Checkout: FC<CheckoutProps> = ({ children, layout: Layout, useRedirect }) => {
|
|
47
33
|
const {
|
|
48
34
|
customer: { customerId, country, segment },
|
|
49
35
|
basePath,
|
|
@@ -57,14 +43,6 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
57
43
|
const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
58
44
|
const [pricing, pricingStatus] = useViewPricingByCheckoutId({ checkoutId: checkout?.id as string });
|
|
59
45
|
|
|
60
|
-
const [checkoutFlow, checkoutFlowStatus, paymentFlowComponent] = useCheckoutFlow({
|
|
61
|
-
checkout,
|
|
62
|
-
order,
|
|
63
|
-
subscription,
|
|
64
|
-
getAuthToken,
|
|
65
|
-
onSuccess: onCheckoutFlowSuccess,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
46
|
useTrackPageView({
|
|
69
47
|
page: TrackingPage.CHECKOUT,
|
|
70
48
|
country,
|
|
@@ -73,16 +51,17 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
73
51
|
});
|
|
74
52
|
|
|
75
53
|
const navigate = useNavigate();
|
|
54
|
+
|
|
76
55
|
const trackPressContinue = useTrackPressContinue({
|
|
77
56
|
page: TrackingPage.CHECKOUT,
|
|
78
57
|
country,
|
|
79
58
|
segment,
|
|
80
59
|
checkoutId: checkout?.id,
|
|
81
60
|
});
|
|
82
|
-
const handleOnSubmit = useCallback(
|
|
61
|
+
const handleOnSubmit = useCallback(() => {
|
|
83
62
|
trackPressContinue();
|
|
84
|
-
|
|
85
|
-
}, [
|
|
63
|
+
navigate(`${basePath}/${Routes.CHECKOUT}/${Routes.CHECKOUT_PAYMENT}`, { replace: true });
|
|
64
|
+
}, [basePath, navigate, trackPressContinue]);
|
|
86
65
|
|
|
87
66
|
const checkoutItemsKept = useMemo(
|
|
88
67
|
() =>
|
|
@@ -136,12 +115,6 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
136
115
|
>
|
|
137
116
|
<Box size={{ L: "2/3" }} style={screen.L && style.desktopListSpacing}>
|
|
138
117
|
<View style={[style.contentWrapper, screen.L && style.desktopContentWrapper]}>
|
|
139
|
-
{country === Country.NL && (
|
|
140
|
-
<View style={style.paymentSelectorNL}>
|
|
141
|
-
<PaymentInstrument useRedirect={useRedirect} />
|
|
142
|
-
</View>
|
|
143
|
-
)}
|
|
144
|
-
|
|
145
118
|
<Text level={3} style={style.title} heading>
|
|
146
119
|
{titleText}
|
|
147
120
|
</Text>
|
|
@@ -165,11 +138,9 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
165
138
|
</View>
|
|
166
139
|
))}
|
|
167
140
|
|
|
168
|
-
{
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
</View>
|
|
172
|
-
)}
|
|
141
|
+
<View style={style.paymentSelector}>
|
|
142
|
+
<PaymentInstrument useRedirect={useRedirect} />
|
|
143
|
+
</View>
|
|
173
144
|
</View>
|
|
174
145
|
</Box>
|
|
175
146
|
|
|
@@ -179,11 +150,7 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
179
150
|
<Pricing pricing={pricing} totalCheckoutItemsKept={checkoutItemsKept?.length || 0} />
|
|
180
151
|
|
|
181
152
|
{screen.L ? (
|
|
182
|
-
<Button
|
|
183
|
-
busy={checkoutFlowStatus === "loading"}
|
|
184
|
-
testID="confirm-checkout-button"
|
|
185
|
-
onPress={handleOnSubmit}
|
|
186
|
-
>
|
|
153
|
+
<Button testID="confirm-checkout-button" onPress={handleOnSubmit}>
|
|
187
154
|
{submitButtonText}
|
|
188
155
|
</Button>
|
|
189
156
|
) : null}
|
|
@@ -196,19 +163,14 @@ const Checkout: FC<CheckoutProps> = ({
|
|
|
196
163
|
{pricing && !screen.L ? (
|
|
197
164
|
<Sticky style={style.sticky} onLayout={Platform.OS !== "web" ? handleOnPricingLayout : undefined}>
|
|
198
165
|
<Body>
|
|
199
|
-
<Button
|
|
200
|
-
busy={checkoutFlowStatus === "loading"}
|
|
201
|
-
testID="confirm-checkout-button"
|
|
202
|
-
small
|
|
203
|
-
onPress={handleOnSubmit}
|
|
204
|
-
>
|
|
166
|
+
<Button testID="confirm-checkout-button" small onPress={handleOnSubmit}>
|
|
205
167
|
{submitButtonText}
|
|
206
168
|
</Button>
|
|
207
169
|
</Body>
|
|
208
170
|
</Sticky>
|
|
209
171
|
) : null}
|
|
210
172
|
|
|
211
|
-
{
|
|
173
|
+
{children}
|
|
212
174
|
</Layout>
|
|
213
175
|
);
|
|
214
176
|
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { waitFor } from "@testing-library/react-native";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { QueryStatus } from "@lookiero/messaging-react";
|
|
4
|
+
import { Country } from "@lookiero/sty-psp-locale";
|
|
5
|
+
import { Segment } from "@lookiero/sty-psp-segment";
|
|
6
|
+
import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
|
|
7
|
+
import { checkout } from "../../../../../projection/checkout/checkout.mock";
|
|
8
|
+
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
9
|
+
import { useViewIsSizeChangeEnabledByCheckoutId } from "../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId";
|
|
10
|
+
import { paymentFlowPayload as mockPaymentFlowPayload } from "../../../../../projection/payment/paymentFlowPayload.mock";
|
|
11
|
+
import { useViewPaymentFlowPayloadByCheckoutId } from "../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId";
|
|
12
|
+
import { pricing } from "../../../../../projection/pricing/pricing.mock";
|
|
13
|
+
import { useViewPricingByCheckoutId } from "../../../../../projection/pricing/react/useViewPricingByCheckoutId";
|
|
14
|
+
import { useSubmitCheckout } from "../../../../hooks/useSubmitCheckout";
|
|
15
|
+
import { Routes } from "../../../../routing/routes";
|
|
16
|
+
import { render } from "../../../../test/render";
|
|
17
|
+
import { CheckoutPaymentModal } from "./CheckoutPaymentModal";
|
|
18
|
+
|
|
19
|
+
const customerId = "a8fff6d7-708c-41a7-b42a-58c5706d33df";
|
|
20
|
+
const country = Country.ES;
|
|
21
|
+
const segment = Segment.WOMEN;
|
|
22
|
+
const orderNumber = 3691625;
|
|
23
|
+
const isFirstOrder = false;
|
|
24
|
+
const getAuthToken = () => Promise.resolve("token");
|
|
25
|
+
const mockCheckout = checkout({
|
|
26
|
+
items: [
|
|
27
|
+
{ status: CheckoutItemStatus.RETURNED },
|
|
28
|
+
{ status: CheckoutItemStatus.KEPT },
|
|
29
|
+
{ status: CheckoutItemStatus.KEPT },
|
|
30
|
+
{ status: CheckoutItemStatus.KEPT },
|
|
31
|
+
{ status: CheckoutItemStatus.REPLACED },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
const mockPricing = pricing();
|
|
35
|
+
|
|
36
|
+
jest.mock("../../../../hooks/useStaticInfo", () => ({
|
|
37
|
+
useStaticInfo: () => ({
|
|
38
|
+
customer: { customerId, country, segment },
|
|
39
|
+
basePath: "",
|
|
40
|
+
}),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
jest.mock("../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId");
|
|
44
|
+
jest.mock("../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId");
|
|
45
|
+
jest.mock("../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId");
|
|
46
|
+
jest.mock("../../../../../projection/pricing/react/useViewPricingByCheckoutId");
|
|
47
|
+
jest.mock("../../../../hooks/useSubmitCheckout");
|
|
48
|
+
jest.mock("../../../../../tracking/useTrackCheckout");
|
|
49
|
+
|
|
50
|
+
const mockStartLegacyBoxCheckout = jest.fn();
|
|
51
|
+
jest.mock("@lookiero/payments-front", () => {
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
53
|
+
const { useImperativeHandle, forwardRef } = require("react");
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
CheckoutStatus: {
|
|
57
|
+
REJECTED: "REJECTED",
|
|
58
|
+
ERROR: "ERROR",
|
|
59
|
+
FULFILLED: "FULFILLED",
|
|
60
|
+
},
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention, react/display-name
|
|
64
|
+
PaymentFlow: forwardRef((params, ref) => {
|
|
65
|
+
useImperativeHandle(ref, () => ({
|
|
66
|
+
startLegacyBoxCheckout: mockStartLegacyBoxCheckout,
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
}),
|
|
71
|
+
PaymentMethod: {
|
|
72
|
+
["GOOGLE_PAY"]: "google_pay",
|
|
73
|
+
},
|
|
74
|
+
Section: {
|
|
75
|
+
["BOX_CHECKOUT"]: "box-checkout",
|
|
76
|
+
},
|
|
77
|
+
PaymentInstrumentSelect: jest.fn(() => <></>),
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const mockUseNavigate = jest.fn();
|
|
82
|
+
jest.mock("react-router-native", () => ({
|
|
83
|
+
...jest.requireActual("react-router-native"),
|
|
84
|
+
useNavigate: () => mockUseNavigate,
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
describe("CheckoutPaymentModal component", () => {
|
|
88
|
+
it("renders correctly", async () => {
|
|
89
|
+
const mockSubmitCheckout = jest.fn();
|
|
90
|
+
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
91
|
+
(useViewPaymentFlowPayloadByCheckoutId as jest.Mock).mockReturnValue([mockPaymentFlowPayload, QueryStatus.SUCCESS]);
|
|
92
|
+
(useViewIsSizeChangeEnabledByCheckoutId as jest.Mock).mockReturnValue([true, QueryStatus.SUCCESS]);
|
|
93
|
+
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([mockPricing, QueryStatus.SUCCESS]);
|
|
94
|
+
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, "success"]);
|
|
95
|
+
|
|
96
|
+
await waitFor(() => {
|
|
97
|
+
render(
|
|
98
|
+
<CheckoutPaymentModal
|
|
99
|
+
coupon={null}
|
|
100
|
+
getAuthToken={getAuthToken}
|
|
101
|
+
isFirstOrder={isFirstOrder}
|
|
102
|
+
orderNumber={orderNumber}
|
|
103
|
+
subscription="b"
|
|
104
|
+
/>,
|
|
105
|
+
);
|
|
106
|
+
expect(mockSubmitCheckout).toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("navigates back to /checkout when checkout-flow fails", async () => {
|
|
111
|
+
(useViewFirstAvailableCheckoutByCustomerId as jest.Mock).mockReturnValue([mockCheckout, QueryStatus.SUCCESS]);
|
|
112
|
+
(useViewPaymentFlowPayloadByCheckoutId as jest.Mock).mockReturnValue([mockPaymentFlowPayload, QueryStatus.SUCCESS]);
|
|
113
|
+
(useViewIsSizeChangeEnabledByCheckoutId as jest.Mock).mockReturnValue([true, QueryStatus.SUCCESS]);
|
|
114
|
+
(useViewPricingByCheckoutId as jest.Mock).mockReturnValue([mockPricing, QueryStatus.SUCCESS]);
|
|
115
|
+
(useSubmitCheckout as jest.Mock).mockImplementation(({ onError }) => {
|
|
116
|
+
const submitCheckout = () => onError();
|
|
117
|
+
|
|
118
|
+
return [submitCheckout, "error"];
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
render(
|
|
123
|
+
<CheckoutPaymentModal
|
|
124
|
+
coupon={null}
|
|
125
|
+
getAuthToken={getAuthToken}
|
|
126
|
+
isFirstOrder={isFirstOrder}
|
|
127
|
+
orderNumber={orderNumber}
|
|
128
|
+
subscription="b"
|
|
129
|
+
/>,
|
|
130
|
+
);
|
|
131
|
+
expect(mockUseNavigate).toHaveBeenCalledWith(`/${Routes.CHECKOUT}`, { replace: true });
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React, { FC, useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useNavigate } from "react-router-native";
|
|
3
|
+
import { QueryStatus } from "@lookiero/messaging-react";
|
|
4
|
+
import { PaymentFlow, PaymentFlowRef } from "@lookiero/payments-front";
|
|
5
|
+
import { useLogger } from "@lookiero/sty-psp-logging";
|
|
6
|
+
import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
|
|
7
|
+
import { Currency } from "../../../../../../domain/checkoutItem/model/currency";
|
|
8
|
+
import { Subscription } from "../../../../../../projection/subscription/subscription";
|
|
9
|
+
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
10
|
+
import { useViewIsSizeChangeEnabledByCheckoutId } from "../../../../../projection/checkout/react/useViewIsSizeChangeEnabledByCheckoutId";
|
|
11
|
+
import { useViewPaymentFlowPayloadByCheckoutId } from "../../../../../projection/payment/react/useViewPaymentFlowPayloadByCheckoutId";
|
|
12
|
+
import { useViewPricingByCheckoutId } from "../../../../../projection/pricing/react/useViewPricingByCheckoutId";
|
|
13
|
+
import { TrackingPage } from "../../../../../tracking/tracking";
|
|
14
|
+
import { useTrackCheckout } from "../../../../../tracking/useTrackCheckout";
|
|
15
|
+
import { useStaticInfo } from "../../../../hooks/useStaticInfo";
|
|
16
|
+
import { useSubmitCheckout } from "../../../../hooks/useSubmitCheckout";
|
|
17
|
+
import { Routes } from "../../../../routing/routes";
|
|
18
|
+
|
|
19
|
+
interface CheckoutPaymentModalProps {
|
|
20
|
+
readonly coupon: string | null;
|
|
21
|
+
readonly orderNumber: number;
|
|
22
|
+
readonly isFirstOrder: boolean;
|
|
23
|
+
readonly subscription: Subscription;
|
|
24
|
+
readonly getAuthToken: () => Promise<string>;
|
|
25
|
+
readonly onCheckoutSubmitted?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const CheckoutPaymentModal: FC<CheckoutPaymentModalProps> = ({
|
|
29
|
+
coupon,
|
|
30
|
+
isFirstOrder,
|
|
31
|
+
subscription,
|
|
32
|
+
orderNumber,
|
|
33
|
+
getAuthToken,
|
|
34
|
+
onCheckoutSubmitted,
|
|
35
|
+
}) => {
|
|
36
|
+
const paymentFlowRef = useRef<PaymentFlowRef>(null);
|
|
37
|
+
const logger = useLogger();
|
|
38
|
+
const {
|
|
39
|
+
customer: { customerId, country, segment },
|
|
40
|
+
basePath,
|
|
41
|
+
} = useStaticInfo();
|
|
42
|
+
|
|
43
|
+
const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
44
|
+
const [paymentFlowPayload] = useViewPaymentFlowPayloadByCheckoutId({
|
|
45
|
+
checkoutId: checkout?.id as string,
|
|
46
|
+
});
|
|
47
|
+
const [sizeChangeEnabled] = useViewIsSizeChangeEnabledByCheckoutId({ checkoutId: checkout?.id as string });
|
|
48
|
+
const [pricing] = useViewPricingByCheckoutId({
|
|
49
|
+
checkoutId: checkout?.id as string,
|
|
50
|
+
queryOptions: { refetchOnMount: true },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const [authToken, setAuthToken] = useState<string>();
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const loadAuthToken = async () => setAuthToken(await getAuthToken());
|
|
56
|
+
loadAuthToken();
|
|
57
|
+
}, [getAuthToken]);
|
|
58
|
+
|
|
59
|
+
const navigate = useNavigate();
|
|
60
|
+
const trackCheckout = useTrackCheckout({
|
|
61
|
+
page: TrackingPage.CHECKOUT,
|
|
62
|
+
checkoutId: checkout?.id as string,
|
|
63
|
+
country,
|
|
64
|
+
segment,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const handleOnSubmitCheckoutError = useCallback(
|
|
68
|
+
() => navigate(`${basePath}/${Routes.CHECKOUT}`, { replace: true }),
|
|
69
|
+
[basePath, navigate],
|
|
70
|
+
);
|
|
71
|
+
const handleOnSubmitCheckoutSuccess = useCallback(() => {
|
|
72
|
+
const checkoutItemsKept = checkout?.items.filter(
|
|
73
|
+
(checkoutItem) =>
|
|
74
|
+
checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED,
|
|
75
|
+
);
|
|
76
|
+
const totalReplacedFor = checkoutItemsKept?.filter(({ replacedFor }) => Boolean(replacedFor)).length || 0;
|
|
77
|
+
|
|
78
|
+
trackCheckout({
|
|
79
|
+
totalCheckoutItemsKept: checkoutItemsKept?.length || 0,
|
|
80
|
+
pendingToPay: (pricing?.pendingToPay.amount as number) / 100,
|
|
81
|
+
coupon,
|
|
82
|
+
currencyCode: pricing?.pendingToPay.currency as Currency,
|
|
83
|
+
isFirstOrder,
|
|
84
|
+
orderNumber,
|
|
85
|
+
totalReplacedFor,
|
|
86
|
+
subscription,
|
|
87
|
+
userId: customerId,
|
|
88
|
+
});
|
|
89
|
+
onCheckoutSubmitted?.();
|
|
90
|
+
}, [
|
|
91
|
+
checkout?.items,
|
|
92
|
+
coupon,
|
|
93
|
+
customerId,
|
|
94
|
+
isFirstOrder,
|
|
95
|
+
onCheckoutSubmitted,
|
|
96
|
+
orderNumber,
|
|
97
|
+
pricing?.pendingToPay.amount,
|
|
98
|
+
pricing?.pendingToPay.currency,
|
|
99
|
+
subscription,
|
|
100
|
+
trackCheckout,
|
|
101
|
+
]);
|
|
102
|
+
const [submitCheckout] = useSubmitCheckout({
|
|
103
|
+
checkoutId: checkout?.id as string,
|
|
104
|
+
checkoutBookingId: checkout?.checkoutBookingId as string,
|
|
105
|
+
paymentFlowRef,
|
|
106
|
+
onError: handleOnSubmitCheckoutError,
|
|
107
|
+
onSuccess: handleOnSubmitCheckoutSuccess,
|
|
108
|
+
logger,
|
|
109
|
+
});
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (paymentFlowPayload && sizeChangeEnabled !== undefined && pricing !== undefined) {
|
|
112
|
+
submitCheckout({ paymentFlowPayload, sizeChangeEnabled });
|
|
113
|
+
}
|
|
114
|
+
}, [paymentFlowPayload, pricing, sizeChangeEnabled, submitCheckout]);
|
|
115
|
+
|
|
116
|
+
const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
|
|
117
|
+
const dependenciesLoaded = (dependenciesLoadedStatuses.includes(checkoutStatus) || checkout) && authToken;
|
|
118
|
+
|
|
119
|
+
if (!dependenciesLoaded) return null;
|
|
120
|
+
|
|
121
|
+
return <PaymentFlow ref={paymentFlowRef} token={authToken} />;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export { CheckoutPaymentModal };
|
package/src/infrastructure/ui/views/checkout/components/paymentInstrument/PaymentInstrument.tsx
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import React, { FC } from "react";
|
|
1
|
+
import React, { FC, useRef } from "react";
|
|
2
2
|
import { PaymentInstrumentSelect, Section } from "@lookiero/payments-front";
|
|
3
|
-
import {
|
|
3
|
+
import { useLogger } from "@lookiero/sty-psp-logging";
|
|
4
|
+
import { usePaymentInstrumentEvents } from "../../../../hooks/usePaymentInstrumentEvents";
|
|
4
5
|
|
|
5
6
|
interface PaymentInstrumentProps {
|
|
6
7
|
readonly useRedirect: () => Record<string, string>;
|
|
7
8
|
}
|
|
8
9
|
const PaymentInstrument: FC<PaymentInstrumentProps> = ({ useRedirect }) => {
|
|
10
|
+
const paymentInstrumentSelectRef = useRef(null);
|
|
9
11
|
const { returnUrl } = useRedirect();
|
|
10
|
-
const
|
|
12
|
+
const logger = useLogger();
|
|
13
|
+
|
|
14
|
+
usePaymentInstrumentEvents({ logger });
|
|
11
15
|
|
|
12
16
|
return (
|
|
13
17
|
<PaymentInstrumentSelect
|
|
18
|
+
ref={paymentInstrumentSelectRef}
|
|
14
19
|
beforeRedirect={returnUrl ? () => Promise.resolve(returnUrl) : undefined}
|
|
15
20
|
hasError={false}
|
|
16
21
|
section={Section.BOX_CHECKOUT}
|
|
17
|
-
userInformation={{
|
|
18
|
-
email: customer.email,
|
|
19
|
-
name: customer.name,
|
|
20
|
-
}}
|
|
21
|
-
showSingleUsePaymentMethods
|
|
22
22
|
/>
|
|
23
23
|
);
|
|
24
24
|
};
|
|
@@ -52,6 +52,12 @@ jest.mock("../../../../../tracking/useTrackReturnItem", () => ({
|
|
|
52
52
|
useTrackReturnItem: () => mockReturnItem,
|
|
53
53
|
}));
|
|
54
54
|
|
|
55
|
+
jest.mock("../../../../hooks/useStaticInfo", () => ({
|
|
56
|
+
useStaticInfo: () => ({
|
|
57
|
+
basePath: "",
|
|
58
|
+
}),
|
|
59
|
+
}));
|
|
60
|
+
|
|
55
61
|
describe("Return", () => {
|
|
56
62
|
test("renders correctly", async () => {
|
|
57
63
|
(useListReturnQuestionsByCheckoutItemId as jest.Mock).mockReturnValue([mockReturnQuestions, QueryStatus.SUCCESS]);
|
|
@@ -29,6 +29,7 @@ interface SummaryProps {
|
|
|
29
29
|
const Summary: FC<SummaryProps> = ({ layout: Layout, children }) => {
|
|
30
30
|
const {
|
|
31
31
|
customer: { customerId, country, segment },
|
|
32
|
+
basePath,
|
|
32
33
|
} = useStaticInfo();
|
|
33
34
|
const [pricingCollapsed, setPricingCollapsed] = useState(true);
|
|
34
35
|
const [pricingHeight, setPricingHeight] = useState(0);
|
|
@@ -42,7 +43,6 @@ const Summary: FC<SummaryProps> = ({ layout: Layout, children }) => {
|
|
|
42
43
|
const [fiveItemsDiscount = 0, fiveItemsDiscountStatus] = useViewFiveItemsDiscountByCustomerId({ customerId });
|
|
43
44
|
|
|
44
45
|
const navigate = useNavigate();
|
|
45
|
-
const { basePath } = useStaticInfo();
|
|
46
46
|
|
|
47
47
|
const match = useMatch(`${basePath}/${Routes.SUMMARY}/${Routes.SUMMARY_TABS}`);
|
|
48
48
|
const tab = match?.params.tab;
|
|
@@ -21,7 +21,10 @@ const checkoutMock = checkout({
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
jest.mock("../../hooks/useStaticInfo", () => ({
|
|
24
|
-
useStaticInfo: () => ({
|
|
24
|
+
useStaticInfo: () => ({
|
|
25
|
+
customer: { customerId, country, segment },
|
|
26
|
+
basePath: "",
|
|
27
|
+
}),
|
|
25
28
|
}));
|
|
26
29
|
|
|
27
30
|
const mockUseNavigate = jest.fn();
|
|
Binary file
|