@lookiero/checkout 0.1.1

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.
Files changed (202) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc +72 -0
  3. package/.expo-shared/assets.json +4 -0
  4. package/.github/CODEOWNERS +1 -0
  5. package/.github/actions/version/action.yaml +32 -0
  6. package/.github/dependabot.yaml +10 -0
  7. package/.github/workflows/build.yaml +71 -0
  8. package/.github/workflows/publish.yaml +52 -0
  9. package/.husky/pre-commit +4 -0
  10. package/.husky/prepare-commit-msg +26 -0
  11. package/.husky/prepush +12 -0
  12. package/.lintstagedrc.json +4 -0
  13. package/.nvmrc +1 -0
  14. package/.prettierignore +2 -0
  15. package/.prettierrc.json +6 -0
  16. package/@types/aurora-next.d.ts +1 -0
  17. package/README.md +19 -0
  18. package/app.config.js +4 -0
  19. package/app.json +31 -0
  20. package/assets/adaptive-icon.png +0 -0
  21. package/assets/favicon.png +0 -0
  22. package/assets/icon.png +0 -0
  23. package/assets/splash.png +0 -0
  24. package/babel.config.js +6 -0
  25. package/dist/domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem.d.ts +15 -0
  26. package/dist/domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem.js +6 -0
  27. package/dist/domain/checkoutBooking/model/checkoutBooking.d.ts +8 -0
  28. package/dist/domain/checkoutBooking/model/checkoutBooking.js +16 -0
  29. package/dist/domain/checkoutBooking/model/checkoutBookingBooked.d.ts +15 -0
  30. package/dist/domain/checkoutBooking/model/checkoutBookingBooked.js +6 -0
  31. package/dist/domain/checkoutBooking/model/checkoutBookings.d.ts +6 -0
  32. package/dist/domain/checkoutBooking/model/checkoutBookings.js +1 -0
  33. package/dist/domain/uiSetting/command/updateUiSetting.d.ts +14 -0
  34. package/dist/domain/uiSetting/command/updateUiSetting.js +9 -0
  35. package/dist/domain/uiSetting/model/uiSetting.d.ts +8 -0
  36. package/dist/domain/uiSetting/model/uiSetting.js +10 -0
  37. package/dist/domain/uiSetting/model/uiSettingUpdated.d.ts +13 -0
  38. package/dist/domain/uiSetting/model/uiSettingUpdated.js +6 -0
  39. package/dist/domain/uiSetting/model/uiSettings.d.ts +6 -0
  40. package/dist/domain/uiSetting/model/uiSettings.js +1 -0
  41. package/dist/index.d.ts +22 -0
  42. package/dist/index.js +13 -0
  43. package/dist/infrastructure/delivery/_mock/bootstrap.mock.d.ts +6 -0
  44. package/dist/infrastructure/delivery/_mock/bootstrap.mock.js +17 -0
  45. package/dist/infrastructure/delivery/_mock/checkout.mock.d.ts +9 -0
  46. package/dist/infrastructure/delivery/_mock/checkout.mock.js +45 -0
  47. package/dist/infrastructure/delivery/bootstrap.d.ts +10 -0
  48. package/dist/infrastructure/delivery/bootstrap.js +21 -0
  49. package/dist/infrastructure/delivery/http/fetchHttpClient.d.ts +14 -0
  50. package/dist/infrastructure/delivery/http/fetchHttpClient.js +18 -0
  51. package/dist/infrastructure/delivery/http/httpClient.d.ts +14 -0
  52. package/dist/infrastructure/delivery/http/httpClient.js +1 -0
  53. package/dist/infrastructure/domain/checkoutBooking/model/httpCheckoutBookings.d.ts +10 -0
  54. package/dist/infrastructure/domain/checkoutBooking/model/httpCheckoutBookings.js +19 -0
  55. package/dist/infrastructure/domain/react/useBookSizeReplaceableProductVariantsForCheckoutItem.d.ts +15 -0
  56. package/dist/infrastructure/domain/react/useBookSizeReplaceableProductVariantsForCheckoutItem.js +17 -0
  57. package/dist/infrastructure/domain/react/useUpdateUiSetting.d.ts +17 -0
  58. package/dist/infrastructure/domain/react/useUpdateUiSetting.js +16 -0
  59. package/dist/infrastructure/domain/uiSetting/model/storageUiSettings.d.ts +18 -0
  60. package/dist/infrastructure/domain/uiSetting/model/storageUiSettings.js +19 -0
  61. package/dist/infrastructure/persistence/asyncStorageStorage.d.ts +4 -0
  62. package/dist/infrastructure/persistence/asyncStorageStorage.js +6 -0
  63. package/dist/infrastructure/persistence/storage.d.ts +6 -0
  64. package/dist/infrastructure/persistence/storage.js +1 -0
  65. package/dist/infrastructure/persistence/uiSettingData.d.ts +5 -0
  66. package/dist/infrastructure/persistence/uiSettingData.js +1 -0
  67. package/dist/infrastructure/projection/httpBookedSizeReplaceableProductVariantsForCheckoutItemView.d.ts +36 -0
  68. package/dist/infrastructure/projection/httpBookedSizeReplaceableProductVariantsForCheckoutItemView.js +30 -0
  69. package/dist/infrastructure/projection/httpFirstAvailableCheckoutByCustomerIdView.d.ts +60 -0
  70. package/dist/infrastructure/projection/httpFirstAvailableCheckoutByCustomerIdView.js +54 -0
  71. package/dist/infrastructure/projection/httpIsCheckoutEnabledByCustomerIdView.d.ts +12 -0
  72. package/dist/infrastructure/projection/httpIsCheckoutEnabledByCustomerIdView.js +7 -0
  73. package/dist/infrastructure/projection/react/useViewBookedSizeReplaceableProductVariantsForCheckoutItem.d.ts +10 -0
  74. package/dist/infrastructure/projection/react/useViewBookedSizeReplaceableProductVariantsForCheckoutItem.js +8 -0
  75. package/dist/infrastructure/projection/react/useViewFirstAvailableCheckoutByCustomerId.d.ts +10 -0
  76. package/dist/infrastructure/projection/react/useViewFirstAvailableCheckoutByCustomerId.js +8 -0
  77. package/dist/infrastructure/projection/react/useViewIsCheckoutAccessibleByCustomerId.d.ts +10 -0
  78. package/dist/infrastructure/projection/react/useViewIsCheckoutAccessibleByCustomerId.js +8 -0
  79. package/dist/infrastructure/projection/react/useViewIsCheckoutIntroVisible.d.ts +3 -0
  80. package/dist/infrastructure/projection/react/useViewIsCheckoutIntroVisible.js +3 -0
  81. package/dist/infrastructure/projection/react/useViewUiSettingByKey.d.ts +10 -0
  82. package/dist/infrastructure/projection/react/useViewUiSettingByKey.js +8 -0
  83. package/dist/infrastructure/projection/storageUiSettingByKeyView.d.ts +13 -0
  84. package/dist/infrastructure/projection/storageUiSettingByKeyView.js +14 -0
  85. package/dist/infrastructure/ui/Root.d.ts +15 -0
  86. package/dist/infrastructure/ui/Root.js +25 -0
  87. package/dist/infrastructure/ui/components/atoms/spiner/Spinner.d.ts +3 -0
  88. package/dist/infrastructure/ui/components/atoms/spiner/Spinner.js +11 -0
  89. package/dist/infrastructure/ui/components/atoms/spiner/Spinner.style.d.ts +9 -0
  90. package/dist/infrastructure/ui/components/atoms/spiner/Spinner.style.js +10 -0
  91. package/dist/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.d.ts +8 -0
  92. package/dist/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.js +16 -0
  93. package/dist/infrastructure/ui/routing/CheckoutMiddleware.d.ts +7 -0
  94. package/dist/infrastructure/ui/routing/CheckoutMiddleware.js +34 -0
  95. package/dist/infrastructure/ui/routing/Routing.d.ts +7 -0
  96. package/dist/infrastructure/ui/routing/Routing.js +49 -0
  97. package/dist/infrastructure/ui/routing/router/Router.d.ts +1 -0
  98. package/dist/infrastructure/ui/routing/router/Router.js +1 -0
  99. package/dist/infrastructure/ui/routing/router/Router.native.d.ts +1 -0
  100. package/dist/infrastructure/ui/routing/router/Router.native.js +1 -0
  101. package/dist/infrastructure/ui/routing/routes.d.ts +6 -0
  102. package/dist/infrastructure/ui/routing/routes.js +7 -0
  103. package/dist/infrastructure/ui/test/render.d.ts +19 -0
  104. package/dist/infrastructure/ui/test/render.js +14 -0
  105. package/dist/infrastructure/ui/views/intro/Intro.d.ts +3 -0
  106. package/dist/infrastructure/ui/views/intro/Intro.js +20 -0
  107. package/dist/infrastructure/ui/views/item/Item.d.ts +3 -0
  108. package/dist/infrastructure/ui/views/item/Item.js +23 -0
  109. package/dist/infrastructure/ui/views/summary/Summary.d.ts +3 -0
  110. package/dist/infrastructure/ui/views/summary/Summary.js +20 -0
  111. package/dist/projection/bookedSizeReplaceableProductVariants/viewBookedSizeReplaceableProductVariantsForCheckoutItem.d.ts +43 -0
  112. package/dist/projection/bookedSizeReplaceableProductVariants/viewBookedSizeReplaceableProductVariantsForCheckoutItem.js +8 -0
  113. package/dist/projection/checkout/viewFirstAvailableCheckoutByCustomerId.d.ts +104 -0
  114. package/dist/projection/checkout/viewFirstAvailableCheckoutByCustomerId.js +43 -0
  115. package/dist/projection/checkout/viewIsCheckoutAccessibleByCustomerId.d.ts +16 -0
  116. package/dist/projection/checkout/viewIsCheckoutAccessibleByCustomerId.js +13 -0
  117. package/dist/projection/checkout/viewIsCheckoutEnabledByCustomerId.d.ts +23 -0
  118. package/dist/projection/checkout/viewIsCheckoutEnabledByCustomerId.js +8 -0
  119. package/dist/projection/uiSetting/viewUiSettingByKey.d.ts +28 -0
  120. package/dist/projection/uiSetting/viewUiSettingByKey.js +7 -0
  121. package/jest.config.js +9 -0
  122. package/jest.setup.js +3 -0
  123. package/package.json +75 -0
  124. package/src/Expo.tsx +59 -0
  125. package/src/domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem.test.ts +26 -0
  126. package/src/domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem.ts +27 -0
  127. package/src/domain/checkoutBooking/model/checkoutBooking.test.ts +30 -0
  128. package/src/domain/checkoutBooking/model/checkoutBooking.ts +31 -0
  129. package/src/domain/checkoutBooking/model/checkoutBookingBooked.test.ts +23 -0
  130. package/src/domain/checkoutBooking/model/checkoutBookingBooked.ts +23 -0
  131. package/src/domain/checkoutBooking/model/checkoutBookings.ts +6 -0
  132. package/src/domain/uiSetting/command/updateUiSetting.test.ts +19 -0
  133. package/src/domain/uiSetting/command/updateUiSetting.ts +23 -0
  134. package/src/domain/uiSetting/model/uiSetting.test.ts +26 -0
  135. package/src/domain/uiSetting/model/uiSetting.ts +22 -0
  136. package/src/domain/uiSetting/model/uiSettingUpdated.test.ts +17 -0
  137. package/src/domain/uiSetting/model/uiSettingUpdated.ts +19 -0
  138. package/src/domain/uiSetting/model/uiSettings.ts +6 -0
  139. package/src/index.ts +44 -0
  140. package/src/infrastructure/delivery/_mock/bootstrap.mock.ts +38 -0
  141. package/src/infrastructure/delivery/_mock/checkout.mock.ts +81 -0
  142. package/src/infrastructure/delivery/bootstrap.ts +42 -0
  143. package/src/infrastructure/delivery/http/fetchHttpClient.test.ts +51 -0
  144. package/src/infrastructure/delivery/http/fetchHttpClient.ts +41 -0
  145. package/src/infrastructure/delivery/http/httpClient.ts +18 -0
  146. package/src/infrastructure/domain/checkoutBooking/model/httpCheckoutBookings.test.ts +45 -0
  147. package/src/infrastructure/domain/checkoutBooking/model/httpCheckoutBookings.ts +32 -0
  148. package/src/infrastructure/domain/react/useBookSizeReplaceableProductVariantsForCheckoutItem.test.ts +38 -0
  149. package/src/infrastructure/domain/react/useBookSizeReplaceableProductVariantsForCheckoutItem.ts +44 -0
  150. package/src/infrastructure/domain/react/useUpdateUiSetting.test.ts +31 -0
  151. package/src/infrastructure/domain/react/useUpdateUiSetting.ts +43 -0
  152. package/src/infrastructure/domain/uiSetting/model/storageUiSettings.test.ts +50 -0
  153. package/src/infrastructure/domain/uiSetting/model/storageUiSettings.ts +50 -0
  154. package/src/infrastructure/persistence/asyncStorageStorage.ts +12 -0
  155. package/src/infrastructure/persistence/storage.ts +7 -0
  156. package/src/infrastructure/persistence/uiSettingData.ts +5 -0
  157. package/src/infrastructure/projection/httpBookedSizeReplaceableProductVariantsForCheckoutItemView.test.ts +65 -0
  158. package/src/infrastructure/projection/httpBookedSizeReplaceableProductVariantsForCheckoutItemView.ts +102 -0
  159. package/src/infrastructure/projection/httpFirstAvailableCheckoutByCustomerIdView.test.ts +99 -0
  160. package/src/infrastructure/projection/httpFirstAvailableCheckoutByCustomerIdView.ts +128 -0
  161. package/src/infrastructure/projection/httpIsCheckoutEnabledByCustomerIdView.test.ts +32 -0
  162. package/src/infrastructure/projection/httpIsCheckoutEnabledByCustomerIdView.ts +27 -0
  163. package/src/infrastructure/projection/react/useViewBookedSizeReplaceableProductVariantsForCheckoutItem.test.ts +51 -0
  164. package/src/infrastructure/projection/react/useViewBookedSizeReplaceableProductVariantsForCheckoutItem.ts +25 -0
  165. package/src/infrastructure/projection/react/useViewFirstAvailableCheckoutByCustomerId.test.ts +28 -0
  166. package/src/infrastructure/projection/react/useViewFirstAvailableCheckoutByCustomerId.ts +22 -0
  167. package/src/infrastructure/projection/react/useViewIsCheckoutAccessibleByCustomerId.test.ts +44 -0
  168. package/src/infrastructure/projection/react/useViewIsCheckoutAccessibleByCustomerId.ts +24 -0
  169. package/src/infrastructure/projection/react/useViewIsCheckoutIntroVisible.ts +5 -0
  170. package/src/infrastructure/projection/react/useViewUiSettingByKey.test.ts +30 -0
  171. package/src/infrastructure/projection/react/useViewUiSettingByKey.ts +18 -0
  172. package/src/infrastructure/projection/storageUiSettingByKeyView.test.ts +32 -0
  173. package/src/infrastructure/projection/storageUiSettingByKeyView.ts +35 -0
  174. package/src/infrastructure/ui/Root.tsx +53 -0
  175. package/src/infrastructure/ui/components/atoms/spiner/Spinner.style.ts +12 -0
  176. package/src/infrastructure/ui/components/atoms/spiner/Spinner.tsx +35 -0
  177. package/src/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.test.tsx +70 -0
  178. package/src/infrastructure/ui/routing/CheckoutAccessibilityMiddleware.tsx +31 -0
  179. package/src/infrastructure/ui/routing/CheckoutMiddleware.test.tsx +100 -0
  180. package/src/infrastructure/ui/routing/CheckoutMiddleware.tsx +52 -0
  181. package/src/infrastructure/ui/routing/Routing.tsx +64 -0
  182. package/src/infrastructure/ui/routing/router/Router.native.ts +1 -0
  183. package/src/infrastructure/ui/routing/router/Router.ts +1 -0
  184. package/src/infrastructure/ui/routing/routes.ts +6 -0
  185. package/src/infrastructure/ui/test/render.tsx +47 -0
  186. package/src/infrastructure/ui/views/intro/Intro.test.tsx +15 -0
  187. package/src/infrastructure/ui/views/intro/Intro.tsx +27 -0
  188. package/src/infrastructure/ui/views/item/Item.tsx +31 -0
  189. package/src/infrastructure/ui/views/summary/Summary.tsx +27 -0
  190. package/src/projection/bookedSizeReplaceableProductVariants/viewBookedSizeReplaceableProductVariantsForCheckoutItem.test.ts +40 -0
  191. package/src/projection/bookedSizeReplaceableProductVariants/viewBookedSizeReplaceableProductVariantsForCheckoutItem.ts +85 -0
  192. package/src/projection/checkout/viewFirstAvailableCheckoutByCustomerId.test.ts +22 -0
  193. package/src/projection/checkout/viewFirstAvailableCheckoutByCustomerId.ts +149 -0
  194. package/src/projection/checkout/viewIsCheckoutAccessibleByCustomerId.test.ts +54 -0
  195. package/src/projection/checkout/viewIsCheckoutAccessibleByCustomerId.ts +46 -0
  196. package/src/projection/checkout/viewIsCheckoutEnabledByCustomerId.test.ts +20 -0
  197. package/src/projection/checkout/viewIsCheckoutEnabledByCustomerId.ts +51 -0
  198. package/src/projection/uiSetting/viewUiSettingByKey.test.ts +22 -0
  199. package/src/projection/uiSetting/viewUiSettingByKey.ts +47 -0
  200. package/tsconfig.build.json +16 -0
  201. package/tsconfig.json +19 -0
  202. package/webpack.config.js +17 -0
