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

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 (397) hide show
  1. package/api/index.d.ts +0 -4
  2. package/api/index.js +2 -10
  3. package/api/search/facet/hit.d.ts +3 -1
  4. package/api/search/facet/index.d.ts +1 -3
  5. package/api/search/index.d.ts +1 -2
  6. package/api/search/index.js +1 -2
  7. package/commons/components/leftnav/LeftNavDrawer.js +1 -1
  8. package/components/app/App.d.ts +1 -0
  9. package/components/app/App.js +20 -42
  10. package/components/app/hooks/useMatchers.d.ts +1 -1
  11. package/components/app/hooks/useMatchers.js +11 -23
  12. package/components/app/hooks/useMatchers.test.js +22 -22
  13. package/components/app/hooks/useTitle.js +5 -5
  14. package/components/app/providers/FavouritesProvider.js +2 -6
  15. package/components/app/providers/HitProvider.d.ts +22 -0
  16. package/components/app/providers/{RecordProvider.js → HitProvider.js} +42 -42
  17. package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
  18. package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
  19. package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +71 -52
  20. package/components/app/providers/ModalProvider.d.ts +1 -1
  21. package/components/app/providers/ModalProvider.js +5 -1
  22. package/components/app/providers/ParameterProvider.d.ts +2 -9
  23. package/components/app/providers/ParameterProvider.js +240 -165
  24. package/components/app/providers/ParameterProvider.test.js +94 -346
  25. package/components/app/providers/SocketProvider.d.ts +2 -11
  26. package/components/app/providers/SocketProvider.js +5 -18
  27. package/components/app/providers/UserListProvider.js +8 -28
  28. package/components/elements/PluginTypography.d.ts +1 -2
  29. package/components/elements/PluginTypography.js +2 -3
  30. package/components/elements/UserList.d.ts +2 -5
  31. package/components/elements/UserList.js +8 -18
  32. package/components/elements/addons/buttons/CustomButton.test.js +106 -0
  33. package/components/elements/addons/layout/FlexOne.test.js +30 -0
  34. package/components/elements/addons/lists/TuiListBase.js +4 -0
  35. package/components/elements/addons/lists/TuiListElement.js +6 -3
  36. package/components/elements/addons/lists/index.d.ts +1 -0
  37. package/components/elements/addons/search/SearchPagination.test.js +80 -0
  38. package/components/elements/addons/search/SearchTotal.test.js +45 -0
  39. package/components/elements/addons/search/phrase/Phrase.js +1 -1
  40. package/components/elements/display/ChipPopper.d.ts +1 -1
  41. package/components/elements/display/ChipPopper.js +5 -5
  42. package/components/elements/display/DocumentationButton.test.js +84 -0
  43. package/components/elements/display/DynamicTabs.test.js +93 -0
  44. package/components/elements/display/HowlerAvatar.js +2 -1
  45. package/components/elements/display/HowlerCard.js +1 -1
  46. package/components/elements/display/HowlerCard.test.js +55 -0
  47. package/components/elements/display/Image.test.js +79 -0
  48. package/components/elements/display/Markdown.js +9 -2
  49. package/components/elements/display/Modal.js +0 -2
  50. package/components/elements/display/QueryResultText.test.js +48 -0
  51. package/components/elements/display/TextDivider.test.js +43 -0
  52. package/components/elements/display/TypingIndicator.test.js +33 -0
  53. package/components/elements/display/icons/BundleButton.d.ts +6 -0
  54. package/components/elements/display/icons/BundleButton.js +32 -0
  55. package/components/elements/display/modals/ConfirmDeleteModal.d.ts +4 -0
  56. package/components/elements/display/modals/ConfirmDeleteModal.js +4 -2
  57. package/components/elements/hit/HitActions.js +4 -4
  58. package/components/elements/hit/HitBanner.d.ts +0 -1
  59. package/components/elements/hit/HitBanner.js +49 -31
  60. package/components/elements/hit/HitCard.d.ts +0 -2
  61. package/components/elements/hit/HitCard.js +7 -7
  62. package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
  63. package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
  64. package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
  65. package/components/elements/hit/HitLabels.js +2 -2
  66. package/components/elements/hit/HitOutline.d.ts +0 -1
  67. package/components/elements/hit/HitOutline.js +3 -3
  68. package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
  69. package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
  70. package/components/elements/hit/HitRelated.d.ts +6 -0
  71. package/components/elements/hit/HitRelated.js +7 -0
  72. package/components/elements/hit/HitSummary.d.ts +1 -2
  73. package/components/elements/hit/HitSummary.js +8 -8
  74. package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
  75. package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
  76. package/components/elements/hit/aggregate/HitGraph.js +8 -8
  77. package/components/elements/hit/elements/{Assigned.d.ts → HitUsers.d.ts} +5 -2
  78. package/components/elements/hit/elements/HitUsers.js +33 -0
  79. package/components/elements/hit/outlines/DefaultOutline.js +1 -1
  80. package/components/elements/view/ViewTitle.d.ts +0 -1
  81. package/components/elements/view/ViewTitle.js +2 -9
  82. package/components/hooks/useHitActions.d.ts +1 -1
  83. package/components/hooks/useHitActions.js +4 -4
  84. package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
  85. package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
  86. package/components/hooks/useMyPreferences.js +3 -11
  87. package/components/hooks/useMySearch.js +2 -2
  88. package/components/hooks/useMySitemap.js +1 -4
  89. package/components/hooks/useMyTheme.js +2 -9
  90. package/components/routes/403.test.js +27 -0
  91. package/components/routes/404.test.js +26 -0
  92. package/components/routes/ErrorBoundary.test.js +43 -0
  93. package/components/routes/ErrorOccured.test.js +35 -0
  94. package/components/routes/Logout.test.js +59 -0
  95. package/components/routes/action/edit/ActionEditor.js +2 -2
  96. package/components/routes/action/view/ActionDetails.js +4 -2
  97. package/components/routes/action/view/ActionSearch.js +12 -7
  98. package/components/routes/advanced/QueryBuilder.js +1 -1
  99. package/components/routes/advanced/QueryEditor.js +3 -3
  100. package/components/routes/advanced/historyCompletionProvider.js +3 -3
  101. package/components/routes/analytics/AnalyticDetails.js +12 -8
  102. package/components/routes/analytics/AnalyticSearch.js +1 -1
  103. package/components/routes/dossiers/DossierEditor.js +2 -2
  104. package/components/routes/dossiers/DossierEditor.test.js +1 -1
  105. package/components/routes/dossiers/Dossiers.js +15 -11
  106. package/components/routes/help/ApiDocumentation.js +1 -1
  107. package/components/routes/help/BundleDocumentation.d.ts +3 -0
  108. package/components/routes/help/BundleDocumentation.js +12 -0
  109. package/components/routes/help/HitBannerDocumentation.js +0 -1
  110. package/components/routes/help/HitDocumentation.js +3 -1
  111. package/components/routes/help/markdown/en/bundles.md.js +1 -0
  112. package/components/routes/help/markdown/en/client.md.js +1 -1
  113. package/components/routes/help/markdown/fr/bundles.md.js +1 -0
  114. package/components/routes/help/markdown/fr/client.md.js +1 -1
  115. package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
  116. package/components/routes/hits/search/BundleParentMenu.js +32 -0
  117. package/components/routes/hits/search/BundleScroller.d.ts +2 -0
  118. package/components/routes/hits/search/BundleScroller.js +6 -0
  119. package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
  120. package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
  121. package/components/routes/hits/search/HitContextMenu.js +239 -0
  122. package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +43 -98
  123. package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
  124. package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
  125. package/components/routes/hits/search/InformationPane.d.ts +0 -1
  126. package/components/routes/hits/search/InformationPane.js +63 -50
  127. package/components/routes/hits/search/LayoutSettings.js +3 -3
  128. package/components/routes/hits/search/QuerySettings.js +1 -2
  129. package/components/routes/hits/search/QuerySettings.test.js +9 -14
  130. package/components/routes/hits/search/SearchPane.js +49 -26
  131. package/components/routes/hits/search/ViewLink.js +3 -3
  132. package/components/routes/hits/search/ViewLink.test.js +8 -8
  133. package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
  134. package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
  135. package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
  136. package/components/routes/hits/search/grid/HitGrid.js +18 -20
  137. package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
  138. package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +9 -11
  139. package/components/routes/hits/search/shared/CustomSpan.js +7 -7
  140. package/components/routes/hits/search/shared/CustomSpan.test.js +170 -0
  141. package/components/routes/hits/view/HitViewer.js +13 -12
  142. package/components/routes/home/AddNewCard.test.js +68 -0
  143. package/components/routes/home/ViewCard.js +41 -47
  144. package/components/routes/home/ViewRefresh.test.js +79 -0
  145. package/components/routes/overviews/OverviewCard.test.js +108 -0
  146. package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
  147. package/components/routes/overviews/OverviewViewer.js +14 -10
  148. package/components/routes/overviews/Overviews.js +15 -11
  149. package/components/routes/settings/LocalSection.js +2 -1
  150. package/components/routes/settings/SettingsSection.test.js +19 -0
  151. package/components/routes/templates/TemplateCard.d.ts +3 -1
  152. package/components/routes/templates/TemplateCard.js +8 -4
  153. package/components/routes/templates/TemplateCard.test.d.ts +1 -0
  154. package/components/routes/templates/TemplateCard.test.js +115 -0
  155. package/components/routes/templates/TemplateViewer.js +57 -16
  156. package/components/routes/templates/Templates.js +23 -6
  157. package/components/routes/views/ViewComposer.js +19 -46
  158. package/components/routes/views/Views.js +8 -4
  159. package/locales/en/translation.json +17 -117
  160. package/locales/fr/translation.json +19 -119
  161. package/models/WithMetadata.d.ts +1 -2
  162. package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
  163. package/models/entities/generated/Hit.d.ts +1 -1
  164. package/models/entities/generated/Howler.d.ts +6 -0
  165. package/models/entities/generated/Rule.d.ts +9 -6
  166. package/models/entities/generated/Threat.d.ts +2 -2
  167. package/models/entities/generated/UserUser.d.ts +2 -0
  168. package/models/entities/generated/View.d.ts +0 -1
  169. package/package.json +112 -132
  170. package/plugins/clue/components/ClueTypography.js +2 -2
  171. package/plugins/clue/utils.d.ts +1 -2
  172. package/rest/FetchClient.test.d.ts +1 -0
  173. package/rest/FetchClient.test.js +81 -0
  174. package/setupTests.js +5 -0
  175. package/tests/mocks.d.ts +1 -11
  176. package/tests/mocks.js +7 -12
  177. package/tests/server-handlers.js +1 -6
  178. package/tests/utils.d.ts +0 -4
  179. package/tests/utils.js +0 -20
  180. package/utils/Throttler.test.d.ts +1 -0
  181. package/utils/Throttler.test.js +135 -0
  182. package/utils/actionUtils.test.d.ts +1 -0
  183. package/utils/actionUtils.test.js +140 -0
  184. package/utils/constants.d.ts +4 -4
  185. package/utils/constants.js +1 -6
  186. package/utils/hitFunctions.d.ts +1 -2
  187. package/utils/hitFunctions.js +4 -4
  188. package/utils/hitFunctions.test.d.ts +1 -0
  189. package/utils/hitFunctions.test.js +52 -0
  190. package/utils/localStorage.test.d.ts +1 -0
  191. package/utils/localStorage.test.js +96 -0
  192. package/utils/menuUtils.test.d.ts +1 -0
  193. package/utils/menuUtils.test.js +198 -0
  194. package/utils/sessionStorage.test.d.ts +1 -0
  195. package/utils/sessionStorage.test.js +81 -0
  196. package/utils/socketUtils.d.ts +0 -14
  197. package/utils/socketUtils.js +1 -17
  198. package/utils/socketUtils.test.js +16 -49
  199. package/utils/stringUtils.test.d.ts +1 -0
  200. package/utils/stringUtils.test.js +159 -0
  201. package/utils/utils.test.d.ts +1 -0
  202. package/utils/utils.test.js +292 -0
  203. package/utils/viewUtils.js +0 -3
  204. package/utils/viewUtils.test.d.ts +1 -0
  205. package/utils/viewUtils.test.js +45 -0
  206. package/utils/xsrf.test.d.ts +1 -0
  207. package/utils/xsrf.test.js +41 -0
  208. package/api/search/case.d.ts +0 -4
  209. package/api/search/case.js +0 -8
  210. package/api/socket/index.d.ts +0 -3
  211. package/api/socket/index.js +0 -6
  212. package/api/socket/viewers.d.ts +0 -2
  213. package/api/socket/viewers.js +0 -8
  214. package/api/socket/viewers.test.js +0 -44
  215. package/api/v2/case/index.d.ts +0 -9
  216. package/api/v2/case/index.js +0 -21
  217. package/api/v2/case/items.d.ts +0 -6
  218. package/api/v2/case/items.js +0 -18
  219. package/api/v2/case/rules.d.ts +0 -6
  220. package/api/v2/case/rules.js +0 -18
  221. package/api/v2/index.d.ts +0 -4
  222. package/api/v2/index.js +0 -6
  223. package/api/v2/search/facet.d.ts +0 -3
  224. package/api/v2/search/facet.js +0 -12
  225. package/api/v2/search/index.d.ts +0 -5
  226. package/api/v2/search/index.js +0 -24
  227. package/components/app/providers/RecordProvider.d.ts +0 -23
  228. package/components/elements/ContextMenu.d.ts +0 -56
  229. package/components/elements/ContextMenu.js +0 -109
  230. package/components/elements/ContextMenu.test.js +0 -215
  231. package/components/elements/ObjectDetails.d.ts +0 -6
  232. package/components/elements/case/CaseCard.d.ts +0 -12
  233. package/components/elements/case/CaseCard.js +0 -42
  234. package/components/elements/case/CasePreview.d.ts +0 -6
  235. package/components/elements/case/CasePreview.js +0 -17
  236. package/components/elements/case/StatusIcon.d.ts +0 -5
  237. package/components/elements/case/StatusIcon.js +0 -13
  238. package/components/elements/hit/elements/AnalyticLink.d.ts +0 -9
  239. package/components/elements/hit/elements/AnalyticLink.js +0 -22
  240. package/components/elements/hit/elements/Assigned.js +0 -34
  241. package/components/elements/hit/elements/Assigned.test.js +0 -65
  242. package/components/elements/hit/related/RelatedRecords.js +0 -63
  243. package/components/elements/observable/ObservableCard.d.ts +0 -6
  244. package/components/elements/observable/ObservableCard.js +0 -22
  245. package/components/elements/observable/ObservablePreview.d.ts +0 -6
  246. package/components/elements/observable/ObservablePreview.js +0 -12
  247. package/components/elements/record/RecordContextMenu.js +0 -268
  248. package/components/elements/record/RecordRelated.d.ts +0 -7
  249. package/components/elements/record/RecordRelated.js +0 -34
  250. package/components/hooks/useRelatedRecords.d.ts +0 -13
  251. package/components/hooks/useRelatedRecords.js +0 -32
  252. package/components/routes/cases/CaseViewer.d.ts +0 -2
  253. package/components/routes/cases/CaseViewer.js +0 -44
  254. package/components/routes/cases/CaseViewer.test.js +0 -133
  255. package/components/routes/cases/Cases.d.ts +0 -2
  256. package/components/routes/cases/Cases.js +0 -148
  257. package/components/routes/cases/constants.d.ts +0 -6
  258. package/components/routes/cases/constants.js +0 -6
  259. package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
  260. package/components/routes/cases/detail/AlertPanel.js +0 -33
  261. package/components/routes/cases/detail/CaseAssets.d.ts +0 -11
  262. package/components/routes/cases/detail/CaseAssets.js +0 -104
  263. package/components/routes/cases/detail/CaseAssets.test.js +0 -167
  264. package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
  265. package/components/routes/cases/detail/CaseDashboard.js +0 -66
  266. package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
  267. package/components/routes/cases/detail/CaseDetails.js +0 -70
  268. package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
  269. package/components/routes/cases/detail/CaseOverview.js +0 -43
  270. package/components/routes/cases/detail/CaseRules.d.ts +0 -7
  271. package/components/routes/cases/detail/CaseRules.js +0 -57
  272. package/components/routes/cases/detail/CaseRules.test.js +0 -221
  273. package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
  274. package/components/routes/cases/detail/CaseSidebar.js +0 -107
  275. package/components/routes/cases/detail/CaseSidebar.test.js +0 -266
  276. package/components/routes/cases/detail/CaseTask.d.ts +0 -11
  277. package/components/routes/cases/detail/CaseTask.js +0 -66
  278. package/components/routes/cases/detail/CaseTimeline.d.ts +0 -12
  279. package/components/routes/cases/detail/CaseTimeline.js +0 -106
  280. package/components/routes/cases/detail/CaseTimeline.test.js +0 -320
  281. package/components/routes/cases/detail/CreateRuleDialog.d.ts +0 -9
  282. package/components/routes/cases/detail/CreateRuleDialog.js +0 -163
  283. package/components/routes/cases/detail/CreateRuleDialog.test.js +0 -259
  284. package/components/routes/cases/detail/ItemPage.d.ts +0 -6
  285. package/components/routes/cases/detail/ItemPage.js +0 -95
  286. package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
  287. package/components/routes/cases/detail/RelatedCasePanel.js +0 -34
  288. package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
  289. package/components/routes/cases/detail/TaskPanel.js +0 -52
  290. package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -11
  291. package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -24
  292. package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
  293. package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -26
  294. package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
  295. package/components/routes/cases/detail/assets/Asset.js +0 -12
  296. package/components/routes/cases/detail/assets/Asset.test.js +0 -72
  297. package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -20
  298. package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -83
  299. package/components/routes/cases/detail/sidebar/CaseFolder.test.js +0 -295
  300. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
  301. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -103
  302. package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -363
  303. package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +0 -25
  304. package/components/routes/cases/detail/sidebar/FolderEntry.js +0 -88
  305. package/components/routes/cases/detail/sidebar/FolderEntry.test.js +0 -206
  306. package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +0 -5
  307. package/components/routes/cases/detail/sidebar/RootDropZone.js +0 -33
  308. package/components/routes/cases/detail/sidebar/types.d.ts +0 -9
  309. package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
  310. package/components/routes/cases/detail/sidebar/utils.js +0 -29
  311. package/components/routes/cases/detail/sidebar/utils.test.js +0 -82
  312. package/components/routes/cases/hooks/useCase.d.ts +0 -13
  313. package/components/routes/cases/hooks/useCase.js +0 -69
  314. package/components/routes/cases/hooks/useCase.test.js +0 -141
  315. package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
  316. package/components/routes/cases/modals/AddToCaseModal.js +0 -59
  317. package/components/routes/cases/modals/AddToCaseModal.test.js +0 -313
  318. package/components/routes/cases/modals/CaseRecordRow.d.ts +0 -9
  319. package/components/routes/cases/modals/CaseRecordRow.js +0 -15
  320. package/components/routes/cases/modals/CreateCaseModal.d.ts +0 -7
  321. package/components/routes/cases/modals/CreateCaseModal.js +0 -55
  322. package/components/routes/cases/modals/CreateCaseModal.test.js +0 -358
  323. package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
  324. package/components/routes/cases/modals/RenameItemModal.js +0 -48
  325. package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
  326. package/components/routes/cases/modals/ResolveModal.js +0 -115
  327. package/components/routes/cases/modals/ResolveModal.test.js +0 -394
  328. package/components/routes/cases/modals/hooks.d.ts +0 -7
  329. package/components/routes/cases/modals/hooks.js +0 -44
  330. package/components/routes/cases/modals/types.d.ts +0 -5
  331. package/components/routes/cases/search/CaseAssigneeFilter.d.ts +0 -6
  332. package/components/routes/cases/search/CaseAssigneeFilter.js +0 -33
  333. package/components/routes/cases/search/CaseAssigneeFilter.test.js +0 -127
  334. package/components/routes/cases/search/CaseDateFilter.d.ts +0 -13
  335. package/components/routes/cases/search/CaseDateFilter.js +0 -26
  336. package/components/routes/cases/search/CaseDateFilter.test.js +0 -115
  337. package/components/routes/cases/search/CaseStatusFilter.d.ts +0 -6
  338. package/components/routes/cases/search/CaseStatusFilter.js +0 -13
  339. package/components/routes/cases/search/CaseStatusFilter.test.js +0 -86
  340. package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
  341. package/components/routes/hits/search/shared/IndexPicker.js +0 -20
  342. package/components/routes/observables/ObservableViewer.d.ts +0 -7
  343. package/components/routes/observables/ObservableViewer.js +0 -27
  344. package/models/entities/generated/AttachmentsFile.d.ts +0 -12
  345. package/models/entities/generated/Case.d.ts +0 -28
  346. package/models/entities/generated/DestinationOriginal.d.ts +0 -19
  347. package/models/entities/generated/EmailAttachment.d.ts +0 -8
  348. package/models/entities/generated/EmailParent.d.ts +0 -19
  349. package/models/entities/generated/Enrichments.d.ts +0 -7
  350. package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
  351. package/models/entities/generated/HttpResponse.d.ts +0 -11
  352. package/models/entities/generated/Item.d.ts +0 -9
  353. package/models/entities/generated/Observable.d.ts +0 -85
  354. package/models/entities/generated/ObservableCloud.d.ts +0 -20
  355. package/models/entities/generated/ObservableDestination.d.ts +0 -23
  356. package/models/entities/generated/ObservableEmail.d.ts +0 -30
  357. package/models/entities/generated/ObservableFile.d.ts +0 -36
  358. package/models/entities/generated/ObservableHowler.d.ts +0 -42
  359. package/models/entities/generated/ObservableHttp.d.ts +0 -11
  360. package/models/entities/generated/ObservableObserver.d.ts +0 -21
  361. package/models/entities/generated/ObservableOrganization.d.ts +0 -7
  362. package/models/entities/generated/ObservableProcess.d.ts +0 -34
  363. package/models/entities/generated/ObservableSource.d.ts +0 -23
  364. package/models/entities/generated/ObservableThreat.d.ts +0 -21
  365. package/models/entities/generated/ObservableTls.d.ts +0 -12
  366. package/models/entities/generated/ObserverIngress.d.ts +0 -9
  367. package/models/entities/generated/Task.d.ts +0 -10
  368. package/models/socket/CaseUpdate.d.ts +0 -5
  369. package/models/socket/ViewersUpdate.d.ts +0 -4
  370. package/utils/typeUtils.d.ts +0 -7
  371. package/utils/typeUtils.js +0 -27
  372. /package/{api/socket/viewers.test.d.ts → components/app/providers/HitSearchProvider.test.d.ts} +0 -0
  373. /package/components/{app/providers/RecordSearchProvider.test.d.ts → elements/addons/buttons/CustomButton.test.d.ts} +0 -0
  374. /package/components/elements/{ContextMenu.test.d.ts → addons/layout/FlexOne.test.d.ts} +0 -0
  375. /package/components/elements/{hit/elements/Assigned.test.d.ts → addons/search/SearchPagination.test.d.ts} +0 -0
  376. /package/components/elements/{record/RecordContextMenu.test.d.ts → addons/search/SearchTotal.test.d.ts} +0 -0
  377. /package/components/{routes/cases/CaseViewer.test.d.ts → elements/display/DocumentationButton.test.d.ts} +0 -0
  378. /package/components/{routes/cases/detail/CaseAssets.test.d.ts → elements/display/DynamicTabs.test.d.ts} +0 -0
  379. /package/components/{routes/cases/detail/CaseRules.test.d.ts → elements/display/HowlerCard.test.d.ts} +0 -0
  380. /package/components/{routes/cases/detail/CaseSidebar.test.d.ts → elements/display/Image.test.d.ts} +0 -0
  381. /package/components/{routes/cases/detail/CaseTimeline.test.d.ts → elements/display/QueryResultText.test.d.ts} +0 -0
  382. /package/components/{routes/cases/detail/CreateRuleDialog.test.d.ts → elements/display/TextDivider.test.d.ts} +0 -0
  383. /package/components/{routes/cases/detail/assets/Asset.test.d.ts → elements/display/TypingIndicator.test.d.ts} +0 -0
  384. /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
  385. /package/components/routes/{cases/detail/sidebar/CaseFolder.test.d.ts → 403.test.d.ts} +0 -0
  386. /package/components/routes/{cases/detail/sidebar/CaseFolderContextMenu.test.d.ts → 404.test.d.ts} +0 -0
  387. /package/components/routes/{cases/detail/sidebar/FolderEntry.test.d.ts → ErrorBoundary.test.d.ts} +0 -0
  388. /package/components/routes/{cases/detail/sidebar/utils.test.d.ts → ErrorOccured.test.d.ts} +0 -0
  389. /package/components/routes/{cases/hooks/useCase.test.d.ts → Logout.test.d.ts} +0 -0
  390. /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
  391. /package/components/routes/{cases/modals/AddToCaseModal.test.d.ts → hits/search/HitContextMenu.test.d.ts} +0 -0
  392. /package/components/routes/{cases/modals/CreateCaseModal.test.d.ts → hits/search/shared/CustomSpan.test.d.ts} +0 -0
  393. /package/components/routes/{cases/modals/ResolveModal.test.d.ts → home/AddNewCard.test.d.ts} +0 -0
  394. /package/components/routes/{cases/search/CaseAssigneeFilter.test.d.ts → home/ViewRefresh.test.d.ts} +0 -0
  395. /package/components/routes/{cases/search/CaseDateFilter.test.d.ts → overviews/OverviewCard.test.d.ts} +0 -0
  396. /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
  397. /package/components/routes/{cases/search/CaseStatusFilter.test.d.ts → settings/SettingsSection.test.d.ts} +0 -0
