@cccsaurora/howler-ui 3.1.0-dev.1656 → 3.1.0-dev.1675

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 (353) hide show
  1. package/api/action/execute.test.d.ts +1 -0
  2. package/api/action/execute.test.js +38 -0
  3. package/api/action/index.test.d.ts +1 -0
  4. package/api/action/index.test.js +59 -0
  5. package/api/action/operations.test.d.ts +1 -0
  6. package/api/action/operations.test.js +23 -0
  7. package/api/analytic/comments/index.test.d.ts +1 -0
  8. package/api/analytic/comments/index.test.js +43 -0
  9. package/api/analytic/comments/react.test.d.ts +1 -0
  10. package/api/analytic/comments/react.test.js +27 -0
  11. package/api/analytic/favourite.test.d.ts +1 -0
  12. package/api/analytic/favourite.test.js +20 -0
  13. package/api/analytic/index.test.d.ts +1 -0
  14. package/api/analytic/index.test.js +54 -0
  15. package/api/analytic/notebooks/index.test.d.ts +1 -0
  16. package/api/analytic/notebooks/index.test.js +31 -0
  17. package/api/analytic/owner.test.d.ts +1 -0
  18. package/api/analytic/owner.test.js +25 -0
  19. package/api/auth/apikey.test.d.ts +1 -0
  20. package/api/auth/apikey.test.js +34 -0
  21. package/api/auth/index.test.d.ts +1 -0
  22. package/api/auth/index.test.js +21 -0
  23. package/api/auth/login.test.d.ts +1 -0
  24. package/api/auth/login.test.js +53 -0
  25. package/api/configs/index.test.d.ts +1 -0
  26. package/api/configs/index.test.js +22 -0
  27. package/api/dossier/index.test.d.ts +1 -0
  28. package/api/dossier/index.test.js +57 -0
  29. package/api/help.test.d.ts +1 -0
  30. package/api/help.test.js +25 -0
  31. package/api/hit/assign.test.d.ts +1 -0
  32. package/api/hit/assign.test.js +22 -0
  33. package/api/hit/comments/index.test.d.ts +1 -0
  34. package/api/hit/comments/index.test.js +47 -0
  35. package/api/hit/comments/react.test.d.ts +1 -0
  36. package/api/hit/comments/react.test.js +27 -0
  37. package/api/hit/index.test.d.ts +1 -0
  38. package/api/hit/index.test.js +49 -0
  39. package/api/hit/labels.test.d.ts +1 -0
  40. package/api/hit/labels.test.js +29 -0
  41. package/api/hit/overwrite.test.d.ts +1 -0
  42. package/api/hit/overwrite.test.js +23 -0
  43. package/api/hit/transition.test.d.ts +1 -0
  44. package/api/hit/transition.test.js +24 -0
  45. package/api/index.test.d.ts +1 -0
  46. package/api/index.test.js +137 -0
  47. package/api/notebook/environments.test.d.ts +1 -0
  48. package/api/notebook/environments.test.js +22 -0
  49. package/api/notebook/index.test.d.ts +1 -0
  50. package/api/notebook/index.test.js +28 -0
  51. package/api/overview/index.test.d.ts +1 -0
  52. package/api/overview/index.test.js +42 -0
  53. package/api/search/fields/index.test.d.ts +1 -0
  54. package/api/search/fields/index.test.js +31 -0
  55. package/api/search/grouped/index.test.d.ts +1 -0
  56. package/api/search/grouped/index.test.js +18 -0
  57. package/api/search/grouped/user.test.d.ts +1 -0
  58. package/api/search/grouped/user.test.js +33 -0
  59. package/api/search/index.test.d.ts +1 -0
  60. package/api/search/index.test.js +41 -0
  61. package/api/search/user.test.d.ts +1 -0
  62. package/api/search/user.test.js +41 -0
  63. package/api/socket/index.test.d.ts +1 -0
  64. package/api/socket/index.test.js +15 -0
  65. package/api/template/index.test.d.ts +1 -0
  66. package/api/template/index.test.js +43 -0
  67. package/api/user/avatar/index.test.d.ts +1 -0
  68. package/api/user/avatar/index.test.js +22 -0
  69. package/api/user/groups.test.d.ts +1 -0
  70. package/api/user/groups.test.js +22 -0
  71. package/api/user/index.test.d.ts +1 -0
  72. package/api/user/index.test.js +37 -0
  73. package/api/user/whoami.test.d.ts +1 -0
  74. package/api/user/whoami.test.js +26 -0
  75. package/api/utils/createPermissionsApi.test.d.ts +1 -0
  76. package/api/utils/createPermissionsApi.test.js +30 -0
  77. package/api/v2/case/index.test.d.ts +1 -0
  78. package/api/v2/case/index.test.js +57 -0
  79. package/api/v2/case/items.test.d.ts +1 -0
  80. package/api/v2/case/items.test.js +37 -0
  81. package/api/v2/case/rules.test.d.ts +1 -0
  82. package/api/v2/case/rules.test.js +33 -0
  83. package/api/v2/fuzzy.test.d.ts +1 -0
  84. package/api/v2/fuzzy.test.js +30 -0
  85. package/api/v2/index.test.d.ts +1 -0
  86. package/api/v2/index.test.js +13 -0
  87. package/api/v2/search/index.test.d.ts +1 -0
  88. package/api/v2/search/index.test.js +42 -0
  89. package/api/view/index.test.d.ts +1 -0
  90. package/api/view/index.test.js +54 -0
  91. package/branding/AppBrand.test.d.ts +1 -0
  92. package/branding/AppBrand.test.js +29 -0
  93. package/components/app/drawers/ApiKeyDrawer.test.d.ts +1 -0
  94. package/components/app/drawers/ApiKeyDrawer.test.js +119 -0
  95. package/components/app/drawers/AssignUserDrawer.test.d.ts +1 -0
  96. package/components/app/drawers/AssignUserDrawer.test.js +75 -0
  97. package/components/app/hooks/useTitle.test.d.ts +1 -0
  98. package/components/app/hooks/useTitle.test.js +103 -0
  99. package/components/app/providers/AnalyticProvider.test.d.ts +1 -0
  100. package/components/app/providers/AnalyticProvider.test.js +58 -0
  101. package/components/app/providers/AppBarProvider.test.d.ts +1 -0
  102. package/components/app/providers/AppBarProvider.test.js +23 -0
  103. package/components/app/providers/AppDrawerProvider.test.d.ts +1 -0
  104. package/components/app/providers/AppDrawerProvider.test.js +42 -0
  105. package/components/app/providers/AvatarProvider.test.d.ts +1 -0
  106. package/components/app/providers/AvatarProvider.test.js +50 -0
  107. package/components/app/providers/CustomPluginProvider.test.d.ts +1 -0
  108. package/components/app/providers/CustomPluginProvider.test.js +29 -0
  109. package/components/app/providers/FavouritesProvider.test.d.ts +1 -0
  110. package/components/app/providers/FavouritesProvider.test.js +115 -0
  111. package/components/app/providers/FieldProvider.test.d.ts +1 -0
  112. package/components/app/providers/FieldProvider.test.js +28 -0
  113. package/components/app/providers/LocalStorageProvider.test.d.ts +1 -0
  114. package/components/app/providers/LocalStorageProvider.test.js +42 -0
  115. package/components/app/providers/ModalProvider.test.d.ts +1 -0
  116. package/components/app/providers/ModalProvider.test.js +35 -0
  117. package/components/app/providers/OverviewProvider.test.d.ts +1 -0
  118. package/components/app/providers/OverviewProvider.test.js +62 -0
  119. package/components/app/providers/RecordProvider.test.d.ts +1 -0
  120. package/components/app/providers/RecordProvider.test.js +66 -0
  121. package/components/app/providers/contextUtils.test.d.ts +1 -0
  122. package/components/app/providers/contextUtils.test.js +8 -0
  123. package/components/elements/Comment.test.d.ts +1 -0
  124. package/components/elements/Comment.test.js +109 -0
  125. package/components/elements/EditRow.test.d.ts +1 -0
  126. package/components/elements/EditRow.test.js +88 -0
  127. package/components/elements/MarkdownEditor.test.d.ts +1 -0
  128. package/components/elements/MarkdownEditor.test.js +75 -0
  129. package/components/elements/ObjectDetails.test.d.ts +1 -0
  130. package/components/elements/ObjectDetails.test.js +70 -0
  131. package/components/elements/PluginChip.d.ts +3 -2
  132. package/components/elements/PluginChip.js +2 -2
  133. package/components/elements/PluginChip.test.d.ts +1 -0
  134. package/components/elements/PluginChip.test.js +30 -0
  135. package/components/elements/ThemedEditor.test.d.ts +1 -0
  136. package/components/elements/ThemedEditor.test.js +66 -0
  137. package/components/elements/addons/layout/FlexPort.test.d.ts +1 -0
  138. package/components/elements/addons/layout/FlexPort.test.js +34 -0
  139. package/components/elements/addons/lists/TuiListBase.test.d.ts +1 -0
  140. package/components/elements/addons/lists/TuiListBase.test.js +44 -0
  141. package/components/elements/addons/lists/TuiListProvider.test.d.ts +1 -0
  142. package/components/elements/addons/lists/TuiListProvider.test.js +50 -0
  143. package/components/elements/addons/search/phrase/Phrase.test.d.ts +1 -0
  144. package/components/elements/addons/search/phrase/Phrase.test.js +77 -0
  145. package/components/elements/addons/search/phrase/PhraseLexer.test.d.ts +1 -0
  146. package/components/elements/addons/search/phrase/PhraseLexer.test.js +92 -0
  147. package/components/elements/addons/search/phrase/index.test.d.ts +1 -0
  148. package/components/elements/addons/search/phrase/index.test.js +20 -0
  149. package/components/elements/case/CaseCard.test.d.ts +1 -0
  150. package/components/elements/case/CaseCard.test.js +107 -0
  151. package/components/elements/display/ClassificationChip.test.d.ts +1 -0
  152. package/components/elements/display/ClassificationChip.test.js +60 -0
  153. package/components/elements/display/HandlebarsMarkdown.test.d.ts +1 -0
  154. package/components/elements/display/HandlebarsMarkdown.test.js +41 -0
  155. package/components/elements/display/HowlerAvatar.test.d.ts +1 -0
  156. package/components/elements/display/HowlerAvatar.test.js +54 -0
  157. package/components/elements/display/ItemManager.test.d.ts +1 -0
  158. package/components/elements/display/ItemManager.test.js +68 -0
  159. package/components/elements/display/UserPageWrapper.test.d.ts +1 -0
  160. package/components/elements/display/UserPageWrapper.test.js +22 -0
  161. package/components/elements/display/handlebars/helpers.test.d.ts +1 -0
  162. package/components/elements/display/handlebars/helpers.test.js +104 -0
  163. package/components/elements/display/icons/SocketBadge.test.d.ts +1 -0
  164. package/components/elements/display/icons/SocketBadge.test.js +49 -0
  165. package/components/elements/display/markdownPlugins/tabs.test.d.ts +1 -0
  166. package/components/elements/display/markdownPlugins/tabs.test.js +50 -0
  167. package/components/elements/hit/HitActions.js +1 -1
  168. package/components/elements/hit/HitActions.test.d.ts +1 -0
  169. package/components/elements/hit/HitActions.test.js +166 -0
  170. package/components/elements/hit/HitLabels.test.js +34 -3
  171. package/components/elements/hit/HitNotebooks.test.d.ts +1 -0
  172. package/components/elements/hit/HitNotebooks.test.js +138 -0
  173. package/components/elements/hit/HitSummary.test.d.ts +1 -0
  174. package/components/elements/hit/HitSummary.test.js +188 -0
  175. package/components/elements/hit/actions/ButtonActions.test.d.ts +1 -0
  176. package/components/elements/hit/actions/ButtonActions.test.js +51 -0
  177. package/components/elements/hit/actions/DropdownActions.test.d.ts +1 -0
  178. package/components/elements/hit/actions/DropdownActions.test.js +60 -0
  179. package/components/elements/hit/grid/AddColumnModal.test.d.ts +1 -0
  180. package/components/elements/hit/grid/AddColumnModal.test.js +88 -0
  181. package/components/elements/hit/grid/RecordTable.test.d.ts +1 -0
  182. package/components/elements/hit/grid/RecordTable.test.js +99 -0
  183. package/components/elements/record/RecordComments.test.d.ts +1 -0
  184. package/components/elements/record/RecordComments.test.js +151 -0
  185. package/components/elements/record/RecordRelated.test.d.ts +1 -0
  186. package/components/elements/record/RecordRelated.test.js +77 -0
  187. package/components/elements/record/RecordWorklog.test.d.ts +1 -0
  188. package/components/elements/record/RecordWorklog.test.js +77 -0
  189. package/components/hooks/useHitActions.test.d.ts +1 -0
  190. package/components/hooks/useHitActions.test.js +189 -0
  191. package/components/hooks/useMyApi.test.d.ts +1 -0
  192. package/components/hooks/useMyApi.test.js +31 -0
  193. package/components/hooks/useMyChart.test.d.ts +1 -0
  194. package/components/hooks/useMyChart.test.js +62 -0
  195. package/components/hooks/useMySearch.test.d.ts +1 -0
  196. package/components/hooks/useMySearch.test.js +96 -0
  197. package/components/hooks/useMySnackbar.test.d.ts +1 -0
  198. package/components/hooks/useMySnackbar.test.js +36 -0
  199. package/components/hooks/useMyUserFunctions.test.d.ts +1 -0
  200. package/components/hooks/useMyUserFunctions.test.js +142 -0
  201. package/components/hooks/useRecordSelection.test.d.ts +1 -0
  202. package/components/hooks/useRecordSelection.test.js +102 -0
  203. package/components/logins/Login.test.d.ts +1 -0
  204. package/components/logins/Login.test.js +67 -0
  205. package/components/logins/hooks/useLogin.test.d.ts +1 -0
  206. package/components/logins/hooks/useLogin.test.js +142 -0
  207. package/components/routes/action/shared/OperationEntry.test.d.ts +1 -0
  208. package/components/routes/action/shared/OperationEntry.test.js +80 -0
  209. package/components/routes/action/shared/OperationStep.test.d.ts +1 -0
  210. package/components/routes/action/shared/OperationStep.test.js +84 -0
  211. package/components/routes/action/useMyActionFunctions.test.d.ts +1 -0
  212. package/components/routes/action/useMyActionFunctions.test.js +217 -0
  213. package/components/routes/action/view/ActionDetails.test.d.ts +1 -0
  214. package/components/routes/action/view/ActionDetails.test.js +170 -0
  215. package/components/routes/action/view/ActionSearch.test.d.ts +1 -0
  216. package/components/routes/action/view/ActionSearch.test.js +185 -0
  217. package/components/routes/action/view/Integrations.test.d.ts +1 -0
  218. package/components/routes/action/view/Integrations.test.js +62 -0
  219. package/components/routes/admin/users/UserEditor.test.d.ts +1 -0
  220. package/components/routes/admin/users/UserEditor.test.js +74 -0
  221. package/components/routes/admin/users/UserSearch.test.d.ts +1 -0
  222. package/components/routes/admin/users/UserSearch.test.js +146 -0
  223. package/components/routes/advanced/QueryBuilder.test.d.ts +1 -0
  224. package/components/routes/advanced/QueryBuilder.test.js +185 -0
  225. package/components/routes/advanced/QueryEditor.test.d.ts +1 -0
  226. package/components/routes/advanced/QueryEditor.test.js +130 -0
  227. package/components/routes/advanced/completionProviders.test.d.ts +1 -0
  228. package/components/routes/advanced/completionProviders.test.js +144 -0
  229. package/components/routes/analytics/AnalyticComments.test.d.ts +1 -0
  230. package/components/routes/analytics/AnalyticComments.test.js +156 -0
  231. package/components/routes/analytics/AnalyticDetails.test.d.ts +1 -0
  232. package/components/routes/analytics/AnalyticDetails.test.js +161 -0
  233. package/components/routes/analytics/AnalyticNotebooks.test.d.ts +1 -0
  234. package/components/routes/analytics/AnalyticNotebooks.test.js +112 -0
  235. package/components/routes/analytics/AnalyticOverview.test.d.ts +1 -0
  236. package/components/routes/analytics/AnalyticOverview.test.js +97 -0
  237. package/components/routes/analytics/AnalyticOverviews.test.d.ts +1 -0
  238. package/components/routes/analytics/AnalyticOverviews.test.js +70 -0
  239. package/components/routes/analytics/AnalyticSearch.test.d.ts +1 -0
  240. package/components/routes/analytics/AnalyticSearch.test.js +167 -0
  241. package/components/routes/analytics/AnalyticTemplates.test.d.ts +1 -0
  242. package/components/routes/analytics/AnalyticTemplates.test.js +63 -0
  243. package/components/routes/analytics/TriageSettings.test.d.ts +1 -0
  244. package/components/routes/analytics/TriageSettings.test.js +89 -0
  245. package/components/routes/analytics/widgets/Assessment.test.d.ts +1 -0
  246. package/components/routes/analytics/widgets/Assessment.test.js +45 -0
  247. package/components/routes/analytics/widgets/Created.test.d.ts +1 -0
  248. package/components/routes/analytics/widgets/Created.test.js +41 -0
  249. package/components/routes/analytics/widgets/DetectionStatus.test.d.ts +1 -0
  250. package/components/routes/analytics/widgets/DetectionStatus.test.js +21 -0
  251. package/components/routes/analytics/widgets/Stacked.test.d.ts +1 -0
  252. package/components/routes/analytics/widgets/Stacked.test.js +51 -0
  253. package/components/routes/cases/Cases.test.d.ts +1 -0
  254. package/components/routes/cases/Cases.test.js +132 -0
  255. package/components/routes/dossiers/Dossiers.test.d.ts +1 -0
  256. package/components/routes/dossiers/Dossiers.test.js +150 -0
  257. package/components/routes/help/ActionDocumentation.test.d.ts +1 -0
  258. package/components/routes/help/ActionDocumentation.test.js +58 -0
  259. package/components/routes/help/ActionIntroductionDocumentation.test.d.ts +1 -0
  260. package/components/routes/help/ActionIntroductionDocumentation.test.js +68 -0
  261. package/components/routes/help/ApiDocumentation.test.d.ts +1 -0
  262. package/components/routes/help/ApiDocumentation.test.js +79 -0
  263. package/components/routes/help/AuthDocumentation.test.d.ts +1 -0
  264. package/components/routes/help/AuthDocumentation.test.js +34 -0
  265. package/components/routes/help/ClientDocumentation.test.d.ts +1 -0
  266. package/components/routes/help/ClientDocumentation.test.js +34 -0
  267. package/components/routes/help/Help.test.d.ts +1 -0
  268. package/components/routes/help/Help.test.js +57 -0
  269. package/components/routes/help/HitBannerDocumentation.test.d.ts +1 -0
  270. package/components/routes/help/HitBannerDocumentation.test.js +25 -0
  271. package/components/routes/help/HitDocumentation.test.d.ts +1 -0
  272. package/components/routes/help/HitDocumentation.test.js +53 -0
  273. package/components/routes/help/HitLabelsDocumentation.test.d.ts +1 -0
  274. package/components/routes/help/HitLabelsDocumentation.test.js +17 -0
  275. package/components/routes/help/HitLinksDocumentation.test.d.ts +1 -0
  276. package/components/routes/help/HitLinksDocumentation.test.js +27 -0
  277. package/components/routes/help/HitSchemaDocumentation.test.d.ts +1 -0
  278. package/components/routes/help/HitSchemaDocumentation.test.js +67 -0
  279. package/components/routes/help/NotebookDocumentation.test.d.ts +1 -0
  280. package/components/routes/help/NotebookDocumentation.test.js +34 -0
  281. package/components/routes/help/OverviewDocumentation.test.d.ts +1 -0
  282. package/components/routes/help/OverviewDocumentation.test.js +26 -0
  283. package/components/routes/help/RetentionDocumentation.test.d.ts +1 -0
  284. package/components/routes/help/RetentionDocumentation.test.js +61 -0
  285. package/components/routes/help/SearchDocumentation.test.d.ts +1 -0
  286. package/components/routes/help/SearchDocumentation.test.js +65 -0
  287. package/components/routes/help/TemplateDocumentation.test.d.ts +1 -0
  288. package/components/routes/help/TemplateDocumentation.test.js +58 -0
  289. package/components/routes/help/ViewDocumentation.test.d.ts +1 -0
  290. package/components/routes/help/ViewDocumentation.test.js +34 -0
  291. package/components/routes/help/components/HelpTabs.test.d.ts +1 -0
  292. package/components/routes/help/components/HelpTabs.test.js +26 -0
  293. package/components/routes/hits/search/InformationPane.test.d.ts +1 -0
  294. package/components/routes/hits/search/InformationPane.test.js +251 -0
  295. package/components/routes/hits/search/RecordBrowser.test.js +179 -51
  296. package/components/routes/hits/search/RecordQuery.test.d.ts +1 -0
  297. package/components/routes/hits/search/RecordQuery.test.js +117 -0
  298. package/components/routes/hits/search/SearchPane.test.d.ts +1 -0
  299. package/components/routes/hits/search/SearchPane.test.js +178 -0
  300. package/components/routes/hits/search/grid/RecordGrid.test.d.ts +1 -0
  301. package/components/routes/hits/search/grid/RecordGrid.test.js +107 -0
  302. package/components/routes/hits/search/shared/CustomSort.test.d.ts +1 -0
  303. package/components/routes/hits/search/shared/CustomSort.test.js +77 -0
  304. package/components/routes/hits/search/shared/HitFilter.test.d.ts +1 -0
  305. package/components/routes/hits/search/shared/HitFilter.test.js +83 -0
  306. package/components/routes/hits/search/shared/HitSort.test.d.ts +1 -0
  307. package/components/routes/hits/search/shared/HitSort.test.js +61 -0
  308. package/components/routes/hits/search/shared/LayoutSettings.test.d.ts +1 -0
  309. package/components/routes/hits/search/shared/LayoutSettings.test.js +84 -0
  310. package/components/routes/hits/search/shared/SearchSpan.test.d.ts +1 -0
  311. package/components/routes/hits/search/shared/SearchSpan.test.js +84 -0
  312. package/components/routes/home/AnalyticCard.test.d.ts +1 -0
  313. package/components/routes/home/AnalyticCard.test.js +87 -0
  314. package/components/routes/home/index.test.d.ts +1 -0
  315. package/components/routes/home/index.test.js +190 -0
  316. package/components/routes/overviews/OverviewViewer.test.d.ts +1 -0
  317. package/components/routes/overviews/OverviewViewer.test.js +151 -0
  318. package/components/routes/overviews/Overviews.test.d.ts +1 -0
  319. package/components/routes/overviews/Overviews.test.js +154 -0
  320. package/components/routes/settings/AdminSection.test.d.ts +1 -0
  321. package/components/routes/settings/AdminSection.test.js +26 -0
  322. package/components/routes/settings/LocalSection.test.d.ts +1 -0
  323. package/components/routes/settings/LocalSection.test.js +75 -0
  324. package/components/routes/settings/ProfileSection.test.d.ts +1 -0
  325. package/components/routes/settings/ProfileSection.test.js +95 -0
  326. package/components/routes/settings/SecuritySection.test.d.ts +1 -0
  327. package/components/routes/settings/SecuritySection.test.js +90 -0
  328. package/components/routes/settings/Settings.test.d.ts +1 -0
  329. package/components/routes/settings/Settings.test.js +60 -0
  330. package/components/routes/templates/TemplateEditor.test.d.ts +1 -0
  331. package/components/routes/templates/TemplateEditor.test.js +94 -0
  332. package/components/routes/templates/TemplateViewer.test.d.ts +1 -0
  333. package/components/routes/templates/TemplateViewer.test.js +145 -0
  334. package/components/routes/templates/Templates.test.d.ts +1 -0
  335. package/components/routes/templates/Templates.test.js +165 -0
  336. package/components/routes/views/ViewComposer.test.d.ts +1 -0
  337. package/components/routes/views/ViewComposer.test.js +320 -0
  338. package/components/routes/views/Views.test.d.ts +1 -0
  339. package/components/routes/views/Views.test.js +247 -0
  340. package/package.json +2 -2
  341. package/plugins/HowlerPlugin.test.d.ts +1 -0
  342. package/plugins/HowlerPlugin.test.js +103 -0
  343. package/plugins/clue/components/ClueChip.js +2 -2
  344. package/plugins/clue/helpers.test.d.ts +1 -0
  345. package/plugins/clue/helpers.test.js +100 -0
  346. package/plugins/store.test.d.ts +1 -0
  347. package/plugins/store.test.js +53 -0
  348. package/utils/classificationParser.test.d.ts +1 -0
  349. package/utils/classificationParser.test.js +199 -0
  350. package/utils/constants.test.d.ts +1 -0
  351. package/utils/constants.test.js +62 -0
  352. package/utils/typeUtils.test.d.ts +1 -0
  353. package/utils/typeUtils.test.js +22 -0
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+ const mockSetColumnWidth = vi.hoisted(() => vi.fn());
6
+ const mockSetColumns = vi.hoisted(() => vi.fn());
7
+ const mockGetMatchingAnalytic = vi.hoisted(() => vi.fn().mockResolvedValue({ analytic_id: 'analytic-1' }));
8
+ const gridColumnsContextToken = vi.hoisted(() => ({ name: 'grid-columns-context' }));
9
+ vi.mock('@dnd-kit/core', () => ({
10
+ DndContext: ({ children, onDragEnd }) => (_jsxs("div", { children: [_jsx("button", { onClick: () => onDragEnd({ active: { id: 'col1' }, over: { id: 'col2' } }), children: "drag-column" }), children] })),
11
+ KeyboardSensor: vi.fn(),
12
+ PointerSensor: vi.fn(),
13
+ pointerWithin: vi.fn(),
14
+ useSensor: vi.fn(),
15
+ useSensors: vi.fn(() => [])
16
+ }));
17
+ vi.mock('@dnd-kit/sortable', () => ({
18
+ SortableContext: ({ children }) => _jsx("div", { children: children }),
19
+ arrayMove: (items, from, to) => {
20
+ const next = [...items];
21
+ const [moved] = next.splice(from, 1);
22
+ next.splice(to, 0, moved);
23
+ return next;
24
+ },
25
+ sortableKeyboardCoordinates: vi.fn()
26
+ }));
27
+ vi.mock('@mui/icons-material', () => ({
28
+ FormatIndentDecrease: () => _jsx("div", { children: "collapse" }),
29
+ FormatIndentIncrease: () => _jsx("div", { children: "expand" }),
30
+ Search: () => _jsx("div", { children: "search-icon" })
31
+ }));
32
+ vi.mock('@mui/material', () => ({
33
+ IconButton: ({ children, onClick }) => _jsx("button", { onClick: onClick, children: children }),
34
+ Stack: ({ children }) => _jsx("div", { children: children }),
35
+ Table: ({ children }) => _jsx("div", { children: children }),
36
+ TableBody: ({ children }) => _jsx("div", { children: children }),
37
+ TableCell: ({ children }) => _jsx("div", { children: children }),
38
+ TableHead: ({ children }) => _jsx("div", { children: children }),
39
+ TableRow: ({ children }) => _jsx("div", { children: children })
40
+ }));
41
+ vi.mock('components/app/hooks/useMatchers', () => ({
42
+ default: () => ({ getMatchingAnalytic: mockGetMatchingAnalytic })
43
+ }));
44
+ vi.mock('components/app/providers/GridColumnsProvider', () => ({
45
+ GridColumnsContext: gridColumnsContextToken
46
+ }));
47
+ vi.mock('components/hooks/useMyLocalStorage', () => ({
48
+ useMyLocalStorageItem: () => [false, vi.fn()]
49
+ }));
50
+ vi.mock('utils/constants', () => ({
51
+ StorageKey: { GRID_COLLAPSE_COLUMN: 'GRID_COLLAPSE_COLUMN' }
52
+ }));
53
+ vi.mock('utils/typeUtils', () => ({
54
+ isHit: (record) => record?.__index === 'hit'
55
+ }));
56
+ vi.mock('./ColumnHeader', () => ({
57
+ default: ({ col, onMouseDown }) => _jsx("button", { onMouseDown: e => onMouseDown(col, e), children: col })
58
+ }));
59
+ vi.mock('./RecordRow', () => ({
60
+ default: ({ record, analyticIds }) => (_jsx("div", { children: `row:${record.howler.id}:${analyticIds[record.howler.analytic] ?? ''}` }))
61
+ }));
62
+ vi.mock('react', async (importOriginal) => {
63
+ const actual = await importOriginal();
64
+ return {
65
+ ...actual,
66
+ useContext: (context) => {
67
+ if (context === gridColumnsContextToken) {
68
+ return {
69
+ columns: ['col1', 'col2'],
70
+ columnWidths: { col1: 120, col2: 80 },
71
+ columnSources: { col1: 's1', col2: 's2' },
72
+ setColumnWidth: mockSetColumnWidth,
73
+ setColumns: mockSetColumns,
74
+ isReady: true
75
+ };
76
+ }
77
+ return actual.useContext(context);
78
+ }
79
+ };
80
+ });
81
+ import RecordTable from './RecordTable';
82
+ describe('RecordTable', () => {
83
+ it('loads analytic ids, refreshes, reorders columns, and resizes columns', async () => {
84
+ render(_jsx(RecordTable, { query: "status:open", items: [{ __index: 'hit', howler: { id: 'hit-1', analytic: 'A' } }], refreshItems: vi.fn() }));
85
+ await waitFor(() => expect(mockGetMatchingAnalytic).toHaveBeenCalled());
86
+ expect(screen.getByText('row:hit-1:analytic-1')).toBeInTheDocument();
87
+ fireEvent.click(screen.getByText('search-icon'));
88
+ fireEvent.click(screen.getByText('drag-column'));
89
+ expect(mockSetColumns).toHaveBeenCalledWith(['col2', 'col1']);
90
+ const header = screen.getByText('col1');
91
+ Object.defineProperty(header.parentElement, 'getBoundingClientRect', {
92
+ value: () => ({ width: 120 })
93
+ });
94
+ fireEvent.mouseDown(header);
95
+ fireEvent.mouseMove(window, { movementX: 20 });
96
+ fireEvent.mouseUp(window);
97
+ expect(mockSetColumnWidth).toHaveBeenCalledWith('col1', 140);
98
+ });
99
+ });
@@ -0,0 +1,151 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
+ const mockDispatchApi = vi.hoisted(() => vi.fn());
6
+ const mockEmit = vi.hoisted(() => vi.fn());
7
+ const mockAddListener = vi.hoisted(() => vi.fn());
8
+ const mockRemoveListener = vi.hoisted(() => vi.fn());
9
+ const mockNavigate = vi.hoisted(() => vi.fn());
10
+ const mockGetMatchingAnalytic = vi.hoisted(() => vi.fn());
11
+ let socketHandler;
12
+ vi.mock('api', () => ({
13
+ default: {
14
+ hit: {
15
+ comments: {
16
+ post: (id, value) => ({ op: 'post', id, value }),
17
+ del: (id, ids) => ({ op: 'del', id, ids }),
18
+ put: (id, commentId, value) => ({ op: 'put', id, commentId, value }),
19
+ react: {
20
+ put: (id, commentId, type) => ({ op: 'react-put', id, commentId, type }),
21
+ del: (id, commentId) => ({ op: 'react-del', id, commentId })
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }));
27
+ vi.mock('@mui/icons-material', () => ({
28
+ Clear: () => _jsx("div", { children: "clear-icon" }),
29
+ KeyboardArrowDown: () => _jsx("div", { children: "expand-icon" }),
30
+ Send: () => _jsx("div", { children: "send-icon" })
31
+ }));
32
+ vi.mock('@mui/material', () => ({
33
+ Accordion: ({ children }) => _jsx("div", { children: children }),
34
+ AccordionDetails: ({ children }) => _jsx("div", { children: children }),
35
+ AccordionSummary: ({ children }) => _jsx("div", { children: children }),
36
+ AvatarGroup: ({ children }) => _jsx("div", { children: children }),
37
+ Chip: ({ label, onClick }) => _jsx("button", { onClick: onClick, children: label }),
38
+ IconButton: ({ children, onClick, disabled }) => (_jsx("button", { onClick: onClick, disabled: disabled, children: children })),
39
+ Skeleton: () => _jsx("div", { children: "loading" }),
40
+ Stack: ({ children }) => _jsx("div", { children: children }),
41
+ TextField: ({ placeholder, inputRef, onKeyDown, onChangeCapture, onFocus, onBlur, error }) => (_jsx("textarea", { "aria-label": placeholder, ref: inputRef, onKeyDown: onKeyDown, onInput: onChangeCapture, onFocus: onFocus, onBlur: onBlur, "data-error": error })),
42
+ Typography: ({ children }) => _jsx("div", { children: children })
43
+ }));
44
+ vi.mock('@tui/core', () => ({
45
+ useAppUser: () => ({ user: { username: 'alice' } })
46
+ }));
47
+ vi.mock('components/app/hooks/useMatchers', () => ({
48
+ default: () => ({ getMatchingAnalytic: mockGetMatchingAnalytic })
49
+ }));
50
+ vi.mock('components/app/providers/SocketProvider', () => ({
51
+ SocketContext: { name: 'socket-context' }
52
+ }));
53
+ vi.mock('components/elements/addons/layout/FlexOne', () => ({
54
+ default: () => _jsx("div", { children: "flex" })
55
+ }));
56
+ vi.mock('components/hooks/useMyApi', () => ({
57
+ default: () => ({ dispatchApi: mockDispatchApi })
58
+ }));
59
+ vi.mock('react', async (importOriginal) => {
60
+ const actual = await importOriginal();
61
+ return {
62
+ ...actual,
63
+ useContext: (context) => {
64
+ if (context?.name === 'socket-context') {
65
+ return {
66
+ addListener: (...args) => {
67
+ socketHandler = args[1];
68
+ return mockAddListener(...args);
69
+ },
70
+ removeListener: mockRemoveListener,
71
+ emit: mockEmit
72
+ };
73
+ }
74
+ return actual.useContext(context);
75
+ }
76
+ };
77
+ });
78
+ vi.mock('react-i18next', () => ({
79
+ useTranslation: () => ({ t: (key, params) => (params?.count ? `${key}:${params.count}` : key) })
80
+ }));
81
+ vi.mock('react-router', () => ({
82
+ useNavigate: () => mockNavigate
83
+ }));
84
+ vi.mock('utils/typeUtils', () => ({
85
+ isHit: (record) => record?.__index === 'hit'
86
+ }));
87
+ vi.mock('utils/utils', () => ({
88
+ compareTimestamp: (a, b) => new Date(a).getTime() - new Date(b).getTime(),
89
+ sortByTimestamp: (items) => [...items].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())
90
+ }));
91
+ vi.mock('../Comment', () => ({
92
+ default: ({ comment, extra, handleDelete, handleEdit, handleQuote, handleReact }) => (_jsxs("div", { children: [_jsx("div", { children: comment.value }), extra, handleDelete && _jsxs("button", { onClick: handleDelete, children: ["delete-", comment.id] }), handleEdit && _jsxs("button", { onClick: () => handleEdit(`edited-${comment.id}`), children: ["edit-", comment.id] }), handleQuote && _jsxs("button", { onClick: handleQuote, children: ["quote-", comment.id] }), handleReact && _jsxs("button", { onClick: () => handleReact('smile'), children: ["react-", comment.id] }), handleReact && _jsxs("button", { onClick: () => handleReact(null), children: ["unreact-", comment.id] })] }))
93
+ }));
94
+ vi.mock('../display/HowlerAvatar', () => ({
95
+ default: ({ userId }) => _jsx("div", { children: `avatar:${userId}` })
96
+ }));
97
+ vi.mock('../display/TypingIndicator', () => ({
98
+ default: () => _jsx("div", { children: "typing-indicator" })
99
+ }));
100
+ import RecordComments from './RecordComments';
101
+ describe('RecordComments', () => {
102
+ beforeEach(() => {
103
+ socketHandler = undefined;
104
+ mockDispatchApi.mockReset();
105
+ mockEmit.mockReset();
106
+ mockAddListener.mockReset();
107
+ mockRemoveListener.mockReset();
108
+ mockNavigate.mockReset();
109
+ mockGetMatchingAnalytic.mockReset();
110
+ });
111
+ it('submits comments, reacts to socket typing, and routes analytic comment chips', async () => {
112
+ mockGetMatchingAnalytic.mockResolvedValue({
113
+ analytic_id: 'analytic-1',
114
+ comment: [
115
+ { id: 'analytic-comment', value: 'analytic note', timestamp: '2026-01-01T00:00:00Z', detection: 'det-1' }
116
+ ]
117
+ });
118
+ mockDispatchApi.mockResolvedValue({
119
+ howler: {
120
+ comment: [{ id: 'comment-2', value: 'saved comment', timestamp: '2026-01-02T00:00:00Z', reactions: {} }]
121
+ }
122
+ });
123
+ render(_jsx(RecordComments, { users: { alice: { name: 'Alice' }, bob: { name: 'Bob' } }, record: {
124
+ __index: 'hit',
125
+ howler: {
126
+ id: 'hit-1',
127
+ analytic: 'Rule 1',
128
+ detection: 'det-1',
129
+ comment: [
130
+ { id: 'comment-1', value: 'existing comment', timestamp: '2026-01-01T00:00:00Z', reactions: {} }
131
+ ]
132
+ }
133
+ } }));
134
+ const input = screen.getByLabelText('comments.add');
135
+ fireEvent.focus(input);
136
+ expect(mockEmit).toHaveBeenCalledWith({ broadcast: true, action: 'typing', id: 'hit-1' });
137
+ fireEvent.change(input, { target: { value: 'new comment' } });
138
+ fireEvent.keyDown(input, { key: 'Enter', ctrlKey: true, currentTarget: input });
139
+ await waitFor(() => expect(mockDispatchApi).toHaveBeenCalledWith({ op: 'post', id: 'hit-1', value: 'new comment' }, expect.any(Object)));
140
+ expect(screen.getByText('saved comment')).toBeInTheDocument();
141
+ await act(async () => {
142
+ socketHandler?.({ event: { action: 'typing', username: 'bob' } });
143
+ });
144
+ expect(screen.getByText('typing-indicator')).toBeInTheDocument();
145
+ expect(screen.getByText('avatar:bob')).toBeInTheDocument();
146
+ fireEvent.click(screen.getByText('Rule 1 - det-1'));
147
+ expect(mockNavigate).toHaveBeenCalledWith('/analytics/analytic-1?tab=comments&filter=det-1');
148
+ fireEvent.blur(input);
149
+ expect(mockEmit).toHaveBeenCalledWith({ broadcast: true, action: 'stop_typing', id: 'hit-1' });
150
+ });
151
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { fireEvent, render, screen } from '@testing-library/react';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+ vi.mock('@mui/material', () => ({
6
+ Box: ({ children }) => _jsx("div", { children: children }),
7
+ Stack: ({ children }) => _jsx("div", { children: children }),
8
+ Tab: ({ label }) => _jsx("span", { children: label }),
9
+ Tabs: ({ children, onChange }) => (_jsx("div", { children: Array.isArray(children)
10
+ ? children.map((child) => child ? (_jsx("button", { onClick: () => onChange?.(null, child.props.value), children: child }, child.props.value)) : null)
11
+ : children })),
12
+ useTheme: () => ({ palette: { divider: '#ddd' } })
13
+ }));
14
+ vi.mock('components/elements/event/EventCard', () => ({
15
+ default: ({ event }) => _jsx("div", { children: `event:${event.howler.id}` })
16
+ }));
17
+ vi.mock('components/hooks/useRelatedRecords', () => ({
18
+ default: () => [
19
+ { __index: 'hit', howler: { id: 'hit-2' } },
20
+ { __index: 'case', case_id: 'case-1' },
21
+ { __index: 'event', howler: { id: 'event-1' } }
22
+ ]
23
+ }));
24
+ vi.mock('lodash-es', () => ({
25
+ groupBy: (records, key) => records.reduce((acc, item) => {
26
+ const group = item[key];
27
+ acc[group] = [...(acc[group] ?? []), item];
28
+ return acc;
29
+ }, {})
30
+ }));
31
+ vi.mock('react-i18next', () => ({
32
+ useTranslation: () => ({ t: (key) => key })
33
+ }));
34
+ vi.mock('react-router', () => ({
35
+ Link: ({ children, to }) => _jsx("a", { href: to, children: children })
36
+ }));
37
+ vi.mock('utils/typeUtils', () => ({
38
+ isCase: (record) => record?.__index === 'case',
39
+ isEvent: (record) => record?.__index === 'event',
40
+ isHit: (record) => record?.__index === 'hit'
41
+ }));
42
+ vi.mock('../case/CaseCard', () => ({
43
+ default: ({ case: record }) => _jsx("div", { children: `case:${record.case_id}` })
44
+ }));
45
+ vi.mock('../hit/HitCard', () => ({
46
+ default: ({ id }) => _jsx("div", { children: `hit:${id}` })
47
+ }));
48
+ vi.mock('../hit/HitLayout', () => ({
49
+ HitLayout: { NORMAL: 'normal' }
50
+ }));
51
+ vi.mock('../hit/related/RelatedLink', () => ({
52
+ default: ({ title, href }) => _jsx("div", { children: `${title}:${href}` })
53
+ }));
54
+ import RecordRelated from './RecordRelated';
55
+ describe('RecordRelated', () => {
56
+ it('renders links and switches between hit, case, and event related records', () => {
57
+ render(_jsx(RecordRelated, { record: {
58
+ __index: 'hit',
59
+ howler: {
60
+ id: 'hit-1',
61
+ related: ['ref-1'],
62
+ links: [{ title: 'Example', href: 'https://example.com' }]
63
+ }
64
+ } }));
65
+ expect(screen.getByText('Example:https://example.com')).toBeInTheDocument();
66
+ fireEvent.click(screen.getAllByText('hit.related.tab.hit')[0]);
67
+ expect(screen.getByText('hit:hit-2')).toBeInTheDocument();
68
+ fireEvent.click(screen.getAllByText('hit.related.tab.case')[0]);
69
+ expect(screen.getByText('case:case-1')).toBeInTheDocument();
70
+ fireEvent.click(screen.getAllByText('hit.related.tab.event')[0]);
71
+ expect(screen.getByText('event:event-1')).toBeInTheDocument();
72
+ });
73
+ it('returns nothing when no record is provided', () => {
74
+ const { container } = render(_jsx(RecordRelated, { record: undefined }));
75
+ expect(container).toBeEmptyDOMElement();
76
+ });
77
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
+ let versions = {};
6
+ const mockSetVersions = vi.hoisted(() => vi.fn((next) => {
7
+ versions = next;
8
+ }));
9
+ vi.mock('components/hooks/useMyLocalStorage', () => ({
10
+ useMyLocalStorageItem: () => [versions, mockSetVersions]
11
+ }));
12
+ vi.mock('components/hooks/useMyUtils', () => ({
13
+ default: () => ({ shiftColor: (color) => color })
14
+ }));
15
+ vi.mock('react-i18next', () => ({
16
+ useTranslation: () => ({ t: (key) => key })
17
+ }));
18
+ vi.mock('utils/constants', () => ({
19
+ StorageKey: { LAST_SEEN: 'LAST_SEEN' }
20
+ }));
21
+ vi.mock('utils/utils', () => ({
22
+ compareTimestamp: (a, b) => new Date(a).getTime() - new Date(b).getTime(),
23
+ twitterShort: (value) => `short:${value}`
24
+ }));
25
+ vi.mock('../display/HowlerAvatar', () => ({
26
+ default: ({ userId }) => _jsx("div", { children: `avatar:${userId}` })
27
+ }));
28
+ vi.mock('../display/HowlerCard', () => ({
29
+ default: ({ children }) => _jsx("div", { children: children })
30
+ }));
31
+ vi.mock('../display/Markdown', () => ({
32
+ default: ({ md }) => _jsx("div", { children: `markdown:${md}` })
33
+ }));
34
+ import RecordWorklog from './RecordWorklog';
35
+ describe('RecordWorklog', () => {
36
+ beforeEach(() => {
37
+ versions = {};
38
+ mockSetVersions.mockClear();
39
+ });
40
+ it('renders grouped worklog entries and marks new entries', () => {
41
+ const { unmount } = render(_jsx(RecordWorklog, { users: { alice: { name: 'Alice' } }, record: {
42
+ __index: 'hit',
43
+ howler: {
44
+ id: 'hit-1',
45
+ log: [
46
+ { user: 'alice', previous_version: 'v3', timestamp: '2026-01-03T00:00:00Z', explanation: ' hello ' },
47
+ {
48
+ user: 'alice',
49
+ previous_version: 'v2',
50
+ timestamp: '2026-01-02T00:03:00Z',
51
+ key: 'status',
52
+ type: 'set',
53
+ previous_value: 'open',
54
+ new_value: 'closed'
55
+ },
56
+ {
57
+ user: 'bob',
58
+ previous_version: 'v1',
59
+ timestamp: '2026-01-02T00:00:00Z',
60
+ key: 'labels',
61
+ type: 'appended',
62
+ previous_value: '[old]',
63
+ new_value: 'new'
64
+ }
65
+ ]
66
+ }
67
+ } }));
68
+ expect(screen.getAllByText('avatar:alice').length).toBeGreaterThan(0);
69
+ expect(screen.getAllByText('Alice').length).toBeGreaterThan(0);
70
+ expect(screen.getByText('markdown:hello')).toBeInTheDocument();
71
+ expect(screen.getByText('short:2026-01-03T00:00:00Z')).toBeInTheDocument();
72
+ expect(screen.getByText(/hit\.worklog\.set/)).toBeInTheDocument();
73
+ expect(screen.getByText(/hit\.worklog\.appended/)).toBeInTheDocument();
74
+ unmount();
75
+ expect(mockSetVersions).toHaveBeenCalled();
76
+ });
77
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,189 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { act, renderHook } from '@testing-library/react';
4
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
+ const mockDispatchApi = vi.hoisted(() => vi.fn());
6
+ const mockShowWarningMessage = vi.hoisted(() => vi.fn());
7
+ const mockUpdateRecord = vi.hoisted(() => vi.fn());
8
+ const mockDrawerOpen = vi.hoisted(() => vi.fn());
9
+ const mockDrawerClose = vi.hoisted(() => vi.fn());
10
+ const mockShowModal = vi.hoisted(() => vi.fn());
11
+ const mockTransitionPost = vi.hoisted(() => vi.fn());
12
+ const mockHitGet = vi.hoisted(() => vi.fn());
13
+ const apiConfigContextToken = vi.hoisted(() => ({ name: 'api-config-context' }));
14
+ const appDrawerContextToken = vi.hoisted(() => ({ name: 'drawer-context' }));
15
+ const modalContextToken = vi.hoisted(() => ({ name: 'modal-context' }));
16
+ const recordContextToken = vi.hoisted(() => ({ name: 'record-context' }));
17
+ let configValue = {
18
+ config: {
19
+ lookups: {
20
+ transitions: {
21
+ open: ['assign_to_me', 'assign_to_other', 'promote'],
22
+ 'in-progress': ['release', 'pause', 'demote']
23
+ }
24
+ }
25
+ }
26
+ };
27
+ vi.mock('@tui/core', () => ({
28
+ useAppUser: () => ({
29
+ user: { username: 'alice', email: 'alice@example.com' }
30
+ })
31
+ }));
32
+ vi.mock('api', () => ({
33
+ default: {
34
+ hit: {
35
+ transition: { post: mockTransitionPost },
36
+ get: mockHitGet
37
+ }
38
+ }
39
+ }));
40
+ vi.mock('components/app/drawers/AssignUserDrawer', () => ({
41
+ default: ({ onAssigned }) => _jsx("button", { onClick: () => onAssigned('bob'), children: "assign-user" })
42
+ }));
43
+ vi.mock('components/app/providers/ApiConfigProvider', () => ({
44
+ ApiConfigContext: apiConfigContextToken
45
+ }));
46
+ vi.mock('components/app/providers/AppDrawerProvider', () => ({
47
+ AppDrawerContext: appDrawerContextToken
48
+ }));
49
+ vi.mock('components/app/providers/ModalProvider', () => ({
50
+ ModalContext: modalContextToken
51
+ }));
52
+ vi.mock('components/app/providers/RecordProvider', () => ({
53
+ RecordContext: recordContextToken
54
+ }));
55
+ vi.mock('components/elements/display/modals/RationaleModal', () => ({
56
+ default: ({ onSubmit }) => _jsx("button", { onClick: () => onSubmit('modal rationale'), children: "submit-rationale" })
57
+ }));
58
+ vi.mock('components/hooks/useMyApi', () => ({
59
+ default: () => ({ dispatchApi: mockDispatchApi })
60
+ }));
61
+ vi.mock('components/hooks/useMySnackbar', () => ({
62
+ default: () => ({ showWarningMessage: mockShowWarningMessage })
63
+ }));
64
+ vi.mock('react-i18next', () => ({
65
+ useTranslation: () => ({ t: (key, vars) => (vars?.assessment ? `${key}:${vars.assessment}` : key) })
66
+ }));
67
+ vi.mock('use-context-selector', async (importOriginal) => {
68
+ const actual = await importOriginal();
69
+ return {
70
+ ...actual,
71
+ useContextSelector: (_context, selector) => selector({ updateRecord: mockUpdateRecord })
72
+ };
73
+ });
74
+ vi.mock('react', async (importOriginal) => {
75
+ const actual = await importOriginal();
76
+ return {
77
+ ...actual,
78
+ useContext: (context) => {
79
+ if (context === apiConfigContextToken)
80
+ return configValue;
81
+ if (context === appDrawerContextToken)
82
+ return { open: mockDrawerOpen, close: mockDrawerClose };
83
+ if (context === modalContextToken)
84
+ return { showModal: mockShowModal };
85
+ return actual.useContext(context);
86
+ }
87
+ };
88
+ });
89
+ import useHitActions from './useHitActions';
90
+ describe('useHitActions', () => {
91
+ beforeEach(() => {
92
+ configValue = {
93
+ config: {
94
+ lookups: {
95
+ transitions: {
96
+ open: ['assign_to_me', 'assign_to_other', 'promote'],
97
+ 'in-progress': ['release', 'pause', 'demote']
98
+ }
99
+ }
100
+ }
101
+ };
102
+ mockDispatchApi.mockReset();
103
+ mockShowWarningMessage.mockReset();
104
+ mockUpdateRecord.mockReset();
105
+ mockDrawerOpen.mockReset();
106
+ mockDrawerClose.mockReset();
107
+ mockShowModal.mockReset();
108
+ mockTransitionPost.mockReset();
109
+ mockHitGet.mockReset();
110
+ });
111
+ it('computes voting and transition state for a single hit', () => {
112
+ const hit = {
113
+ howler: {
114
+ id: 'hit-1',
115
+ status: 'open',
116
+ assignment: 'bob',
117
+ escalation: 'hit',
118
+ votes: { benign: ['alice@example.com'] }
119
+ }
120
+ };
121
+ const { result } = renderHook(() => useHitActions(hit));
122
+ expect(result.current.canVote).toBe(true);
123
+ expect(result.current.canAssess).toBe(true);
124
+ expect(result.current.selectedVote).toBe('benign');
125
+ expect(result.current.availableTransitions.map(v => v.name)).toEqual([
126
+ 'assign_to_other',
127
+ 'assign_to_me',
128
+ 'promote'
129
+ ]);
130
+ });
131
+ it('votes and updates hits, including conflict recovery', async () => {
132
+ const hit = { howler: { id: 'hit-1', status: 'open', assignment: 'bob', escalation: 'hit', votes: {} } };
133
+ mockTransitionPost.mockImplementation((id, body) => ({ id, body }));
134
+ mockDispatchApi.mockImplementationOnce(async (_req, opts) => {
135
+ await opts.onConflict();
136
+ return { howler: { id: 'hit-1', vote: 'malicious' } };
137
+ });
138
+ mockHitGet.mockResolvedValueOnce({ howler: { id: 'hit-1', refreshed: true } });
139
+ mockTransitionPost.mockResolvedValueOnce({ howler: { id: 'hit-1', vote: 'malicious' } });
140
+ const { result } = renderHook(() => useHitActions(hit));
141
+ await act(async () => {
142
+ await result.current.vote('malicious');
143
+ });
144
+ expect(mockHitGet).toHaveBeenCalledWith('hit-1');
145
+ expect(mockUpdateRecord).toHaveBeenCalledWith({ howler: { id: 'hit-1', vote: 'malicious' } });
146
+ });
147
+ it('assesses hits with modal rationale and warns on conflicting server updates', async () => {
148
+ const hit = {
149
+ howler: { id: 'hit-2', status: 'open', assignment: 'bob', assessment: null, escalation: 'hit' }
150
+ };
151
+ mockTransitionPost.mockImplementation((id, body) => ({ id, body }));
152
+ mockShowModal.mockImplementation((node) => node.props.onSubmit('modal rationale'));
153
+ mockDispatchApi.mockImplementationOnce(async (_req, opts) => {
154
+ await opts.onConflict();
155
+ return null;
156
+ });
157
+ mockHitGet.mockResolvedValueOnce({ howler: { id: 'hit-2', assessment: 'existing' } });
158
+ const { result } = renderHook(() => useHitActions(hit));
159
+ await act(async () => {
160
+ await result.current.assess('malicious');
161
+ });
162
+ expect(mockUpdateRecord).toHaveBeenCalledWith({ howler: { id: 'hit-2', assessment: 'existing' } });
163
+ expect(mockShowWarningMessage).toHaveBeenCalledWith('hit.actions.conflict.assess');
164
+ });
165
+ it('uses provided rationale and manages assignment workflows', async () => {
166
+ const hit = {
167
+ howler: { id: 'hit-3', status: 'open', assignment: 'bob', assessment: null, escalation: 'alert' }
168
+ };
169
+ mockTransitionPost.mockImplementation((id, body) => ({ id, body }));
170
+ mockDispatchApi.mockResolvedValue({ howler: { id: 'hit-3', updated: true } });
171
+ mockDrawerOpen.mockImplementation((props) => props.children.props.onAssigned('bob'));
172
+ const { result } = renderHook(() => useHitActions(hit));
173
+ await act(async () => {
174
+ await result.current.assess('benign', true, 'manual rationale');
175
+ });
176
+ expect(mockTransitionPost).toHaveBeenCalledWith('hit-3', {
177
+ transition: 'assess',
178
+ data: { assessment: 'benign', rationale: 'manual rationale' }
179
+ });
180
+ await act(async () => {
181
+ await result.current.manage('assign_to_other');
182
+ });
183
+ expect(mockDrawerOpen).toHaveBeenCalled();
184
+ expect(mockTransitionPost).toHaveBeenCalledWith('hit-3', {
185
+ transition: 'assign_to_other',
186
+ data: { assignee: 'bob' }
187
+ });
188
+ });
189
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ /// <reference types="vitest" />
2
+ import { renderHook } from '@testing-library/react';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ const mockShowErrorMessage = vi.hoisted(() => vi.fn());
5
+ vi.mock('./useMySnackbar', () => ({
6
+ default: () => ({ showErrorMessage: mockShowErrorMessage })
7
+ }));
8
+ import useMyApi from './useMyApi';
9
+ describe('useMyApi', () => {
10
+ it('returns successful API responses', async () => {
11
+ const { result } = renderHook(() => useMyApi());
12
+ await expect(result.current.dispatchApi(Promise.resolve('ok'))).resolves.toBe('ok');
13
+ });
14
+ it('handles conflicts through the callback without surfacing an error', async () => {
15
+ const { result } = renderHook(() => useMyApi());
16
+ const onConflict = vi.fn();
17
+ const error = Object.assign(new Error('conflict'), { cause: { api_status_code: 409 } });
18
+ await expect(result.current.dispatchApi(Promise.reject(error), { onConflict })).resolves.toBeUndefined();
19
+ expect(onConflict).toHaveBeenCalled();
20
+ expect(mockShowErrorMessage).not.toHaveBeenCalled();
21
+ });
22
+ it('shows, logs, and rethrows errors according to config', async () => {
23
+ const { result } = renderHook(() => useMyApi());
24
+ const error = new Error('failed');
25
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
26
+ await expect(result.current.dispatchApi(Promise.reject(error), { logError: true })).rejects.toThrow('failed');
27
+ expect(mockShowErrorMessage).toHaveBeenCalledWith('failed');
28
+ expect(consoleSpy).toHaveBeenCalledWith(error);
29
+ await expect(result.current.dispatchApi(Promise.reject(error), { throwError: false, showError: false, logError: false })).resolves.toBeUndefined();
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export {};