@adobe/react-native-aepmessaging 7.2.1 → 7.4.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.
Files changed (293) hide show
  1. package/RCTAEPMessaging.podspec +1 -1
  2. package/README.md +145 -16
  3. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingConstants.java +3 -0
  4. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java +103 -33
  5. package/babel.config.js +3 -0
  6. package/dist/module/Messaging.js +334 -0
  7. package/dist/module/Messaging.js.map +1 -0
  8. package/dist/module/index.js +30 -0
  9. package/dist/module/index.js.map +1 -0
  10. package/dist/module/models/ContentCard.js +24 -0
  11. package/dist/module/models/ContentCard.js.map +1 -0
  12. package/dist/{models → module/models}/HTMLProposition.js +8 -9
  13. package/dist/module/models/HTMLProposition.js.map +1 -0
  14. package/dist/module/models/InAppMessage.js +4 -0
  15. package/dist/module/models/InAppMessage.js.map +1 -0
  16. package/dist/module/models/JSONProposition.js +22 -0
  17. package/dist/module/models/JSONProposition.js.map +1 -0
  18. package/dist/module/models/Message.js +182 -0
  19. package/dist/module/models/Message.js.map +1 -0
  20. package/dist/module/models/MessagingDelegate.js +4 -0
  21. package/dist/module/models/MessagingDelegate.js.map +1 -0
  22. package/dist/module/models/MessagingEdgeEventType.js +24 -0
  23. package/dist/module/models/MessagingEdgeEventType.js.map +1 -0
  24. package/dist/module/models/MessagingProposition.js +57 -0
  25. package/dist/module/models/MessagingProposition.js.map +1 -0
  26. package/dist/module/models/MessagingPropositionItem.js +4 -0
  27. package/dist/module/models/MessagingPropositionItem.js.map +1 -0
  28. package/dist/module/models/PersonalizationSchema.js +26 -0
  29. package/dist/module/models/PersonalizationSchema.js.map +1 -0
  30. package/dist/module/models/PropositionItem.js +113 -0
  31. package/dist/module/models/PropositionItem.js.map +1 -0
  32. package/dist/module/models/ScopeDetails.js +2 -0
  33. package/dist/module/models/ScopeDetails.js.map +1 -0
  34. package/dist/{models/JSONProposition.js → module/models/index.js} +14 -12
  35. package/dist/module/models/index.js.map +1 -0
  36. package/dist/module/ui/components/Button/Button.js +57 -0
  37. package/dist/module/ui/components/Button/Button.js.map +1 -0
  38. package/dist/module/ui/components/Button/Button.spec.js +476 -0
  39. package/dist/module/ui/components/Button/Button.spec.js.map +1 -0
  40. package/dist/module/ui/components/ContentCardView/ContentCardView.js +257 -0
  41. package/dist/module/ui/components/ContentCardView/ContentCardView.js.map +1 -0
  42. package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js +363 -0
  43. package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js.map +1 -0
  44. package/dist/module/ui/components/DismissButton/DismissButton.js +70 -0
  45. package/dist/module/ui/components/DismissButton/DismissButton.js.map +1 -0
  46. package/dist/module/ui/components/DismissButton/DismissButton.spec.js +279 -0
  47. package/dist/module/ui/components/DismissButton/DismissButton.spec.js.map +1 -0
  48. package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js +34 -0
  49. package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js.map +1 -0
  50. package/dist/module/ui/components/Inbox/EmptyState.js +64 -0
  51. package/dist/module/ui/components/Inbox/EmptyState.js.map +1 -0
  52. package/dist/module/ui/components/Inbox/Inbox.js +235 -0
  53. package/dist/module/ui/components/Inbox/Inbox.js.map +1 -0
  54. package/dist/module/ui/components/Inbox/Inbox.spec.js +847 -0
  55. package/dist/module/ui/components/Inbox/Inbox.spec.js.map +1 -0
  56. package/dist/module/ui/components/Pagination/Pagination.js +176 -0
  57. package/dist/module/ui/components/Pagination/Pagination.js.map +1 -0
  58. package/dist/module/ui/components/Pagination/Pagination.spec.js +193 -0
  59. package/dist/module/ui/components/Pagination/Pagination.spec.js.map +1 -0
  60. package/dist/module/ui/components/UnreadIcon/UnreadIcon.js +184 -0
  61. package/dist/module/ui/components/UnreadIcon/UnreadIcon.js.map +1 -0
  62. package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js +815 -0
  63. package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js.map +1 -0
  64. package/dist/{models/ContentCard.js → module/ui/components/index.js} +12 -12
  65. package/dist/module/ui/components/index.js.map +1 -0
  66. package/dist/module/ui/hooks/index.js +18 -0
  67. package/dist/module/ui/hooks/index.js.map +1 -0
  68. package/dist/module/ui/hooks/useAspectRatio.js +33 -0
  69. package/dist/module/ui/hooks/useAspectRatio.js.map +1 -0
  70. package/dist/module/ui/hooks/useAspectRatio.spec.js +65 -0
  71. package/dist/module/ui/hooks/useAspectRatio.spec.js.map +1 -0
  72. package/dist/module/ui/hooks/useContentCardUI.js +51 -0
  73. package/dist/module/ui/hooks/useContentCardUI.js.map +1 -0
  74. package/dist/module/ui/hooks/useContentCardUI.spec.js +85 -0
  75. package/dist/module/ui/hooks/useContentCardUI.spec.js.map +1 -0
  76. package/dist/module/ui/hooks/useInbox.js +49 -0
  77. package/dist/module/ui/hooks/useInbox.js.map +1 -0
  78. package/dist/module/ui/hooks/useInbox.spec.js +93 -0
  79. package/dist/module/ui/hooks/useInbox.spec.js.map +1 -0
  80. package/dist/module/ui/hooks/useInboxSettings.js +26 -0
  81. package/dist/module/ui/hooks/useInboxSettings.js.map +1 -0
  82. package/dist/module/ui/hooks/useInboxSettings.spec.js +50 -0
  83. package/dist/module/ui/hooks/useInboxSettings.spec.js.map +1 -0
  84. package/dist/module/ui/index.js +10 -0
  85. package/dist/module/ui/index.js.map +1 -0
  86. package/dist/module/ui/providers/InboxProvider.js +27 -0
  87. package/dist/module/ui/providers/InboxProvider.js.map +1 -0
  88. package/dist/module/ui/theme/Theme.js +2 -0
  89. package/dist/module/ui/theme/Theme.js.map +1 -0
  90. package/dist/module/ui/theme/ThemeProvider.js +112 -0
  91. package/dist/module/ui/theme/ThemeProvider.js.map +1 -0
  92. package/dist/{models/InAppMessage.js → module/ui/theme/index.js} +6 -3
  93. package/dist/module/ui/theme/index.js.map +1 -0
  94. package/dist/module/ui/types/ContentViewEvent.js +2 -0
  95. package/dist/module/ui/types/ContentViewEvent.js.map +1 -0
  96. package/dist/module/ui/types/Templates.js +26 -0
  97. package/dist/module/ui/types/Templates.js.map +1 -0
  98. package/dist/{models/ScopeDetails.js → module/ui/types/index.js} +6 -3
  99. package/dist/module/ui/types/index.js.map +1 -0
  100. package/dist/module/ui/utils/generateCardHash.js +50 -0
  101. package/dist/module/ui/utils/generateCardHash.js.map +1 -0
  102. package/dist/module/ui/utils/generateCardHash.spec.js +103 -0
  103. package/dist/module/ui/utils/generateCardHash.spec.js.map +1 -0
  104. package/dist/module/ui/utils/inboxStorage.js +65 -0
  105. package/dist/module/ui/utils/inboxStorage.js.map +1 -0
  106. package/dist/module/ui/utils/inboxStorage.spec.js +123 -0
  107. package/dist/module/ui/utils/inboxStorage.spec.js.map +1 -0
  108. package/dist/module/ui/utils/index.js +5 -0
  109. package/dist/module/ui/utils/index.js.map +1 -0
  110. package/dist/{Messaging.d.ts → typescript/Messaging.d.ts} +23 -7
  111. package/dist/typescript/Messaging.d.ts.map +1 -0
  112. package/dist/{index.d.ts → typescript/index.d.ts} +4 -2
  113. package/dist/typescript/index.d.ts.map +1 -0
  114. package/dist/typescript/models/ContentCard.d.ts +57 -0
  115. package/dist/typescript/models/ContentCard.d.ts.map +1 -0
  116. package/dist/{models → typescript/models}/HTMLProposition.d.ts +1 -0
  117. package/dist/typescript/models/HTMLProposition.d.ts.map +1 -0
  118. package/dist/{models → typescript/models}/InAppMessage.d.ts +1 -0
  119. package/dist/typescript/models/InAppMessage.d.ts.map +1 -0
  120. package/dist/{models → typescript/models}/JSONProposition.d.ts +1 -0
  121. package/dist/typescript/models/JSONProposition.d.ts.map +1 -0
  122. package/dist/{models → typescript/models}/Message.d.ts +14 -0
  123. package/dist/typescript/models/Message.d.ts.map +1 -0
  124. package/dist/{models → typescript/models}/MessagingDelegate.d.ts +1 -0
  125. package/dist/typescript/models/MessagingDelegate.d.ts.map +1 -0
  126. package/dist/{models → typescript/models}/MessagingEdgeEventType.d.ts +1 -0
  127. package/dist/typescript/models/MessagingEdgeEventType.d.ts.map +1 -0
  128. package/dist/{models → typescript/models}/MessagingProposition.d.ts +1 -0
  129. package/dist/typescript/models/MessagingProposition.d.ts.map +1 -0
  130. package/dist/{models → typescript/models}/MessagingPropositionItem.d.ts +1 -0
  131. package/dist/typescript/models/MessagingPropositionItem.d.ts.map +1 -0
  132. package/dist/{models → typescript/models}/PersonalizationSchema.d.ts +2 -0
  133. package/dist/typescript/models/PersonalizationSchema.d.ts.map +1 -0
  134. package/dist/{models → typescript/models}/PropositionItem.d.ts +1 -0
  135. package/dist/typescript/models/PropositionItem.d.ts.map +1 -0
  136. package/dist/{models → typescript/models}/ScopeDetails.d.ts +1 -0
  137. package/dist/typescript/models/ScopeDetails.d.ts.map +1 -0
  138. package/dist/typescript/models/index.d.ts +11 -0
  139. package/dist/typescript/models/index.d.ts.map +1 -0
  140. package/dist/typescript/ui/components/Button/Button.d.ts +14 -0
  141. package/dist/typescript/ui/components/Button/Button.d.ts.map +1 -0
  142. package/dist/typescript/ui/components/Button/Button.spec.d.ts +2 -0
  143. package/dist/typescript/ui/components/Button/Button.spec.d.ts.map +1 -0
  144. package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts +39 -0
  145. package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts.map +1 -0
  146. package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts +2 -0
  147. package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts.map +1 -0
  148. package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts +13 -0
  149. package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts.map +1 -0
  150. package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts +2 -0
  151. package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts.map +1 -0
  152. package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts +5 -0
  153. package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts.map +1 -0
  154. package/dist/typescript/ui/components/Inbox/EmptyState.d.ts +19 -0
  155. package/dist/typescript/ui/components/Inbox/EmptyState.d.ts.map +1 -0
  156. package/dist/typescript/ui/components/Inbox/Inbox.d.ts +21 -0
  157. package/dist/typescript/ui/components/Inbox/Inbox.d.ts.map +1 -0
  158. package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts +2 -0
  159. package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts.map +1 -0
  160. package/dist/typescript/ui/components/Pagination/Pagination.d.ts +14 -0
  161. package/dist/typescript/ui/components/Pagination/Pagination.d.ts.map +1 -0
  162. package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts +2 -0
  163. package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts.map +1 -0
  164. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts +14 -0
  165. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts.map +1 -0
  166. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts +2 -0
  167. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts.map +1 -0
  168. package/dist/typescript/ui/components/index.d.ts +10 -0
  169. package/dist/typescript/ui/components/index.d.ts.map +1 -0
  170. package/dist/typescript/ui/hooks/index.d.ts +4 -0
  171. package/dist/typescript/ui/hooks/index.d.ts.map +1 -0
  172. package/dist/typescript/ui/hooks/useAspectRatio.d.ts +3 -0
  173. package/dist/typescript/ui/hooks/useAspectRatio.d.ts.map +1 -0
  174. package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts +2 -0
  175. package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts.map +1 -0
  176. package/dist/typescript/ui/hooks/useContentCardUI.d.ts +14 -0
  177. package/dist/typescript/ui/hooks/useContentCardUI.d.ts.map +1 -0
  178. package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts +2 -0
  179. package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts.map +1 -0
  180. package/dist/typescript/ui/hooks/useInbox.d.ts +12 -0
  181. package/dist/typescript/ui/hooks/useInbox.d.ts.map +1 -0
  182. package/dist/typescript/ui/hooks/useInbox.spec.d.ts +2 -0
  183. package/dist/typescript/ui/hooks/useInbox.spec.d.ts.map +1 -0
  184. package/dist/typescript/ui/hooks/useInboxSettings.d.ts +7 -0
  185. package/dist/typescript/ui/hooks/useInboxSettings.d.ts.map +1 -0
  186. package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts +2 -0
  187. package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts.map +1 -0
  188. package/dist/typescript/ui/index.d.ts +8 -0
  189. package/dist/typescript/ui/index.d.ts.map +1 -0
  190. package/dist/typescript/ui/providers/InboxProvider.d.ts +56 -0
  191. package/dist/typescript/ui/providers/InboxProvider.d.ts.map +1 -0
  192. package/dist/typescript/ui/theme/Theme.d.ts +44 -0
  193. package/dist/typescript/ui/theme/Theme.d.ts.map +1 -0
  194. package/dist/typescript/ui/theme/ThemeProvider.d.ts +21 -0
  195. package/dist/typescript/ui/theme/ThemeProvider.d.ts.map +1 -0
  196. package/dist/typescript/ui/theme/index.d.ts +3 -0
  197. package/dist/typescript/ui/theme/index.d.ts.map +1 -0
  198. package/dist/typescript/ui/types/ContentViewEvent.d.ts +9 -0
  199. package/dist/typescript/ui/types/ContentViewEvent.d.ts.map +1 -0
  200. package/dist/typescript/ui/types/Templates.d.ts +43 -0
  201. package/dist/typescript/ui/types/Templates.d.ts.map +1 -0
  202. package/dist/typescript/ui/types/index.d.ts +3 -0
  203. package/dist/typescript/ui/types/index.d.ts.map +1 -0
  204. package/dist/typescript/ui/utils/generateCardHash.d.ts +21 -0
  205. package/dist/typescript/ui/utils/generateCardHash.d.ts.map +1 -0
  206. package/dist/typescript/ui/utils/generateCardHash.spec.d.ts +2 -0
  207. package/dist/typescript/ui/utils/generateCardHash.spec.d.ts.map +1 -0
  208. package/dist/typescript/ui/utils/inboxStorage.d.ts +20 -0
  209. package/dist/typescript/ui/utils/inboxStorage.d.ts.map +1 -0
  210. package/dist/typescript/ui/utils/inboxStorage.spec.d.ts +2 -0
  211. package/dist/typescript/ui/utils/inboxStorage.spec.d.ts.map +1 -0
  212. package/dist/typescript/ui/utils/index.d.ts +3 -0
  213. package/dist/typescript/ui/utils/index.d.ts.map +1 -0
  214. package/ios/src/RCTAEPMessaging.mm +15 -0
  215. package/ios/src/RCTAEPMessaging.swift +61 -3
  216. package/ios/src/RCTAEPMessagingConstants.swift +4 -1
  217. package/jest.config.js +15 -0
  218. package/package.json +33 -5
  219. package/src/Messaging.ts +288 -32
  220. package/src/index.ts +3 -3
  221. package/src/models/ContentCard.ts +52 -27
  222. package/src/models/HTMLProposition.ts +1 -1
  223. package/src/models/JSONProposition.ts +1 -1
  224. package/src/models/Message.ts +50 -0
  225. package/src/models/PersonalizationSchema.ts +1 -0
  226. package/src/models/index.ts +22 -0
  227. package/src/ui/components/Button/Button.spec.tsx +496 -0
  228. package/src/ui/components/Button/Button.tsx +76 -0
  229. package/src/ui/components/ContentCardView/ContentCardView.spec.tsx +278 -0
  230. package/src/ui/components/ContentCardView/ContentCardView.tsx +400 -0
  231. package/src/ui/components/DismissButton/DismissButton.spec.tsx +314 -0
  232. package/src/ui/components/DismissButton/DismissButton.tsx +100 -0
  233. package/src/ui/components/FullScreenCenterView/FullScreenCenterView.tsx +32 -0
  234. package/src/ui/components/Inbox/EmptyState.tsx +89 -0
  235. package/src/ui/components/Inbox/Inbox.spec.tsx +478 -0
  236. package/src/ui/components/Inbox/Inbox.tsx +275 -0
  237. package/src/ui/components/Pagination/Pagination.spec.tsx +159 -0
  238. package/src/ui/components/Pagination/Pagination.tsx +222 -0
  239. package/src/ui/components/UnreadIcon/UnreadIcon.spec.tsx +878 -0
  240. package/src/ui/components/UnreadIcon/UnreadIcon.tsx +234 -0
  241. package/src/ui/components/index.ts +22 -0
  242. package/{dist/models/MessagingPropositionItem.js → src/ui/hooks/index.ts} +5 -4
  243. package/src/ui/hooks/useAspectRatio.spec.tsx +66 -0
  244. package/src/ui/hooks/useAspectRatio.tsx +39 -0
  245. package/src/ui/hooks/useContentCardUI.spec.tsx +82 -0
  246. package/src/ui/hooks/useContentCardUI.ts +48 -0
  247. package/src/ui/hooks/useInbox.spec.tsx +87 -0
  248. package/src/ui/hooks/useInbox.ts +46 -0
  249. package/src/ui/hooks/useInboxSettings.spec.tsx +41 -0
  250. package/src/ui/hooks/useInboxSettings.ts +24 -0
  251. package/src/ui/index.ts +7 -0
  252. package/src/ui/providers/InboxProvider.tsx +79 -0
  253. package/src/ui/theme/Theme.ts +57 -0
  254. package/src/ui/theme/ThemeProvider.tsx +120 -0
  255. package/src/ui/theme/index.ts +14 -0
  256. package/src/ui/types/ContentViewEvent.ts +20 -0
  257. package/src/ui/types/Templates.ts +77 -0
  258. package/src/ui/types/index.ts +14 -0
  259. package/src/ui/utils/generateCardHash.spec.tsx +86 -0
  260. package/src/ui/utils/generateCardHash.ts +59 -0
  261. package/src/ui/utils/inboxStorage.spec.tsx +136 -0
  262. package/src/ui/utils/inboxStorage.ts +64 -0
  263. package/src/ui/utils/index.ts +3 -0
  264. package/tutorials/ContentCardCustomizationGuide.md +661 -0
  265. package/tutorials/ContentCards.md +419 -0
  266. package/tutorials/In-App Messaging.md +31 -0
  267. package/tutorials/Inbox.md +515 -0
  268. package/tutorials/resources/image-only-template.png +0 -0
  269. package/tutorials/resources/large-image-template.png +0 -0
  270. package/tutorials/resources/small-image-template.png +0 -0
  271. package/dist/Messaging.js +0 -151
  272. package/dist/Messaging.js.map +0 -1
  273. package/dist/index.js +0 -34
  274. package/dist/index.js.map +0 -1
  275. package/dist/models/ContentCard.d.ts +0 -51
  276. package/dist/models/ContentCard.js.map +0 -1
  277. package/dist/models/HTMLProposition.js.map +0 -1
  278. package/dist/models/InAppMessage.js.map +0 -1
  279. package/dist/models/JSONProposition.js.map +0 -1
  280. package/dist/models/Message.js +0 -114
  281. package/dist/models/Message.js.map +0 -1
  282. package/dist/models/MessagingDelegate.js +0 -14
  283. package/dist/models/MessagingDelegate.js.map +0 -1
  284. package/dist/models/MessagingEdgeEventType.js +0 -24
  285. package/dist/models/MessagingEdgeEventType.js.map +0 -1
  286. package/dist/models/MessagingProposition.js +0 -59
  287. package/dist/models/MessagingProposition.js.map +0 -1
  288. package/dist/models/MessagingPropositionItem.js.map +0 -1
  289. package/dist/models/PersonalizationSchema.js +0 -25
  290. package/dist/models/PersonalizationSchema.js.map +0 -1
  291. package/dist/models/PropositionItem.js +0 -78
  292. package/dist/models/PropositionItem.js.map +0 -1
  293. package/dist/models/ScopeDetails.js.map +0 -1
