@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
@@ -0,0 +1,198 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import AppMenuBuilder from './menuUtils';
3
+ // Avoid pulling in react-pluggable (and its transitive React deps) by mocking the store module.
4
+ // vi.mock is hoisted by Vitest before any imports, so the mock is applied even though this call appears below the imports.
5
+ vi.mock('plugins/store', () => ({
6
+ MainMenuInsertOperation: {
7
+ Insert: 'INSERT',
8
+ InsertAfter: 'AFTER',
9
+ InsertBefore: 'BEFORE'
10
+ }
11
+ }));
12
+ // ---------------------------------------------------------------------------
13
+ // Helpers
14
+ // ---------------------------------------------------------------------------
15
+ const makeItem = (id, overrides = {}) => ({
16
+ type: 'item',
17
+ element: { id, i18nKey: `key.${id}`, route: `/${id}`, ...overrides }
18
+ });
19
+ const makeGroup = (id, items = []) => ({
20
+ type: 'group',
21
+ element: { id, i18nKey: `key.${id}`, items }
22
+ });
23
+ const makeDivider = () => ({ type: 'divider', element: null });
24
+ // ---------------------------------------------------------------------------
25
+ // Tests
26
+ // ---------------------------------------------------------------------------
27
+ describe('AppMenuBuilder', () => {
28
+ let initial;
29
+ let builder;
30
+ beforeEach(() => {
31
+ initial = [makeItem('home'), makeItem('search'), makeGroup('tools', [{ id: 'view', route: '/view' }])];
32
+ builder = new AppMenuBuilder(initial);
33
+ });
34
+ // -------------------------------------------------------------------------
35
+ // menu getter
36
+ // -------------------------------------------------------------------------
37
+ describe('menu getter', () => {
38
+ it('returns the initial menu unchanged when no operations are applied', () => {
39
+ expect(builder.menu).toHaveLength(3);
40
+ });
41
+ });
42
+ // -------------------------------------------------------------------------
43
+ // indexOfMenuId
44
+ // -------------------------------------------------------------------------
45
+ describe('indexOfMenuId', () => {
46
+ it('returns index -1 for the "root" pseudo-target', () => {
47
+ expect(builder.indexOfMenuId('root')).toEqual({ index: -1 });
48
+ });
49
+ it('returns the correct top-level index for a root item', () => {
50
+ expect(builder.indexOfMenuId('home')).toEqual({ index: 0 });
51
+ });
52
+ it('returns both index and subIndex for a nested item', () => {
53
+ expect(builder.indexOfMenuId('view')).toEqual({ index: 2, subIndex: 0 });
54
+ });
55
+ it('throws for an unknown id', () => {
56
+ expect(() => builder.indexOfMenuId('unknown-id')).toThrow();
57
+ });
58
+ });
59
+ // -------------------------------------------------------------------------
60
+ // insert
61
+ // -------------------------------------------------------------------------
62
+ describe('insert', () => {
63
+ it('appends a new item to the root when targetId is "root"', () => {
64
+ builder.insert('root', makeItem('new'));
65
+ expect(builder.menu).toHaveLength(4);
66
+ expect(builder.menu[3].element.id).toBe('new');
67
+ });
68
+ it('appends a new item to an existing group', () => {
69
+ builder.insert('tools', makeItem('extra'));
70
+ const group = builder.menu.find(el => el.type === 'group');
71
+ expect(group.element.items).toHaveLength(2);
72
+ expect(group.element.items[1].id).toBe('extra');
73
+ });
74
+ it('converts a root-level item into a group when inserting into it', () => {
75
+ builder.insert('home', makeItem('sub-home'));
76
+ const converted = builder.menu.find(el => el.type === 'group');
77
+ // The first group found should be the newly converted one.
78
+ expect(converted).toBeDefined();
79
+ expect(converted.element.items.some((i) => i.id === 'sub-home')).toBe(true);
80
+ });
81
+ it('marks nested items added to a group as nested:true', () => {
82
+ builder.insert('tools', makeItem('nested-item'));
83
+ const group = builder.menu.find(el => el.type === 'group');
84
+ const last = group.element.items[group.element.items.length - 1];
85
+ expect(last.nested).toBe(true);
86
+ });
87
+ it('does not insert a divider into a group (logs warning and returns)', () => {
88
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
89
+ const before = builder.menu.find(el => el.type === 'group').element.items.length;
90
+ builder.insert('tools', makeDivider());
91
+ const after = builder.menu.find(el => el.type === 'group').element.items.length;
92
+ expect(after).toBe(before);
93
+ warnSpy.mockRestore();
94
+ });
95
+ });
96
+ // -------------------------------------------------------------------------
97
+ // insertBefore
98
+ // -------------------------------------------------------------------------
99
+ describe('insertBefore', () => {
100
+ it('inserts a new root-level item before the target', () => {
101
+ builder.insertBefore('search', makeItem('before-search'));
102
+ // 'search' was at index 1; the new item should now be at index 1.
103
+ expect(builder.menu[1].element.id).toBe('before-search');
104
+ expect(builder.menu[2].element.id).toBe('search');
105
+ });
106
+ it('inserts before the first root item (index 0)', () => {
107
+ builder.insertBefore('home', makeItem('new-first'));
108
+ expect(builder.menu[0].element.id).toBe('new-first');
109
+ });
110
+ it('inserts a new nested item before a sub-item within its group', () => {
111
+ builder.insertBefore('view', makeItem('before-view'));
112
+ const group = builder.menu.find(el => el.type === 'group');
113
+ expect(group.element.items[0].id).toBe('before-view');
114
+ expect(group.element.items[1].id).toBe('view');
115
+ });
116
+ it('marks items inserted into a group as nested:true', () => {
117
+ builder.insertBefore('view', makeItem('nested-before'));
118
+ const group = builder.menu.find(el => el.type === 'group');
119
+ expect(group.element.items[0].nested).toBe(true);
120
+ });
121
+ it('does not insert a divider before a sub-item (logs warning)', () => {
122
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
123
+ const before = builder.menu.find(el => el.type === 'group').element.items.length;
124
+ builder.insertBefore('view', makeDivider());
125
+ const after = builder.menu.find(el => el.type === 'group').element.items.length;
126
+ expect(after).toBe(before);
127
+ warnSpy.mockRestore();
128
+ });
129
+ });
130
+ // -------------------------------------------------------------------------
131
+ // insertAfter
132
+ // -------------------------------------------------------------------------
133
+ describe('insertAfter', () => {
134
+ it('inserts a new root-level item after the target', () => {
135
+ builder.insertAfter('home', makeItem('after-home'));
136
+ expect(builder.menu[1].element.id).toBe('after-home');
137
+ expect(builder.menu[2].element.id).toBe('search');
138
+ });
139
+ it('inserts after the last root item', () => {
140
+ builder.insertAfter('tools', makeItem('very-last'));
141
+ expect(builder.menu[builder.menu.length - 1].element.id).toBe('very-last');
142
+ });
143
+ it('inserts a new nested item after a sub-item within its group', () => {
144
+ builder.insertAfter('view', makeItem('after-view'));
145
+ const group = builder.menu.find(el => el.type === 'group');
146
+ expect(group.element.items[0].id).toBe('view');
147
+ expect(group.element.items[1].id).toBe('after-view');
148
+ });
149
+ it('marks items inserted into a group as nested:true', () => {
150
+ builder.insertAfter('view', makeItem('nested-after'));
151
+ const group = builder.menu.find(el => el.type === 'group');
152
+ expect(group.element.items[1].nested).toBe(true);
153
+ });
154
+ it('does not insert a divider after a sub-item (logs warning)', () => {
155
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
156
+ const before = builder.menu.find(el => el.type === 'group').element.items.length;
157
+ builder.insertAfter('view', makeDivider());
158
+ const after = builder.menu.find(el => el.type === 'group').element.items.length;
159
+ expect(after).toBe(before);
160
+ warnSpy.mockRestore();
161
+ });
162
+ });
163
+ // -------------------------------------------------------------------------
164
+ // applyOperations
165
+ // -------------------------------------------------------------------------
166
+ describe('applyOperations', () => {
167
+ it('applies an INSERT operation to the root', () => {
168
+ builder.applyOperations([{ operation: 'INSERT', targetId: 'root', item: makeItem('op-item') }]);
169
+ expect(builder.menu.some((el) => el.element?.id === 'op-item')).toBe(true);
170
+ });
171
+ it('applies a BEFORE operation', () => {
172
+ builder.applyOperations([{ operation: 'BEFORE', targetId: 'search', item: makeItem('before-op') }]);
173
+ const idx = builder.menu.findIndex((el) => el.element?.id === 'before-op');
174
+ const searchIdx = builder.menu.findIndex((el) => el.element?.id === 'search');
175
+ expect(idx).toBeLessThan(searchIdx);
176
+ });
177
+ it('applies an AFTER operation', () => {
178
+ builder.applyOperations([{ operation: 'AFTER', targetId: 'home', item: makeItem('after-op') }]);
179
+ const homeIdx = builder.menu.findIndex((el) => el.element?.id === 'home');
180
+ const afterIdx = builder.menu.findIndex((el) => el.element?.id === 'after-op');
181
+ expect(afterIdx).toBe(homeIdx + 1);
182
+ });
183
+ it('applies multiple operations in order', () => {
184
+ builder.applyOperations([
185
+ { operation: 'INSERT', targetId: 'root', item: makeItem('first') },
186
+ { operation: 'INSERT', targetId: 'root', item: makeItem('second') }
187
+ ]);
188
+ const ids = builder.menu.map((el) => el.element?.id).filter(Boolean);
189
+ expect(ids).toContain('first');
190
+ expect(ids).toContain('second');
191
+ });
192
+ it('ignores operations with unknown operation strings', () => {
193
+ const lenBefore = builder.menu.length;
194
+ builder.applyOperations([{ operation: 'UNKNOWN', targetId: 'root', item: makeItem('x') }]);
195
+ expect(builder.menu.length).toBe(lenBefore);
196
+ });
197
+ });
198
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,81 @@
1
+ /// <reference types="vitest" />
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { StorageKey } from './constants';
4
+ import { getAxiosCache, getStored, removeStored, setAxiosCache, setStored } from './sessionStorage';
5
+ // The sessionStorage module uses a debounced write (3 000 ms). Tests that need to verify
6
+ // persistence to sessionStorage use fake timers and advance past the debounce delay.
7
+ // afterEach flushes any pending changes so module-level state is clean for the next test.
8
+ const DEBOUNCE_MS = 3100; // slightly past the 3 000 ms throttle
9
+ const SESSION_PREFIX = 'howler.ui.cache';
10
+ describe('sessionStorage utilities', () => {
11
+ beforeEach(() => {
12
+ vi.useFakeTimers();
13
+ });
14
+ afterEach(() => {
15
+ // Flush pending debounced writes – this also resets the internal `changes` map.
16
+ vi.advanceTimersByTime(DEBOUNCE_MS);
17
+ vi.useRealTimers();
18
+ sessionStorage.clear();
19
+ });
20
+ // -------------------------------------------------------------------------
21
+ // getAxiosCache / setAxiosCache
22
+ // -------------------------------------------------------------------------
23
+ describe('getAxiosCache / setAxiosCache', () => {
24
+ it('returns an empty object when no cache has been stored', () => {
25
+ expect(getAxiosCache()).toEqual({});
26
+ });
27
+ it('stores and retrieves a cached entry after the debounce fires', () => {
28
+ setAxiosCache('etag-1', { data: 'value' });
29
+ vi.advanceTimersByTime(DEBOUNCE_MS);
30
+ expect(getAxiosCache()['etag-1']).toEqual({ data: 'value' });
31
+ });
32
+ it('accumulates multiple distinct cache entries', () => {
33
+ setAxiosCache('etag-a', { data: 'a' });
34
+ setAxiosCache('etag-b', { data: 'b' });
35
+ vi.advanceTimersByTime(DEBOUNCE_MS);
36
+ const cache = getAxiosCache();
37
+ expect(cache['etag-a']).toEqual({ data: 'a' });
38
+ expect(cache['etag-b']).toEqual({ data: 'b' });
39
+ });
40
+ it('overwrites an earlier entry for the same etag', () => {
41
+ setAxiosCache('etag-1', { data: 'old' });
42
+ setAxiosCache('etag-1', { data: 'new' });
43
+ vi.advanceTimersByTime(DEBOUNCE_MS);
44
+ expect(getAxiosCache()['etag-1']).toEqual({ data: 'new' });
45
+ });
46
+ });
47
+ // -------------------------------------------------------------------------
48
+ // setStored / getStored (object values)
49
+ // -------------------------------------------------------------------------
50
+ describe('setStored / getStored', () => {
51
+ it('makes an object value immediately readable via getStored (in-memory, before flush)', () => {
52
+ setStored(StorageKey.AXIOS_CACHE, { immediate: true });
53
+ // Timer has NOT advanced yet – data is in the in-memory `changes` map.
54
+ const result = getStored(StorageKey.AXIOS_CACHE);
55
+ expect(result.immediate).toBe(true);
56
+ });
57
+ it('persists an object value to sessionStorage after the debounce delay', () => {
58
+ const key = `${SESSION_PREFIX}.${StorageKey.AXIOS_CACHE}`;
59
+ setStored(StorageKey.AXIOS_CACHE, { persisted: true });
60
+ // Before flush: sessionStorage is still empty.
61
+ expect(sessionStorage.getItem(key)).toBeNull();
62
+ vi.advanceTimersByTime(DEBOUNCE_MS);
63
+ expect(JSON.parse(sessionStorage.getItem(key))).toEqual({ persisted: true });
64
+ });
65
+ });
66
+ // -------------------------------------------------------------------------
67
+ // removeStored
68
+ // -------------------------------------------------------------------------
69
+ describe('removeStored', () => {
70
+ it('removes a key from sessionStorage', () => {
71
+ setStored(StorageKey.AXIOS_CACHE, { val: 1 });
72
+ vi.advanceTimersByTime(DEBOUNCE_MS);
73
+ removeStored(StorageKey.AXIOS_CACHE);
74
+ const key = `${SESSION_PREFIX}.${StorageKey.AXIOS_CACHE}`;
75
+ expect(sessionStorage.getItem(key)).toBeNull();
76
+ });
77
+ it('does not throw when removing a key that was never set', () => {
78
+ expect(() => removeStored(StorageKey.AXIOS_CACHE)).not.toThrow();
79
+ });
80
+ });
81
+ });
@@ -1,22 +1,8 @@
1
1
  import type { RecievedDataType } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
2
- import type { CaseUpdate } from '@cccsaurora/howler-ui/models/socket/CaseUpdate';
3
2
  import type { HitUpdate } from '@cccsaurora/howler-ui/models/socket/HitUpdate';
4
- import type { ViewersUpdate } from '@cccsaurora/howler-ui/models/socket/ViewersUpdate';
5
3
  /**
6
4
  * Checks to see if the data recieved from the socket is a hit update
7
5
  * @param data The data recieved from the socket
8
6
  * @returns whether the data is a hit update
9
7
  */
10
8
  export declare const isHitUpdate: (data: any) => data is RecievedDataType<HitUpdate>;
11
- /**
12
- * Checks to see if the data received from the socket is a case update
13
- * @param data The data received from the socket
14
- * @returns whether the data is a case update
15
- */
16
- export declare const isCaseUpdate: (data: any) => data is RecievedDataType<CaseUpdate>;
17
- /**
18
- * Checks to see if the data received from the socket is a viewers update
19
- * @param data The data received from the socket
20
- * @returns whether the data is a viewers update
21
- */
22
- export declare const isViewersUpdate: (data: any) => data is RecievedDataType<ViewersUpdate>;
@@ -4,21 +4,5 @@
4
4
  * @returns whether the data is a hit update
5
5
  */
6
6
  export const isHitUpdate = (data) => {
7
- return !!(data.version && data.hit);
8
- };
9
- /**
10
- * Checks to see if the data received from the socket is a case update
11
- * @param data The data received from the socket
12
- * @returns whether the data is a case update
13
- */
14
- export const isCaseUpdate = (data) => {
15
- return !!(data.type === 'cases' && data.case);
16
- };
17
- /**
18
- * Checks to see if the data received from the socket is a viewers update
19
- * @param data The data received from the socket
20
- * @returns whether the data is a viewers update
21
- */
22
- export const isViewersUpdate = (data) => {
23
- return !!(data.type === 'viewers_update' && data.viewers && data.id);
7
+ return data.version && data.hit;
24
8
  };
@@ -1,59 +1,26 @@
1
+ /// <reference types="vitest" />
1
2
  import { describe, expect, it } from 'vitest';
2
- import { isCaseUpdate, isHitUpdate, isViewersUpdate } from './socketUtils';
3
+ import { isHitUpdate } from './socketUtils';
3
4
  describe('isHitUpdate', () => {
4
- it('returns true when data has version and hit', () => {
5
- const data = { version: '1', hit: { howler: { id: 'h1' } }, type: 'hits', error: false, message: '', status: 200 };
6
- expect(isHitUpdate(data)).toBeTruthy();
5
+ it('returns truthy when data has both a truthy version and a hit', () => {
6
+ expect(isHitUpdate({ version: 1, hit: {} })).toBeTruthy();
7
7
  });
8
- it('returns false when hit is missing', () => {
9
- const data = { version: '1', type: 'hits', error: false, message: '', status: 200 };
10
- expect(isHitUpdate(data)).toBeFalsy();
8
+ it('returns truthy when version is a non-empty string', () => {
9
+ expect(isHitUpdate({ version: 'v1', hit: { howler: {} } })).toBeTruthy();
11
10
  });
12
- it('returns false when version is missing', () => {
13
- const data = { hit: { howler: { id: 'h1' } }, type: 'hits', error: false, message: '', status: 200 };
14
- expect(isHitUpdate(data)).toBeFalsy();
11
+ it('returns falsy when version is missing', () => {
12
+ expect(isHitUpdate({ hit: {} })).toBeFalsy();
15
13
  });
16
- });
17
- describe('isCaseUpdate', () => {
18
- it('returns true when type is cases and case is present', () => {
19
- const data = { type: 'cases', case: { case_id: 'c1' }, error: false, message: '', status: 200 };
20
- expect(isCaseUpdate(data)).toBeTruthy();
21
- });
22
- it('returns false when type is not cases', () => {
23
- const data = { type: 'hits', case: { case_id: 'c1' }, error: false, message: '', status: 200 };
24
- expect(isCaseUpdate(data)).toBeFalsy();
25
- });
26
- it('returns false when case is missing', () => {
27
- const data = { type: 'cases', error: false, message: '', status: 200 };
28
- expect(isCaseUpdate(data)).toBeFalsy();
29
- });
30
- it('returns false when case is null', () => {
31
- const data = { type: 'cases', case: null, error: false, message: '', status: 200 };
32
- expect(isCaseUpdate(data)).toBeFalsy();
33
- });
34
- });
35
- describe('isViewersUpdate', () => {
36
- it('returns true when type is viewers_update with id and viewers', () => {
37
- const data = {
38
- type: 'viewers_update',
39
- id: 'entity-1',
40
- viewers: ['alice', 'bob'],
41
- error: false,
42
- message: '',
43
- status: 200
44
- };
45
- expect(isViewersUpdate(data)).toBeTruthy();
14
+ it('returns falsy when hit is missing', () => {
15
+ expect(isHitUpdate({ version: 1 })).toBeFalsy();
46
16
  });
47
- it('returns false when type is wrong', () => {
48
- const data = { type: 'cases', id: 'entity-1', viewers: ['alice'], error: false, message: '', status: 200 };
49
- expect(isViewersUpdate(data)).toBeFalsy();
17
+ it('returns falsy when both fields are missing', () => {
18
+ expect(isHitUpdate({})).toBeFalsy();
50
19
  });
51
- it('returns false when viewers is missing', () => {
52
- const data = { type: 'viewers_update', id: 'entity-1', error: false, message: '', status: 200 };
53
- expect(isViewersUpdate(data)).toBeFalsy();
20
+ it('returns falsy when version is 0 (falsy)', () => {
21
+ expect(isHitUpdate({ version: 0, hit: {} })).toBeFalsy();
54
22
  });
55
- it('returns false when id is missing', () => {
56
- const data = { type: 'viewers_update', viewers: ['alice'], error: false, message: '', status: 200 };
57
- expect(isViewersUpdate(data)).toBeFalsy();
23
+ it('returns falsy when hit is null (falsy)', () => {
24
+ expect(isHitUpdate({ version: 1, hit: null })).toBeFalsy();
58
25
  });
59
26
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,159 @@
1
+ /// <reference types="vitest" />
2
+ import { describe, expect, it } from 'vitest';
3
+ import { maxLenStr, nameToInitials, safeFieldValue, safeFieldValueURI, safeStringPropertyCompare, sanitizeLuceneQuery, sanitizeMultilineLucene, validateRegex } from './stringUtils';
4
+ describe('nameToInitials', () => {
5
+ it('returns initials from a standard first-last name', () => {
6
+ expect(nameToInitials('John Doe')).toEqual(['J', 'D']);
7
+ });
8
+ it('returns a single initial when there is only one word', () => {
9
+ expect(nameToInitials('John')).toEqual(['J']);
10
+ });
11
+ it('uses only the first two words even when there are more', () => {
12
+ expect(nameToInitials('John Middle Doe')).toEqual(['J', 'M']);
13
+ });
14
+ it('reverses order when the name is in "last, first" comma format', () => {
15
+ // "Smith, John" → parts = ["Smith,", "John"] → reversed → ["John", "Smith,"] → initials ["J", "S"]
16
+ expect(nameToInitials('Smith, John')).toEqual(['J', 'S']);
17
+ });
18
+ it('returns uppercase initials regardless of input case', () => {
19
+ expect(nameToInitials('alice bob')).toEqual(['A', 'B']);
20
+ });
21
+ });
22
+ describe('maxLenStr', () => {
23
+ it('returns the string unchanged when shorter than the limit', () => {
24
+ expect(maxLenStr('hello', 10)).toBe('hello');
25
+ });
26
+ it('returns the string unchanged when length equals the limit', () => {
27
+ expect(maxLenStr('hello', 5)).toBe('hello');
28
+ });
29
+ it('truncates and appends "..." when string exceeds the limit', () => {
30
+ expect(maxLenStr('hello world', 8)).toBe('hello...');
31
+ });
32
+ it('handles an empty string', () => {
33
+ expect(maxLenStr('', 5)).toBe('');
34
+ });
35
+ it('truncation accounts for the three-character ellipsis', () => {
36
+ // len=6 → keeps first 3 chars then "..."
37
+ expect(maxLenStr('abcdefgh', 6)).toBe('abc...');
38
+ });
39
+ });
40
+ describe('safeFieldValue', () => {
41
+ it('wraps a plain string in double quotes', () => {
42
+ expect(safeFieldValue('test')).toBe('"test"');
43
+ });
44
+ it('escapes backslashes', () => {
45
+ expect(safeFieldValue('back\\slash')).toBe('"back\\\\slash"');
46
+ });
47
+ it('escapes embedded double quotes', () => {
48
+ expect(safeFieldValue('say "hello"')).toBe('"say \\"hello\\""');
49
+ });
50
+ it('converts a number to a quoted string', () => {
51
+ expect(safeFieldValue(42)).toBe('"42"');
52
+ });
53
+ it('converts a boolean to a quoted string', () => {
54
+ expect(safeFieldValue(true)).toBe('"true"');
55
+ });
56
+ it('escapes backslash before escaping quotes (order matters)', () => {
57
+ expect(safeFieldValue('\\"')).toBe('"\\\\\\"\"');
58
+ });
59
+ });
60
+ describe('safeFieldValueURI', () => {
61
+ it('URI-encodes the safe field value of a plain string', () => {
62
+ expect(safeFieldValueURI('hello')).toBe(encodeURIComponent('"hello"'));
63
+ });
64
+ it('URI-encodes special characters', () => {
65
+ expect(safeFieldValueURI('hello world')).toBe(encodeURIComponent('"hello world"'));
66
+ });
67
+ it('handles strings that already contain lucene special chars', () => {
68
+ const input = 'field:value';
69
+ expect(safeFieldValueURI(input)).toBe(encodeURIComponent(safeFieldValue(input)));
70
+ });
71
+ });
72
+ describe('sanitizeLuceneQuery', () => {
73
+ it('escapes colons', () => {
74
+ expect(sanitizeLuceneQuery('field:value')).toBe('field\\:value');
75
+ });
76
+ it('escapes forward slashes', () => {
77
+ expect(sanitizeLuceneQuery('path/to')).toBe('path\\/to');
78
+ });
79
+ it('escapes opening parenthesis', () => {
80
+ expect(sanitizeLuceneQuery('(term)')).toBe('\\(term\\)');
81
+ });
82
+ it('escapes square brackets', () => {
83
+ expect(sanitizeLuceneQuery('[a TO b]')).toBe('\\[a TO b\\]');
84
+ });
85
+ it('escapes curly braces', () => {
86
+ expect(sanitizeLuceneQuery('{a TO b}')).toBe('\\{a TO b\\}');
87
+ });
88
+ it('escapes carets', () => {
89
+ expect(sanitizeLuceneQuery('term^2')).toBe('term\\^2');
90
+ });
91
+ it('escapes double-ampersand (&&)', () => {
92
+ expect(sanitizeLuceneQuery('a && b')).toBe('a \\&& b');
93
+ });
94
+ it('escapes double-pipe (||)', () => {
95
+ expect(sanitizeLuceneQuery('a || b')).toBe('a \\|| b');
96
+ });
97
+ it('leaves a plain alphanumeric string unchanged', () => {
98
+ expect(sanitizeLuceneQuery('plainterm')).toBe('plainterm');
99
+ });
100
+ });
101
+ describe('safeStringPropertyCompare', () => {
102
+ const compare = safeStringPropertyCompare('name');
103
+ it('returns a negative number when a sorts before b', () => {
104
+ expect(compare({ name: 'Alice' }, { name: 'Bob' })).toBeLessThan(0);
105
+ });
106
+ it('returns a positive number when a sorts after b', () => {
107
+ expect(compare({ name: 'Bob' }, { name: 'Alice' })).toBeGreaterThan(0);
108
+ });
109
+ it('returns 0 for equal strings', () => {
110
+ expect(compare({ name: 'Alice' }, { name: 'Alice' })).toBe(0);
111
+ });
112
+ it('returns 1 when only a has the property', () => {
113
+ expect(compare({ name: 'Alice' }, {})).toBe(1);
114
+ });
115
+ it('returns 0 when only b has the property', () => {
116
+ expect(compare({}, { name: 'Bob' })).toBe(0);
117
+ });
118
+ it('returns 0 when neither object has the property', () => {
119
+ expect(compare({}, {})).toBe(0);
120
+ });
121
+ it('supports nested property paths', () => {
122
+ const nestedCompare = safeStringPropertyCompare('user.name');
123
+ expect(nestedCompare({ user: { name: 'Alice' } }, { user: { name: 'Bob' } })).toBeLessThan(0);
124
+ });
125
+ });
126
+ describe('sanitizeMultilineLucene', () => {
127
+ it('removes a trailing inline comment', () => {
128
+ expect(sanitizeMultilineLucene('query # comment')).toBe('query ');
129
+ });
130
+ it('removes a full-line comment', () => {
131
+ expect(sanitizeMultilineLucene('# full line\nquery')).toBe('\nquery');
132
+ });
133
+ it('collapses two or more consecutive newlines into one', () => {
134
+ expect(sanitizeMultilineLucene('a\n\n\nb')).toBe('a\nb');
135
+ });
136
+ it('collapses exactly two consecutive newlines', () => {
137
+ expect(sanitizeMultilineLucene('a\n\nb')).toBe('a\nb');
138
+ });
139
+ it('leaves a clean single-line query unchanged', () => {
140
+ expect(sanitizeMultilineLucene('howler.status:open')).toBe('howler.status:open');
141
+ });
142
+ });
143
+ describe('validateRegex', () => {
144
+ it('returns true for a valid regex pattern', () => {
145
+ expect(validateRegex('[a-z]+')).toBe(true);
146
+ });
147
+ it('returns true for an empty string (valid zero-length pattern)', () => {
148
+ expect(validateRegex('')).toBe(true);
149
+ });
150
+ it('returns false for an invalid regex pattern', () => {
151
+ expect(validateRegex('[unclosed')).toBe(false);
152
+ });
153
+ it('returns true for a complex but valid regex', () => {
154
+ expect(validateRegex('^(\\d{4})-(\\d{2})-(\\d{2})$')).toBe(true);
155
+ });
156
+ it('returns true for a pattern with quantifiers', () => {
157
+ expect(validateRegex('a{2,5}')).toBe(true);
158
+ });
159
+ });
@@ -0,0 +1 @@
1
+ export {};