@lookiero/checkout 9.8.2 → 9.8.3
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/src/ExpoRoot.js +3 -3
- package/dist/src/infrastructure/ui/Root.d.ts +2 -0
- package/dist/src/infrastructure/ui/Root.js +4 -2
- package/dist/src/infrastructure/ui/hooks/useQueryBus.d.ts +9 -0
- package/dist/src/infrastructure/ui/hooks/useQueryBus.js +10 -0
- package/dist/src/infrastructure/ui/hooks/useSubmitCheckout.js +27 -3
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
- package/src/ExpoRoot.tsx +3 -2
- package/src/infrastructure/ui/Root.tsx +22 -16
- package/src/infrastructure/ui/hooks/useQueryBus.test.tsx +23 -0
- package/src/infrastructure/ui/hooks/useQueryBus.tsx +27 -0
- package/src/infrastructure/ui/hooks/useSubmitCheckout.test.ts +58 -0
- package/src/infrastructure/ui/hooks/useSubmitCheckout.ts +35 -3
package/dist/src/ExpoRoot.js
CHANGED
|
@@ -24,7 +24,7 @@ const order = {
|
|
|
24
24
|
coupon: "MYLOOKIERO",
|
|
25
25
|
};
|
|
26
26
|
const customer = {
|
|
27
|
-
customerId: "
|
|
27
|
+
customerId: "d3c2354b-9811-46d4-b2ba-5fba012ed94d",
|
|
28
28
|
country: Country.ES,
|
|
29
29
|
segment: Segment.WOMEN,
|
|
30
30
|
};
|
|
@@ -79,7 +79,7 @@ const kameleoonConfig = {
|
|
|
79
79
|
siteCode: "aplm4v3ckn",
|
|
80
80
|
experiments: {},
|
|
81
81
|
};
|
|
82
|
-
const { Component: Messaging } = process.env.EXPO_PUBLIC_APP_VARIANT === "test"
|
|
82
|
+
const { Component: Messaging, queryBus } = process.env.EXPO_PUBLIC_APP_VARIANT === "test"
|
|
83
83
|
? checkoutMockBootstrap()
|
|
84
84
|
: checkoutBootstrap({ apiUrl: () => apiUrl, getAuthToken });
|
|
85
85
|
const I18n = i18n({
|
|
@@ -109,7 +109,7 @@ const ExpoRoot = () => {
|
|
|
109
109
|
isAccessible === false && React.createElement(Text, { heading: true }, "Checkout is not accessible!"),
|
|
110
110
|
React.createElement(Router, null,
|
|
111
111
|
React.createElement(Routes, null,
|
|
112
|
-
React.createElement(Route, { path: "/checkout/*", element: React.createElement(Root, { basePath: "/checkout", customer: customer, layout: DummyLayout, locale: locale, order: order, subscription: subscription, useRedirect: useRedirect, onNotAccessible: onNotAccessible }) }),
|
|
112
|
+
React.createElement(Route, { path: "/checkout/*", element: React.createElement(Root, { basePath: "/checkout", customer: customer, layout: DummyLayout, locale: locale, order: order, queryBus: queryBus, subscription: subscription, useRedirect: useRedirect, onNotAccessible: onNotAccessible }) }),
|
|
113
113
|
React.createElement(Route, { element: React.createElement(Navigate, { to: "/checkout", replace: true }), path: "*" }))))))) : null;
|
|
114
114
|
};
|
|
115
115
|
export { ExpoRoot };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ComponentType } from "react";
|
|
2
2
|
import { useRoutes as reactRouterUseRoutes } from "react-router-native";
|
|
3
3
|
import { I18n } from "@lookiero/i18n-react";
|
|
4
|
+
import { QueryBus } from "@lookiero/messaging";
|
|
4
5
|
import { MessagingRoot } from "@lookiero/messaging-react/bootstrap";
|
|
5
6
|
import { Locale } from "@lookiero/sty-psp-locale";
|
|
6
7
|
import { SentryEnvironment, SentryLoggerFunctionArgs } from "@lookiero/sty-psp-logging";
|
|
@@ -27,6 +28,7 @@ interface RootProps {
|
|
|
27
28
|
readonly order: Order | undefined;
|
|
28
29
|
readonly subscription: Subscription | undefined;
|
|
29
30
|
readonly layout: Layout;
|
|
31
|
+
readonly queryBus: QueryBus;
|
|
30
32
|
readonly onNotAccessible: () => void;
|
|
31
33
|
readonly onCheckoutSubmitted?: () => void;
|
|
32
34
|
readonly useRedirect: () => Record<string, string>;
|
|
@@ -4,15 +4,17 @@ import { Platform } from "react-native";
|
|
|
4
4
|
import { useRoutes as reactRouterUseRoutes } from "react-router-native";
|
|
5
5
|
import { Locale } from "@lookiero/sty-psp-locale";
|
|
6
6
|
import { sentryLogger, sentryLoggerHOC } from "@lookiero/sty-psp-logging";
|
|
7
|
+
import { QueryBusProvider } from "./hooks/useQueryBus";
|
|
7
8
|
import { Routing } from "./routing/Routing";
|
|
8
9
|
const root = ({ Messaging, I18n, getAuthToken, development, sentry, kameleoon: kameleoonConfig }) => {
|
|
9
10
|
const logger = sentryLogger(sentry);
|
|
10
11
|
const kameleoon = kameleoonConfig();
|
|
11
12
|
// eslint-disable-next-line react/display-name, react/prop-types
|
|
12
|
-
const Root = ({ basePath, locale = Locale.en_GB, customer, order, subscription, layout, onNotAccessible, onCheckoutSubmitted, useRedirect, useRoutes = reactRouterUseRoutes, }) => {
|
|
13
|
+
const Root = ({ basePath, locale = Locale.en_GB, customer, order, subscription, layout, queryBus, onNotAccessible, onCheckoutSubmitted, useRedirect, useRoutes = reactRouterUseRoutes, }) => {
|
|
13
14
|
const handleOnI18nError = useCallback((error) => logger.captureException(error), []);
|
|
14
15
|
return (React.createElement(Messaging, { includeReactQueryDevTools: Platform.OS === "web" },
|
|
15
|
-
React.createElement(
|
|
16
|
+
React.createElement(QueryBusProvider, { queryBus: queryBus },
|
|
17
|
+
React.createElement(Routing, { I18n: I18n, basePath: basePath, customer: customer, getAuthToken: getAuthToken, kameleoon: kameleoon, layout: layout, locale: locale, order: order, subscription: subscription, useRedirect: useRedirect, useRoutes: useRoutes, onCheckoutSubmitted: onCheckoutSubmitted, onI18nError: development ? undefined : handleOnI18nError, onNotAccessible: onNotAccessible }))));
|
|
16
18
|
};
|
|
17
19
|
const hoc = sentryLoggerHOC({ logger });
|
|
18
20
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
|
+
import { QueryBus } from "@lookiero/messaging";
|
|
3
|
+
interface QueryBusProviderProps {
|
|
4
|
+
readonly children: ReactNode;
|
|
5
|
+
readonly queryBus: QueryBus;
|
|
6
|
+
}
|
|
7
|
+
declare const QueryBusProvider: FC<QueryBusProviderProps>;
|
|
8
|
+
declare const useQueryBus: () => QueryBus;
|
|
9
|
+
export { useQueryBus, QueryBusProvider };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React, { createContext, useContext } from "react";
|
|
2
|
+
import invariant from "tiny-invariant";
|
|
3
|
+
const QueryBusContext = createContext(null);
|
|
4
|
+
const QueryBusProvider = ({ children, queryBus }) => (React.createElement(QueryBusContext.Provider, { value: queryBus }, children));
|
|
5
|
+
const useQueryBus = () => {
|
|
6
|
+
const queryBus = useContext(QueryBusContext);
|
|
7
|
+
invariant(queryBus, "Your are trying to use the useQueryBus hook without wrapping your app with the <QueryBusProvider>.");
|
|
8
|
+
return queryBus;
|
|
9
|
+
};
|
|
10
|
+
export { useQueryBus, QueryBusProvider };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { CommandStatus } from "@lookiero/messaging-react";
|
|
3
3
|
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
4
|
+
import { viewCheckoutBookingById, } from "../../../projection/checkoutBooking/viewCheckoutBookingById";
|
|
4
5
|
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
5
6
|
import { useSubmitCheckout as useSubmitCheckoutCommand } from "../../domain/checkout/react/useSubmitCheckout";
|
|
6
7
|
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
7
8
|
import { I18nMessages } from "../i18n/i18n";
|
|
9
|
+
import { useQueryBus } from "./useQueryBus";
|
|
8
10
|
var ChargeStatus;
|
|
9
11
|
(function (ChargeStatus) {
|
|
10
12
|
ChargeStatus["EXECUTED"] = "EXECUTED";
|
|
@@ -17,9 +19,11 @@ var ChargeStatus;
|
|
|
17
19
|
ChargeStatus["UNKNOWN"] = "UNKNOWN";
|
|
18
20
|
})(ChargeStatus || (ChargeStatus = {}));
|
|
19
21
|
const useSubmitCheckout = ({ checkoutId, checkoutBookingId, paymentFlowRef, onError, onSuccess, logger, }) => {
|
|
22
|
+
const queryBus = useQueryBus();
|
|
20
23
|
const [submitCheckoutCommand, submitCheckoutCommandStatus] = useSubmitCheckoutCommand({ checkoutId, logger });
|
|
21
24
|
const [blockCheckoutBooking, blockCheckoutBookingStatus] = useBlockCheckoutBooking({ checkoutBookingId, logger });
|
|
22
25
|
const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
|
|
26
|
+
const [checkoutBookingExpired, setCheckoutBookingExpired] = useState(false);
|
|
23
27
|
const [startLegacyBoxCheckoutStatus, setStartLegacyBoxCheckoutStatus] = useState("idle");
|
|
24
28
|
const submitCheckout = useCallback(async ({ paymentFlowPayload, sizeChangeEnabled }) => {
|
|
25
29
|
try {
|
|
@@ -28,6 +32,11 @@ const useSubmitCheckout = ({ checkoutId, checkoutBookingId, paymentFlowRef, onEr
|
|
|
28
32
|
catch (error) {
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
35
|
+
const checkoutBooking = await queryBus(viewCheckoutBookingById({ checkoutBookingId }));
|
|
36
|
+
if (checkoutBooking?.isExpired) {
|
|
37
|
+
setCheckoutBookingExpired(true);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
31
40
|
paymentFlowRef.current?.startLegacyBoxCheckout(
|
|
32
41
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
33
42
|
// @ts-ignore
|
|
@@ -48,7 +57,15 @@ const useSubmitCheckout = ({ checkoutId, checkoutBookingId, paymentFlowRef, onEr
|
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
59
|
});
|
|
51
|
-
}, [
|
|
60
|
+
}, [
|
|
61
|
+
queryBus,
|
|
62
|
+
checkoutBookingId,
|
|
63
|
+
paymentFlowRef,
|
|
64
|
+
blockCheckoutBooking,
|
|
65
|
+
submitCheckoutCommand,
|
|
66
|
+
onSuccess,
|
|
67
|
+
createNotification,
|
|
68
|
+
]);
|
|
52
69
|
const status = useMemo(() => {
|
|
53
70
|
if (blockCheckoutBookingStatus === CommandStatus.LOADING ||
|
|
54
71
|
startLegacyBoxCheckoutStatus === "loading" ||
|
|
@@ -62,12 +79,19 @@ const useSubmitCheckout = ({ checkoutId, checkoutBookingId, paymentFlowRef, onEr
|
|
|
62
79
|
}
|
|
63
80
|
if (blockCheckoutBookingStatus === CommandStatus.ERROR ||
|
|
64
81
|
startLegacyBoxCheckoutStatus === "error" ||
|
|
65
|
-
submitCheckoutCommandStatus === CommandStatus.ERROR
|
|
82
|
+
submitCheckoutCommandStatus === CommandStatus.ERROR ||
|
|
83
|
+
checkoutBookingExpired) {
|
|
66
84
|
onError();
|
|
67
85
|
return "error";
|
|
68
86
|
}
|
|
69
87
|
return "idle";
|
|
70
|
-
}, [
|
|
88
|
+
}, [
|
|
89
|
+
blockCheckoutBookingStatus,
|
|
90
|
+
startLegacyBoxCheckoutStatus,
|
|
91
|
+
submitCheckoutCommandStatus,
|
|
92
|
+
checkoutBookingExpired,
|
|
93
|
+
onError,
|
|
94
|
+
]);
|
|
71
95
|
return [submitCheckout, status];
|
|
72
96
|
};
|
|
73
97
|
export { useSubmitCheckout };
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "9.8.
|
|
1
|
+
export declare const VERSION = "9.8.3";
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "9.8.
|
|
1
|
+
export const VERSION = "9.8.3";
|
package/package.json
CHANGED
package/src/ExpoRoot.tsx
CHANGED
|
@@ -32,7 +32,7 @@ const order: Order = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const customer: Customer = {
|
|
35
|
-
customerId: "
|
|
35
|
+
customerId: "d3c2354b-9811-46d4-b2ba-5fba012ed94d",
|
|
36
36
|
country: Country.ES,
|
|
37
37
|
segment: Segment.WOMEN,
|
|
38
38
|
};
|
|
@@ -99,7 +99,7 @@ const kameleoonConfig: KameleoonEnvironment = {
|
|
|
99
99
|
experiments: {},
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
const { Component: Messaging } =
|
|
102
|
+
const { Component: Messaging, queryBus } =
|
|
103
103
|
process.env.EXPO_PUBLIC_APP_VARIANT === "test"
|
|
104
104
|
? checkoutMockBootstrap()
|
|
105
105
|
: checkoutBootstrap({ apiUrl: () => apiUrl, getAuthToken });
|
|
@@ -144,6 +144,7 @@ const ExpoRoot: FC = () => {
|
|
|
144
144
|
layout={DummyLayout}
|
|
145
145
|
locale={locale}
|
|
146
146
|
order={order}
|
|
147
|
+
queryBus={queryBus}
|
|
147
148
|
subscription={subscription}
|
|
148
149
|
useRedirect={useRedirect}
|
|
149
150
|
onNotAccessible={onNotAccessible}
|
|
@@ -3,6 +3,7 @@ import React, { ComponentType, useCallback } from "react";
|
|
|
3
3
|
import { Platform } from "react-native";
|
|
4
4
|
import { useRoutes as reactRouterUseRoutes } from "react-router-native";
|
|
5
5
|
import { I18n } from "@lookiero/i18n-react";
|
|
6
|
+
import { QueryBus } from "@lookiero/messaging";
|
|
6
7
|
import { MessagingRoot } from "@lookiero/messaging-react/bootstrap";
|
|
7
8
|
import { Locale } from "@lookiero/sty-psp-locale";
|
|
8
9
|
import { SentryEnvironment, SentryLoggerFunctionArgs, sentryLogger, sentryLoggerHOC } from "@lookiero/sty-psp-logging";
|
|
@@ -11,6 +12,7 @@ import { Customer } from "../../projection/customer/customer";
|
|
|
11
12
|
import { Order } from "../../projection/order/order";
|
|
12
13
|
import { Subscription } from "../../projection/subscription/subscription";
|
|
13
14
|
import { KameleoonEnvironment } from "../ab-testing/kameleoonEnvironment";
|
|
15
|
+
import { QueryBusProvider } from "./hooks/useQueryBus";
|
|
14
16
|
import { Routing } from "./routing/Routing";
|
|
15
17
|
|
|
16
18
|
interface RootFunctionArgs {
|
|
@@ -33,6 +35,7 @@ interface RootProps {
|
|
|
33
35
|
readonly order: Order | undefined;
|
|
34
36
|
readonly subscription: Subscription | undefined;
|
|
35
37
|
readonly layout: Layout;
|
|
38
|
+
readonly queryBus: QueryBus;
|
|
36
39
|
readonly onNotAccessible: () => void;
|
|
37
40
|
readonly onCheckoutSubmitted?: () => void;
|
|
38
41
|
readonly useRedirect: () => Record<string, string>;
|
|
@@ -51,6 +54,7 @@ const root: RootFunction = ({ Messaging, I18n, getAuthToken, development, sentry
|
|
|
51
54
|
order,
|
|
52
55
|
subscription,
|
|
53
56
|
layout,
|
|
57
|
+
queryBus,
|
|
54
58
|
onNotAccessible,
|
|
55
59
|
onCheckoutSubmitted,
|
|
56
60
|
useRedirect,
|
|
@@ -60,22 +64,24 @@ const root: RootFunction = ({ Messaging, I18n, getAuthToken, development, sentry
|
|
|
60
64
|
|
|
61
65
|
return (
|
|
62
66
|
<Messaging includeReactQueryDevTools={Platform.OS === "web"}>
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
<QueryBusProvider queryBus={queryBus}>
|
|
68
|
+
<Routing
|
|
69
|
+
I18n={I18n}
|
|
70
|
+
basePath={basePath}
|
|
71
|
+
customer={customer}
|
|
72
|
+
getAuthToken={getAuthToken}
|
|
73
|
+
kameleoon={kameleoon}
|
|
74
|
+
layout={layout}
|
|
75
|
+
locale={locale}
|
|
76
|
+
order={order}
|
|
77
|
+
subscription={subscription}
|
|
78
|
+
useRedirect={useRedirect}
|
|
79
|
+
useRoutes={useRoutes}
|
|
80
|
+
onCheckoutSubmitted={onCheckoutSubmitted}
|
|
81
|
+
onI18nError={development ? undefined : handleOnI18nError}
|
|
82
|
+
onNotAccessible={onNotAccessible}
|
|
83
|
+
/>
|
|
84
|
+
</QueryBusProvider>
|
|
79
85
|
</Messaging>
|
|
80
86
|
);
|
|
81
87
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { renderHook, waitFor } from "@testing-library/react-native";
|
|
2
|
+
import { mockFn } from "jest-mock-extended";
|
|
3
|
+
import React, { FC } from "react";
|
|
4
|
+
import { QueryBus } from "@lookiero/messaging";
|
|
5
|
+
import { QueryBusProvider, useQueryBus as sut } from "./useQueryBus";
|
|
6
|
+
|
|
7
|
+
const mockQueryBus = mockFn<QueryBus>();
|
|
8
|
+
|
|
9
|
+
interface WrapperProps {
|
|
10
|
+
readonly children: JSX.Element;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Wrapper: FC<WrapperProps> = ({ children }) => (
|
|
14
|
+
<QueryBusProvider queryBus={mockQueryBus}>{children}</QueryBusProvider>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
describe("useQueryBus hook", () => {
|
|
18
|
+
it("returns the QueryBusProvider provided queryBus", async () => {
|
|
19
|
+
const { result } = renderHook(() => sut(), { wrapper: Wrapper });
|
|
20
|
+
|
|
21
|
+
await waitFor(() => expect(result.current).toEqual(mockQueryBus));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { createContext, FC, ReactNode, useContext } from "react";
|
|
2
|
+
import invariant from "tiny-invariant";
|
|
3
|
+
import { QueryBus } from "@lookiero/messaging";
|
|
4
|
+
|
|
5
|
+
const QueryBusContext = createContext<QueryBus>(null as unknown as QueryBus);
|
|
6
|
+
|
|
7
|
+
interface QueryBusProviderProps {
|
|
8
|
+
readonly children: ReactNode;
|
|
9
|
+
readonly queryBus: QueryBus;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const QueryBusProvider: FC<QueryBusProviderProps> = ({ children, queryBus }) => (
|
|
13
|
+
<QueryBusContext.Provider value={queryBus}>{children}</QueryBusContext.Provider>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const useQueryBus = () => {
|
|
17
|
+
const queryBus = useContext(QueryBusContext);
|
|
18
|
+
|
|
19
|
+
invariant(
|
|
20
|
+
queryBus,
|
|
21
|
+
"Your are trying to use the useQueryBus hook without wrapping your app with the <QueryBusProvider>.",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return queryBus;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { useQueryBus, QueryBusProvider };
|
|
@@ -6,9 +6,11 @@ import { PaymentFlowRef } from "@lookiero/payments-front";
|
|
|
6
6
|
import { ChargeStatus } from "@lookiero/payments-front/build/infrastructure/CheckoutAPI";
|
|
7
7
|
import { Logger } from "@lookiero/sty-psp-logging";
|
|
8
8
|
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
9
|
+
import { CheckoutBookingProjection } from "../../../projection/checkoutBooking/checkoutBooking";
|
|
9
10
|
import { useSubmitCheckout } from "../../domain/checkout/react/useSubmitCheckout";
|
|
10
11
|
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
11
12
|
import { paymentFlowPayload as mockPaymentFlowPayload } from "../../projection/payment/paymentFlowPayload.mock";
|
|
13
|
+
import { useQueryBus } from "./useQueryBus";
|
|
12
14
|
import { useSubmitCheckout as sut } from "./useSubmitCheckout";
|
|
13
15
|
|
|
14
16
|
const checkoutId = "9c450400-0cd7-44a4-b0e3-e0002a9bf8df";
|
|
@@ -18,6 +20,7 @@ const errorChargeStatuses = Object.values(ChargeStatus).filter((status) => statu
|
|
|
18
20
|
const logger = mock<Logger>();
|
|
19
21
|
|
|
20
22
|
jest.mock("@lookiero/sty-psp-notifications");
|
|
23
|
+
jest.mock("./useQueryBus");
|
|
21
24
|
jest.mock("../../domain/checkout/react/useSubmitCheckout");
|
|
22
25
|
jest.mock("../../domain/checkoutBooking/react/useBlockCheckoutBooking");
|
|
23
26
|
|
|
@@ -38,6 +41,7 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
38
41
|
},
|
|
39
42
|
};
|
|
40
43
|
|
|
44
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
41
45
|
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
42
46
|
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
43
47
|
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
|
|
@@ -86,6 +90,7 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
86
90
|
},
|
|
87
91
|
};
|
|
88
92
|
|
|
93
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
89
94
|
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
90
95
|
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
91
96
|
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
|
|
@@ -126,6 +131,7 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
126
131
|
},
|
|
127
132
|
};
|
|
128
133
|
|
|
134
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
129
135
|
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.ERROR]);
|
|
130
136
|
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.IDLE]);
|
|
131
137
|
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
|
|
@@ -166,6 +172,7 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
166
172
|
},
|
|
167
173
|
};
|
|
168
174
|
|
|
175
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
169
176
|
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
170
177
|
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
171
178
|
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.IDLE]);
|
|
@@ -212,6 +219,7 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
212
219
|
},
|
|
213
220
|
};
|
|
214
221
|
|
|
222
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: false }) as CheckoutBookingProjection);
|
|
215
223
|
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
216
224
|
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.ERROR]);
|
|
217
225
|
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
@@ -236,4 +244,54 @@ describe("useSubmitCheckout custom hook", () => {
|
|
|
236
244
|
expect(mockOnError).toHaveBeenCalled();
|
|
237
245
|
});
|
|
238
246
|
});
|
|
247
|
+
|
|
248
|
+
test("returns submitCheckout error as the status right after calling 'submitCheckout' if CheckoutBooking isExpired", async () => {
|
|
249
|
+
const mockBlockCheckoutBooking = jest.fn();
|
|
250
|
+
const mockSubmitCheckout = jest.fn();
|
|
251
|
+
const mockCreateToastNotification = jest.fn();
|
|
252
|
+
const mockOnError = jest.fn();
|
|
253
|
+
const mockOnSuccess = jest.fn();
|
|
254
|
+
const mockPaymentFlowRef: RefObject<PaymentFlowRef> = {
|
|
255
|
+
current: {
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
257
|
+
startLegacyBoxCheckout: jest.fn().mockImplementation((_payload, callback) => {
|
|
258
|
+
callback({ status: ChargeStatus.EXECUTED, final: true });
|
|
259
|
+
}),
|
|
260
|
+
startCheckout: jest.fn(),
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
(useQueryBus as jest.Mock).mockReturnValue(() => ({ isExpired: true }) as CheckoutBookingProjection);
|
|
265
|
+
(useBlockCheckoutBooking as jest.Mock).mockReturnValue([mockBlockCheckoutBooking, CommandStatus.SUCCESS]);
|
|
266
|
+
(useCreateToastNotification as jest.Mock).mockReturnValue([mockCreateToastNotification, CommandStatus.SUCCESS]);
|
|
267
|
+
(useSubmitCheckout as jest.Mock).mockReturnValue([mockSubmitCheckout, CommandStatus.SUCCESS]);
|
|
268
|
+
|
|
269
|
+
const { result } = renderHook(() =>
|
|
270
|
+
sut({
|
|
271
|
+
checkoutId,
|
|
272
|
+
checkoutBookingId,
|
|
273
|
+
paymentFlowRef: mockPaymentFlowRef,
|
|
274
|
+
onError: mockOnError,
|
|
275
|
+
onSuccess: mockOnSuccess,
|
|
276
|
+
logger,
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
await act(async () => {
|
|
281
|
+
const [submitCheckout] = result.current;
|
|
282
|
+
await submitCheckout({ paymentFlowPayload: mockPaymentFlowPayload, sizeChangeEnabled: true });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
const [, status] = result.current;
|
|
287
|
+
|
|
288
|
+
expect(mockBlockCheckoutBooking).toHaveBeenCalled();
|
|
289
|
+
expect(mockCreateToastNotification).not.toHaveBeenCalled();
|
|
290
|
+
expect(mockSubmitCheckout).not.toHaveBeenCalled();
|
|
291
|
+
|
|
292
|
+
expect(status).toBe("error");
|
|
293
|
+
expect(mockOnSuccess).not.toHaveBeenCalled();
|
|
294
|
+
expect(mockOnError).toHaveBeenCalled();
|
|
295
|
+
});
|
|
296
|
+
});
|
|
239
297
|
});
|
|
@@ -3,11 +3,17 @@ import { CommandStatus } from "@lookiero/messaging-react";
|
|
|
3
3
|
import { PaymentFlowRef } from "@lookiero/payments-front";
|
|
4
4
|
import { Logger } from "@lookiero/sty-psp-logging";
|
|
5
5
|
import { NotificationLevel, useCreateToastNotification } from "@lookiero/sty-psp-notifications";
|
|
6
|
+
import {
|
|
7
|
+
ViewCheckoutBookingById,
|
|
8
|
+
viewCheckoutBookingById,
|
|
9
|
+
ViewCheckoutBookingByIdResult,
|
|
10
|
+
} from "../../../projection/checkoutBooking/viewCheckoutBookingById";
|
|
6
11
|
import { PaymentFlowPayloadProjection } from "../../../projection/payment/paymentFlowPayload";
|
|
7
12
|
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
8
13
|
import { useSubmitCheckout as useSubmitCheckoutCommand } from "../../domain/checkout/react/useSubmitCheckout";
|
|
9
14
|
import { useBlockCheckoutBooking } from "../../domain/checkoutBooking/react/useBlockCheckoutBooking";
|
|
10
15
|
import { I18nMessages } from "../i18n/i18n";
|
|
16
|
+
import { useQueryBus } from "./useQueryBus";
|
|
11
17
|
|
|
12
18
|
enum ChargeStatus {
|
|
13
19
|
EXECUTED = "EXECUTED",
|
|
@@ -63,9 +69,11 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
|
|
|
63
69
|
onSuccess,
|
|
64
70
|
logger,
|
|
65
71
|
}) => {
|
|
72
|
+
const queryBus = useQueryBus();
|
|
66
73
|
const [submitCheckoutCommand, submitCheckoutCommandStatus] = useSubmitCheckoutCommand({ checkoutId, logger });
|
|
67
74
|
const [blockCheckoutBooking, blockCheckoutBookingStatus] = useBlockCheckoutBooking({ checkoutBookingId, logger });
|
|
68
75
|
const [createNotification] = useCreateToastNotification({ contextId: MESSAGING_CONTEXT_ID, logger });
|
|
76
|
+
const [checkoutBookingExpired, setCheckoutBookingExpired] = useState(false);
|
|
69
77
|
|
|
70
78
|
const [startLegacyBoxCheckoutStatus, setStartLegacyBoxCheckoutStatus] = useState<Status>("idle");
|
|
71
79
|
|
|
@@ -77,6 +85,15 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
|
|
|
77
85
|
return;
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
const checkoutBooking = await queryBus<ViewCheckoutBookingById, ViewCheckoutBookingByIdResult>(
|
|
89
|
+
viewCheckoutBookingById({ checkoutBookingId }),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (checkoutBooking?.isExpired) {
|
|
93
|
+
setCheckoutBookingExpired(true);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
80
97
|
paymentFlowRef.current?.startLegacyBoxCheckout(
|
|
81
98
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
82
99
|
// @ts-ignore
|
|
@@ -100,7 +117,15 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
|
|
|
100
117
|
},
|
|
101
118
|
);
|
|
102
119
|
},
|
|
103
|
-
[
|
|
120
|
+
[
|
|
121
|
+
queryBus,
|
|
122
|
+
checkoutBookingId,
|
|
123
|
+
paymentFlowRef,
|
|
124
|
+
blockCheckoutBooking,
|
|
125
|
+
submitCheckoutCommand,
|
|
126
|
+
onSuccess,
|
|
127
|
+
createNotification,
|
|
128
|
+
],
|
|
104
129
|
);
|
|
105
130
|
|
|
106
131
|
const status: Status = useMemo(() => {
|
|
@@ -121,14 +146,21 @@ const useSubmitCheckout: UseSubmitCheckoutFunction = ({
|
|
|
121
146
|
if (
|
|
122
147
|
blockCheckoutBookingStatus === CommandStatus.ERROR ||
|
|
123
148
|
startLegacyBoxCheckoutStatus === "error" ||
|
|
124
|
-
submitCheckoutCommandStatus === CommandStatus.ERROR
|
|
149
|
+
submitCheckoutCommandStatus === CommandStatus.ERROR ||
|
|
150
|
+
checkoutBookingExpired
|
|
125
151
|
) {
|
|
126
152
|
onError();
|
|
127
153
|
return "error";
|
|
128
154
|
}
|
|
129
155
|
|
|
130
156
|
return "idle";
|
|
131
|
-
}, [
|
|
157
|
+
}, [
|
|
158
|
+
blockCheckoutBookingStatus,
|
|
159
|
+
startLegacyBoxCheckoutStatus,
|
|
160
|
+
submitCheckoutCommandStatus,
|
|
161
|
+
checkoutBookingExpired,
|
|
162
|
+
onError,
|
|
163
|
+
]);
|
|
132
164
|
|
|
133
165
|
return [submitCheckout, status];
|
|
134
166
|
};
|