@cccsaurora/howler-ui 2.19.0-cases.863 → 2.19.0-dev.1023

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 (435) hide show
  1. package/api/action/index.d.ts +5 -4
  2. package/api/action/index.js +8 -8
  3. package/api/analytic/index.d.ts +3 -2
  4. package/api/analytic/index.js +4 -4
  5. package/api/analytic/owner.d.ts +2 -1
  6. package/api/analytic/owner.js +2 -2
  7. package/api/analytic/rules.d.ts +2 -1
  8. package/api/analytic/rules.js +2 -2
  9. package/api/dossier/index.d.ts +4 -3
  10. package/api/dossier/index.js +6 -6
  11. package/api/hit/index.d.ts +3 -2
  12. package/api/hit/index.js +4 -4
  13. package/api/hit/labels.d.ts +3 -2
  14. package/api/hit/labels.js +4 -4
  15. package/api/hit/overwrite.d.ts +2 -1
  16. package/api/hit/overwrite.js +2 -2
  17. package/api/hit/transition.d.ts +2 -1
  18. package/api/hit/transition.js +2 -2
  19. package/api/index.d.ts +5 -8
  20. package/api/index.js +10 -18
  21. package/api/overview/index.d.ts +4 -3
  22. package/api/overview/index.js +6 -6
  23. package/api/search/facet/hit.d.ts +3 -1
  24. package/api/search/facet/index.d.ts +1 -3
  25. package/api/search/index.d.ts +1 -2
  26. package/api/search/index.js +1 -2
  27. package/api/search/overview.d.ts +2 -2
  28. package/api/template/index.d.ts +4 -3
  29. package/api/template/index.js +6 -6
  30. package/api/user/index.d.ts +2 -1
  31. package/api/user/index.js +2 -2
  32. package/api/view/index.d.ts +4 -3
  33. package/api/view/index.js +6 -6
  34. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  35. package/components/app/App.d.ts +1 -0
  36. package/components/app/App.js +20 -42
  37. package/components/app/hooks/useMatchers.d.ts +1 -1
  38. package/components/app/hooks/useMatchers.js +11 -23
  39. package/components/app/hooks/useMatchers.test.js +22 -22
  40. package/components/app/hooks/useTitle.js +5 -5
  41. package/components/app/providers/FavouritesProvider.js +2 -6
  42. package/components/app/providers/HitProvider.d.ts +22 -0
  43. package/components/app/providers/{RecordProvider.js → HitProvider.js} +42 -42
  44. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  45. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  46. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +71 -52
  47. package/components/app/providers/ModalProvider.d.ts +1 -1
  48. package/components/app/providers/ModalProvider.js +5 -1
  49. package/components/app/providers/ParameterProvider.d.ts +2 -9
  50. package/components/app/providers/ParameterProvider.js +240 -165
  51. package/components/app/providers/ParameterProvider.test.js +94 -346
  52. package/components/app/providers/SearchResponseProvider.d.ts +21 -0
  53. package/components/app/providers/SearchResponseProvider.js +81 -0
  54. package/components/app/providers/SearchResponseProvider.test.js +406 -0
  55. package/components/app/providers/SocketProvider.d.ts +2 -11
  56. package/components/app/providers/SocketProvider.js +5 -18
  57. package/components/app/providers/UserListProvider.js +8 -28
  58. package/components/app/providers/ViewProvider.test.js +4 -1
  59. package/components/elements/PluginTypography.d.ts +1 -2
  60. package/components/elements/PluginTypography.js +2 -3
  61. package/components/elements/UserList.d.ts +2 -5
  62. package/components/elements/UserList.js +8 -18
  63. package/components/elements/addons/buttons/CustomButton.test.js +106 -0
  64. package/components/elements/addons/layout/FlexOne.test.js +30 -0
  65. package/components/elements/addons/lists/TuiListBase.js +4 -0
  66. package/components/elements/addons/lists/TuiListElement.js +6 -3
  67. package/components/elements/addons/lists/index.d.ts +1 -0
  68. package/components/elements/addons/search/SearchPagination.d.ts +2 -1
  69. package/components/elements/addons/search/SearchPagination.js +4 -4
  70. package/components/elements/addons/search/SearchPagination.test.js +80 -0
  71. package/components/elements/addons/search/SearchTotal.test.js +47 -0
  72. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  73. package/components/elements/display/ChipPopper.d.ts +1 -1
  74. package/components/elements/display/ChipPopper.js +5 -5
  75. package/components/elements/display/DocumentationButton.test.js +84 -0
  76. package/components/elements/display/DynamicTabs.test.js +93 -0
  77. package/components/elements/display/HowlerAvatar.js +2 -1
  78. package/components/elements/display/HowlerCard.js +1 -1
  79. package/components/elements/display/HowlerCard.test.js +55 -0
  80. package/components/elements/display/Image.test.js +79 -0
  81. package/components/elements/display/ItemManager.d.ts +2 -2
  82. package/components/elements/display/ItemManager.js +1 -1
  83. package/components/elements/display/Markdown.js +9 -2
  84. package/components/elements/display/Modal.js +0 -2
  85. package/components/elements/display/QueryResultText.test.js +48 -0
  86. package/components/elements/display/TextDivider.test.js +43 -0
  87. package/components/elements/display/TypingIndicator.test.js +33 -0
  88. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  89. package/components/elements/display/icons/BundleButton.js +32 -0
  90. package/components/elements/display/modals/ConfirmDeleteModal.d.ts +4 -0
  91. package/components/elements/display/modals/ConfirmDeleteModal.js +4 -2
  92. package/components/elements/hit/HitActions.js +4 -4
  93. package/components/elements/hit/HitBanner.d.ts +0 -1
  94. package/components/elements/hit/HitBanner.js +49 -31
  95. package/components/elements/hit/HitCard.d.ts +0 -2
  96. package/components/elements/hit/HitCard.js +7 -7
  97. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  98. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  99. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  100. package/components/elements/hit/HitLabels.js +2 -2
  101. package/components/elements/hit/HitOutline.d.ts +0 -1
  102. package/components/elements/hit/HitOutline.js +3 -3
  103. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  104. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  105. package/components/elements/hit/HitRelated.d.ts +6 -0
  106. package/components/elements/hit/HitRelated.js +7 -0
  107. package/components/elements/hit/HitSummary.d.ts +1 -2
  108. package/components/elements/hit/HitSummary.js +8 -8
  109. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  110. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  111. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  112. package/components/elements/hit/elements/{Assigned.d.ts → HitUsers.d.ts} +5 -2
  113. package/components/elements/hit/elements/HitUsers.js +33 -0
  114. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  115. package/components/elements/view/ViewTitle.d.ts +0 -1
  116. package/components/elements/view/ViewTitle.js +2 -9
  117. package/components/hooks/useHitActions.d.ts +1 -1
  118. package/components/hooks/useHitActions.js +4 -4
  119. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  120. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  121. package/components/hooks/useMyApi.d.ts +1 -1
  122. package/components/hooks/useMyPreferences.js +3 -11
  123. package/components/hooks/useMySearch.js +2 -2
  124. package/components/hooks/useMySitemap.js +1 -4
  125. package/components/hooks/useMyTheme.js +2 -9
  126. package/components/routes/403.test.js +27 -0
  127. package/components/routes/404.test.js +26 -0
  128. package/components/routes/ErrorBoundary.test.js +43 -0
  129. package/components/routes/ErrorOccured.test.js +35 -0
  130. package/components/routes/Logout.test.js +59 -0
  131. package/components/routes/action/edit/ActionEditor.js +2 -2
  132. package/components/routes/action/useMyActionFunctions.js +5 -2
  133. package/components/routes/action/view/ActionDetails.js +10 -2
  134. package/components/routes/action/view/ActionSearch.js +30 -19
  135. package/components/routes/advanced/QueryBuilder.js +1 -1
  136. package/components/routes/advanced/QueryEditor.js +3 -3
  137. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  138. package/components/routes/analytics/AnalyticDetails.js +12 -8
  139. package/components/routes/analytics/AnalyticSearch.js +7 -7
  140. package/components/routes/dossiers/DossierEditor.js +2 -2
  141. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  142. package/components/routes/dossiers/Dossiers.js +25 -19
  143. package/components/routes/help/ApiDocumentation.js +1 -1
  144. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  145. package/components/routes/help/BundleDocumentation.js +12 -0
  146. package/components/routes/help/HitBannerDocumentation.js +0 -1
  147. package/components/routes/help/HitDocumentation.js +3 -1
  148. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  149. package/components/routes/help/markdown/en/client.md.js +1 -1
  150. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  151. package/components/routes/help/markdown/fr/client.md.js +1 -1
  152. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  153. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  154. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  155. package/components/routes/hits/search/BundleScroller.js +6 -0
  156. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  157. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  158. package/components/routes/hits/search/HitContextMenu.js +239 -0
  159. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +43 -98
  160. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  161. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  162. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  163. package/components/routes/hits/search/InformationPane.js +63 -50
  164. package/components/routes/hits/search/LayoutSettings.js +3 -3
  165. package/components/routes/hits/search/QuerySettings.js +1 -2
  166. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  167. package/components/routes/hits/search/SearchPane.js +49 -26
  168. package/components/routes/hits/search/ViewLink.js +3 -3
  169. package/components/routes/hits/search/ViewLink.test.js +8 -8
  170. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  171. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  172. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  173. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  174. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  175. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +9 -11
  176. package/components/routes/hits/search/shared/CustomSpan.js +7 -7
  177. package/components/routes/hits/search/shared/CustomSpan.test.js +170 -0
  178. package/components/routes/hits/view/HitViewer.js +13 -12
  179. package/components/routes/home/AddNewCard.test.js +86 -0
  180. package/components/routes/home/ViewCard.js +41 -47
  181. package/components/routes/home/ViewRefresh.test.js +79 -0
  182. package/components/routes/overviews/OverviewCard.test.js +108 -0
  183. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  184. package/components/routes/overviews/OverviewViewer.js +18 -10
  185. package/components/routes/overviews/Overviews.js +25 -19
  186. package/components/routes/settings/LocalSection.js +2 -1
  187. package/components/routes/settings/SettingsSection.test.d.ts +1 -0
  188. package/components/routes/settings/SettingsSection.test.js +19 -0
  189. package/components/routes/templates/TemplateCard.d.ts +3 -1
  190. package/components/routes/templates/TemplateCard.js +8 -4
  191. package/components/routes/templates/TemplateCard.test.d.ts +1 -0
  192. package/components/routes/templates/TemplateCard.test.js +115 -0
  193. package/components/routes/templates/TemplateViewer.js +62 -16
  194. package/components/routes/templates/Templates.js +35 -13
  195. package/components/routes/views/ViewComposer.js +19 -46
  196. package/components/routes/views/Views.js +21 -14
  197. package/locales/en/translation.json +21 -118
  198. package/locales/fr/translation.json +23 -120
  199. package/models/WithMetadata.d.ts +1 -2
  200. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  201. package/models/entities/generated/Hit.d.ts +1 -1
  202. package/models/entities/generated/Howler.d.ts +6 -0
  203. package/models/entities/generated/Rule.d.ts +9 -6
  204. package/models/entities/generated/Threat.d.ts +2 -2
  205. package/models/entities/generated/UserUser.d.ts +2 -0
  206. package/models/entities/generated/View.d.ts +0 -1
  207. package/package.json +125 -145
  208. package/plugins/clue/components/ClueTypography.js +2 -2
  209. package/plugins/clue/utils.d.ts +1 -2
  210. package/rest/FetchClient.test.d.ts +1 -0
  211. package/rest/FetchClient.test.js +81 -0
  212. package/setupTests.js +5 -0
  213. package/tests/mocks.d.ts +1 -11
  214. package/tests/mocks.js +7 -12
  215. package/tests/server-handlers.js +1 -6
  216. package/tests/utils.d.ts +0 -4
  217. package/tests/utils.js +0 -20
  218. package/utils/Throttler.test.d.ts +1 -0
  219. package/utils/Throttler.test.js +135 -0
  220. package/utils/actionUtils.test.d.ts +1 -0
  221. package/utils/actionUtils.test.js +140 -0
  222. package/utils/constants.d.ts +4 -4
  223. package/utils/constants.js +1 -6
  224. package/utils/hitFunctions.d.ts +1 -2
  225. package/utils/hitFunctions.js +4 -4
  226. package/utils/hitFunctions.test.d.ts +1 -0
  227. package/utils/hitFunctions.test.js +52 -0
  228. package/utils/localStorage.test.d.ts +1 -0
  229. package/utils/localStorage.test.js +96 -0
  230. package/utils/menuUtils.test.d.ts +1 -0
  231. package/utils/menuUtils.test.js +198 -0
  232. package/utils/sessionStorage.test.d.ts +1 -0
  233. package/utils/sessionStorage.test.js +81 -0
  234. package/utils/socketUtils.d.ts +0 -14
  235. package/utils/socketUtils.js +1 -17
  236. package/utils/socketUtils.test.js +16 -49
  237. package/utils/stringUtils.test.d.ts +1 -0
  238. package/utils/stringUtils.test.js +159 -0
  239. package/utils/utils.test.d.ts +1 -0
  240. package/utils/utils.test.js +292 -0
  241. package/utils/viewUtils.js +0 -3
  242. package/utils/viewUtils.test.d.ts +1 -0
  243. package/utils/viewUtils.test.js +45 -0
  244. package/utils/xsrf.test.d.ts +1 -0
  245. package/utils/xsrf.test.js +41 -0
  246. package/api/search/case.d.ts +0 -4
  247. package/api/search/case.js +0 -8
  248. package/api/socket/index.d.ts +0 -3
  249. package/api/socket/index.js +0 -6
  250. package/api/socket/viewers.d.ts +0 -2
  251. package/api/socket/viewers.js +0 -8
  252. package/api/socket/viewers.test.js +0 -44
  253. package/api/v2/case/index.d.ts +0 -9
  254. package/api/v2/case/index.js +0 -21
  255. package/api/v2/case/items.d.ts +0 -6
  256. package/api/v2/case/items.js +0 -18
  257. package/api/v2/case/rules.d.ts +0 -6
  258. package/api/v2/case/rules.js +0 -18
  259. package/api/v2/index.d.ts +0 -4
  260. package/api/v2/index.js +0 -6
  261. package/api/v2/search/facet.d.ts +0 -3
  262. package/api/v2/search/facet.js +0 -12
  263. package/api/v2/search/index.d.ts +0 -5
  264. package/api/v2/search/index.js +0 -24
  265. package/components/app/providers/RecordProvider.d.ts +0 -23
  266. package/components/elements/ContextMenu.d.ts +0 -56
  267. package/components/elements/ContextMenu.js +0 -109
  268. package/components/elements/ContextMenu.test.js +0 -215
  269. package/components/elements/ObjectDetails.d.ts +0 -6
  270. package/components/elements/case/CaseCard.d.ts +0 -12
  271. package/components/elements/case/CaseCard.js +0 -42
  272. package/components/elements/case/CasePreview.d.ts +0 -6
  273. package/components/elements/case/CasePreview.js +0 -17
  274. package/components/elements/case/StatusIcon.d.ts +0 -5
  275. package/components/elements/case/StatusIcon.js +0 -13
  276. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -9
  277. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  278. package/components/elements/hit/elements/Assigned.js +0 -34
  279. package/components/elements/hit/elements/Assigned.test.js +0 -65
  280. package/components/elements/hit/related/RelatedRecords.js +0 -63
  281. package/components/elements/observable/ObservableCard.d.ts +0 -6
  282. package/components/elements/observable/ObservableCard.js +0 -22
  283. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  284. package/components/elements/observable/ObservablePreview.js +0 -12
  285. package/components/elements/record/RecordContextMenu.js +0 -268
  286. package/components/elements/record/RecordRelated.d.ts +0 -7
  287. package/components/elements/record/RecordRelated.js +0 -34
  288. package/components/hooks/useRelatedRecords.d.ts +0 -13
  289. package/components/hooks/useRelatedRecords.js +0 -32
  290. package/components/routes/cases/CaseViewer.d.ts +0 -2
  291. package/components/routes/cases/CaseViewer.js +0 -44
  292. package/components/routes/cases/CaseViewer.test.js +0 -133
  293. package/components/routes/cases/Cases.d.ts +0 -2
  294. package/components/routes/cases/Cases.js +0 -148
  295. package/components/routes/cases/constants.d.ts +0 -6
  296. package/components/routes/cases/constants.js +0 -6
  297. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  298. package/components/routes/cases/detail/AlertPanel.js +0 -33
  299. package/components/routes/cases/detail/CaseAssets.d.ts +0 -11
  300. package/components/routes/cases/detail/CaseAssets.js +0 -104
  301. package/components/routes/cases/detail/CaseAssets.test.js +0 -167
  302. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  303. package/components/routes/cases/detail/CaseDashboard.js +0 -66
  304. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  305. package/components/routes/cases/detail/CaseDetails.js +0 -70
  306. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  307. package/components/routes/cases/detail/CaseOverview.js +0 -43
  308. package/components/routes/cases/detail/CaseRules.d.ts +0 -7
  309. package/components/routes/cases/detail/CaseRules.js +0 -57
  310. package/components/routes/cases/detail/CaseRules.test.js +0 -221
  311. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
  312. package/components/routes/cases/detail/CaseSidebar.js +0 -107
  313. package/components/routes/cases/detail/CaseSidebar.test.js +0 -266
  314. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  315. package/components/routes/cases/detail/CaseTask.js +0 -66
  316. package/components/routes/cases/detail/CaseTimeline.d.ts +0 -12
  317. package/components/routes/cases/detail/CaseTimeline.js +0 -106
  318. package/components/routes/cases/detail/CaseTimeline.test.js +0 -320
  319. package/components/routes/cases/detail/CreateRuleDialog.d.ts +0 -9
  320. package/components/routes/cases/detail/CreateRuleDialog.js +0 -163
  321. package/components/routes/cases/detail/CreateRuleDialog.test.js +0 -259
  322. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  323. package/components/routes/cases/detail/ItemPage.js +0 -95
  324. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  325. package/components/routes/cases/detail/RelatedCasePanel.js +0 -34
  326. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  327. package/components/routes/cases/detail/TaskPanel.js +0 -52
  328. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -11
  329. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -24
  330. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  331. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -26
  332. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  333. package/components/routes/cases/detail/assets/Asset.js +0 -12
  334. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  335. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -20
  336. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -83
  337. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +0 -295
  338. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
  339. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -103
  340. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -363
  341. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +0 -25
  342. package/components/routes/cases/detail/sidebar/FolderEntry.js +0 -88
  343. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +0 -206
  344. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +0 -5
  345. package/components/routes/cases/detail/sidebar/RootDropZone.js +0 -33
  346. package/components/routes/cases/detail/sidebar/types.d.ts +0 -9
  347. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  348. package/components/routes/cases/detail/sidebar/utils.js +0 -29
  349. package/components/routes/cases/detail/sidebar/utils.test.js +0 -82
  350. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  351. package/components/routes/cases/hooks/useCase.js +0 -69
  352. package/components/routes/cases/hooks/useCase.test.js +0 -141
  353. package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
  354. package/components/routes/cases/modals/AddToCaseModal.js +0 -59
  355. package/components/routes/cases/modals/AddToCaseModal.test.js +0 -313
  356. package/components/routes/cases/modals/CaseRecordRow.d.ts +0 -9
  357. package/components/routes/cases/modals/CaseRecordRow.js +0 -15
  358. package/components/routes/cases/modals/CreateCaseModal.d.ts +0 -7
  359. package/components/routes/cases/modals/CreateCaseModal.js +0 -55
  360. package/components/routes/cases/modals/CreateCaseModal.test.js +0 -358
  361. package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
  362. package/components/routes/cases/modals/RenameItemModal.js +0 -48
  363. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  364. package/components/routes/cases/modals/ResolveModal.js +0 -115
  365. package/components/routes/cases/modals/ResolveModal.test.js +0 -394
  366. package/components/routes/cases/modals/hooks.d.ts +0 -7
  367. package/components/routes/cases/modals/hooks.js +0 -44
  368. package/components/routes/cases/modals/types.d.ts +0 -5
  369. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +0 -6
  370. package/components/routes/cases/search/CaseAssigneeFilter.js +0 -33
  371. package/components/routes/cases/search/CaseAssigneeFilter.test.js +0 -127
  372. package/components/routes/cases/search/CaseDateFilter.d.ts +0 -13
  373. package/components/routes/cases/search/CaseDateFilter.js +0 -26
  374. package/components/routes/cases/search/CaseDateFilter.test.js +0 -115
  375. package/components/routes/cases/search/CaseStatusFilter.d.ts +0 -6
  376. package/components/routes/cases/search/CaseStatusFilter.js +0 -13
  377. package/components/routes/cases/search/CaseStatusFilter.test.js +0 -86
  378. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  379. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  380. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  381. package/components/routes/observables/ObservableViewer.js +0 -27
  382. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  383. package/models/entities/generated/Case.d.ts +0 -28
  384. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  385. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  386. package/models/entities/generated/EmailParent.d.ts +0 -19
  387. package/models/entities/generated/Enrichments.d.ts +0 -7
  388. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  389. package/models/entities/generated/HttpResponse.d.ts +0 -11
  390. package/models/entities/generated/Item.d.ts +0 -9
  391. package/models/entities/generated/Observable.d.ts +0 -85
  392. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  393. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  394. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  395. package/models/entities/generated/ObservableFile.d.ts +0 -36
  396. package/models/entities/generated/ObservableHowler.d.ts +0 -42
  397. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  398. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  399. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  400. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  401. package/models/entities/generated/ObservableSource.d.ts +0 -23
  402. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  403. package/models/entities/generated/ObservableTls.d.ts +0 -12
  404. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  405. package/models/entities/generated/Task.d.ts +0 -10
  406. package/models/socket/CaseUpdate.d.ts +0 -5
  407. package/models/socket/ViewersUpdate.d.ts +0 -4
  408. package/utils/typeUtils.d.ts +0 -7
  409. package/utils/typeUtils.js +0 -27
  410. /package/{api/socket/viewers.test.d.ts → components/app/providers/HitSearchProvider.test.d.ts} +0 -0
  411. /package/components/app/providers/{RecordSearchProvider.test.d.ts → SearchResponseProvider.test.d.ts} +0 -0
  412. /package/components/elements/{ContextMenu.test.d.ts → addons/buttons/CustomButton.test.d.ts} +0 -0
  413. /package/components/elements/{hit/elements/Assigned.test.d.ts → addons/layout/FlexOne.test.d.ts} +0 -0
  414. /package/components/elements/{record/RecordContextMenu.test.d.ts → addons/search/SearchPagination.test.d.ts} +0 -0
  415. /package/components/{routes/cases/CaseViewer.test.d.ts → elements/addons/search/SearchTotal.test.d.ts} +0 -0
  416. /package/components/{routes/cases/detail/CaseAssets.test.d.ts → elements/display/DocumentationButton.test.d.ts} +0 -0
  417. /package/components/{routes/cases/detail/CaseRules.test.d.ts → elements/display/DynamicTabs.test.d.ts} +0 -0
  418. /package/components/{routes/cases/detail/CaseSidebar.test.d.ts → elements/display/HowlerCard.test.d.ts} +0 -0
  419. /package/components/{routes/cases/detail/CaseTimeline.test.d.ts → elements/display/Image.test.d.ts} +0 -0
  420. /package/components/{routes/cases/detail/CreateRuleDialog.test.d.ts → elements/display/QueryResultText.test.d.ts} +0 -0
  421. /package/components/{routes/cases/detail/assets/Asset.test.d.ts → elements/display/TextDivider.test.d.ts} +0 -0
  422. /package/components/{routes/cases/detail/sidebar/CaseFolder.test.d.ts → elements/display/TypingIndicator.test.d.ts} +0 -0
  423. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  424. /package/components/routes/{cases/detail/sidebar/CaseFolderContextMenu.test.d.ts → 403.test.d.ts} +0 -0
  425. /package/components/routes/{cases/detail/sidebar/FolderEntry.test.d.ts → 404.test.d.ts} +0 -0
  426. /package/components/routes/{cases/detail/sidebar/utils.test.d.ts → ErrorBoundary.test.d.ts} +0 -0
  427. /package/components/routes/{cases/hooks/useCase.test.d.ts → ErrorOccured.test.d.ts} +0 -0
  428. /package/components/routes/{cases/modals/AddToCaseModal.test.d.ts → Logout.test.d.ts} +0 -0
  429. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  430. /package/components/routes/{cases/modals/CreateCaseModal.test.d.ts → hits/search/HitContextMenu.test.d.ts} +0 -0
  431. /package/components/routes/{cases/modals/ResolveModal.test.d.ts → hits/search/shared/CustomSpan.test.d.ts} +0 -0
  432. /package/components/routes/{cases/search/CaseAssigneeFilter.test.d.ts → home/AddNewCard.test.d.ts} +0 -0
  433. /package/components/routes/{cases/search/CaseDateFilter.test.d.ts → home/ViewRefresh.test.d.ts} +0 -0
  434. /package/components/routes/{cases/search/CaseStatusFilter.test.d.ts → overviews/OverviewCard.test.d.ts} +0 -0
  435. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