@@ -0,0 +1,81 @@
1
+ import { v4 as uuid } from "uuid";
2
+ import {
3
+ CheckoutItemProjection,
4
+ CheckoutItemStatus,
5
+ CheckoutProjection,
6
+ CheckoutStatus,
7
+ Currency,
8
+ MediaPerspective,
9
+ } from "../../../projection/checkout/viewFirstAvailableCheckoutByCustomerId";
10
+
11
+ interface CheckoutItemFunctionArgs {
12
+ readonly status: CheckoutItemStatus;
13
+ }
14
+
15
+ interface CheckoutItemFunction {
16
+ (args: CheckoutItemFunctionArgs): CheckoutItemProjection;
17
+ }
18
+
19
+ const checkoutItem: CheckoutItemFunction = ({ status }) => ({
20
+ id: uuid(),
21
+ status,
22
+ productVariant: {
23
+ id: uuid(),
24
+ media: [
25
+ {
26
+ id: uuid(),
27
+ url: "https://cdn-catalog-back-prod.envs.lookiero.tech/d2/65/d2655b80-7745-4537-bb17-eb38f960a8de.jpg",
28
+ perspective: MediaPerspective.FRONT,
29
+ },
30
+ ],
31
+ brand: "CKS",
32
+ name: "Sari Tshirt lines",
33
+ price: {
34
+ amount: 3999,
35
+ currency: Currency.EUR,
36
+ },
37
+ size: {
38
+ id: uuid(),
39
+ lookiero: "M",
40
+ uk: "M",
41
+ it: "M",
42
+ europe: "M",
43
+ unique: false,
44
+ },
45
+ color: {
46
+ id: uuid(),
47
+ label: "black_C2",
48
+ },
49
+ },
50
+ });
51
+
52
+ interface CheckoutWithItemsFunctionArgs {
53
+ readonly items: CheckoutItemProjection[];
54
+ }
55
+
56
+ interface CheckoutWithItemsFunction {
57
+ (args: CheckoutWithItemsFunctionArgs): CheckoutProjection;
58
+ }
59
+
60
+ const checkoutWithItems: CheckoutWithItemsFunction = ({ items }) => ({
61
+ id: uuid(),
62
+ status: CheckoutStatus.STARTED,
63
+ customerId: uuid(),
64
+ boxId: uuid(),
65
+ bookingId: uuid(),
66
+ expiresOn: new Date("2022-09-20"),
67
+ items,
68
+ });
69
+
70
+ interface StartedCheckoutWithStatusFunctionArgs {
71
+ readonly status: CheckoutItemStatus[];
72
+ }
73
+
74
+ interface StartedCheckoutWithStatusFunction {
75
+ (args: StartedCheckoutWithStatusFunctionArgs): CheckoutProjection;
76
+ }
77
+
78
+ const startedCheckoutWithItems: StartedCheckoutWithStatusFunction = ({ status }) =>
79
+ checkoutWithItems({ items: status.map((status) => checkoutItem({ status })) });
80
+
81
+ export { startedCheckoutWithItems };
@@ -0,0 +1,42 @@
1
+ import { bootstrap as messagingBootstrap, BuildBootstrapFunctionReturn } from "@lookiero/messaging-react";
2
+ import {
3
+ viewFirstAvailableCheckoutByCustomerIdHandler,
4
+ VIEW_FIRST_AVAILABLE_CHECKOUT_BY_CUSTOMER_ID,
5
+ } from "../../projection/checkout/viewFirstAvailableCheckoutByCustomerId";
6
+ import {
7
+ viewIsCheckoutAccessibleByCustomerIdHandler,
8
+ VIEW_IS_CHECKOUT_ACCESSIBLE_BY_CUSTOMER_ID,
9
+ } from "../../projection/checkout/viewIsCheckoutAccessibleByCustomerId";
10
+ import {
11
+ viewIsCheckoutEnabledByCustomerIdHandler,
12
+ VIEW_IS_CHECKOUT_ENABLED_BY_CUSTOMER_ID,
13
+ } from "../../projection/checkout/viewIsCheckoutEnabledByCustomerId";
14
+ import { httpFirstAvailableCheckoutByCustomerIdView } from "../projection/httpFirstAvailableCheckoutByCustomerIdView";
15
+ import { httpIsCheckoutEnabledByCustomerIdView } from "../projection/httpIsCheckoutEnabledByCustomerIdView";
16
+ import { fetchHttpGet, fetchHttpPost } from "./http/fetchHttpClient";
17
+
18
+ interface BootstrapFunctionArgs {
19
+ readonly getAuthToken: () => string;
20
+ readonly apiUrl: string;
21
+ }
22
+
23
+ interface BootstrapFunction {
24
+ (args: BootstrapFunctionArgs): BuildBootstrapFunctionReturn;
25
+ }
26
+
27
+ const bootstrap: BootstrapFunction = ({ apiUrl, getAuthToken }) => {
28
+ const httpGet = fetchHttpGet({ apiUrl, getAuthToken });
29
+ const httpPost = fetchHttpPost({ apiUrl, getAuthToken });
30
+
31
+ return messagingBootstrap({ id: "Checkout" })
32
+ .query(VIEW_FIRST_AVAILABLE_CHECKOUT_BY_CUSTOMER_ID, viewFirstAvailableCheckoutByCustomerIdHandler, {
33
+ view: httpFirstAvailableCheckoutByCustomerIdView({ httpPost }),
34
+ })
35
+ .query(VIEW_IS_CHECKOUT_ENABLED_BY_CUSTOMER_ID, viewIsCheckoutEnabledByCustomerIdHandler, {
36
+ view: httpIsCheckoutEnabledByCustomerIdView({ httpGet }),
37
+ })
38
+ .query(VIEW_IS_CHECKOUT_ACCESSIBLE_BY_CUSTOMER_ID, viewIsCheckoutAccessibleByCustomerIdHandler, {})
39
+ .build();
40
+ };
41
+
42
+ export { bootstrap };
@@ -0,0 +1,51 @@
1
+ import { fetchHttpGet as sutGet, fetchHttpPost as sutPost } from "./fetchHttpClient";
2
+
3
+ const fetchMock = jest.fn();
4
+ global.fetch = fetchMock;
5
+
6
+ const apiUrl = "/api";
7
+ const authToken = "token";
8
+ const post = sutPost({ apiUrl, getAuthToken: () => authToken });
9
+ const get = sutGet({ apiUrl, getAuthToken: () => authToken });
10
+
11
+ afterEach(() => {
12
+ fetchMock.mockClear();
13
+ });
14
+
15
+ describe("fetchHttpClient", () => {
16
+ it("does a POST http request with the expected parameters", async () => {
17
+ const endpoint = "/dummy-endpoint";
18
+ const body = { foo: "bar" };
19
+ const expectedEndpoint = `${apiUrl}${endpoint}`;
20
+ const expectedPayload = {
21
+ method: "POST",
22
+ headers: {
23
+ ["Content-Type"]: "application/json",
24
+ authorization: `Bearer ${authToken}`,
25
+ },
26
+ body: JSON.stringify(body),
27
+ };
28
+
29
+ await post({ endpoint, body });
30
+
31
+ expect(fetchMock).toHaveBeenCalledWith(expectedEndpoint, expectedPayload);
32
+ });
33
+
34
+ it("does a GET http request with the expected parameters", async () => {
35
+ const endpoint = "/dummy-endpoint";
36
+ const expectedEndpoint = `${apiUrl}${endpoint}`;
37
+ const expectedPayload = {
38
+ method: "GET",
39
+ credentials: "include",
40
+ mode: "no-cors",
41
+ headers: {
42
+ authorization: `Bearer ${authToken}`,
43
+ },
44
+ referrer: "",
45
+ };
46
+
47
+ await get({ endpoint });
48
+
49
+ expect(fetchMock).toHaveBeenCalledWith(expectedEndpoint, expectedPayload);
50
+ });
51
+ });
@@ -0,0 +1,41 @@
1
+ import { HttpGetFunction, HttpPostFunction } from "./httpClient";
2
+
3
+ interface FetchHttpFunctionArgs {
4
+ readonly apiUrl: string;
5
+ readonly getAuthToken: () => string;
6
+ }
7
+
8
+ interface FetchHttpPostFunction {
9
+ (args: FetchHttpFunctionArgs): HttpPostFunction;
10
+ }
11
+
12
+ interface FetchHttpGetFunction {
13
+ (args: FetchHttpFunctionArgs): HttpGetFunction;
14
+ }
15
+
16
+ const fetchHttpPost: FetchHttpPostFunction =
17
+ ({ apiUrl, getAuthToken }) =>
18
+ async ({ endpoint, body }) =>
19
+ fetch(`${apiUrl}${endpoint}`, {
20
+ method: "POST",
21
+ headers: {
22
+ ["Content-Type"]: "application/json",
23
+ authorization: `Bearer ${getAuthToken()}`,
24
+ },
25
+ body: JSON.stringify(body),
26
+ });
27
+
28
+ const fetchHttpGet: FetchHttpGetFunction =
29
+ ({ apiUrl, getAuthToken }) =>
30
+ async ({ endpoint }) =>
31
+ fetch(`${apiUrl}${endpoint}`, {
32
+ method: "GET",
33
+ credentials: "include",
34
+ mode: "no-cors",
35
+ headers: {
36
+ authorization: `Bearer ${getAuthToken()}`,
37
+ },
38
+ referrer: "",
39
+ });
40
+
41
+ export { fetchHttpGet, fetchHttpPost };
@@ -0,0 +1,18 @@
1
+ interface HttpGetFunctionArgs {
2
+ readonly endpoint: string;
3
+ }
4
+
5
+ interface HttpGetFunction {
6
+ (args: HttpGetFunctionArgs): Promise<Response>;
7
+ }
8
+
9
+ interface HttpPostFunctionArgs {
10
+ readonly endpoint: string;
11
+ readonly body: Record<string, unknown>;
12
+ }
13
+
14
+ interface HttpPostFunction {
15
+ (args: HttpPostFunctionArgs): Promise<Response>;
16
+ }
17
+
18
+ export { HttpGetFunction, HttpPostFunction };
@@ -0,0 +1,45 @@
1
+ import { v4 as uuid } from "uuid";
2
+ import { saveCheckoutBooking as sut } from "./httpCheckoutBookings";
3
+
4
+ const saveCheckoutBookingResponseOk: Response = {
5
+ ok: true,
6
+ } as Response;
7
+ const saveCheckoutBookingResponseError: Response = {
8
+ ok: false,
9
+ } as Response;
10
+
11
+ const httpPostMock = jest.fn();
12
+
13
+ const checkoutBooking = {
14
+ checkoutId: "this-should-be-a-uuid",
15
+ checkoutItemId: "this-should-be-a-uuid",
16
+ checkoutBookingId: "this-should-be-a-uuid",
17
+ };
18
+
19
+ describe("httpCheckoutBookings", () => {
20
+ it("saveCheckoutBooking: calls '/book-size-replaceable-product-variants-for-checkout-item' endpoint", async () => {
21
+ const expectedEndpoint = "/book-size-replaceable-product-variants-for-checkout-item";
22
+ httpPostMock.mockReturnValue(Promise.resolve(saveCheckoutBookingResponseOk));
23
+
24
+ const checkoutBookingsSave = { aggregateId: uuid(), ...checkoutBooking, domainEvents: [] };
25
+
26
+ await sut({ httpPost: httpPostMock })(checkoutBookingsSave);
27
+
28
+ expect(httpPostMock).toBeCalledWith({
29
+ endpoint: expectedEndpoint,
30
+ body: {
31
+ checkout_id: checkoutBooking.checkoutId,
32
+ checkout_item_id: checkoutBooking.checkoutItemId,
33
+ checkout_booking_id: checkoutBooking.checkoutBookingId,
34
+ },
35
+ });
36
+ });
37
+
38
+ it("saveCheckoutBooking: throws for an unsuccessfull response", async () => {
39
+ httpPostMock.mockReturnValue(Promise.resolve(saveCheckoutBookingResponseError));
40
+
41
+ const checkoutBookingsSave = { aggregateId: uuid(), ...checkoutBooking, domainEvents: [] };
42
+
43
+ await expect(sut({ httpPost: httpPostMock })(checkoutBookingsSave)).rejects.toThrow();
44
+ });
45
+ });
@@ -0,0 +1,32 @@
1
+ import { CheckoutBookingsSaveFunction } from "../../../../domain/checkoutBooking/model/checkoutBookings";
2
+ import { HttpPostFunction } from "../../../delivery/http/httpClient";
3
+
4
+ interface HttpCheckoutBookingsSaveFunctionArgs {
5
+ readonly httpPost: HttpPostFunction;
6
+ }
7
+
8
+ interface HttpCheckoutBookingsSaveFunction {
9
+ (args: HttpCheckoutBookingsSaveFunctionArgs): CheckoutBookingsSaveFunction;
10
+ }
11
+
12
+ export const saveCheckoutBooking: HttpCheckoutBookingsSaveFunction =
13
+ ({ httpPost }) =>
14
+ async ({ checkoutId, checkoutItemId, checkoutBookingId }) => {
15
+ try {
16
+ const response = await httpPost({
17
+ endpoint: "/book-size-replaceable-product-variants-for-checkout-item",
18
+ body: {
19
+ checkout_id: checkoutId,
20
+ checkout_item_id: checkoutItemId,
21
+ checkout_booking_id: checkoutBookingId,
22
+ },
23
+ });
24
+
25
+ if (!response.ok) {
26
+ throw new Error("Book size replaceable product variants for checkout item");
27
+ }
28
+ return;
29
+ } catch (ignored) {
30
+ throw new Error("Could not fetch BookSizeReplaceableProductVariantsForCheckoutItem");
31
+ }
32
+ };
@@ -0,0 +1,38 @@
1
+ import { bootstrap, CommandStatus } from "@lookiero/messaging-react";
2
+ import { act, renderHook } from "@testing-library/react-hooks";
3
+ import { BOOK_SIZE_REPLACEABLE_PRODUCT_VARIANTS_FOR_CHECKOUT_ITEM } from "../../../domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem";
4
+ import { bookSizeReplaceableProductVariantsForCheckoutItemHandler } from "../../../domain/checkoutBooking/model/checkoutBooking";
5
+ import { useBookSizeReplaceableProductVariantsForCheckoutItem as sut } from "./useBookSizeReplaceableProductVariantsForCheckoutItem";
6
+
7
+ const checkoutBooking = {
8
+ checkoutId: "this-should-be-a-uuid",
9
+ checkoutItemId: "this-should-be-a-uuid",
10
+ checkoutBookingId: "this-should-be-a-uuid",
11
+ };
12
+
13
+ const saveMock = jest.fn();
14
+ const { Component: Messaging } = bootstrap()
15
+ .command(
16
+ BOOK_SIZE_REPLACEABLE_PRODUCT_VARIANTS_FOR_CHECKOUT_ITEM,
17
+ bookSizeReplaceableProductVariantsForCheckoutItemHandler,
18
+ {
19
+ get: jest.fn(),
20
+ save: saveMock,
21
+ },
22
+ )
23
+ .build();
24
+
25
+ describe("useBookSizeReplaceableProductVariantsForCheckoutItem integration hook", () => {
26
+ it("returns success as the status right after calling 'book'", async () => {
27
+ saveMock.mockResolvedValue("success");
28
+ const { result, waitFor } = renderHook(() => sut({ ...checkoutBooking }), { wrapper: Messaging });
29
+
30
+ expect(result.current.status).toBe(CommandStatus.IDLE);
31
+
32
+ await act(() => result.current.book());
33
+
34
+ await waitFor(() => {
35
+ expect(result.current.status).toBe(CommandStatus.SUCCESS);
36
+ });
37
+ });
38
+ });
@@ -0,0 +1,44 @@
1
+ import { CommandStatus, useCommand } from "@lookiero/messaging-react";
2
+ import { useCallback } from "react";
3
+ import { v4 as uuid } from "uuid";
4
+ import { bookSizeReplaceableProductVariantsForCheckoutItem } from "../../../domain/checkoutBooking/command/bookSizeReplaceableProductVariantsForCheckoutItem";
5
+
6
+ interface UseBookSizeReplaceableProductVariantsForCheckoutItem {
7
+ readonly book: () => Promise<void>;
8
+ readonly status: CommandStatus;
9
+ }
10
+
11
+ interface UseBookSizeReplaceableProductVariantsForCheckoutItemFunctionArgs {
12
+ readonly checkoutId: string;
13
+ readonly checkoutItemId: string;
14
+ readonly checkoutBookingId: string;
15
+ }
16
+
17
+ interface UseBookSizeReplaceableProductVariantsForCheckoutItemFunction {
18
+ (
19
+ args: UseBookSizeReplaceableProductVariantsForCheckoutItemFunctionArgs,
20
+ ): UseBookSizeReplaceableProductVariantsForCheckoutItem;
21
+ }
22
+
23
+ export const useBookSizeReplaceableProductVariantsForCheckoutItem: UseBookSizeReplaceableProductVariantsForCheckoutItemFunction =
24
+ ({ checkoutId, checkoutItemId, checkoutBookingId }) => {
25
+ const [commandBus, status] = useCommand();
26
+
27
+ const book = useCallback(
28
+ async () =>
29
+ await commandBus(
30
+ bookSizeReplaceableProductVariantsForCheckoutItem({
31
+ aggregateId: uuid(),
32
+ checkoutId,
33
+ checkoutItemId,
34
+ checkoutBookingId,
35
+ }),
36
+ ),
37
+ [checkoutBookingId, checkoutId, checkoutItemId, commandBus],
38
+ );
39
+
40
+ return {
41
+ book,
42
+ status,
43
+ };
44
+ };
@@ -0,0 +1,31 @@
1
+ import { bootstrap, CommandStatus } from "@lookiero/messaging-react";
2
+ import { act, renderHook } from "@testing-library/react-hooks";
3
+ import { UPDATE_UI_SETTING } from "../../../domain/uiSetting/command/updateUiSetting";
4
+ import { updateUiSettingHandler } from "../../../domain/uiSetting/model/uiSetting";
5
+ import { useUpdateUiSetting as sut } from "./useUpdateUiSetting";
6
+
7
+ const uiSettingKey = "ui_setting_key";
8
+ const uiSettingValue = "ui_setting_value";
9
+
10
+ const saveMock = jest.fn();
11
+ const { Component: Messaging } = bootstrap()
12
+ .command(UPDATE_UI_SETTING, updateUiSettingHandler, {
13
+ get: jest.fn(),
14
+ save: saveMock,
15
+ })
16
+ .build();
17
+
18
+ describe("useUpdateUiSetting integration hook", () => {
19
+ it("returns success as the status right after calling 'update'", async () => {
20
+ saveMock.mockResolvedValue("success");
21
+ const { result, waitFor } = renderHook(() => sut(), { wrapper: Messaging });
22
+
23
+ expect(result.current.status).toBe(CommandStatus.IDLE);
24
+
25
+ await act(() => result.current.update({ key: uiSettingKey, value: uiSettingValue }));
26
+
27
+ await waitFor(() => {
28
+ expect(result.current.status).toBe(CommandStatus.SUCCESS);
29
+ });
30
+ });
31
+ });
@@ -0,0 +1,43 @@
1
+ import { CommandStatus, useCommand } from "@lookiero/messaging-react";
2
+ import { useCallback } from "react";
3
+ import { v4 as uuid } from "uuid";
4
+ import { updateUiSetting } from "../../../domain/uiSetting/command/updateUiSetting";
5
+
6
+ interface UpdateFunctionArgs {
7
+ readonly key: string;
8
+ readonly value: unknown;
9
+ }
10
+
11
+ interface UpdateFunction {
12
+ (args: UpdateFunctionArgs): Promise<void>;
13
+ }
14
+
15
+ interface UseUpdateUiSetting {
16
+ readonly update: UpdateFunction;
17
+ readonly status: CommandStatus;
18
+ }
19
+
20
+ interface UseUpdateUiSettingFunction {
21
+ (): UseUpdateUiSetting;
22
+ }
23
+
24
+ export const useUpdateUiSetting: UseUpdateUiSettingFunction = () => {
25
+ const [commandBus, status] = useCommand();
26
+
27
+ const update: UpdateFunction = useCallback(
28
+ async ({ key, value }) =>
29
+ await commandBus(
30
+ updateUiSetting({
31
+ aggregateId: uuid(),
32
+ key,
33
+ value,
34
+ }),
35
+ ),
36
+ [commandBus],
37
+ );
38
+
39
+ return {
40
+ update,
41
+ status,
42
+ };
43
+ };
@@ -0,0 +1,50 @@
1
+ import { v4 as uuid } from "uuid";
2
+ import { getUiSetting as getSut, saveUiSetting as saveSut } from "./storageUiSettings";
3
+
4
+ const readMock = jest.fn();
5
+ const writeMock = jest.fn();
6
+
7
+ describe("storageUiSettings", () => {
8
+ it("gets uiSetting based on the given ui setting key", async () => {
9
+ const aggregateId = uuid();
10
+ const uiSettingKey = "ui_setting_key";
11
+ const uiSettingValue = "ui_setting_value";
12
+
13
+ readMock.mockReturnValue({
14
+ id: aggregateId,
15
+ key: uiSettingKey,
16
+ value: uiSettingValue,
17
+ });
18
+
19
+ const getResponse = await getSut({ read: readMock })(uiSettingKey);
20
+
21
+ expect(getResponse).toEqual({ aggregateId, key: uiSettingKey, value: uiSettingValue, domainEvents: [] });
22
+ });
23
+
24
+ it("throws an Error when uiSetting based on the given ui setting key does not exist", async () => {
25
+ const uiSettingKey = "ui_setting_key";
26
+
27
+ readMock.mockReturnValue(undefined);
28
+
29
+ await expect(getSut({ read: readMock })(uiSettingKey)).rejects.toThrow();
30
+ });
31
+
32
+ it("saves the given uiSetting", async () => {
33
+ const aggregateId = uuid();
34
+ const uiSettingKey = "ui_setting_key";
35
+ const uiSettingValue = "ui_setting_value";
36
+
37
+ const uiSettingDto = { aggregateId, key: uiSettingKey, value: uiSettingValue };
38
+ const uiSetting = { ...uiSettingDto, domainEvents: [] };
39
+
40
+ writeMock.mockResolvedValue("success");
41
+
42
+ await saveSut({ write: writeMock })(uiSetting);
43
+
44
+ expect(writeMock).toHaveBeenCalledWith(uiSettingKey, {
45
+ id: aggregateId,
46
+ key: uiSettingKey,
47
+ value: uiSettingValue,
48
+ });
49
+ });
50
+ });
@@ -0,0 +1,50 @@
1
+ import { UiSetting } from "../../../../domain/uiSetting/model/uiSetting";
2
+ import { UiSettingGetFunction, UiSettingSaveFunction } from "../../../../domain/uiSetting/model/uiSettings";
3
+ import { ReadFunction, WriteFunction } from "../../../persistence/storage";
4
+ import { UiSettingDto } from "../../../persistence/uiSettingData";
5
+
6
+ const toDomain = (uiSettingDto: UiSettingDto): UiSetting => ({
7
+ aggregateId: uiSettingDto.id,
8
+ key: uiSettingDto.key,
9
+ value: uiSettingDto.value,
10
+ domainEvents: [],
11
+ });
12
+
13
+ const fromDomain = (uiSetting: UiSetting): UiSettingDto => ({
14
+ id: uiSetting.aggregateId,
15
+ key: uiSetting.key,
16
+ value: uiSetting.value,
17
+ });
18
+
19
+ interface StorageUiSettingsGetFunctionArgs {
20
+ readonly read: ReadFunction<UiSettingDto>;
21
+ }
22
+
23
+ interface StorageUiSettingsGetFunction {
24
+ (args: StorageUiSettingsGetFunctionArgs): UiSettingGetFunction;
25
+ }
26
+
27
+ export const getUiSetting: StorageUiSettingsGetFunction =
28
+ ({ read }) =>
29
+ async (uiSettingKey) => {
30
+ const uiSettingDto = await read(uiSettingKey);
31
+
32
+ if (!uiSettingDto) {
33
+ throw new Error(`UiSetting not found: ${uiSettingKey}`);
34
+ }
35
+
36
+ return toDomain(uiSettingDto);
37
+ };
38
+
39
+ interface StorageUiSettingsSaveFunctionArgs {
40
+ readonly write: WriteFunction<UiSettingDto>;
41
+ }
42
+
43
+ interface StorageUiSettingsSaveFunction {
44
+ (args: StorageUiSettingsSaveFunctionArgs): UiSettingSaveFunction;
45
+ }
46
+
47
+ export const saveUiSetting: StorageUiSettingsSaveFunction =
48
+ ({ write }) =>
49
+ async (aggregateRoot) =>
50
+ await write(aggregateRoot.key, fromDomain(aggregateRoot));
@@ -0,0 +1,12 @@
1
+ import AsyncStorage from "@react-native-async-storage/async-storage";
2
+ import { ReadFunction, WriteFunction } from "./storage";
3
+ import { UiSettingDto } from "./uiSettingData";
4
+
5
+ export const read: ReadFunction<UiSettingDto> = async (key) => {
6
+ const value = await AsyncStorage.getItem(key);
7
+
8
+ return value ? JSON.parse(value) : undefined;
9
+ };
10
+
11
+ export const write: WriteFunction<UiSettingDto> = async (key, value) =>
12
+ AsyncStorage.setItem(key, JSON.stringify(value));
@@ -0,0 +1,7 @@
1
+ export interface ReadFunction<T> {
2
+ (key: string): Promise<T | undefined>;
3
+ }
4
+
5
+ export interface WriteFunction<T> {
6
+ (key: string, value: T): Promise<void>;
7
+ }
@@ -0,0 +1,5 @@
1
+ export interface UiSettingDto {
2
+ readonly id: string;
3
+ readonly key: string;
4
+ readonly value: unknown;
5
+ }
@@ -0,0 +1,65 @@
1
+ import {
2
+ BookedSizeReplaceableProductVariantsDto,
3
+ httpBookedSizeReplaceableProductVariantsForCheckoutItemView,
4
+ toBookedSizeReplaceableProductVariants,
5
+ } from "./httpBookedSizeReplaceableProductVariantsForCheckoutItemView";
6
+
7
+ const checkoutItemId = "6f720954-8039-4bed-9596-34fc5a02131b";
8
+ const bookedSizeReplaceableProductVariantsResponse: { result: BookedSizeReplaceableProductVariantsDto } = {
9
+ result: {
10
+ booking: "b00e2c2d-7d9c-4808-88a4-e1c845ae6889",
11
+ product_variants: [
12
+ {
13
+ id: "383fab95-56eb-4639-bb18-530ab792b4d3",
14
+ size: {
15
+ id: "4f8a26b8-8d2d-4012-b49a-7d2820095598",
16
+ europe: { size: "36", format: "36" },
17
+ lookiero: { size: "S", format: "S" },
18
+ uk: { size: "8", format: "8" },
19
+ it: { size: "M", format: "M" },
20
+ },
21
+ },
22
+ ],
23
+ },
24
+ };
25
+ const bookedSizeReplaceableProductVariantsResponseOk: Response = {
26
+ ok: true,
27
+ status: 200,
28
+ json: () => Promise.resolve(bookedSizeReplaceableProductVariantsResponse),
29
+ } as Response;
30
+ const bookedSizeReplaceableProductVariantsResponseNotFound: Response = {
31
+ ok: false,
32
+ status: 404,
33
+ } as Response;
34
+ const bookedSizeReplaceableProductVariantsResponseError: Response = {
35
+ ok: false,
36
+ } as Response;
37
+
38
+ const httpPostMock = jest.fn();
39
+ const view = httpBookedSizeReplaceableProductVariantsForCheckoutItemView({ httpPost: httpPostMock });
40
+
41
+ describe("httpBookedSizeReplaceableProductVariantsForCheckoutItemView", () => {
42
+ it("returns a BookedSizeReplaceableProductVariants projection for a 200 response", async () => {
43
+ httpPostMock.mockReturnValue(Promise.resolve(bookedSizeReplaceableProductVariantsResponseOk));
44
+
45
+ const returnedBookedSizeReplaceableProductVariants = await view({ checkoutItemId });
46
+
47
+ expect(returnedBookedSizeReplaceableProductVariants).toStrictEqual(
48
+ toBookedSizeReplaceableProductVariants(bookedSizeReplaceableProductVariantsResponse.result),
49
+ );
50
+ });
51
+
52
+ it("returns undefined for a 404 response", async () => {
53
+ httpPostMock.mockReturnValue(Promise.resolve(bookedSizeReplaceableProductVariantsResponseNotFound));
54
+
55
+ const returnedBookedSizeReplaceableProductVariants = await view({ checkoutItemId });
56
+
57
+ expect(returnedBookedSizeReplaceableProductVariants).toBeUndefined();
58
+ });
59
+
60
+ it("throws for an unsuccessfull response", async () => {
61
+ httpPostMock.mockReturnValue(Promise.resolve(bookedSizeReplaceableProductVariantsResponseError));
62
+
63
+ await expect(view({ checkoutItemId })).rejects.toThrow();
64
+ });
65
+ });