@@ -0,0 +1,847 @@
1
+ "use strict";
2
+
3
+ /*
4
+ Copyright 2026 Adobe. All rights reserved.
5
+ This file is licensed to you under the Apache License, Version 2.0 (the
6
+ "License"); you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
9
+ or agreed to in writing, software distributed under the License is
10
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
11
+ ANY KIND, either express or implied. See the License for the specific
12
+ language governing permissions and limitations under the License.
13
+ */
14
+
15
+ import { render, screen, act, waitFor } from '@testing-library/react-native';
16
+ import React from 'react';
17
+ import { Dimensions, Text } from 'react-native';
18
+ import { generateCardHash } from "../../utils/generateCardHash.js";
19
+ import EmptyState from "./EmptyState.js";
20
+ import { Inbox } from "./Inbox.js";
21
+ jest.mock('../../hooks', () => ({
22
+ useContentCardUI: jest.fn(),
23
+ useInbox: jest.fn()
24
+ }));
25
+ jest.mock('../../utils/inboxStorage', () => ({
26
+ loadInboxState: jest.fn().mockResolvedValue({
27
+ dismissed: [],
28
+ interacted: []
29
+ }),
30
+ saveInboxState: jest.fn().mockResolvedValue(undefined)
31
+ }));
32
+ const mockContentCardView = jest.fn((..._args) => null);
33
+ jest.mock('../ContentCardView/ContentCardView', () => {
34
+ return {
35
+ ContentCardView: props => {
36
+ mockContentCardView(props);
37
+ return null;
38
+ }
39
+ };
40
+ });
41
+ jest.mock('../../providers/InboxProvider', () => ({
42
+ __esModule: true,
43
+ default: ({
44
+ children
45
+ }) => children
46
+ }));
47
+
48
+ // jest.mock('../../hooks/useAspectRatio', () => ({
49
+ // __esModule: true,
50
+ // default: () => 1.5,
51
+ // }));
52
+
53
+ const {
54
+ useContentCardUI,
55
+ useInbox
56
+ } = jest.requireMock('../../hooks');
57
+ const {
58
+ loadInboxState,
59
+ saveInboxState
60
+ } = jest.requireMock('../../utils/inboxStorage');
61
+ describe('Inbox', () => {
62
+ const surface = 'test-surface';
63
+ const baseSettings = {
64
+ content: {
65
+ heading: {
66
+ content: 'Heading'
67
+ },
68
+ layout: {
69
+ orientation: 'horizontal'
70
+ },
71
+ capacity: 10,
72
+ emptyStateSettings: {
73
+ message: {
74
+ content: 'No Content Available'
75
+ },
76
+ image: {
77
+ light: {
78
+ url: 'https://example.com/image.png'
79
+ }
80
+ }
81
+ },
82
+ unread_indicator: {
83
+ unread_bg: {
84
+ clr: {
85
+ light: '#EEE',
86
+ dark: '#111'
87
+ }
88
+ },
89
+ unread_icon: {
90
+ placement: 'topright',
91
+ image: {
92
+ url: 'https://example.com/icon.png'
93
+ }
94
+ }
95
+ },
96
+ isUnreadEnabled: true
97
+ },
98
+ showPagination: false
99
+ };
100
+ beforeEach(() => {
101
+ jest.clearAllMocks();
102
+ jest.spyOn(Dimensions, 'get').mockReturnValue({
103
+ width: 400,
104
+ height: 800,
105
+ scale: 2,
106
+ fontScale: 2
107
+ });
108
+ });
109
+ describe('outer inbox states', () => {
110
+ it('renders loading state', () => {
111
+ useContentCardUI.mockReturnValue({
112
+ content: undefined,
113
+ isLoading: true,
114
+ error: null
115
+ });
116
+ const Loading = /*#__PURE__*/React.createElement(Text, null, "Loading...");
117
+ const CC = Inbox;
118
+ render(/*#__PURE__*/React.createElement(CC, {
119
+ surface: surface,
120
+ settings: baseSettings,
121
+ isLoading: true,
122
+ LoadingComponent: Loading
123
+ }));
124
+ expect(screen.getByText('Loading...')).toBeTruthy();
125
+ });
126
+ it('renders error state', () => {
127
+ useContentCardUI.mockReturnValue({
128
+ content: undefined,
129
+ isLoading: false,
130
+ error: null
131
+ });
132
+ const ErrorComp = /*#__PURE__*/React.createElement(Text, null, "Error!");
133
+ const CC = Inbox;
134
+ render(/*#__PURE__*/React.createElement(CC, {
135
+ surface: surface,
136
+ settings: baseSettings,
137
+ error: new Error('x'),
138
+ ErrorComponent: ErrorComp
139
+ }));
140
+ expect(screen.getByText('Error!')).toBeTruthy();
141
+ });
142
+ it('renders fallback when no settings provided', () => {
143
+ useContentCardUI.mockReturnValue({
144
+ content: undefined,
145
+ isLoading: false,
146
+ error: null
147
+ });
148
+ const Fallback = /*#__PURE__*/React.createElement(Text, null, "Fallback");
149
+ const CC = Inbox;
150
+ render(/*#__PURE__*/React.createElement(CC, {
151
+ surface: surface,
152
+ settings: null,
153
+ FallbackComponent: Fallback
154
+ }));
155
+ expect(screen.getByText('Fallback')).toBeTruthy();
156
+ });
157
+ it('renders outer LoadingComponent when inbox is loading', () => {
158
+ useContentCardUI.mockReturnValue({
159
+ content: undefined,
160
+ isLoading: false,
161
+ error: null
162
+ });
163
+ const Loading = /*#__PURE__*/React.createElement(Text, {
164
+ testID: "outer-loading"
165
+ }, "Loading outer...");
166
+ const CC = Inbox;
167
+ render(/*#__PURE__*/React.createElement(CC, {
168
+ surface: surface,
169
+ settings: baseSettings,
170
+ isLoading: true,
171
+ LoadingComponent: Loading
172
+ }));
173
+ expect(screen.getByTestId('outer-loading')).toBeTruthy();
174
+ });
175
+ });
176
+ describe('empty content rendering', () => {
177
+ it('renders empty state when content is empty', () => {
178
+ useContentCardUI.mockReturnValue({
179
+ content: [],
180
+ isLoading: false,
181
+ error: null
182
+ });
183
+ const CC = Inbox;
184
+ render(/*#__PURE__*/React.createElement(CC, {
185
+ surface: surface,
186
+ settings: baseSettings
187
+ }));
188
+ expect(screen.getByText('No Content Available')).toBeTruthy();
189
+ });
190
+ it('handles empty content array', () => {
191
+ useInbox.mockReturnValue({
192
+ settings: baseSettings,
193
+ isLoading: false,
194
+ error: null
195
+ });
196
+ useContentCardUI.mockReturnValue({
197
+ content: [],
198
+ isLoading: false,
199
+ error: null
200
+ });
201
+ const CC = Inbox;
202
+ render(/*#__PURE__*/React.createElement(CC, {
203
+ surface: surface,
204
+ settings: baseSettings
205
+ }));
206
+ expect(mockContentCardView.mock.calls.length).toBe(0);
207
+ expect(screen.getByText('No Content Available')).toBeTruthy();
208
+ });
209
+ it('uses light image when colorScheme is null and falls back to default message', () => {
210
+ jest.spyOn(require('react-native'), 'useColorScheme').mockReturnValue(null);
211
+ const settings = {
212
+ ...baseSettings,
213
+ content: {
214
+ ...baseSettings.content,
215
+ emptyStateSettings: {
216
+ image: {
217
+ url: 'https://example.com/light-only.png'
218
+ }
219
+ }
220
+ }
221
+ };
222
+ useContentCardUI.mockReturnValue({
223
+ content: [],
224
+ isLoading: false,
225
+ error: null
226
+ });
227
+ const CC = Inbox;
228
+ const {
229
+ UNSAFE_getByType
230
+ } = render(/*#__PURE__*/React.createElement(CC, {
231
+ surface: surface,
232
+ settings: settings
233
+ }));
234
+ const empty = UNSAFE_getByType(EmptyState);
235
+ expect(empty.props.image).toBe('https://example.com/light-only.png');
236
+ expect(empty.props.text).toBe('No Content Available');
237
+ });
238
+ });
239
+ describe('heading and layout', () => {
240
+ it('sets heading color based on color scheme: dark -> #FFFFFF', () => {
241
+ jest.spyOn(require('react-native'), 'useColorScheme').mockReturnValue('dark');
242
+ useInbox.mockReturnValue({
243
+ settings: baseSettings,
244
+ isLoading: false,
245
+ error: null
246
+ });
247
+ const template = {
248
+ id: '1',
249
+ type: 'SmallImage',
250
+ data: {
251
+ content: {
252
+ title: {
253
+ content: 'T'
254
+ },
255
+ body: {
256
+ content: 'B'
257
+ },
258
+ image: {
259
+ url: 'u'
260
+ }
261
+ }
262
+ }
263
+ };
264
+ useContentCardUI.mockReturnValue({
265
+ content: [template],
266
+ isLoading: false,
267
+ error: null
268
+ });
269
+ const CC = Inbox;
270
+ const {
271
+ getByText
272
+ } = render(/*#__PURE__*/React.createElement(CC, {
273
+ surface: surface,
274
+ settings: baseSettings
275
+ }));
276
+ const heading = getByText('Heading');
277
+ const styles = Array.isArray(heading.props.style) ? heading.props.style : [heading.props.style];
278
+ expect(styles.some(s => s && s.color === '#FFFFFF')).toBe(true);
279
+ });
280
+ });
281
+ describe('inner inbox states', () => {
282
+ it('renders inner ErrorComponent when data hook errors', () => {
283
+ useInbox.mockReturnValue({
284
+ settings: baseSettings,
285
+ isLoading: false,
286
+ error: null
287
+ });
288
+ useContentCardUI.mockReturnValue({
289
+ content: undefined,
290
+ isLoading: false,
291
+ error: new Error('inner')
292
+ });
293
+ const ErrorComp = /*#__PURE__*/React.createElement(Text, {
294
+ testID: "inner-error"
295
+ }, "Inner Error!");
296
+ const CC = Inbox;
297
+ render(/*#__PURE__*/React.createElement(CC, {
298
+ surface: surface,
299
+ settings: baseSettings,
300
+ ErrorComponent: ErrorComp
301
+ }));
302
+ expect(screen.getByTestId('inner-error')).toBeTruthy();
303
+ });
304
+ it('uses provided EmptyComponent and passes empty state settings (inner)', () => {
305
+ useInbox.mockReturnValue({
306
+ settings: baseSettings,
307
+ isLoading: false,
308
+ error: null
309
+ });
310
+ useContentCardUI.mockReturnValue({
311
+ content: [],
312
+ isLoading: false,
313
+ error: null
314
+ });
315
+ const EmptyStub = ({
316
+ message
317
+ }) => /*#__PURE__*/React.createElement(Text, {
318
+ testID: "inner-empty"
319
+ }, message?.content);
320
+ const CC = Inbox;
321
+ render(/*#__PURE__*/React.createElement(CC, {
322
+ surface: surface,
323
+ settings: baseSettings,
324
+ EmptyComponent: /*#__PURE__*/React.createElement(EmptyStub, null)
325
+ }));
326
+ expect(screen.getByTestId('inner-empty')).toBeTruthy();
327
+ expect(screen.getByText('No Content Available')).toBeTruthy();
328
+ });
329
+ it('renders inner FallbackComponent when content is undefined and settings exist', () => {
330
+ useInbox.mockReturnValue({
331
+ settings: baseSettings,
332
+ isLoading: false,
333
+ error: null
334
+ });
335
+ useContentCardUI.mockReturnValue({
336
+ content: undefined,
337
+ isLoading: false,
338
+ error: null
339
+ });
340
+ const Fallback = /*#__PURE__*/React.createElement(Text, {
341
+ testID: "inner-fallback"
342
+ }, "Inner Fallback");
343
+ const CC = Inbox;
344
+ render(/*#__PURE__*/React.createElement(CC, {
345
+ surface: surface,
346
+ FallbackComponent: Fallback
347
+ }));
348
+ expect(screen.getByTestId('inner-fallback')).toBeTruthy();
349
+ });
350
+ });
351
+ describe('renderItem passthrough', () => {
352
+ it('passes expected props to ContentCardView via renderItem (horizontal)', () => {
353
+ useInbox.mockReturnValue({
354
+ settings: baseSettings,
355
+ isLoading: false,
356
+ error: null
357
+ });
358
+ const template = {
359
+ id: '1',
360
+ type: 'SmallImage',
361
+ data: {
362
+ content: {
363
+ title: {
364
+ content: 'T'
365
+ },
366
+ body: {
367
+ content: 'B'
368
+ },
369
+ image: {
370
+ url: 'u'
371
+ }
372
+ }
373
+ }
374
+ };
375
+ useContentCardUI.mockReturnValue({
376
+ content: [template],
377
+ isLoading: false,
378
+ error: null
379
+ });
380
+ const CC = Inbox;
381
+ render(/*#__PURE__*/React.createElement(CC, {
382
+ surface: surface,
383
+ settings: baseSettings
384
+ }));
385
+ expect(mockContentCardView).toHaveBeenCalled();
386
+ const args = mockContentCardView.mock.calls[0][0];
387
+ expect(args.template).toEqual(template);
388
+ expect(args.style).toEqual(expect.arrayContaining([expect.anything()]));
389
+ });
390
+ });
391
+ describe('capacity and dismissal', () => {
392
+ it('renders up to capacity and backfills after dismiss', async () => {
393
+ const capSettings = {
394
+ ...baseSettings,
395
+ content: {
396
+ ...baseSettings.content,
397
+ capacity: 2
398
+ }
399
+ };
400
+ useInbox.mockReturnValue({
401
+ settings: capSettings,
402
+ isLoading: false,
403
+ error: null
404
+ });
405
+ const t1 = {
406
+ id: '1',
407
+ type: 'SmallImage',
408
+ data: {
409
+ content: {
410
+ title: {
411
+ content: 'T1'
412
+ },
413
+ body: {
414
+ content: 'B1'
415
+ },
416
+ image: {
417
+ url: 'u1'
418
+ }
419
+ }
420
+ }
421
+ };
422
+ const t2 = {
423
+ id: '2',
424
+ type: 'SmallImage',
425
+ data: {
426
+ content: {
427
+ title: {
428
+ content: 'T2'
429
+ },
430
+ body: {
431
+ content: 'B2'
432
+ },
433
+ image: {
434
+ url: 'u2'
435
+ }
436
+ }
437
+ }
438
+ };
439
+ const t3 = {
440
+ id: '3',
441
+ type: 'SmallImage',
442
+ data: {
443
+ content: {
444
+ title: {
445
+ content: 'T3'
446
+ },
447
+ body: {
448
+ content: 'B3'
449
+ },
450
+ image: {
451
+ url: 'u3'
452
+ }
453
+ }
454
+ }
455
+ };
456
+ useContentCardUI.mockReturnValue({
457
+ content: [t1, t2, t3],
458
+ isLoading: false,
459
+ error: null
460
+ });
461
+ const CC = Inbox;
462
+ const utils = render(/*#__PURE__*/React.createElement(CC, {
463
+ surface: surface,
464
+ settings: capSettings
465
+ }));
466
+ await waitFor(() => {
467
+ expect(mockContentCardView.mock.calls.length).toBeGreaterThanOrEqual(2);
468
+ });
469
+ // Get the last render's calls (component may render multiple times)
470
+ const lastTwoCalls = mockContentCardView.mock.calls.slice(-2);
471
+ expect(lastTwoCalls.length).toBe(2);
472
+ const firstProps = lastTwoCalls[0][0];
473
+ await act(async () => {
474
+ firstProps.listener?.('onDismiss', firstProps.template);
475
+ });
476
+ mockContentCardView.mockClear();
477
+ utils.rerender(/*#__PURE__*/React.createElement(CC, {
478
+ surface: surface,
479
+ settings: capSettings,
480
+ extraData: () => {}
481
+ }));
482
+ await waitFor(() => {
483
+ expect(mockContentCardView.mock.calls.length).toBeGreaterThanOrEqual(1);
484
+ });
485
+ const renderedIds = mockContentCardView.mock.calls.map(c => c[0].template.id);
486
+ expect(renderedIds).toEqual(expect.arrayContaining(['3']));
487
+ expect(renderedIds).not.toContain('1');
488
+ });
489
+ });
490
+ describe('layout and styling', () => {
491
+ it('renders cards vertically when layout orientation is vertical', () => {
492
+ const verticalSettings = {
493
+ ...baseSettings,
494
+ content: {
495
+ ...baseSettings.content,
496
+ layout: {
497
+ orientation: 'vertical'
498
+ }
499
+ }
500
+ };
501
+ useInbox.mockReturnValue({
502
+ settings: verticalSettings,
503
+ isLoading: false,
504
+ error: null
505
+ });
506
+ const template = {
507
+ id: '1',
508
+ type: 'SmallImage',
509
+ data: {
510
+ content: {
511
+ title: {
512
+ content: 'T'
513
+ },
514
+ body: {
515
+ content: 'B'
516
+ },
517
+ image: {
518
+ url: 'u'
519
+ }
520
+ }
521
+ }
522
+ };
523
+ useContentCardUI.mockReturnValue({
524
+ content: [template],
525
+ isLoading: false,
526
+ error: null
527
+ });
528
+ const CC = Inbox;
529
+ render(/*#__PURE__*/React.createElement(CC, {
530
+ surface: surface,
531
+ settings: verticalSettings
532
+ }));
533
+ expect(mockContentCardView).toHaveBeenCalled();
534
+ const args = mockContentCardView.mock.calls[0][0];
535
+ expect(args.style).toBeUndefined();
536
+ });
537
+ it('does not render heading when heading content is not provided', () => {
538
+ const settingsWithoutHeading = {
539
+ ...baseSettings,
540
+ content: {
541
+ ...baseSettings.content,
542
+ heading: undefined
543
+ }
544
+ };
545
+ useInbox.mockReturnValue({
546
+ settings: settingsWithoutHeading,
547
+ isLoading: false,
548
+ error: null
549
+ });
550
+ const template = {
551
+ id: '1',
552
+ type: 'SmallImage',
553
+ data: {
554
+ content: {
555
+ title: {
556
+ content: 'T'
557
+ },
558
+ body: {
559
+ content: 'B'
560
+ },
561
+ image: {
562
+ url: 'u'
563
+ }
564
+ }
565
+ }
566
+ };
567
+ useContentCardUI.mockReturnValue({
568
+ content: [template],
569
+ isLoading: false,
570
+ error: null
571
+ });
572
+ const CC = Inbox;
573
+ const {
574
+ queryByText
575
+ } = render(/*#__PURE__*/React.createElement(CC, {
576
+ surface: surface,
577
+ settings: settingsWithoutHeading
578
+ }));
579
+ expect(queryByText('Heading')).toBeNull();
580
+ });
581
+ });
582
+ describe('interaction tracking', () => {
583
+ it('does not add duplicate entries to store on multiple interactions', async () => {
584
+ const testSurface = 'test-surface-duplicate-interact';
585
+ useInbox.mockReturnValue({
586
+ settings: baseSettings,
587
+ isLoading: false,
588
+ error: null
589
+ });
590
+ const template = {
591
+ id: '1',
592
+ type: 'SmallImage',
593
+ data: {
594
+ content: {
595
+ title: {
596
+ content: 'T'
597
+ },
598
+ body: {
599
+ content: 'B'
600
+ },
601
+ image: {
602
+ url: 'u'
603
+ }
604
+ }
605
+ }
606
+ };
607
+ useContentCardUI.mockReturnValue({
608
+ content: [template],
609
+ isLoading: false,
610
+ error: null
611
+ });
612
+ const CC = Inbox;
613
+ render(/*#__PURE__*/React.createElement(CC, {
614
+ surface: testSurface,
615
+ settings: baseSettings
616
+ }));
617
+ await waitFor(() => {
618
+ expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0);
619
+ });
620
+ const args = mockContentCardView.mock.calls[0][0];
621
+ await act(async () => {
622
+ args.listener?.('onInteract', args.template);
623
+ args.listener?.('onInteract', args.template);
624
+ args.listener?.('onInteract', args.template);
625
+ });
626
+ const newContent = [{
627
+ ...template
628
+ }];
629
+ useContentCardUI.mockReturnValue({
630
+ content: newContent,
631
+ isLoading: false,
632
+ error: null
633
+ });
634
+ mockContentCardView.mockClear();
635
+ render(/*#__PURE__*/React.createElement(CC, {
636
+ surface: testSurface,
637
+ settings: baseSettings
638
+ }));
639
+ await waitFor(() => {
640
+ expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0);
641
+ });
642
+ const updatedArgs = mockContentCardView.mock.calls[0][0];
643
+ expect(updatedArgs.template.isRead).toBe(true);
644
+ });
645
+ });
646
+ describe('loadInboxState effect (persisted state merge)', () => {
647
+ const template = {
648
+ id: '1',
649
+ type: 'SmallImage',
650
+ data: {
651
+ content: {
652
+ title: {
653
+ content: 'T'
654
+ },
655
+ body: {
656
+ content: 'B'
657
+ },
658
+ image: {
659
+ url: 'u'
660
+ }
661
+ }
662
+ }
663
+ };
664
+ it('merges persisted dismissed ids from loadInboxState into the store so those cards are not shown', async () => {
665
+ const hash = generateCardHash(template);
666
+ loadInboxState.mockResolvedValueOnce({
667
+ dismissed: [hash],
668
+ interacted: []
669
+ });
670
+ useContentCardUI.mockReturnValue({
671
+ content: [template],
672
+ isLoading: false,
673
+ error: null
674
+ });
675
+ const settingsWithActivity = {
676
+ ...baseSettings,
677
+ activityId: 'activity-load-merge'
678
+ };
679
+ const CC = Inbox;
680
+ render(/*#__PURE__*/React.createElement(CC, {
681
+ surface: "surface-load-merge",
682
+ settings: settingsWithActivity
683
+ }));
684
+ await waitFor(() => {
685
+ expect(loadInboxState).toHaveBeenCalledWith('activity-load-merge');
686
+ });
687
+ await waitFor(() => {
688
+ expect(screen.getByText('No Content Available')).toBeTruthy();
689
+ });
690
+ });
691
+ it('merges persisted interacted ids when isUnreadEnabled and marks matching cards as read', async () => {
692
+ const hash = generateCardHash(template);
693
+ loadInboxState.mockResolvedValueOnce({
694
+ dismissed: [],
695
+ interacted: [hash]
696
+ });
697
+ useContentCardUI.mockReturnValue({
698
+ content: [template],
699
+ isLoading: false,
700
+ error: null
701
+ });
702
+ const settingsWithActivity = {
703
+ ...baseSettings,
704
+ activityId: 'activity-load-interacted',
705
+ content: {
706
+ ...baseSettings.content,
707
+ isUnreadEnabled: true
708
+ }
709
+ };
710
+ const CC = Inbox;
711
+ render(/*#__PURE__*/React.createElement(CC, {
712
+ surface: "surface-load-interacted",
713
+ settings: settingsWithActivity
714
+ }));
715
+ await waitFor(() => expect(loadInboxState).toHaveBeenCalledWith('activity-load-interacted'));
716
+ await waitFor(() => expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0));
717
+ const props = mockContentCardView.mock.calls[mockContentCardView.mock.calls.length - 1][0];
718
+ expect(props.template.isRead).toBe(true);
719
+ });
720
+ it('applies cleanup so resolving loadInboxState after unmount does not throw', async () => {
721
+ let resolveLoad;
722
+ const loadPromise = new Promise(resolve => {
723
+ resolveLoad = resolve;
724
+ });
725
+ loadInboxState.mockImplementationOnce(() => loadPromise);
726
+ useContentCardUI.mockReturnValue({
727
+ content: [template],
728
+ isLoading: false,
729
+ error: null
730
+ });
731
+ const settingsWithActivity = {
732
+ ...baseSettings,
733
+ activityId: 'activity-load-cancel'
734
+ };
735
+ const CC = Inbox;
736
+ const {
737
+ unmount
738
+ } = render(/*#__PURE__*/React.createElement(CC, {
739
+ surface: "surface-load-cancel",
740
+ settings: settingsWithActivity
741
+ }));
742
+ unmount();
743
+ await act(async () => {
744
+ resolveLoad({
745
+ dismissed: [],
746
+ interacted: []
747
+ });
748
+ await loadPromise;
749
+ });
750
+ });
751
+ });
752
+ describe('saveInboxState on card events', () => {
753
+ const template = {
754
+ id: '1',
755
+ type: 'SmallImage',
756
+ data: {
757
+ content: {
758
+ title: {
759
+ content: 'T'
760
+ },
761
+ body: {
762
+ content: 'B'
763
+ },
764
+ image: {
765
+ url: 'u'
766
+ }
767
+ }
768
+ }
769
+ };
770
+ it('calls saveInboxState on onDismiss when activityId is set', async () => {
771
+ useContentCardUI.mockReturnValue({
772
+ content: [template],
773
+ isLoading: false,
774
+ error: null
775
+ });
776
+ const settingsWithActivity = {
777
+ ...baseSettings,
778
+ activityId: 'activity-save-dismiss'
779
+ };
780
+ const CC = Inbox;
781
+ render(/*#__PURE__*/React.createElement(CC, {
782
+ surface: "surface-save-dismiss",
783
+ settings: settingsWithActivity
784
+ }));
785
+ await waitFor(() => expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0));
786
+ const props = mockContentCardView.mock.calls[0][0];
787
+ const expectedHash = generateCardHash(template);
788
+ await act(async () => {
789
+ props.listener?.('onDismiss', props.template);
790
+ });
791
+ expect(saveInboxState).toHaveBeenCalledWith('activity-save-dismiss', expect.objectContaining({
792
+ dismissed: expect.arrayContaining([expectedHash]),
793
+ interacted: []
794
+ }));
795
+ });
796
+ it('calls saveInboxState on onInteract when activityId is set and unread is enabled', async () => {
797
+ useContentCardUI.mockReturnValue({
798
+ content: [template],
799
+ isLoading: false,
800
+ error: null
801
+ });
802
+ const settingsWithActivity = {
803
+ ...baseSettings,
804
+ activityId: 'activity-save-interact',
805
+ content: {
806
+ ...baseSettings.content,
807
+ isUnreadEnabled: true
808
+ }
809
+ };
810
+ const CC = Inbox;
811
+ render(/*#__PURE__*/React.createElement(CC, {
812
+ surface: "surface-save-interact",
813
+ settings: settingsWithActivity
814
+ }));
815
+ await waitFor(() => expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0));
816
+ const props = mockContentCardView.mock.calls[0][0];
817
+ const expectedHash = generateCardHash(template);
818
+ await act(async () => {
819
+ props.listener?.('onInteract', props.template);
820
+ });
821
+ expect(saveInboxState).toHaveBeenCalledWith('activity-save-interact', expect.objectContaining({
822
+ dismissed: [],
823
+ interacted: expect.arrayContaining([expectedHash])
824
+ }));
825
+ });
826
+ it('does not call saveInboxState on dismiss when activityId is missing', async () => {
827
+ useContentCardUI.mockReturnValue({
828
+ content: [template],
829
+ isLoading: false,
830
+ error: null
831
+ });
832
+ saveInboxState.mockClear();
833
+ const CC = Inbox;
834
+ render(/*#__PURE__*/React.createElement(CC, {
835
+ surface: "surface-no-activity",
836
+ settings: baseSettings
837
+ }));
838
+ await waitFor(() => expect(mockContentCardView.mock.calls.length).toBeGreaterThan(0));
839
+ const props = mockContentCardView.mock.calls[0][0];
840
+ await act(async () => {
841
+ props.listener?.('onDismiss', props.template);
842
+ });
843
+ expect(saveInboxState).not.toHaveBeenCalled();
844
+ });
845
+ });
846
+ });
847
+ //# sourceMappingURL=Inbox.spec.js.map