@@ -30,12 +30,7 @@ export const MOCK_RESPONSES = {
30
30
  total: 1,
31
31
  rows: 1
32
32
  },
33
- '/api/v1/analytic': [createMockAnalytic()],
34
- '/api/v2/search/hit,observable': {
35
- items: [],
36
- total: 0,
37
- rows: 0
38
- }
33
+ '/api/v1/analytic': [createMockAnalytic()]
39
34
  };
40
35
  const handlers = [
41
36
  ...Object.entries(MOCK_RESPONSES).map(([path, data]) => http.all(path, async () => HttpResponse.json({ api_response: data }))),
package/tests/utils.d.ts CHANGED
@@ -1,20 +1,16 @@
1
1
  import type { Action } from '@cccsaurora/howler-ui/models/entities/generated/Action';
2
2
  import type { Analytic } from '@cccsaurora/howler-ui/models/entities/generated/Analytic';
3
- import type { Case } from '@cccsaurora/howler-ui/models/entities/generated/Case';
4
3
  import type { Dossier } from '@cccsaurora/howler-ui/models/entities/generated/Dossier';
5
4
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
6
- import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
7
5
  import type { Template } from '@cccsaurora/howler-ui/models/entities/generated/Template';
8
6
  import type { View } from '@cccsaurora/howler-ui/models/entities/generated/View';
9
7
  type RecursivePartial<T> = {
10
8
  [P in keyof T]?: T[P] extends (infer U)[] ? RecursivePartial<U>[] : T[P] extends object | undefined ? RecursivePartial<T[P]> : T[P];
11
9
  };
12
10
  export declare const createMockHit: (overrides?: RecursivePartial<Hit>) => Hit;
13
- export declare const createMockObservable: (overrides?: RecursivePartial<Observable>) => Observable;
14
11
  export declare const createMockAnalytic: (overrides?: Partial<Analytic>) => Analytic;
15
12
  export declare const createMockTemplate: (overrides?: Partial<Template>) => Template;
16
13
  export declare const createMockAction: (overrides?: Partial<Action>) => Action;
17
14
  export declare const createMockView: (overrides?: Partial<View>) => View;
18
- export declare const createMockCase: (overrides?: Partial<Case>) => Case;
19
15
  export declare const createMockDossier: (overrides?: Partial<Dossier>) => Dossier;
20
16
  export {};
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 {};