package/tests/utils.js CHANGED
@@ -1,7 +1,5 @@
1
1
  // Mock data factories
2
2
  export const createMockHit = (overrides) => ({
3
- ...overrides,
4
- __index: 'hit',
5
3
  howler: {
6
4
  id: 'test-hit-1',
7
5
  analytic: 'test-analytic',
@@ -16,17 +14,6 @@ export const createMockHit = (overrides) => ({
16
14
  ...overrides?.event
17
15
  }
18
16
  });
19
- export const createMockObservable = (overrides) => ({
20
- ...overrides,
21
- __index: 'observable',
22
- howler: {
23
- id: 'test-observable-1',
24
- analytic: 'test-analytic',
25
- detection: 'Test Detection',
26
- hash: '',
27
- ...overrides?.howler
28
- }
29
- });
30
17
  export const createMockAnalytic = (overrides) => ({
31
18
  analytic_id: 'test-analytic-id',
32
19
  name: 'test-analytic',
@@ -65,13 +52,6 @@ export const createMockView = (overrides) => ({
65
52
  },
66
53
  ...overrides
67
54
  });
68
- export const createMockCase = (overrides) => ({
69
- case_id: 'test-case-id',
70
- title: 'Test Case',
71
- summary: 'Test case summary',
72
- items: [],
73
- ...overrides
74
- });
75
55
  // Helper function to create mock dossiers
76
56
  export const createMockDossier = (overrides) => ({
77
57
  dossier_id: 'test-dossier-id',
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,135 @@
1
+ /// <reference types="vitest" />
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import Throttler from './Throttler';
4
+ describe('Throttler', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ });
8
+ afterEach(() => {
9
+ vi.useRealTimers();
10
+ });
11
+ // -------------------------------------------------------------------------
12
+ // throttle
13
+ // -------------------------------------------------------------------------
14
+ describe('throttle', () => {
15
+ it('calls fn immediately on the first invocation', () => {
16
+ const fn = vi.fn();
17
+ const t = new Throttler(100);
18
+ t.throttle(fn);
19
+ expect(fn).toHaveBeenCalledTimes(1);
20
+ });
21
+ it('suppresses subsequent calls within the throttle window', () => {
22
+ const fn = vi.fn();
23
+ const t = new Throttler(100);
24
+ t.throttle(fn);
25
+ t.throttle(fn);
26
+ t.throttle(fn);
27
+ expect(fn).toHaveBeenCalledTimes(1);
28
+ });
29
+ it('allows another call after the throttle interval expires', () => {
30
+ const fn = vi.fn();
31
+ const t = new Throttler(100);
32
+ t.throttle(fn);
33
+ vi.advanceTimersByTime(150);
34
+ t.throttle(fn);
35
+ expect(fn).toHaveBeenCalledTimes(2);
36
+ });
37
+ it('does not fire a second call if the interval has not yet elapsed', () => {
38
+ const fn = vi.fn();
39
+ const t = new Throttler(200);
40
+ t.throttle(fn);
41
+ vi.advanceTimersByTime(100);
42
+ t.throttle(fn);
43
+ expect(fn).toHaveBeenCalledTimes(1);
44
+ });
45
+ });
46
+ // -------------------------------------------------------------------------
47
+ // debounce
48
+ // -------------------------------------------------------------------------
49
+ describe('debounce', () => {
50
+ it('does not call fn immediately', () => {
51
+ const fn = vi.fn();
52
+ const t = new Throttler(100);
53
+ t.debounce(fn);
54
+ expect(fn).not.toHaveBeenCalled();
55
+ });
56
+ it('calls fn after the delay elapses', () => {
57
+ const fn = vi.fn();
58
+ const t = new Throttler(100);
59
+ t.debounce(fn);
60
+ vi.advanceTimersByTime(100);
61
+ expect(fn).toHaveBeenCalledTimes(1);
62
+ });
63
+ it('resets the delay when called again before it fires', () => {
64
+ const fn = vi.fn();
65
+ const t = new Throttler(100);
66
+ t.debounce(fn);
67
+ vi.advanceTimersByTime(80);
68
+ t.debounce(fn);
69
+ vi.advanceTimersByTime(80);
70
+ expect(fn).not.toHaveBeenCalled();
71
+ vi.advanceTimersByTime(30);
72
+ expect(fn).toHaveBeenCalledTimes(1);
73
+ });
74
+ it('calls fn only once after many rapid invocations', () => {
75
+ const fn = vi.fn();
76
+ const t = new Throttler(100);
77
+ for (let i = 0; i < 10; i++) {
78
+ t.debounce(fn);
79
+ }
80
+ vi.advanceTimersByTime(200);
81
+ expect(fn).toHaveBeenCalledTimes(1);
82
+ });
83
+ it('fires a second call correctly after the first has resolved', () => {
84
+ const fn = vi.fn();
85
+ const t = new Throttler(100);
86
+ t.debounce(fn);
87
+ vi.advanceTimersByTime(150);
88
+ t.debounce(fn);
89
+ vi.advanceTimersByTime(150);
90
+ expect(fn).toHaveBeenCalledTimes(2);
91
+ });
92
+ });
93
+ // -------------------------------------------------------------------------
94
+ // delayAsync
95
+ // -------------------------------------------------------------------------
96
+ describe('delayAsync', () => {
97
+ it('does not invoke fn before the delay', async () => {
98
+ const fn = vi.fn().mockResolvedValue('x');
99
+ const t = new Throttler(100);
100
+ t.delayAsync(fn);
101
+ expect(fn).not.toHaveBeenCalled();
102
+ });
103
+ it('resolves with the return value of fn', async () => {
104
+ const fn = vi.fn().mockResolvedValue('result');
105
+ const t = new Throttler(100);
106
+ const promise = t.delayAsync(fn);
107
+ vi.advanceTimersByTime(100);
108
+ await expect(promise).resolves.toBe('result');
109
+ });
110
+ it('passes variadic arguments through to fn', async () => {
111
+ const fn = vi.fn().mockImplementation(async (a, b) => a + b);
112
+ const t = new Throttler(100);
113
+ const promise = t.delayAsync(fn, 3, 4);
114
+ vi.advanceTimersByTime(100);
115
+ await expect(promise).resolves.toBe(7);
116
+ });
117
+ it('rejects when fn throws', async () => {
118
+ const fn = vi.fn().mockRejectedValue(new Error('boom'));
119
+ const t = new Throttler(100);
120
+ const promise = t.delayAsync(fn);
121
+ vi.advanceTimersByTime(100);
122
+ await expect(promise).rejects.toThrow('boom');
123
+ });
124
+ it('cancels the previous pending call when invoked again', async () => {
125
+ const fn = vi.fn().mockResolvedValue(42);
126
+ const t = new Throttler(100);
127
+ t.delayAsync(fn);
128
+ vi.advanceTimersByTime(50);
129
+ const second = t.delayAsync(fn);
130
+ vi.advanceTimersByTime(100);
131
+ await expect(second).resolves.toBe(42);
132
+ expect(fn).toHaveBeenCalledTimes(1);
133
+ });
134
+ });
135
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,140 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { checkArgsAreFilled, getArgsByContext, getOptionsByContext, operationReady } from './actionUtils';
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers
5
+ // ---------------------------------------------------------------------------
6
+ const makeStep = (args, options = {}) => ({
7
+ args,
8
+ options,
9
+ validation: {}
10
+ });
11
+ const makeAction = (steps) => ({
12
+ id: 'test-action',
13
+ title: 'Test Action',
14
+ i18nKey: 'test.action',
15
+ description: { short: '', long: '' },
16
+ roles: [],
17
+ steps,
18
+ triggers: []
19
+ });
20
+ // ---------------------------------------------------------------------------
21
+ // checkArgsAreFilled
22
+ // ---------------------------------------------------------------------------
23
+ describe('checkArgsAreFilled', () => {
24
+ it('returns false when values is empty/falsy', () => {
25
+ const step = makeStep({ status: [] });
26
+ expect(checkArgsAreFilled(step, '')).toBe(false);
27
+ });
28
+ it('returns true when all required args are present and truthy', () => {
29
+ const step = makeStep({ status: [] });
30
+ expect(checkArgsAreFilled(step, JSON.stringify({ status: 'open' }))).toBe(true);
31
+ });
32
+ it('returns false when a required arg is missing from values', () => {
33
+ const step = makeStep({ status: [], assignment: [] });
34
+ expect(checkArgsAreFilled(step, JSON.stringify({ status: 'open' }))).toBe(false);
35
+ });
36
+ it('returns false when a required arg is present but falsy (empty string)', () => {
37
+ const step = makeStep({ status: [] });
38
+ expect(checkArgsAreFilled(step, JSON.stringify({ status: '' }))).toBe(false);
39
+ });
40
+ it('returns true when there are no required args', () => {
41
+ const step = makeStep({});
42
+ expect(checkArgsAreFilled(step, JSON.stringify({}))).toBe(true);
43
+ });
44
+ });
45
+ // ---------------------------------------------------------------------------
46
+ // getOptionsByContext
47
+ // ---------------------------------------------------------------------------
48
+ describe('getOptionsByContext', () => {
49
+ it('returns an empty array when the arg is not in options', () => {
50
+ const options = {};
51
+ expect(getOptionsByContext(options, 'status', '{}')).toEqual([]);
52
+ });
53
+ it('returns the full list when the arg maps to a plain array', () => {
54
+ const options = { status: ['open', 'in-progress', 'resolved'] };
55
+ expect(getOptionsByContext(options, 'status', '{}')).toEqual(['open', 'in-progress', 'resolved']);
56
+ });
57
+ it('returns options matching the current context when arg has conditional options', () => {
58
+ const options = {
59
+ assessment: {
60
+ 'status:open': ['false_positive', 'legitimate'],
61
+ 'status:resolved': ['correct']
62
+ }
63
+ };
64
+ const context = JSON.stringify({ status: 'open' });
65
+ expect(getOptionsByContext(options, 'assessment', context)).toEqual(['false_positive', 'legitimate']);
66
+ });
67
+ it('returns an empty array when context does not match any conditional key', () => {
68
+ const options = {
69
+ assessment: {
70
+ 'status:open': ['false_positive']
71
+ }
72
+ };
73
+ const context = JSON.stringify({ status: 'resolved' });
74
+ expect(getOptionsByContext(options, 'assessment', context)).toEqual([]);
75
+ });
76
+ });
77
+ // ---------------------------------------------------------------------------
78
+ // getArgsByContext
79
+ // ---------------------------------------------------------------------------
80
+ describe('getArgsByContext', () => {
81
+ it('always returns an arg with an empty conditions array', () => {
82
+ const args = { status: [] };
83
+ expect(getArgsByContext(args, '{}')).toContain('status');
84
+ });
85
+ it('includes an arg whose condition matches the current context', () => {
86
+ const args = { assessment: ['status:open'] };
87
+ const values = JSON.stringify({ status: 'open' });
88
+ expect(getArgsByContext(args, values)).toContain('assessment');
89
+ });
90
+ it('excludes an arg whose condition does not match the current context', () => {
91
+ const args = { assessment: ['status:open'] };
92
+ const values = JSON.stringify({ status: 'resolved' });
93
+ expect(getArgsByContext(args, values)).not.toContain('assessment');
94
+ });
95
+ it('includes an arg when any one of multiple conditions matches (OR logic)', () => {
96
+ const args = { rationale: ['status:open', 'status:in-progress'] };
97
+ const values = JSON.stringify({ status: 'in-progress' });
98
+ expect(getArgsByContext(args, values)).toContain('rationale');
99
+ });
100
+ it('returns an empty array when no args match', () => {
101
+ const args = { assessment: ['status:open'] };
102
+ const values = JSON.stringify({ status: 'resolved' });
103
+ expect(getArgsByContext(args, values)).toEqual([]);
104
+ });
105
+ });
106
+ // ---------------------------------------------------------------------------
107
+ // operationReady
108
+ // ---------------------------------------------------------------------------
109
+ describe('operationReady', () => {
110
+ it('returns false when data is null/falsy', () => {
111
+ const action = makeAction([makeStep({ status: [] })]);
112
+ expect(operationReady(null, action)).toBe(false);
113
+ });
114
+ it('returns falsy when action is falsy', () => {
115
+ expect(operationReady(JSON.stringify({ status: 'open' }), null)).toBeFalsy();
116
+ });
117
+ it('returns true when all unconditional args are present', () => {
118
+ const action = makeAction([makeStep({ status: [] })]);
119
+ const data = JSON.stringify({ status: 'open' });
120
+ expect(operationReady(data, action)).toBe(true);
121
+ });
122
+ it('returns false when a required unconditional arg is missing', () => {
123
+ const action = makeAction([makeStep({ status: [], assessment: [] })]);
124
+ const data = JSON.stringify({ status: 'open' });
125
+ expect(operationReady(data, action)).toBe(false);
126
+ });
127
+ it('ignores conditional args that do not apply in the current context', () => {
128
+ // assessment only required when status=open; here status=resolved so assessment is not required
129
+ const action = makeAction([makeStep({ status: [], assessment: ['status:open'] })]);
130
+ const data = JSON.stringify({ status: 'resolved' });
131
+ expect(operationReady(data, action)).toBe(true);
132
+ });
133
+ it('merges args across multiple steps', () => {
134
+ const step1 = makeStep({ status: [] });
135
+ const step2 = makeStep({ assignment: [] });
136
+ const action = makeAction([step1, step2]);
137
+ const data = JSON.stringify({ status: 'open', assignment: 'alice' });
138
+ expect(operationReady(data, action)).toBe(true);
139
+ });
140
+ });
@@ -5,9 +5,9 @@ export declare const VERSION: any;
5
5
  export declare const MY_LOCAL_STORAGE_PREFIX = "howler.ui";
6
6
  export declare const MY_SESSION_STORAGE_PREFIX = "howler.ui.cache";
7
7
  export declare const ESCALATION_COLORS: {
8
- alert: "warning";
9
- evidence: "error";
10
- hit: "primary";
8
+ alert: string;
9
+ evidence: string;
10
+ hit: string;
11
11
  };
12
12
  export declare const STATUS_COLORS: {
13
13
  open: string;
@@ -49,6 +49,7 @@ export declare enum StorageKey {
49
49
  COMPACT_JSON = "compact_json_view",
50
50
  FLATTEN_JSON = "flatten_json_view",
51
51
  FORCE_DRAWER = "force_drawer",
52
+ SHOW_HIT_SUMMARY_GRAPH = "show_hit_summary_graph",
52
53
  LAST_VIEW = "last_view",
53
54
  ONLY_RULES = "only_rules",
54
55
  PAGE_COUNT = "page_count",
@@ -74,7 +75,6 @@ export declare const RULE_INTERVALS: {
74
75
  crontab: string;
75
76
  }[];
76
77
  export declare const DATE_RANGES: string[];
77
- export declare const DATE_RANGE_LUCENE: Partial<Record<(typeof DATE_RANGES)[number], string>>;
78
78
  interface LabelData {
79
79
  icon?: ReactElement;
80
80
  color?: string;
@@ -54,6 +54,7 @@ export var StorageKey;
54
54
  StorageKey["COMPACT_JSON"] = "compact_json_view";
55
55
  StorageKey["FLATTEN_JSON"] = "flatten_json_view";
56
56
  StorageKey["FORCE_DRAWER"] = "force_drawer";
57
+ StorageKey["SHOW_HIT_SUMMARY_GRAPH"] = "show_hit_summary_graph";
57
58
  StorageKey["LAST_VIEW"] = "last_view";
58
59
  StorageKey["ONLY_RULES"] = "only_rules";
59
60
  StorageKey["PAGE_COUNT"] = "page_count";
@@ -104,12 +105,6 @@ export const DATE_RANGES = [
104
105
  'date.range.all',
105
106
  'date.range.custom'
106
107
  ];
107
- export const DATE_RANGE_LUCENE = {
108
- 'date.range.1.day': 'now-1d/d',
109
- 'date.range.3.day': 'now-3d/d',
110
- 'date.range.1.week': 'now-7d/d',
111
- 'date.range.1.month': 'now-1M/M'
112
- };
113
108
  export const LABEL_TYPES = {
114
109
  insight: { icon: _jsx(PsychologyAlt, { fontSize: "small" }), color: '#FFFFFF' }, //brain icon
115
110
  mitigation: { icon: _jsx(LocalPolice, { fontSize: "small" }), color: blue[600] }, //police badge
@@ -1,3 +1,2 @@
1
1
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
2
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
3
- export declare const getUserList: (record: Hit | Observable) => Set<string>;
2
+ export declare const getUserList: (hit: Hit) => Set<string>;
@@ -1,8 +1,8 @@
1
- export const getUserList = (record) => {
1
+ export const getUserList = (hit) => {
2
2
  const ids = new Set();
3
- if (record) {
4
- record.howler?.log?.forEach(l => ids.add(l.user));
5
- record.howler?.comment?.forEach(c => ids.add(c.user), ids);
3
+ if (hit) {
4
+ hit.howler?.log?.forEach(l => ids.add(l.user));
5
+ hit.howler?.comment?.forEach(c => ids.add(c.user), ids);
6
6
  }
7
7
  return ids;
8
8
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getUserList } from './hitFunctions';
3
+ const makeHit = (howlerOverrides = {}) => ({
4
+ howler: {
5
+ log: [],
6
+ comment: [],
7
+ ...howlerOverrides
8
+ }
9
+ });
10
+ describe('getUserList', () => {
11
+ it('returns an empty Set when the hit is null', () => {
12
+ expect(getUserList(null)).toEqual(new Set());
13
+ });
14
+ it('returns an empty Set when the hit is undefined', () => {
15
+ expect(getUserList(undefined)).toEqual(new Set());
16
+ });
17
+ it('returns an empty Set when both log and comment are empty', () => {
18
+ expect(getUserList(makeHit())).toEqual(new Set());
19
+ });
20
+ it('collects users from the log array', () => {
21
+ const hit = makeHit({ log: [{ user: 'alice' }, { user: 'bob' }] });
22
+ expect(getUserList(hit)).toEqual(new Set(['alice', 'bob']));
23
+ });
24
+ it('collects users from the comment array', () => {
25
+ const hit = makeHit({ comment: [{ user: 'carol' }] });
26
+ expect(getUserList(hit)).toEqual(new Set(['carol']));
27
+ });
28
+ it('collects users from both log and comment', () => {
29
+ const hit = makeHit({
30
+ log: [{ user: 'alice' }],
31
+ comment: [{ user: 'bob' }]
32
+ });
33
+ expect(getUserList(hit)).toEqual(new Set(['alice', 'bob']));
34
+ });
35
+ it('deduplicates users that appear in both arrays', () => {
36
+ const hit = makeHit({
37
+ log: [{ user: 'alice' }],
38
+ comment: [{ user: 'alice' }]
39
+ });
40
+ const result = getUserList(hit);
41
+ expect(result).toEqual(new Set(['alice']));
42
+ expect(result.size).toBe(1);
43
+ });
44
+ it('deduplicates users that appear multiple times in the same array', () => {
45
+ const hit = makeHit({ log: [{ user: 'alice' }, { user: 'alice' }] });
46
+ expect(getUserList(hit).size).toBe(1);
47
+ });
48
+ it('handles a hit with no howler field gracefully', () => {
49
+ const hit = {};
50
+ expect(getUserList(hit)).toEqual(new Set());
51
+ });
52
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,96 @@
1
+ /// <reference types="vitest" />
2
+ import { afterEach, describe, expect, it } from 'vitest';
3
+ import { StorageKey } from './constants';
4
+ import { getStored, removeStored, saveLoginCredential, setStored } from './localStorage';
5
+ const PREFIX = 'howler.ui';
6
+ describe('localStorage utilities', () => {
7
+ afterEach(() => {
8
+ localStorage.clear();
9
+ });
10
+ // -------------------------------------------------------------------------
11
+ // setStored / getStored
12
+ // -------------------------------------------------------------------------
13
+ describe('setStored / getStored', () => {
14
+ it('stores and retrieves a string value', () => {
15
+ setStored(StorageKey.USERNAME, 'alice');
16
+ expect(getStored(StorageKey.USERNAME)).toBe('alice');
17
+ });
18
+ it('stores and retrieves an object value', () => {
19
+ const value = { token: 'abc', expires: 123 };
20
+ setStored(StorageKey.APP_TOKEN, value);
21
+ expect(getStored(StorageKey.APP_TOKEN)).toEqual(value);
22
+ });
23
+ it('uses the prefixed key in the underlying localStorage', () => {
24
+ setStored(StorageKey.USERNAME, 'bob');
25
+ expect(localStorage.getItem(`${PREFIX}.${StorageKey.USERNAME}`)).toBe(JSON.stringify('bob'));
26
+ });
27
+ it('returns null for a key that has not been set', () => {
28
+ expect(getStored(StorageKey.USERNAME)).toBeNull();
29
+ });
30
+ it('overwrites an existing value', () => {
31
+ setStored(StorageKey.USERNAME, 'alice');
32
+ setStored(StorageKey.USERNAME, 'bob');
33
+ expect(getStored(StorageKey.USERNAME)).toBe('bob');
34
+ });
35
+ it('stores a number value', () => {
36
+ setStored(StorageKey.PAGE_COUNT, 25);
37
+ expect(getStored(StorageKey.PAGE_COUNT)).toBe(25);
38
+ });
39
+ });
40
+ // -------------------------------------------------------------------------
41
+ // removeStored
42
+ // -------------------------------------------------------------------------
43
+ describe('removeStored', () => {
44
+ it('removes an existing key so it returns null on subsequent reads', () => {
45
+ setStored(StorageKey.USERNAME, 'alice');
46
+ removeStored(StorageKey.USERNAME);
47
+ expect(getStored(StorageKey.USERNAME)).toBeNull();
48
+ });
49
+ it('does not throw when removing a key that was never stored', () => {
50
+ expect(() => removeStored(StorageKey.USERNAME)).not.toThrow();
51
+ });
52
+ it('removes only the targeted key and leaves others intact', () => {
53
+ setStored(StorageKey.USERNAME, 'alice');
54
+ setStored(StorageKey.PROVIDER, 'howler');
55
+ removeStored(StorageKey.USERNAME);
56
+ expect(getStored(StorageKey.USERNAME)).toBeNull();
57
+ expect(getStored(StorageKey.PROVIDER)).toBe('howler');
58
+ });
59
+ });
60
+ // -------------------------------------------------------------------------
61
+ // saveLoginCredential
62
+ // -------------------------------------------------------------------------
63
+ describe('saveLoginCredential', () => {
64
+ it('returns true and stores app_token when app_token is present', () => {
65
+ const result = saveLoginCredential({ app_token: 'tok123' });
66
+ expect(result).toBe(true);
67
+ expect(getStored(StorageKey.APP_TOKEN)).toBe('tok123');
68
+ });
69
+ it('also stores refresh_token and provider when they are present', () => {
70
+ saveLoginCredential({ app_token: 'tok', refresh_token: 'ref', provider: 'howler' });
71
+ expect(getStored(StorageKey.REFRESH_TOKEN)).toBe('ref');
72
+ expect(getStored(StorageKey.PROVIDER)).toBe('howler');
73
+ });
74
+ it('does not store refresh_token when it is absent', () => {
75
+ saveLoginCredential({ app_token: 'tok' });
76
+ expect(getStored(StorageKey.REFRESH_TOKEN)).toBeNull();
77
+ });
78
+ it('returns false and clears stored tokens when app_token is absent', () => {
79
+ setStored(StorageKey.APP_TOKEN, 'old-token');
80
+ setStored(StorageKey.REFRESH_TOKEN, 'old-refresh');
81
+ const result = saveLoginCredential({});
82
+ expect(result).toBe(false);
83
+ expect(getStored(StorageKey.APP_TOKEN)).toBeNull();
84
+ expect(getStored(StorageKey.REFRESH_TOKEN)).toBeNull();
85
+ });
86
+ it('clears APP_TOKEN, REFRESH_TOKEN, and PROVIDER on empty credential', () => {
87
+ setStored(StorageKey.APP_TOKEN, 't');
88
+ setStored(StorageKey.REFRESH_TOKEN, 'r');
89
+ setStored(StorageKey.PROVIDER, 'p');
90
+ saveLoginCredential({});
91
+ expect(getStored(StorageKey.APP_TOKEN)).toBeNull();
92
+ expect(getStored(StorageKey.REFRESH_TOKEN)).toBeNull();
93
+ expect(getStored(StorageKey.PROVIDER)).toBeNull();
94
+ });
95
+ });
96
+ });
@@ -0,0 +1 @@
1
+ export {};