@taskforcehq/taskforce 0.3.311 → 0.3.313

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 (454) hide show
  1. package/README.md +40 -2
  2. package/dist/TaskforceCore.js +1 -1
  3. package/dist/components/features/InitiativesModule.js +1 -1
  4. package/dist/components/features/TaskSettings.js +1 -1
  5. package/dist/components/views/StandaloneLayout.js +3 -6
  6. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +1 -2
  7. package/dist/components/views/standalone/modals/AccountHubModal.js +2 -3
  8. package/dist/components/views/standalone/modals/HelpModal.js +15 -2
  9. package/dist/hooks/useFilterSort.js +1 -1
  10. package/dist/hooks/useTaskforce.js +2 -2
  11. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +5 -2
  12. package/dist/ui/assets/{AgentsModule-D8u9P5dZ.js → AgentsModule-BULqIo8e.js} +1 -1
  13. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CsUauFA2.js → AnnotatedAttachmentWorkspace-DHr4rHK9.js} +1 -1
  14. package/dist/ui/assets/{ContextAttachmentManager-DbQvDHFz.js → ContextAttachmentManager-DoMe2OpY.js} +1 -1
  15. package/dist/ui/assets/{DocumentWorkspace-BmTkN1A8.js → DocumentWorkspace-DumBWhvb.js} +1 -1
  16. package/dist/ui/assets/{EntityActivityTimeline-BXHTM-Nh.js → EntityActivityTimeline-sHk8Nzbq.js} +1 -1
  17. package/dist/ui/assets/{InitiativesModule-D33RyXx0.js → InitiativesModule-3X9UNzHh.js} +1 -1
  18. package/dist/ui/assets/{PlansPage-D0RXlT9_.js → PlansPage-C3A3EYaV.js} +1 -1
  19. package/dist/ui/assets/{TaskContextUpload-Vsg67lpR.js → TaskContextUpload-C8G7pWkX.js} +1 -1
  20. package/dist/ui/assets/{TaskSettings-DshLw7_d.js → TaskSettings-DM1o9Wla.js} +2 -2
  21. package/dist/ui/assets/{WorkflowsModule-CLM-1BnF.js → WorkflowsModule-CW0crzvD.js} +1 -1
  22. package/dist/ui/assets/documentReferences-BZB3nMRb.js +1 -0
  23. package/dist/ui/assets/index-BWFQ9mGn.js +5 -0
  24. package/dist/ui/assets/{index-DOR9PRqt.css → index-DcK_8tuE.css} +1 -1
  25. package/dist/ui/index.html +2 -2
  26. package/package.json +12 -4
  27. package/scripts/postinstall.js +4 -3
  28. package/dist/TaskforceCore.CategoryFallback.test.d.ts +0 -1
  29. package/dist/TaskforceCore.CategoryFallback.test.js +0 -101
  30. package/dist/TaskforceCore.filter-persistence.test.d.ts +0 -1
  31. package/dist/TaskforceCore.filter-persistence.test.js +0 -402
  32. package/dist/TaskforceCore.routing.test.d.ts +0 -1
  33. package/dist/TaskforceCore.routing.test.js +0 -311
  34. package/dist/TaskforceCore.test.d.ts +0 -1
  35. package/dist/TaskforceCore.test.js +0 -7910
  36. package/dist/TaskforceWidget.test.d.ts +0 -1
  37. package/dist/TaskforceWidget.test.js +0 -92
  38. package/dist/UnsavedChangesRelaxed.test.d.ts +0 -1
  39. package/dist/UnsavedChangesRelaxed.test.js +0 -16
  40. package/dist/__tests__/localizationCatalogs.test.d.ts +0 -1
  41. package/dist/__tests__/localizationCatalogs.test.js +0 -54
  42. package/dist/__tests__/workflowSimulation.test.d.ts +0 -1
  43. package/dist/__tests__/workflowSimulation.test.js +0 -113
  44. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts +0 -1
  45. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +0 -278
  46. package/dist/annotatedAttachments/payload.test.d.ts +0 -1
  47. package/dist/annotatedAttachments/payload.test.js +0 -67
  48. package/dist/compat/workspaceSyncCompat.test.d.ts +0 -1
  49. package/dist/compat/workspaceSyncCompat.test.js +0 -124
  50. package/dist/components/features/AgentsModule.test.d.ts +0 -1
  51. package/dist/components/features/AgentsModule.test.js +0 -601
  52. package/dist/components/features/AnnotatedAttachmentWorkspace.test.d.ts +0 -1
  53. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +0 -2883
  54. package/dist/components/features/DocumentWorkspace.test.d.ts +0 -1
  55. package/dist/components/features/DocumentWorkspace.test.js +0 -1439
  56. package/dist/components/features/TaskSettings.test.d.ts +0 -1
  57. package/dist/components/features/TaskSettings.test.js +0 -1674
  58. package/dist/components/features/settings/TaxonomyLibraryManager.test.d.ts +0 -1
  59. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +0 -141
  60. package/dist/components/task/TaskActionHeader.test.d.ts +0 -1
  61. package/dist/components/task/TaskActionHeader.test.js +0 -91
  62. package/dist/components/task/TaskCard.test.d.ts +0 -1
  63. package/dist/components/task/TaskCard.test.js +0 -513
  64. package/dist/components/task/TaskContextUpload.test.d.ts +0 -1
  65. package/dist/components/task/TaskContextUpload.test.js +0 -506
  66. package/dist/components/task/TaskForm.test.d.ts +0 -1
  67. package/dist/components/task/TaskForm.test.js +0 -946
  68. package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +0 -1
  69. package/dist/components/task/TaskKanban.empty-column-drop.test.js +0 -66
  70. package/dist/components/task/TaskKanban.planning-drop.test.d.ts +0 -1
  71. package/dist/components/task/TaskKanban.planning-drop.test.js +0 -93
  72. package/dist/components/task/TaskKanban.test.d.ts +0 -1
  73. package/dist/components/task/TaskKanban.test.js +0 -99
  74. package/dist/components/task/TaskList.test.d.ts +0 -1
  75. package/dist/components/task/TaskList.test.js +0 -473
  76. package/dist/components/task/TaskStatusActions.test.d.ts +0 -1
  77. package/dist/components/task/TaskStatusActions.test.js +0 -54
  78. package/dist/components/task/taskKanbanDropRules.test.d.ts +0 -1
  79. package/dist/components/task/taskKanbanDropRules.test.js +0 -114
  80. package/dist/components/ui/LevelSelector.test.d.ts +0 -1
  81. package/dist/components/ui/LevelSelector.test.js +0 -55
  82. package/dist/components/ui/Markdown.test.d.ts +0 -1
  83. package/dist/components/ui/Markdown.test.js +0 -38
  84. package/dist/components/ui/MultiSelectFilter.test.d.ts +0 -1
  85. package/dist/components/ui/MultiSelectFilter.test.js +0 -74
  86. package/dist/components/ui/TaxonomyDropdown.test.d.ts +0 -1
  87. package/dist/components/ui/TaxonomyDropdown.test.js +0 -130
  88. package/dist/components/ui/TopNoticeLayer.test.d.ts +0 -1
  89. package/dist/components/ui/TopNoticeLayer.test.js +0 -25
  90. package/dist/components/views/AppShellHeader.test.d.ts +0 -1
  91. package/dist/components/views/AppShellHeader.test.js +0 -42
  92. package/dist/components/views/PlanComparisonPage.test.d.ts +0 -1
  93. package/dist/components/views/PlanComparisonPage.test.js +0 -758
  94. package/dist/components/views/PlansPage.test.d.ts +0 -1
  95. package/dist/components/views/PlansPage.test.js +0 -710
  96. package/dist/components/views/chrome/WorkspaceChromeSections.test.d.ts +0 -1
  97. package/dist/components/views/chrome/WorkspaceChromeSections.test.js +0 -54
  98. package/dist/components/views/chrome/workspaceChrome.test.d.ts +0 -1
  99. package/dist/components/views/chrome/workspaceChrome.test.js +0 -52
  100. package/dist/components/views/panels/DocumentFilterToolbar.test.d.ts +0 -1
  101. package/dist/components/views/panels/DocumentFilterToolbar.test.js +0 -19
  102. package/dist/components/views/panels/FilterToolbar.test.d.ts +0 -1
  103. package/dist/components/views/panels/FilterToolbar.test.js +0 -47
  104. package/dist/components/views/panels/PlanningDrawer.test.d.ts +0 -1
  105. package/dist/components/views/panels/PlanningDrawer.test.js +0 -529
  106. package/dist/components/views/planningScope.test.d.ts +0 -1
  107. package/dist/components/views/planningScope.test.js +0 -60
  108. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +0 -1
  109. package/dist/components/views/standalone/modals/AccountHubModal.test.js +0 -72
  110. package/dist/components/views/standalone/modals/HelpModal.test.d.ts +0 -1
  111. package/dist/components/views/standalone/modals/HelpModal.test.js +0 -23
  112. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +0 -1
  113. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +0 -24
  114. package/dist/components/views/teamManagementAccess.test.d.ts +0 -1
  115. package/dist/components/views/teamManagementAccess.test.js +0 -44
  116. package/dist/config/deployment.test.d.ts +0 -1
  117. package/dist/config/deployment.test.js +0 -90
  118. package/dist/config/envSchema.test.d.ts +0 -1
  119. package/dist/config/envSchema.test.js +0 -125
  120. package/dist/core/AiProfileService.test.d.ts +0 -1
  121. package/dist/core/AiProfileService.test.js +0 -184
  122. package/dist/core/AiProfiles.test.d.ts +0 -1
  123. package/dist/core/AiProfiles.test.js +0 -1873
  124. package/dist/core/AuthTokenService.test.d.ts +0 -1
  125. package/dist/core/AuthTokenService.test.js +0 -78
  126. package/dist/core/BulkUpdateFieldsPreview.test.d.ts +0 -1
  127. package/dist/core/BulkUpdateFieldsPreview.test.js +0 -62
  128. package/dist/core/CompletedAt.test.d.ts +0 -1
  129. package/dist/core/CompletedAt.test.js +0 -44
  130. package/dist/core/CreatedBy.test.d.ts +0 -1
  131. package/dist/core/CreatedBy.test.js +0 -188
  132. package/dist/core/DeletedTaskTrash.test.d.ts +0 -1
  133. package/dist/core/DeletedTaskTrash.test.js +0 -104
  134. package/dist/core/EntitlementsPolicy.test.d.ts +0 -1
  135. package/dist/core/EntitlementsPolicy.test.js +0 -280
  136. package/dist/core/GlobalSettingsService.test.d.ts +0 -1
  137. package/dist/core/GlobalSettingsService.test.js +0 -45
  138. package/dist/core/JsonBackupRuntime.test.d.ts +0 -1
  139. package/dist/core/JsonBackupRuntime.test.js +0 -155
  140. package/dist/core/PlanFeatureCatalog.test.d.ts +0 -1
  141. package/dist/core/PlanFeatureCatalog.test.js +0 -139
  142. package/dist/core/PlanVersionPolicy.test.d.ts +0 -1
  143. package/dist/core/PlanVersionPolicy.test.js +0 -236
  144. package/dist/core/PlanningEntities.test.d.ts +0 -1
  145. package/dist/core/PlanningEntities.test.js +0 -509
  146. package/dist/core/ScheduleFields.test.d.ts +0 -1
  147. package/dist/core/ScheduleFields.test.js +0 -193
  148. package/dist/core/SetupState.test.d.ts +0 -1
  149. package/dist/core/SetupState.test.js +0 -113
  150. package/dist/core/SignupMonetizationSettings.test.d.ts +0 -1
  151. package/dist/core/SignupMonetizationSettings.test.js +0 -515
  152. package/dist/core/Stats.test.d.ts +0 -1
  153. package/dist/core/Stats.test.js +0 -36
  154. package/dist/core/SystemAdmin.test.d.ts +0 -1
  155. package/dist/core/SystemAdmin.test.js +0 -1106
  156. package/dist/core/TaskActivitySync.test.d.ts +0 -1
  157. package/dist/core/TaskActivitySync.test.js +0 -90
  158. package/dist/core/TaskAttachmentsCanonical.test.d.ts +0 -1
  159. package/dist/core/TaskAttachmentsCanonical.test.js +0 -356
  160. package/dist/core/TaskAuditTimeline.test.d.ts +0 -1
  161. package/dist/core/TaskAuditTimeline.test.js +0 -319
  162. package/dist/core/TaskTaxonomyValidation.test.d.ts +0 -1
  163. package/dist/core/TaskTaxonomyValidation.test.js +0 -440
  164. package/dist/core/Taskforce.ids.test.d.ts +0 -1
  165. package/dist/core/Taskforce.ids.test.js +0 -611
  166. package/dist/core/Taskforce.listTasksSlim.test.d.ts +0 -1
  167. package/dist/core/Taskforce.listTasksSlim.test.js +0 -91
  168. package/dist/core/Taskforce.mcpAuth.test.d.ts +0 -1
  169. package/dist/core/Taskforce.mcpAuth.test.js +0 -450
  170. package/dist/core/Taskforce.realtimeMutation.test.d.ts +0 -1
  171. package/dist/core/Taskforce.realtimeMutation.test.js +0 -131
  172. package/dist/core/UserProfileAvatarDrafts.test.d.ts +0 -1
  173. package/dist/core/UserProfileAvatarDrafts.test.js +0 -138
  174. package/dist/core/WorkspaceDelete.test.d.ts +0 -1
  175. package/dist/core/WorkspaceDelete.test.js +0 -159
  176. package/dist/core/WorkspacePermissions.test.d.ts +0 -1
  177. package/dist/core/WorkspacePermissions.test.js +0 -270
  178. package/dist/core/WorkspacePlanMode.test.d.ts +0 -1
  179. package/dist/core/WorkspacePlanMode.test.js +0 -383
  180. package/dist/core/WorkspaceSyncSettings.test.d.ts +0 -1
  181. package/dist/core/WorkspaceSyncSettings.test.js +0 -65
  182. package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +0 -1
  183. package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +0 -212
  184. package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +0 -1
  185. package/dist/documentReviews/DocumentReviewSessionStore.test.js +0 -127
  186. package/dist/hooks/authGuardPolicy.test.d.ts +0 -1
  187. package/dist/hooks/authGuardPolicy.test.js +0 -76
  188. package/dist/hooks/bootstrapTimeouts.test.d.ts +0 -1
  189. package/dist/hooks/bootstrapTimeouts.test.js +0 -12
  190. package/dist/hooks/sync/controlPlane.test.d.ts +0 -1
  191. package/dist/hooks/sync/controlPlane.test.js +0 -121
  192. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +0 -1
  193. package/dist/hooks/sync/lifecyclePolicy.test.js +0 -124
  194. package/dist/hooks/sync/orchestratorShared.test.d.ts +0 -1
  195. package/dist/hooks/sync/orchestratorShared.test.js +0 -98
  196. package/dist/hooks/sync/pullLifecycle.test.d.ts +0 -1
  197. package/dist/hooks/sync/pullLifecycle.test.js +0 -80
  198. package/dist/hooks/sync/recovery.test.d.ts +0 -1
  199. package/dist/hooks/sync/recovery.test.js +0 -292
  200. package/dist/hooks/sync/transfers.test.d.ts +0 -1
  201. package/dist/hooks/sync/transfers.test.js +0 -928
  202. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +0 -1
  203. package/dist/hooks/sync/useRecentSyncEvents.test.js +0 -124
  204. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +0 -1
  205. package/dist/hooks/sync/useSyncStatusActions.test.js +0 -133
  206. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +0 -1
  207. package/dist/hooks/sync/useSyncStatusControls.test.js +0 -88
  208. package/dist/hooks/useFilterSort.test.d.ts +0 -1
  209. package/dist/hooks/useFilterSort.test.js +0 -419
  210. package/dist/hooks/useRealtimeSync.test.d.ts +0 -1
  211. package/dist/hooks/useRealtimeSync.test.js +0 -311
  212. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +0 -1
  213. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +0 -54
  214. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +0 -1
  215. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +0 -228
  216. package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +0 -1
  217. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +0 -2027
  218. package/dist/hooks/useTaskData.test.d.ts +0 -1
  219. package/dist/hooks/useTaskData.test.js +0 -291
  220. package/dist/hooks/useTaskMutations.test.d.ts +0 -1
  221. package/dist/hooks/useTaskMutations.test.js +0 -256
  222. package/dist/hooks/useTaskforce.contract.test.d.ts +0 -1
  223. package/dist/hooks/useTaskforce.contract.test.js +0 -7
  224. package/dist/hooks/useTaskforce.priority-filters.test.d.ts +0 -1
  225. package/dist/hooks/useTaskforce.priority-filters.test.js +0 -28
  226. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +0 -1
  227. package/dist/hooks/useTaskforce.runtime-routing.test.js +0 -246
  228. package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +0 -1
  229. package/dist/hooks/useTaskforce.sync-behavior.test.js +0 -3644
  230. package/dist/hooks/useWorkspaceSyncController.test.d.ts +0 -1
  231. package/dist/hooks/useWorkspaceSyncController.test.js +0 -192
  232. package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +0 -1
  233. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +0 -77
  234. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +0 -1
  235. package/dist/hooks/workspace/workspaceLocalState.test.js +0 -59
  236. package/dist/localization/localizationCatalogs.test.d.ts +0 -1
  237. package/dist/localization/localizationCatalogs.test.js +0 -37
  238. package/dist/mcp/aiProfileBootstrap.test.d.ts +0 -1
  239. package/dist/mcp/aiProfileBootstrap.test.js +0 -68
  240. package/dist/mcp/canonicalAssetHelpers.test.d.ts +0 -1
  241. package/dist/mcp/canonicalAssetHelpers.test.js +0 -67
  242. package/dist/mcp/clientIntegrations.test.d.ts +0 -1
  243. package/dist/mcp/clientIntegrations.test.js +0 -219
  244. package/dist/mcp/optionResolution.test.d.ts +0 -1
  245. package/dist/mcp/optionResolution.test.js +0 -43
  246. package/dist/mcp/runtime.test.d.ts +0 -1
  247. package/dist/mcp/runtime.test.js +0 -5211
  248. package/dist/mcp/structuredCommentValidation.test.d.ts +0 -1
  249. package/dist/mcp/structuredCommentValidation.test.js +0 -31
  250. package/dist/mcp/structuredDocValidation.test.d.ts +0 -1
  251. package/dist/mcp/structuredDocValidation.test.js +0 -35
  252. package/dist/mcp/toolRegistry.test.d.ts +0 -1
  253. package/dist/mcp/toolRegistry.test.js +0 -34
  254. package/dist/migrations/billingSchemaParity.test.d.ts +0 -1
  255. package/dist/migrations/billingSchemaParity.test.js +0 -160
  256. package/dist/migrations/taskSchemaMigrations.test.d.ts +0 -1
  257. package/dist/migrations/taskSchemaMigrations.test.js +0 -225
  258. package/dist/qaOpen.test.d.ts +0 -1
  259. package/dist/qaOpen.test.js +0 -56
  260. package/dist/releaseVersion.test.d.ts +0 -1
  261. package/dist/releaseVersion.test.js +0 -32
  262. package/dist/runtime/appGates.test.d.ts +0 -1
  263. package/dist/runtime/appGates.test.js +0 -42
  264. package/dist/server/aiProfileAvatarAssets.test.d.ts +0 -1
  265. package/dist/server/aiProfileAvatarAssets.test.js +0 -213
  266. package/dist/server/annotatedAttachmentsRoutes.test.d.ts +0 -1
  267. package/dist/server/annotatedAttachmentsRoutes.test.js +0 -532
  268. package/dist/server/auth.test.d.ts +0 -1
  269. package/dist/server/auth.test.js +0 -131
  270. package/dist/server/buildInfo.test.d.ts +0 -1
  271. package/dist/server/buildInfo.test.js +0 -29
  272. package/dist/server/cors.test.d.ts +0 -1
  273. package/dist/server/cors.test.js +0 -77
  274. package/dist/server/documentReviewRoutes.test.d.ts +0 -1
  275. package/dist/server/documentReviewRoutes.test.js +0 -190
  276. package/dist/server/email.test.d.ts +0 -1
  277. package/dist/server/email.test.js +0 -113
  278. package/dist/server/index.cookieProxy.test.d.ts +0 -1
  279. package/dist/server/index.cookieProxy.test.js +0 -67
  280. package/dist/server/index.rateLimit.test.d.ts +0 -1
  281. package/dist/server/index.rateLimit.test.js +0 -22
  282. package/dist/server/index.test.d.ts +0 -1
  283. package/dist/server/index.test.js +0 -283
  284. package/dist/server/rateLimit.test.d.ts +0 -1
  285. package/dist/server/rateLimit.test.js +0 -107
  286. package/dist/server/realtimeSync.auth.test.d.ts +0 -1
  287. package/dist/server/realtimeSync.auth.test.js +0 -62
  288. package/dist/server/realtimeSync.replay.test.d.ts +0 -1
  289. package/dist/server/realtimeSync.replay.test.js +0 -192
  290. package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +0 -1
  291. package/dist/server/realtimeSync.workspaceIsolation.test.js +0 -101
  292. package/dist/server/routes/billing.test.d.ts +0 -1
  293. package/dist/server/routes/billing.test.js +0 -3515
  294. package/dist/server/routes/shared.test.d.ts +0 -1
  295. package/dist/server/routes/shared.test.js +0 -32
  296. package/dist/server/routes/sync.integration.test.d.ts +0 -1
  297. package/dist/server/routes/sync.integration.test.js +0 -2932
  298. package/dist/server/routes/syncAuxRoutes.test.d.ts +0 -1
  299. package/dist/server/routes/syncAuxRoutes.test.js +0 -158
  300. package/dist/server/routes/syncRouteGuards.test.d.ts +0 -1
  301. package/dist/server/routes/syncRouteGuards.test.js +0 -94
  302. package/dist/server/routes.test.d.ts +0 -1
  303. package/dist/server/routes.test.js +0 -15612
  304. package/dist/server/runtimeMode.test.d.ts +0 -1
  305. package/dist/server/runtimeMode.test.js +0 -26
  306. package/dist/server/securityHeaders.test.d.ts +0 -1
  307. package/dist/server/securityHeaders.test.js +0 -70
  308. package/dist/services/templateEngine.test.d.ts +0 -1
  309. package/dist/services/templateEngine.test.js +0 -150
  310. package/dist/services/templates.test.d.ts +0 -1
  311. package/dist/services/templates.test.js +0 -45
  312. package/dist/services/workflowDocumentFixtures.test.d.ts +0 -1
  313. package/dist/services/workflowDocumentFixtures.test.js +0 -78
  314. package/dist/services/workflowExportMatrix.test.d.ts +0 -1
  315. package/dist/services/workflowExportMatrix.test.js +0 -113
  316. package/dist/services/workflowExportSnapshots.test.d.ts +0 -1
  317. package/dist/services/workflowExportSnapshots.test.js +0 -30
  318. package/dist/services/workflowGeneration.test.d.ts +0 -1
  319. package/dist/services/workflowGeneration.test.js +0 -12
  320. package/dist/services/workflowSimulation.test.d.ts +0 -1
  321. package/dist/services/workflowSimulation.test.js +0 -113
  322. package/dist/services/workflowV2Policy.test.d.ts +0 -1
  323. package/dist/services/workflowV2Policy.test.js +0 -80
  324. package/dist/shared/customTaxonomyLibrary.test.d.ts +0 -1
  325. package/dist/shared/customTaxonomyLibrary.test.js +0 -20
  326. package/dist/shared/passwordPolicy.test.d.ts +0 -1
  327. package/dist/shared/passwordPolicy.test.js +0 -34
  328. package/dist/shared/runtimeContract.test.d.ts +0 -1
  329. package/dist/shared/runtimeContract.test.js +0 -23
  330. package/dist/shared/taxonomyLibrary.test.d.ts +0 -1
  331. package/dist/shared/taxonomyLibrary.test.js +0 -26
  332. package/dist/shared/taxonomyState.test.d.ts +0 -1
  333. package/dist/shared/taxonomyState.test.js +0 -71
  334. package/dist/soak/preflight.cli.d.ts +0 -1
  335. package/dist/soak/preflight.cli.js +0 -28
  336. package/dist/soak/preflight.d.ts +0 -26
  337. package/dist/soak/preflight.js +0 -113
  338. package/dist/soak/preflight.test.d.ts +0 -1
  339. package/dist/soak/preflight.test.js +0 -39
  340. package/dist/storage/objectStorageClient.test.d.ts +0 -1
  341. package/dist/storage/objectStorageClient.test.js +0 -82
  342. package/dist/storage/objectStorageConfig.test.d.ts +0 -1
  343. package/dist/storage/objectStorageConfig.test.js +0 -50
  344. package/dist/storage/postgresAdapter.test.d.ts +0 -1
  345. package/dist/storage/postgresAdapter.test.js +0 -29
  346. package/dist/storage/workspaceAssetStore.test.d.ts +0 -1
  347. package/dist/storage/workspaceAssetStore.test.js +0 -588
  348. package/dist/sync/cloudSyncApi.test.d.ts +0 -1
  349. package/dist/sync/cloudSyncApi.test.js +0 -111
  350. package/dist/sync/collaborationSyncPayload.test.d.ts +0 -1
  351. package/dist/sync/collaborationSyncPayload.test.js +0 -125
  352. package/dist/sync/contentSyncPayload.test.d.ts +0 -1
  353. package/dist/sync/contentSyncPayload.test.js +0 -143
  354. package/dist/sync/encryptionService.test.d.ts +0 -1
  355. package/dist/sync/encryptionService.test.js +0 -52
  356. package/dist/sync/planningSyncPayload.test.d.ts +0 -1
  357. package/dist/sync/planningSyncPayload.test.js +0 -92
  358. package/dist/sync/realtimeFeature.test.d.ts +0 -1
  359. package/dist/sync/realtimeFeature.test.js +0 -30
  360. package/dist/sync/realtimeReconcile.test.d.ts +0 -1
  361. package/dist/sync/realtimeReconcile.test.js +0 -43
  362. package/dist/sync/syncApplyHandlers.test.d.ts +0 -1
  363. package/dist/sync/syncApplyHandlers.test.js +0 -626
  364. package/dist/sync/syncService.test.d.ts +0 -1
  365. package/dist/sync/syncService.test.js +0 -903
  366. package/dist/sync/taskSyncPayload.test.d.ts +0 -1
  367. package/dist/sync/taskSyncPayload.test.js +0 -54
  368. package/dist/sync/workspacePullFeed.test.d.ts +0 -1
  369. package/dist/sync/workspacePullFeed.test.js +0 -438
  370. package/dist/sync/workspaceSyncModel.test.d.ts +0 -1
  371. package/dist/sync/workspaceSyncModel.test.js +0 -1442
  372. package/dist/test/setup.d.ts +0 -1
  373. package/dist/test/setup.js +0 -15
  374. package/dist/ui/assets/documentReferences-BPWp6ce0.js +0 -1
  375. package/dist/ui/assets/index-zvbo2Jse.js +0 -5
  376. package/dist/utils/accountProfileSummaryCache.test.d.ts +0 -1
  377. package/dist/utils/accountProfileSummaryCache.test.js +0 -63
  378. package/dist/utils/annotatedAttachments.test.d.ts +0 -1
  379. package/dist/utils/annotatedAttachments.test.js +0 -108
  380. package/dist/utils/assignees.test.d.ts +0 -1
  381. package/dist/utils/assignees.test.js +0 -25
  382. package/dist/utils/avatarUpload.test.d.ts +0 -1
  383. package/dist/utils/avatarUpload.test.js +0 -40
  384. package/dist/utils/canonicalAssetKind.test.d.ts +0 -1
  385. package/dist/utils/canonicalAssetKind.test.js +0 -24
  386. package/dist/utils/coalescedRefresh.test.d.ts +0 -1
  387. package/dist/utils/coalescedRefresh.test.js +0 -30
  388. package/dist/utils/constants.icons.test.d.ts +0 -1
  389. package/dist/utils/constants.icons.test.js +0 -41
  390. package/dist/utils/planParser.test.d.ts +0 -1
  391. package/dist/utils/planParser.test.js +0 -183
  392. package/dist/utils/planningReferences.test.d.ts +0 -1
  393. package/dist/utils/planningReferences.test.js +0 -17
  394. package/dist/utils/supportContact.test.d.ts +0 -1
  395. package/dist/utils/supportContact.test.js +0 -19
  396. package/dist/utils/syncEventDetails.test.d.ts +0 -1
  397. package/dist/utils/syncEventDetails.test.js +0 -23
  398. package/dist/utils/syncEventPresentation.test.d.ts +0 -1
  399. package/dist/utils/syncEventPresentation.test.js +0 -64
  400. package/dist/utils/syncStatusPresentation.test.d.ts +0 -1
  401. package/dist/utils/syncStatusPresentation.test.js +0 -164
  402. package/dist/utils/taskActivity.test.d.ts +0 -1
  403. package/dist/utils/taskActivity.test.js +0 -218
  404. package/dist/utils/taskDraftDefaults.test.d.ts +0 -1
  405. package/dist/utils/taskDraftDefaults.test.js +0 -34
  406. package/dist/utils/taskDragZones.test.d.ts +0 -1
  407. package/dist/utils/taskDragZones.test.js +0 -14
  408. package/dist/utils/taskNormalization.test.d.ts +0 -1
  409. package/dist/utils/taskNormalization.test.js +0 -158
  410. package/dist/utils/taskSearch.test.d.ts +0 -1
  411. package/dist/utils/taskSearch.test.js +0 -69
  412. package/dist/utils/taskUtils.test.d.ts +0 -1
  413. package/dist/utils/taskUtils.test.js +0 -19
  414. package/dist/utils/taskforceApiClient.test.d.ts +0 -1
  415. package/dist/utils/taskforceApiClient.test.js +0 -44
  416. package/dist/utils/taxonomyFormDefaults.test.d.ts +0 -1
  417. package/dist/utils/taxonomyFormDefaults.test.js +0 -63
  418. package/dist/utils/uiNotice.test.d.ts +0 -1
  419. package/dist/utils/uiNotice.test.js +0 -18
  420. package/dist/utils/workspaceIdentity.test.d.ts +0 -1
  421. package/dist/utils/workspaceIdentity.test.js +0 -61
  422. package/dist/utils/workspaceLifecycle.test.d.ts +0 -1
  423. package/dist/utils/workspaceLifecycle.test.js +0 -64
  424. package/dist/utils/workspaceSyncPresentation.test.d.ts +0 -1
  425. package/dist/utils/workspaceSyncPresentation.test.js +0 -71
  426. package/scripts/bootstrap-playwright-auth.mjs +0 -155
  427. package/scripts/check-cloud-mcp.mjs +0 -83
  428. package/scripts/check-compat-boundaries.mjs +0 -41
  429. package/scripts/check-env.ts +0 -31
  430. package/scripts/check-localization-strings.mjs +0 -49
  431. package/scripts/check-runtime-boundaries.mjs +0 -44
  432. package/scripts/debug-global.mjs +0 -34
  433. package/scripts/debug-login.mjs +0 -32
  434. package/scripts/debug-server.mjs +0 -36
  435. package/scripts/deploy-prod.sh +0 -41
  436. package/scripts/deploy-staging.sh +0 -23
  437. package/scripts/export-sqlite-to-postgres.mjs +0 -123
  438. package/scripts/generate-env-example.ts +0 -8
  439. package/scripts/mcp-stdio-adapter.mjs +0 -160
  440. package/scripts/migrate-task-data-assets.mjs +0 -408
  441. package/scripts/playwright-auth.sh +0 -19
  442. package/scripts/qa-open-lib.mjs +0 -175
  443. package/scripts/qa-open-lib.mjs.d.ts +0 -49
  444. package/scripts/qa-open.mjs +0 -352
  445. package/scripts/release-version-lib.mjs +0 -88
  446. package/scripts/release-version.mjs +0 -72
  447. package/scripts/run-billing-performance-smoke.sh +0 -24
  448. package/scripts/run-dev-proxy.sh +0 -51
  449. package/scripts/run-e2e-realtime.sh +0 -30
  450. package/scripts/run-smoke.sh +0 -35
  451. package/scripts/run-soak.sh +0 -41
  452. package/scripts/test-global.js +0 -143
  453. package/scripts/update-homebrew-formula.sh +0 -108
  454. package/scripts/with-esbuild.sh +0 -11
@@ -1,1439 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
4
- import userEvent from '@testing-library/user-event';
5
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
- import { buildPrintDocumentHtml, DocumentViewer, schedulePrintCleanup } from './DocumentViewer';
7
- import { DocumentGenerateModal } from './DocumentGenerateModal';
8
- import { DocumentWorkspace, DocumentWorkspaceShell } from './DocumentWorkspace';
9
- const { fetchTaskforceApi } = vi.hoisted(() => ({
10
- fetchTaskforceApi: vi.fn(),
11
- }));
12
- vi.mock('../../utils/taskforceApiClient', () => ({
13
- fetchTaskforceApi,
14
- resolveTaskforceApiUrl: (path) => path,
15
- }));
16
- vi.mock('../../utils/documentManagerApi', async () => {
17
- const actual = await vi.importActual('../../utils/documentManagerApi');
18
- return {
19
- ...actual,
20
- logDocumentManagerDebug: vi.fn(),
21
- };
22
- });
23
- function createDoc(overrides = {}) {
24
- return {
25
- id: 'doc-1',
26
- assetId: 'asset-doc-1',
27
- fsPath: 'workspaces/default/assets/documents/technical-notes.md',
28
- apiUrl: '/api/taskforce/documents/asset-doc-1/content',
29
- taskId: null,
30
- displayName: 'Technical Notes',
31
- title: 'Technical Notes',
32
- docType: 'other',
33
- sizeBytes: 64,
34
- updatedAt: '2026-03-19T11:00:00.000Z',
35
- editable: true,
36
- attachmentCount: 0,
37
- attachmentState: 'unattached',
38
- ...overrides,
39
- };
40
- }
41
- function createEmptyReviewSessionsResponse() {
42
- return {
43
- ok: true,
44
- json: async () => ({ sessions: [] }),
45
- };
46
- }
47
- function renderDocumentWorkspace(props) {
48
- return render(_jsx(DocumentWorkspace, { tasks: props.tasks ?? [], documents: props.documents, loading: false, error: null, selectedDoc: props.selectedDoc ?? null, searchQuery: props.searchQuery ?? '', hasActiveFilters: props.hasActiveFilters ?? false, documentTrayOpen: props.documentTrayOpen, onSearchChange: props.onSearchChange ?? (() => { }), onSelectDoc: props.onSelectDoc ?? (() => { }), onRefresh: props.onRefresh ?? (() => { }), onDeleteSelected: props.onDeleteSelected ?? (() => { }) }));
49
- }
50
- describe('Document workspace management', () => {
51
- beforeEach(() => {
52
- fetchTaskforceApi.mockReset();
53
- });
54
- afterEach(() => {
55
- vi.useRealTimers();
56
- vi.restoreAllMocks();
57
- });
58
- it('filters documents by unattached status and shows a badge in the index', async () => {
59
- const docs = [
60
- {
61
- id: 'doc-attached',
62
- assetId: 'doc-attached',
63
- fsPath: 'workspaces/default/assets/documents/doc-attached.md',
64
- apiUrl: '/api/taskforce/documents/doc-attached/content',
65
- taskId: 'task-1',
66
- displayName: 'Attached notes',
67
- title: 'Attached notes',
68
- docType: 'other',
69
- sizeBytes: 128,
70
- updatedAt: '2026-03-19T10:00:00.000Z',
71
- editable: true,
72
- attachmentCount: 1,
73
- attachmentState: 'attached',
74
- },
75
- {
76
- id: 'doc-unattached',
77
- assetId: 'doc-unattached',
78
- fsPath: 'workspaces/default/assets/documents/doc-unattached.md',
79
- apiUrl: '/api/taskforce/documents/doc-unattached/content',
80
- taskId: null,
81
- displayName: 'Orphan notes',
82
- title: 'Orphan notes',
83
- docType: 'other',
84
- sizeBytes: 64,
85
- updatedAt: '2026-03-19T11:00:00.000Z',
86
- editable: true,
87
- attachmentCount: 0,
88
- attachmentState: 'unattached',
89
- },
90
- ];
91
- function Harness() {
92
- const [selectedDoc, setSelectedDoc] = useState(null);
93
- return (_jsx(DocumentWorkspace, { tasks: [{ id: 'task-1', title: 'Parent task' }], documents: docs.filter((doc) => doc.attachmentState === 'unattached'), loading: false, error: null, selectedDoc: selectedDoc, searchQuery: "", hasActiveFilters: true, onSearchChange: () => { }, onSelectDoc: setSelectedDoc, onRefresh: () => { }, onDeleteSelected: () => { } }));
94
- }
95
- render(_jsx(Harness, {}));
96
- expect(screen.getByRole('button', { name: /Orphan notes/i })).toBeInTheDocument();
97
- expect(screen.queryByRole('button', { name: /Attached notes/i })).not.toBeInTheDocument();
98
- expect(screen.getByText('Unattached')).toBeInTheDocument();
99
- });
100
- it('can render the document index as a collapsible tray', () => {
101
- const docs = [createDoc({ id: 'tray-doc', displayName: 'Tray Document' })];
102
- const { container, rerender } = renderDocumentWorkspace({
103
- documents: docs,
104
- documentTrayOpen: true,
105
- });
106
- expect(container.querySelector('[aria-hidden="false"]')).toHaveTextContent('Tray Document');
107
- rerender(_jsx(DocumentWorkspace, { tasks: [], documents: docs, loading: false, error: null, selectedDoc: null, searchQuery: "", hasActiveFilters: false, documentTrayOpen: false, onSearchChange: () => { }, onSelectDoc: () => { }, onRefresh: () => { }, onDeleteSelected: () => { } }));
108
- expect(container.querySelector('[aria-hidden="true"]')).toHaveTextContent('Tray Document');
109
- });
110
- it('keeps document search in the index without duplicate filter controls', () => {
111
- renderDocumentWorkspace({ documents: [createDoc()] });
112
- expect(screen.getByPlaceholderText('Search documents…')).toBeInTheDocument();
113
- expect(screen.queryByRole('button', { name: 'Attached' })).not.toBeInTheDocument();
114
- expect(screen.queryByRole('button', { name: 'Plans' })).not.toBeInTheDocument();
115
- });
116
- it('shows the document reference badge in the bottom metadata row of index cards', () => {
117
- renderDocumentWorkspace({ documents: [createDoc({ referenceLabel: 'D-42' })] });
118
- expect(screen.getByText('D-42')).toBeInTheDocument();
119
- expect(screen.getByText(/64B/)).toBeInTheDocument();
120
- });
121
- it('shows permanent delete only for unattached docs and deletes after confirmation', async () => {
122
- const user = userEvent.setup();
123
- fetchTaskforceApi
124
- .mockResolvedValueOnce({
125
- ok: true,
126
- text: async () => '# Example\n',
127
- })
128
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
129
- .mockResolvedValueOnce({
130
- ok: true,
131
- json: async () => ({ success: true, softDeleted: true }),
132
- });
133
- const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
134
- const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => { });
135
- const onDeleted = vi.fn();
136
- render(_jsx(DocumentViewer, { doc: {
137
- id: 'doc-unattached',
138
- assetId: 'asset-doc-unattached',
139
- fsPath: 'workspaces/default/assets/documents/doc-unattached.md',
140
- apiUrl: '/api/taskforce/documents/asset-doc-unattached/content',
141
- taskId: null,
142
- displayName: 'Unattached spec',
143
- title: 'Unattached spec',
144
- docType: 'other',
145
- sizeBytes: 64,
146
- updatedAt: '2026-03-19T11:00:00.000Z',
147
- editable: true,
148
- attachmentCount: 0,
149
- attachmentState: 'unattached',
150
- }, taskTitle: null, onDeleted: onDeleted }));
151
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenCalledWith('/api/taskforce/documents/asset-doc-unattached/content', { credentials: 'same-origin' }, ''));
152
- await user.click(screen.getByRole('button', { name: /Delete Permanently/i }));
153
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenLastCalledWith('/api/taskforce/documents/asset-doc-unattached', expect.objectContaining({ method: 'DELETE', credentials: 'include' }), ''));
154
- expect(onDeleted).toHaveBeenCalled();
155
- confirmSpy.mockRestore();
156
- alertSpy.mockRestore();
157
- });
158
- it('opens an attach modal from the document viewer and attaches the document to a task', async () => {
159
- const user = userEvent.setup();
160
- fetchTaskforceApi
161
- .mockResolvedValueOnce({
162
- ok: true,
163
- text: async () => '# Example\n',
164
- })
165
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
166
- .mockResolvedValueOnce({
167
- ok: true,
168
- json: async () => ({ success: true, attached: true, alreadyAttached: false }),
169
- });
170
- const onSaved = vi.fn();
171
- render(_jsx(DocumentViewer, { doc: createDoc({
172
- id: 'doc-attach',
173
- assetId: 'asset-doc-attach',
174
- attachmentCount: 0,
175
- attachmentState: 'unattached',
176
- }), taskTitle: null, tasks: [
177
- { id: 'task-1', title: 'First attach target', referenceLabel: 'T-11' },
178
- { id: 'task-2', title: 'Second attach target', referenceLabel: 'T-12' },
179
- ], onSaved: onSaved }));
180
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenCalledWith('/api/taskforce/documents/asset-doc-1/content', { credentials: 'same-origin' }, ''));
181
- await user.click(screen.getByRole('button', { name: /^Attach$/i }));
182
- expect(screen.getByText(/Attach this document to a task from the document manager/i)).toBeInTheDocument();
183
- expect(screen.getByText('T-11')).toBeInTheDocument();
184
- expect(screen.getByText('T-12')).toBeInTheDocument();
185
- await user.clear(screen.getByPlaceholderText('Search tasks…'));
186
- await user.type(screen.getByPlaceholderText('Search tasks…'), 'T-12');
187
- expect(screen.queryByRole('button', { name: /First attach target/i })).not.toBeInTheDocument();
188
- expect(screen.getByRole('button', { name: /Second attach target/i })).toBeInTheDocument();
189
- await user.clear(screen.getByPlaceholderText('Search tasks…'));
190
- await user.type(screen.getByPlaceholderText('Search tasks…'), 'Second');
191
- await user.click(screen.getByRole('button', { name: /Second attach target/i }));
192
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenLastCalledWith('/api/taskforce/documents/asset-doc-attach/attach', expect.objectContaining({
193
- method: 'POST',
194
- credentials: 'include',
195
- }), ''));
196
- expect(onSaved).toHaveBeenCalled();
197
- });
198
- it('shows delete and unlink for attached docs and sends forceDetach on delete', async () => {
199
- const user = userEvent.setup();
200
- fetchTaskforceApi
201
- .mockResolvedValueOnce({
202
- ok: true,
203
- text: async () => '# Example\n',
204
- })
205
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
206
- .mockResolvedValueOnce({
207
- ok: true,
208
- json: async () => ({ success: true, softDeleted: true, detachedTaskCount: 2 }),
209
- });
210
- const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
211
- const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => { });
212
- const onDeleted = vi.fn();
213
- render(_jsx(DocumentViewer, { doc: {
214
- id: 'doc-attached',
215
- assetId: 'asset-doc-attached',
216
- fsPath: 'workspaces/default/assets/documents/doc-attached.md',
217
- apiUrl: '/api/taskforce/documents/asset-doc-attached/content',
218
- taskId: 'task-1',
219
- displayName: 'Attached spec',
220
- title: 'Attached spec',
221
- docType: 'other',
222
- sizeBytes: 64,
223
- updatedAt: '2026-03-19T11:00:00.000Z',
224
- editable: true,
225
- attachmentCount: 2,
226
- attachmentState: 'attached',
227
- }, taskTitle: null, onDeleted: onDeleted }));
228
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenCalledWith('/api/taskforce/documents/asset-doc-attached/content', { credentials: 'same-origin' }, ''));
229
- await user.click(screen.getByRole('button', { name: /Delete & Unlink/i }));
230
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenLastCalledWith('/api/taskforce/documents/asset-doc-attached', expect.objectContaining({
231
- method: 'DELETE',
232
- credentials: 'include',
233
- headers: { 'Content-Type': 'application/json' },
234
- body: JSON.stringify({ forceDetach: true })
235
- }), ''));
236
- expect(confirmSpy).toHaveBeenCalledWith(expect.stringContaining('remove it from 2 linked tasks'));
237
- expect(onDeleted).toHaveBeenCalled();
238
- confirmSpy.mockRestore();
239
- alertSpy.mockRestore();
240
- });
241
- it('uses same-origin document routes in local runtime even when an api base is configured', async () => {
242
- const user = userEvent.setup();
243
- fetchTaskforceApi
244
- .mockResolvedValueOnce({
245
- ok: true,
246
- json: async () => ({ documents: [createDoc()] }),
247
- })
248
- .mockResolvedValueOnce({
249
- ok: true,
250
- text: async () => '# Example\n',
251
- })
252
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
253
- .mockResolvedValueOnce({
254
- ok: true,
255
- json: async () => ({ updatedAt: '2026-03-19T12:00:00.000Z' }),
256
- });
257
- render(_jsx(DocumentWorkspaceShell, { tasks: [], runtimeMode: "local", apiBaseUrl: "http://127.0.0.1:4280", cloudAuthBaseUrl: "https://cloud.example.com" }));
258
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(1, '/api/taskforce/documents', expect.objectContaining({ credentials: 'include' }), ''));
259
- await screen.findByText('Technical Notes');
260
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(2, '/api/taskforce/documents/asset-doc-1/content', expect.objectContaining({ credentials: 'same-origin' }), ''));
261
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions?assetId=asset-doc-1', expect.objectContaining({ credentials: 'include' }), ''));
262
- await user.click(screen.getByRole('button', { name: /Edit/i }));
263
- const editor = await screen.findByPlaceholderText('Write markdown here…');
264
- await user.clear(editor);
265
- await user.type(editor, '# Updated\n');
266
- await user.click(screen.getByRole('button', { name: /^Save$/i }));
267
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/documents/save', expect.objectContaining({
268
- method: 'POST',
269
- credentials: 'include',
270
- headers: { 'Content-Type': 'application/json' },
271
- }), ''));
272
- });
273
- it('renders document review comments from the document review session API', async () => {
274
- fetchTaskforceApi
275
- .mockResolvedValueOnce({
276
- ok: true,
277
- text: async () => '# Example\n',
278
- })
279
- .mockResolvedValueOnce({
280
- ok: true,
281
- json: async () => ({
282
- sessions: [{
283
- id: 'review-1',
284
- tenantId: 'default',
285
- workspaceId: 'default',
286
- assetId: 'asset-doc-1',
287
- documentId: 'doc-1',
288
- documentVersion: 1,
289
- title: 'Review 1',
290
- status: 'open',
291
- createdByActorId: 'user',
292
- updatedByActorId: 'user',
293
- createdAt: '2026-03-19T12:00:00.000Z',
294
- updatedAt: '2026-03-19T12:10:00.000Z',
295
- deletedAt: null,
296
- createdByActor: null,
297
- updatedByActor: null,
298
- comments: [{
299
- id: 'comment-1',
300
- order: 0,
301
- body: 'Please tighten the rollout section.',
302
- anchor: { kind: 'line-range', startLine: 18, endLine: 24 },
303
- authorActorId: 'user',
304
- authorActor: {
305
- id: 'user',
306
- kind: 'human',
307
- label: 'You',
308
- username: 'you',
309
- icon: 'User',
310
- color: '#5b8def',
311
- isNamed: true,
312
- },
313
- createdAt: '2026-03-19T12:05:00.000Z',
314
- updatedAt: '2026-03-19T12:05:00.000Z',
315
- deletedAt: null,
316
- }],
317
- }],
318
- }),
319
- });
320
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
321
- expect(await screen.findByText('Review & Comments')).toBeInTheDocument();
322
- expect(await screen.findByText('Please tighten the rollout section.')).toBeInTheDocument();
323
- expect(screen.getByText('Lines 18-24')).toBeInTheDocument();
324
- });
325
- it('toggles the review sidebar from the document header', async () => {
326
- const user = userEvent.setup();
327
- fetchTaskforceApi
328
- .mockResolvedValueOnce({
329
- ok: true,
330
- text: async () => '# Example\n',
331
- })
332
- .mockResolvedValueOnce({
333
- ok: true,
334
- json: async () => ({
335
- sessions: [{
336
- id: 'review-1',
337
- tenantId: 'default',
338
- workspaceId: 'default',
339
- assetId: 'asset-doc-1',
340
- documentId: 'doc-1',
341
- documentVersion: 1,
342
- title: 'Review 1',
343
- status: 'open',
344
- createdByActorId: 'user',
345
- updatedByActorId: 'user',
346
- createdByActor: null,
347
- updatedByActor: null,
348
- createdAt: '2026-03-19T12:00:00.000Z',
349
- updatedAt: '2026-03-19T12:05:00.000Z',
350
- deletedAt: null,
351
- comments: [],
352
- }],
353
- }),
354
- });
355
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
356
- expect(await screen.findByText('Review & Comments')).toBeInTheDocument();
357
- expect(screen.getByPlaceholderText('Add a comment for this document review…')).toBeInTheDocument();
358
- await user.click(screen.getByRole('button', { name: 'Hide Review' }));
359
- expect(screen.queryByText('Review & Comments')).not.toBeInTheDocument();
360
- expect(screen.queryByPlaceholderText('Add a comment for this document review…')).not.toBeInTheDocument();
361
- await user.click(screen.getByRole('button', { name: 'Show Review' }));
362
- expect(await screen.findByText('Review & Comments')).toBeInTheDocument();
363
- expect(screen.getByPlaceholderText('Add a comment for this document review…')).toBeInTheDocument();
364
- });
365
- it('creates a review session and posts comments from the document viewer', async () => {
366
- const user = userEvent.setup();
367
- fetchTaskforceApi
368
- .mockResolvedValueOnce({
369
- ok: true,
370
- text: async () => '# Example\n',
371
- })
372
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
373
- .mockResolvedValueOnce({
374
- ok: true,
375
- json: async () => ({
376
- success: true,
377
- session: {
378
- id: 'review-1',
379
- tenantId: 'default',
380
- workspaceId: 'default',
381
- assetId: 'asset-doc-1',
382
- documentId: 'doc-1',
383
- documentVersion: 1,
384
- title: 'Review 1',
385
- status: 'open',
386
- createdByActorId: 'user',
387
- updatedByActorId: 'user',
388
- createdAt: '2026-03-19T12:00:00.000Z',
389
- updatedAt: '2026-03-19T12:00:00.000Z',
390
- deletedAt: null,
391
- createdByActor: null,
392
- updatedByActor: null,
393
- comments: [],
394
- },
395
- }),
396
- })
397
- .mockResolvedValueOnce({
398
- ok: true,
399
- json: async () => ({
400
- success: true,
401
- session: {
402
- id: 'review-1',
403
- tenantId: 'default',
404
- workspaceId: 'default',
405
- assetId: 'asset-doc-1',
406
- documentId: 'doc-1',
407
- documentVersion: 1,
408
- title: 'Review 1',
409
- status: 'open',
410
- createdByActorId: 'user',
411
- updatedByActorId: 'user',
412
- createdAt: '2026-03-19T12:00:00.000Z',
413
- updatedAt: '2026-03-19T12:04:00.000Z',
414
- deletedAt: null,
415
- createdByActor: null,
416
- updatedByActor: null,
417
- comments: [{
418
- id: 'comment-1',
419
- order: 0,
420
- body: 'Needs a stronger summary.',
421
- anchor: { kind: 'document' },
422
- authorActorId: 'user',
423
- authorActor: null,
424
- createdAt: '2026-03-19T12:04:00.000Z',
425
- updatedAt: '2026-03-19T12:04:00.000Z',
426
- deletedAt: null,
427
- }],
428
- },
429
- }),
430
- });
431
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
432
- await screen.findByText('Review & Comments');
433
- await user.type(screen.getByPlaceholderText('Add a comment for this document review…'), 'Needs a stronger summary.');
434
- await user.click(screen.getByRole('button', { name: /Send review comment/i }));
435
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions', expect.objectContaining({
436
- method: 'POST',
437
- credentials: 'include',
438
- headers: { 'Content-Type': 'application/json' },
439
- }), ''));
440
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/document-reviews/sessions/review-1/comments', expect.objectContaining({
441
- method: 'POST',
442
- credentials: 'include',
443
- headers: { 'Content-Type': 'application/json' },
444
- }), ''));
445
- expect(await screen.findByText('Needs a stronger summary.')).toBeInTheDocument();
446
- });
447
- it('anchors a review comment to selected document text', async () => {
448
- const user = userEvent.setup();
449
- const getSelectionSpy = vi.spyOn(window, 'getSelection');
450
- fetchTaskforceApi
451
- .mockResolvedValueOnce({
452
- ok: true,
453
- text: async () => '# Example\n\nSelected rollout sentence for review.\n',
454
- })
455
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
456
- .mockResolvedValueOnce({
457
- ok: true,
458
- json: async () => ({
459
- success: true,
460
- session: {
461
- id: 'review-1',
462
- tenantId: 'default',
463
- workspaceId: 'default',
464
- assetId: 'asset-doc-1',
465
- documentId: 'doc-1',
466
- documentVersion: 1,
467
- title: 'Review 1',
468
- status: 'open',
469
- createdByActorId: 'user',
470
- updatedByActorId: 'user',
471
- createdAt: '2026-03-19T12:00:00.000Z',
472
- updatedAt: '2026-03-19T12:00:00.000Z',
473
- deletedAt: null,
474
- createdByActor: null,
475
- updatedByActor: null,
476
- comments: [],
477
- },
478
- }),
479
- })
480
- .mockResolvedValueOnce({
481
- ok: true,
482
- json: async () => ({
483
- success: true,
484
- session: {
485
- id: 'review-1',
486
- tenantId: 'default',
487
- workspaceId: 'default',
488
- assetId: 'asset-doc-1',
489
- documentId: 'doc-1',
490
- documentVersion: 1,
491
- title: 'Review 1',
492
- status: 'open',
493
- createdByActorId: 'user',
494
- updatedByActorId: 'user',
495
- createdAt: '2026-03-19T12:00:00.000Z',
496
- updatedAt: '2026-03-19T12:04:00.000Z',
497
- deletedAt: null,
498
- createdByActor: null,
499
- updatedByActor: null,
500
- comments: [{
501
- id: 'comment-1',
502
- order: 0,
503
- body: 'This sentence needs stronger ownership.',
504
- anchor: { kind: 'quote', quote: 'Selected rollout sentence for review.' },
505
- authorActorId: 'user',
506
- authorActor: null,
507
- createdAt: '2026-03-19T12:04:00.000Z',
508
- updatedAt: '2026-03-19T12:04:00.000Z',
509
- deletedAt: null,
510
- }],
511
- },
512
- }),
513
- });
514
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
515
- const selectedText = await screen.findByText('Selected rollout sentence for review.');
516
- const selectionNode = selectedText.firstChild || selectedText;
517
- getSelectionSpy.mockReturnValue({
518
- toString: () => 'Selected rollout sentence for review.',
519
- rangeCount: 1,
520
- getRangeAt: () => ({
521
- commonAncestorContainer: selectionNode,
522
- startContainer: selectionNode,
523
- startOffset: 0,
524
- endContainer: selectionNode,
525
- endOffset: 'Selected rollout sentence for review.'.length,
526
- }),
527
- });
528
- fireEvent.mouseUp(selectedText);
529
- expect(await screen.findByText('"Selected rollout sentence for review."')).toBeInTheDocument();
530
- await user.type(screen.getByPlaceholderText('Add a comment for the selected text…'), 'This sentence needs stronger ownership.');
531
- await user.click(screen.getByRole('button', { name: /Send review comment/i }));
532
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/document-reviews/sessions/review-1/comments', expect.objectContaining({
533
- method: 'POST',
534
- credentials: 'include',
535
- headers: { 'Content-Type': 'application/json' },
536
- }), ''));
537
- const commentRequest = fetchTaskforceApi.mock.calls[3]?.[1];
538
- expect(commentRequest).toBeTruthy();
539
- const requestBody = JSON.parse(String(commentRequest.body || '{}'));
540
- expect(requestBody.anchor).toMatchObject({
541
- kind: 'quote',
542
- quote: 'Selected rollout sentence for review.',
543
- });
544
- getSelectionSpy.mockRestore();
545
- });
546
- it('preserves the selected quote occurrence when text repeats in the document', async () => {
547
- const user = userEvent.setup();
548
- const getSelectionSpy = vi.spyOn(window, 'getSelection');
549
- fetchTaskforceApi
550
- .mockResolvedValueOnce({
551
- ok: true,
552
- text: async () => '# Example\n\nRepeated sentence.\n\nSpacer paragraph.\n\nRepeated sentence.\n',
553
- })
554
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
555
- .mockResolvedValueOnce({
556
- ok: true,
557
- json: async () => ({
558
- success: true,
559
- session: {
560
- id: 'review-1',
561
- tenantId: 'default',
562
- workspaceId: 'default',
563
- assetId: 'asset-doc-1',
564
- documentId: 'doc-1',
565
- documentVersion: 1,
566
- title: 'Review 1',
567
- status: 'open',
568
- createdByActorId: 'user',
569
- updatedByActorId: 'user',
570
- createdAt: '2026-03-19T12:00:00.000Z',
571
- updatedAt: '2026-03-19T12:00:00.000Z',
572
- deletedAt: null,
573
- createdByActor: null,
574
- updatedByActor: null,
575
- comments: [],
576
- },
577
- }),
578
- })
579
- .mockResolvedValueOnce({
580
- ok: true,
581
- json: async () => ({
582
- success: true,
583
- session: {
584
- id: 'review-1',
585
- tenantId: 'default',
586
- workspaceId: 'default',
587
- assetId: 'asset-doc-1',
588
- documentId: 'doc-1',
589
- documentVersion: 1,
590
- title: 'Review 1',
591
- status: 'open',
592
- createdByActorId: 'user',
593
- updatedByActorId: 'user',
594
- createdAt: '2026-03-19T12:00:00.000Z',
595
- updatedAt: '2026-03-19T12:04:00.000Z',
596
- deletedAt: null,
597
- createdByActor: null,
598
- updatedByActor: null,
599
- comments: [{
600
- id: 'comment-1',
601
- order: 0,
602
- body: 'Use the second repeated sentence.',
603
- anchor: { kind: 'quote', quote: 'Repeated sentence.', occurrenceIndex: 1 },
604
- authorActorId: 'user',
605
- authorActor: null,
606
- createdAt: '2026-03-19T12:04:00.000Z',
607
- updatedAt: '2026-03-19T12:04:00.000Z',
608
- deletedAt: null,
609
- }],
610
- },
611
- }),
612
- });
613
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
614
- const repeatedTextNodes = await screen.findAllByText('Repeated sentence.');
615
- const secondRepeatedText = repeatedTextNodes[1];
616
- const secondSelectionNode = secondRepeatedText.firstChild || secondRepeatedText;
617
- getSelectionSpy.mockReturnValue({
618
- toString: () => 'Repeated sentence.',
619
- rangeCount: 1,
620
- getRangeAt: () => ({
621
- commonAncestorContainer: secondSelectionNode,
622
- startContainer: secondSelectionNode,
623
- startOffset: 0,
624
- endContainer: secondSelectionNode,
625
- endOffset: 'Repeated sentence.'.length,
626
- }),
627
- });
628
- fireEvent.mouseUp(secondRepeatedText);
629
- await user.type(screen.getByPlaceholderText('Add a comment for the selected text…'), 'Use the second repeated sentence.');
630
- await user.click(screen.getByRole('button', { name: /Send review comment/i }));
631
- const commentRequest = fetchTaskforceApi.mock.calls[3]?.[1];
632
- expect(commentRequest).toBeTruthy();
633
- const requestBody = JSON.parse(String(commentRequest.body || '{}'));
634
- expect(requestBody.anchor).toMatchObject({
635
- kind: 'quote',
636
- quote: 'Repeated sentence.',
637
- occurrenceIndex: 1,
638
- });
639
- getSelectionSpy.mockRestore();
640
- });
641
- it('renames a document review session from the viewer', async () => {
642
- const user = userEvent.setup();
643
- fetchTaskforceApi
644
- .mockResolvedValueOnce({
645
- ok: true,
646
- text: async () => '# Example\n',
647
- })
648
- .mockResolvedValueOnce({
649
- ok: true,
650
- json: async () => ({
651
- sessions: [{
652
- id: 'review-1',
653
- tenantId: 'default',
654
- workspaceId: 'default',
655
- assetId: 'asset-doc-1',
656
- documentId: 'doc-1',
657
- documentVersion: 1,
658
- title: 'Review 1',
659
- status: 'open',
660
- createdByActorId: 'user',
661
- updatedByActorId: 'user',
662
- createdAt: '2026-03-19T12:00:00.000Z',
663
- updatedAt: '2026-03-19T12:10:00.000Z',
664
- deletedAt: null,
665
- createdByActor: null,
666
- updatedByActor: null,
667
- comments: [],
668
- }],
669
- }),
670
- })
671
- .mockResolvedValueOnce({
672
- ok: true,
673
- json: async () => ({
674
- success: true,
675
- session: {
676
- id: 'review-1',
677
- tenantId: 'default',
678
- workspaceId: 'default',
679
- assetId: 'asset-doc-1',
680
- documentId: 'doc-1',
681
- documentVersion: 1,
682
- title: 'Architecture Review',
683
- status: 'open',
684
- createdByActorId: 'user',
685
- updatedByActorId: 'user',
686
- createdAt: '2026-03-19T12:00:00.000Z',
687
- updatedAt: '2026-03-19T12:12:00.000Z',
688
- deletedAt: null,
689
- createdByActor: null,
690
- updatedByActor: null,
691
- comments: [],
692
- },
693
- }),
694
- });
695
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
696
- await screen.findByText('Review 1');
697
- await user.click(screen.getByRole('button', { name: /Rename Review/i }));
698
- const titleInput = screen.getByDisplayValue('Review 1');
699
- await user.clear(titleInput);
700
- await user.type(titleInput, 'Architecture Review');
701
- await user.click(screen.getByRole('button', { name: /Save Title/i }));
702
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions/review-1', expect.objectContaining({
703
- method: 'PATCH',
704
- credentials: 'include',
705
- headers: { 'Content-Type': 'application/json' },
706
- }), ''));
707
- expect(await screen.findByText('Architecture Review')).toBeInTheDocument();
708
- });
709
- it('clears a document review session title from the viewer', async () => {
710
- const user = userEvent.setup();
711
- fetchTaskforceApi
712
- .mockResolvedValueOnce({
713
- ok: true,
714
- text: async () => '# Example\n',
715
- })
716
- .mockResolvedValueOnce({
717
- ok: true,
718
- json: async () => ({
719
- sessions: [{
720
- id: 'review-1',
721
- tenantId: 'default',
722
- workspaceId: 'default',
723
- assetId: 'asset-doc-1',
724
- documentId: 'doc-1',
725
- documentVersion: 1,
726
- title: 'Review 1',
727
- status: 'open',
728
- createdByActorId: 'user',
729
- updatedByActorId: 'user',
730
- createdAt: '2026-03-19T12:00:00.000Z',
731
- updatedAt: '2026-03-19T12:10:00.000Z',
732
- deletedAt: null,
733
- createdByActor: null,
734
- updatedByActor: null,
735
- comments: [],
736
- }],
737
- }),
738
- })
739
- .mockResolvedValueOnce({
740
- ok: true,
741
- json: async () => ({
742
- success: true,
743
- session: {
744
- id: 'review-1',
745
- tenantId: 'default',
746
- workspaceId: 'default',
747
- assetId: 'asset-doc-1',
748
- documentId: 'doc-1',
749
- documentVersion: 1,
750
- title: null,
751
- status: 'open',
752
- createdByActorId: 'user',
753
- updatedByActorId: 'user',
754
- createdAt: '2026-03-19T12:00:00.000Z',
755
- updatedAt: '2026-03-19T12:12:00.000Z',
756
- deletedAt: null,
757
- createdByActor: null,
758
- updatedByActor: null,
759
- comments: [],
760
- },
761
- }),
762
- });
763
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
764
- await screen.findByText('Review 1');
765
- await user.click(screen.getByRole('button', { name: /Rename Review/i }));
766
- const titleInput = screen.getByDisplayValue('Review 1');
767
- await user.clear(titleInput);
768
- await user.click(screen.getByRole('button', { name: /Save Title/i }));
769
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions/review-1', expect.objectContaining({
770
- method: 'PATCH',
771
- credentials: 'include',
772
- headers: { 'Content-Type': 'application/json' },
773
- body: JSON.stringify({ title: null }),
774
- }), ''));
775
- expect(screen.queryByText('Review 1')).not.toBeInTheDocument();
776
- });
777
- it('edits a document review comment from the viewer', async () => {
778
- const user = userEvent.setup();
779
- fetchTaskforceApi
780
- .mockResolvedValueOnce({
781
- ok: true,
782
- text: async () => '# Example\n',
783
- })
784
- .mockResolvedValueOnce({
785
- ok: true,
786
- json: async () => ({
787
- sessions: [{
788
- id: 'review-1',
789
- tenantId: 'default',
790
- workspaceId: 'default',
791
- assetId: 'asset-doc-1',
792
- documentId: 'doc-1',
793
- documentVersion: 1,
794
- title: 'Review 1',
795
- status: 'open',
796
- createdByActorId: 'user',
797
- updatedByActorId: 'user',
798
- createdAt: '2026-03-19T12:00:00.000Z',
799
- updatedAt: '2026-03-19T12:10:00.000Z',
800
- deletedAt: null,
801
- createdByActor: null,
802
- updatedByActor: null,
803
- comments: [{
804
- id: 'comment-1',
805
- order: 0,
806
- body: 'Original review note.',
807
- anchor: { kind: 'document' },
808
- authorActorId: 'user',
809
- authorActor: null,
810
- createdAt: '2026-03-19T12:05:00.000Z',
811
- updatedAt: '2026-03-19T12:05:00.000Z',
812
- deletedAt: null,
813
- }],
814
- }],
815
- }),
816
- })
817
- .mockResolvedValueOnce({
818
- ok: true,
819
- json: async () => ({
820
- success: true,
821
- session: {
822
- id: 'review-1',
823
- tenantId: 'default',
824
- workspaceId: 'default',
825
- assetId: 'asset-doc-1',
826
- documentId: 'doc-1',
827
- documentVersion: 1,
828
- title: 'Review 1',
829
- status: 'open',
830
- createdByActorId: 'user',
831
- updatedByActorId: 'user',
832
- createdAt: '2026-03-19T12:00:00.000Z',
833
- updatedAt: '2026-03-19T12:12:00.000Z',
834
- deletedAt: null,
835
- createdByActor: null,
836
- updatedByActor: null,
837
- comments: [{
838
- id: 'comment-1',
839
- order: 0,
840
- body: 'Updated review note.',
841
- anchor: { kind: 'document' },
842
- authorActorId: 'user',
843
- authorActor: null,
844
- createdAt: '2026-03-19T12:05:00.000Z',
845
- updatedAt: '2026-03-19T12:12:00.000Z',
846
- deletedAt: null,
847
- }],
848
- },
849
- }),
850
- });
851
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
852
- const originalComment = await screen.findByText('Original review note.');
853
- const commentCard = originalComment.closest('[class*="commentText"]')?.parentElement;
854
- expect(commentCard).not.toBeNull();
855
- await user.click(within(commentCard).getByRole('button', { name: 'Edit' }));
856
- const editor = screen.getByDisplayValue('Original review note.');
857
- await user.clear(editor);
858
- await user.type(editor, 'Updated review note.');
859
- await user.click(screen.getByRole('button', { name: /Save Comment/i }));
860
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions/review-1/comments/comment-1', expect.objectContaining({
861
- method: 'PATCH',
862
- credentials: 'include',
863
- headers: { 'Content-Type': 'application/json' },
864
- }), ''));
865
- expect(await screen.findByText('Updated review note.')).toBeInTheDocument();
866
- });
867
- it('deletes a document review comment from the viewer', async () => {
868
- const user = userEvent.setup();
869
- const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
870
- fetchTaskforceApi
871
- .mockResolvedValueOnce({
872
- ok: true,
873
- text: async () => '# Example\n',
874
- })
875
- .mockResolvedValueOnce({
876
- ok: true,
877
- json: async () => ({
878
- sessions: [{
879
- id: 'review-1',
880
- tenantId: 'default',
881
- workspaceId: 'default',
882
- assetId: 'asset-doc-1',
883
- documentId: 'doc-1',
884
- documentVersion: 1,
885
- title: 'Review 1',
886
- status: 'open',
887
- createdByActorId: 'user',
888
- updatedByActorId: 'user',
889
- createdAt: '2026-03-19T12:00:00.000Z',
890
- updatedAt: '2026-03-19T12:10:00.000Z',
891
- deletedAt: null,
892
- createdByActor: null,
893
- updatedByActor: null,
894
- comments: [{
895
- id: 'comment-1',
896
- order: 0,
897
- body: 'Delete me.',
898
- anchor: { kind: 'document' },
899
- authorActorId: 'user',
900
- authorActor: null,
901
- createdAt: '2026-03-19T12:05:00.000Z',
902
- updatedAt: '2026-03-19T12:05:00.000Z',
903
- deletedAt: null,
904
- }],
905
- }],
906
- }),
907
- })
908
- .mockResolvedValueOnce({
909
- ok: true,
910
- json: async () => ({
911
- success: true,
912
- session: {
913
- id: 'review-1',
914
- tenantId: 'default',
915
- workspaceId: 'default',
916
- assetId: 'asset-doc-1',
917
- documentId: 'doc-1',
918
- documentVersion: 1,
919
- title: 'Review 1',
920
- status: 'open',
921
- createdByActorId: 'user',
922
- updatedByActorId: 'user',
923
- createdAt: '2026-03-19T12:00:00.000Z',
924
- updatedAt: '2026-03-19T12:12:00.000Z',
925
- deletedAt: null,
926
- createdByActor: null,
927
- updatedByActor: null,
928
- comments: [],
929
- },
930
- }),
931
- });
932
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
933
- const commentToDelete = await screen.findByText('Delete me.');
934
- const commentCard = commentToDelete.closest('[class*="commentText"]')?.parentElement;
935
- expect(commentCard).not.toBeNull();
936
- await user.click(within(commentCard).getByRole('button', { name: 'Delete' }));
937
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions/review-1/comments/comment-1', expect.objectContaining({
938
- method: 'DELETE',
939
- credentials: 'include',
940
- }), ''));
941
- await waitFor(() => expect(screen.queryByText('Delete me.')).not.toBeInTheDocument());
942
- confirmSpy.mockRestore();
943
- });
944
- it('uses the resolved cloud api base for document list and save actions in cloud runtime', async () => {
945
- const user = userEvent.setup();
946
- fetchTaskforceApi
947
- .mockResolvedValueOnce({
948
- ok: true,
949
- json: async () => ({ documents: [createDoc()] }),
950
- })
951
- .mockResolvedValueOnce({
952
- ok: true,
953
- text: async () => '# Example\n',
954
- })
955
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
956
- .mockResolvedValueOnce({
957
- ok: true,
958
- json: async () => ({ updatedAt: '2026-03-19T12:00:00.000Z' }),
959
- });
960
- render(_jsx(DocumentWorkspaceShell, { tasks: [], runtimeMode: "cloud", apiBaseUrl: "https://api.taskforce.example", cloudAuthBaseUrl: "https://auth.taskforce.example" }));
961
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(1, '/api/taskforce/documents', expect.objectContaining({ credentials: 'include' }), 'https://api.taskforce.example'));
962
- await screen.findAllByText('Technical Notes');
963
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(2, '/api/taskforce/documents/asset-doc-1/content', expect.objectContaining({ credentials: 'same-origin' }), 'https://api.taskforce.example'));
964
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/document-reviews/sessions?assetId=asset-doc-1', expect.objectContaining({ credentials: 'include' }), 'https://api.taskforce.example'));
965
- await user.click(screen.getByRole('button', { name: /Edit/i }));
966
- const editor = await screen.findByPlaceholderText('Write markdown here…');
967
- await user.clear(editor);
968
- await user.type(editor, '# Updated\n');
969
- await user.click(screen.getByRole('button', { name: /^Save$/i }));
970
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/documents/save', expect.objectContaining({
971
- method: 'POST',
972
- credentials: 'include',
973
- headers: { 'Content-Type': 'application/json' },
974
- }), 'https://api.taskforce.example'));
975
- });
976
- it('uses the forwarded api base url for rename, delete, and task generation actions', async () => {
977
- const user = userEvent.setup();
978
- fetchTaskforceApi
979
- .mockResolvedValueOnce({
980
- ok: true,
981
- text: async () => '# Example\n',
982
- })
983
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
984
- .mockResolvedValueOnce({
985
- ok: true,
986
- json: async () => ({ updatedAt: '2026-03-19T12:00:00.000Z' }),
987
- })
988
- .mockResolvedValueOnce({
989
- ok: true,
990
- json: async () => ({ success: true, softDeleted: true }),
991
- })
992
- .mockResolvedValueOnce({
993
- ok: true,
994
- json: async () => ({
995
- tasks: [{
996
- idempotencyKey: 'task-1',
997
- title: 'Generated task',
998
- description: 'Generated description',
999
- type: 'feature',
1000
- priority: 2,
1001
- children: [],
1002
- selected: true,
1003
- }]
1004
- }),
1005
- })
1006
- .mockResolvedValueOnce({
1007
- ok: true,
1008
- json: async () => ({ created: 1 }),
1009
- });
1010
- const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);
1011
- const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => { });
1012
- const doc = createDoc({
1013
- attachmentCount: 0,
1014
- attachmentState: 'unattached',
1015
- });
1016
- const { rerender } = render(_jsx(DocumentViewer, { doc: doc, taskTitle: null, apiBaseUrl: "http://127.0.0.1:4280", enableTaskGeneration: true }));
1017
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(1, '/api/taskforce/documents/asset-doc-1/content', expect.objectContaining({ credentials: 'same-origin' }), 'http://127.0.0.1:4280'));
1018
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(2, '/api/taskforce/document-reviews/sessions?assetId=asset-doc-1', expect.objectContaining({ credentials: 'include' }), 'http://127.0.0.1:4280'));
1019
- await user.dblClick(screen.getByText('Technical Notes'));
1020
- const renameInput = await screen.findByDisplayValue('Technical Notes');
1021
- await user.clear(renameInput);
1022
- await user.type(renameInput, 'Renamed Doc');
1023
- fireEvent.blur(renameInput);
1024
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(3, '/api/taskforce/documents/asset-doc-1', expect.objectContaining({
1025
- method: 'PATCH',
1026
- credentials: 'include',
1027
- headers: { 'Content-Type': 'application/json' },
1028
- }), 'http://127.0.0.1:4280'));
1029
- expect(await screen.findByText('Renamed Doc')).toBeInTheDocument();
1030
- await user.click(screen.getByRole('button', { name: /Delete Permanently/i }));
1031
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/documents/asset-doc-1', expect.objectContaining({
1032
- method: 'DELETE',
1033
- credentials: 'include',
1034
- }), 'http://127.0.0.1:4280'));
1035
- rerender(_jsx(DocumentGenerateModal, { content: "# Plan", docTitle: "Renamed Doc", apiBaseUrl: "http://127.0.0.1:4280", onClose: () => { }, onGenerated: () => { } }));
1036
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(5, '/api/taskforce/documents/generate-tasks/preview', expect.objectContaining({
1037
- method: 'POST',
1038
- credentials: 'include',
1039
- headers: { 'Content-Type': 'application/json' },
1040
- }), 'http://127.0.0.1:4280'));
1041
- await user.click(screen.getByRole('button', { name: /Generate 1 Task/i }));
1042
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(6, '/api/taskforce/documents/generate-tasks/commit', expect.objectContaining({
1043
- method: 'POST',
1044
- credentials: 'include',
1045
- headers: { 'Content-Type': 'application/json' },
1046
- }), 'http://127.0.0.1:4280'));
1047
- confirmSpy.mockRestore();
1048
- alertSpy.mockRestore();
1049
- });
1050
- it('falls back to the readable document name in the viewer when displayName is missing', async () => {
1051
- fetchTaskforceApi
1052
- .mockResolvedValueOnce({
1053
- ok: true,
1054
- text: async () => '# Example\n',
1055
- })
1056
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1057
- render(_jsx(DocumentViewer, { doc: {
1058
- id: 'doc-readable-fallback',
1059
- assetId: 'asset-readable-fallback',
1060
- fsPath: 'workspaces/default/assets/documents/technical-notes.md',
1061
- apiUrl: '/api/taskforce/documents/asset-readable-fallback/content',
1062
- taskId: null,
1063
- displayName: '',
1064
- title: 'Technical Notes',
1065
- docType: 'other',
1066
- sizeBytes: 64,
1067
- updatedAt: '2026-03-19T11:00:00.000Z',
1068
- editable: true,
1069
- attachmentCount: 0,
1070
- attachmentState: 'unattached',
1071
- }, taskTitle: null }));
1072
- expect(await screen.findByText('Technical Notes')).toBeInTheDocument();
1073
- expect(screen.queryByText('asset-readable-fallback')).not.toBeInTheDocument();
1074
- });
1075
- it('syncs editor and preview scrolling in split edit mode', async () => {
1076
- const user = userEvent.setup();
1077
- fetchTaskforceApi
1078
- .mockResolvedValueOnce({
1079
- ok: true,
1080
- text: async () => Array.from({ length: 80 }, (_, index) => `- item ${index + 1}`).join('\n'),
1081
- })
1082
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1083
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: null }));
1084
- await screen.findByText('item 1');
1085
- await user.click(screen.getByRole('button', { name: /Edit/i }));
1086
- const editor = await screen.findByPlaceholderText('Write markdown here…');
1087
- const preview = await screen.findByTestId('doc-preview-scroll');
1088
- Object.defineProperty(editor, 'scrollHeight', { configurable: true, value: 1000 });
1089
- Object.defineProperty(editor, 'clientHeight', { configurable: true, value: 200 });
1090
- Object.defineProperty(preview, 'scrollHeight', { configurable: true, value: 1600 });
1091
- Object.defineProperty(preview, 'clientHeight', { configurable: true, value: 400 });
1092
- editor.scrollTop = 400;
1093
- fireEvent.scroll(editor);
1094
- await waitFor(() => expect(preview.scrollTop).toBeCloseTo(600, 4));
1095
- preview.scrollTop = 300;
1096
- fireEvent.scroll(preview);
1097
- await waitFor(() => expect(editor.scrollTop).toBeCloseTo(200, 4));
1098
- });
1099
- it('builds isolated print html without injecting the viewer file name as a second header', () => {
1100
- const html = buildPrintDocumentHtml(`# Implementation Plan
1101
-
1102
- **Goal**
1103
-
1104
- Body copy.
1105
-
1106
- - [x] Done
1107
- - [ ] Todo
1108
- `);
1109
- expect(html).toContain('@page');
1110
- expect(html).toContain('margin: 0.94in 1.02in 1in;');
1111
- expect(html).toContain('font: 12.15pt/1.66 Georgia, "Times New Roman", Times, serif;');
1112
- expect(html).toContain('max-width: 5.95in;');
1113
- expect(html).toContain('.tf-print-document h1');
1114
- expect(html).toContain('font-size: 32pt;');
1115
- expect(html).toContain('max-width: 84%;');
1116
- expect(html).toContain('.tf-print-document h2');
1117
- expect(html).toContain('font-size: 16.5pt;');
1118
- expect(html).toContain('padding-left: 1.1em;');
1119
- expect(html).toContain('background: transparent;');
1120
- expect(html).toContain('.tf-print-section-label');
1121
- expect(html).toContain('font-size: 10.7pt;');
1122
- expect(html).toContain('letter-spacing: 0.12em;');
1123
- expect(html).toContain('text-transform: uppercase;');
1124
- expect(html).toContain('tf-print-document');
1125
- expect(html).toContain('<h1>Implementation Plan</h1>');
1126
- expect(html).toContain('class="tf-print-section-label"');
1127
- expect(html).toContain('>Goal</p>');
1128
- expect(html).toContain('type="checkbox"');
1129
- expect(html).toContain('readOnly=""');
1130
- expect(html).toContain('checked=""');
1131
- expect(html).not.toContain('Technical Notes');
1132
- expect(html).not.toContain('docViewerTitle');
1133
- });
1134
- it('prints through an isolated iframe document and cleans it up after afterprint', async () => {
1135
- const user = userEvent.setup();
1136
- fetchTaskforceApi
1137
- .mockResolvedValueOnce({
1138
- ok: true,
1139
- text: async () => 'Polished print body\n\nSecond paragraph.',
1140
- })
1141
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1142
- const realCreateElement = document.createElement.bind(document);
1143
- const iframe = realCreateElement('iframe');
1144
- const openSpy = vi.fn();
1145
- const writeSpy = vi.fn();
1146
- const closeSpy = vi.fn();
1147
- const removeSpy = vi.spyOn(iframe, 'remove').mockImplementation(() => { });
1148
- const afterprintHandlers = [];
1149
- const iframePrintSpy = vi.fn();
1150
- const iframeFocusSpy = vi.fn();
1151
- const iframeWindow = {
1152
- focus: iframeFocusSpy,
1153
- print: iframePrintSpy,
1154
- requestAnimationFrame: (callback) => {
1155
- callback(0);
1156
- return 1;
1157
- },
1158
- addEventListener: (eventName, handler) => {
1159
- if (eventName === 'afterprint' && typeof handler === 'function')
1160
- afterprintHandlers.push(handler);
1161
- },
1162
- removeEventListener: vi.fn(),
1163
- };
1164
- const iframeDocument = {
1165
- open: openSpy,
1166
- write: writeSpy,
1167
- close: closeSpy,
1168
- fonts: { ready: Promise.resolve() },
1169
- };
1170
- Object.defineProperty(iframe, 'contentWindow', { configurable: true, value: iframeWindow });
1171
- Object.defineProperty(iframe, 'contentDocument', { configurable: true, value: iframeDocument });
1172
- vi.spyOn(document, 'createElement').mockImplementation(((tagName, options) => {
1173
- if (tagName.toLowerCase() === 'iframe')
1174
- return iframe;
1175
- return realCreateElement(tagName, options);
1176
- }));
1177
- render(_jsx(DocumentViewer, { doc: createDoc(), taskTitle: "Parent task" }));
1178
- await screen.findByText('Polished print body');
1179
- await user.click(screen.getByRole('button', { name: /Print/i }));
1180
- await waitFor(() => expect(writeSpy).toHaveBeenCalledTimes(1));
1181
- expect(writeSpy.mock.calls[0]?.[0]).toContain('Polished print body');
1182
- expect(writeSpy.mock.calls[0]?.[0]).not.toContain('Technical Notes');
1183
- await waitFor(() => expect(iframePrintSpy).toHaveBeenCalledTimes(1));
1184
- expect(afterprintHandlers).toHaveLength(1);
1185
- afterprintHandlers[0](new Event('afterprint'));
1186
- expect(removeSpy).toHaveBeenCalled();
1187
- });
1188
- it('schedules print cleanup with the expected fallback delay', () => {
1189
- const cleanup = vi.fn();
1190
- const timeoutSpy = vi.spyOn(window, 'setTimeout').mockReturnValue(1);
1191
- schedulePrintCleanup(cleanup);
1192
- expect(timeoutSpy).toHaveBeenCalledWith(cleanup, 1500);
1193
- });
1194
- it('falls back to attachmentCount when attachmentState is missing', async () => {
1195
- const user = userEvent.setup();
1196
- const docs = [
1197
- {
1198
- id: 'doc-attached-fallback',
1199
- assetId: 'doc-attached-fallback',
1200
- fsPath: 'workspaces/default/assets/documents/doc-attached-fallback.md',
1201
- apiUrl: '/api/taskforce/documents/doc-attached-fallback/content',
1202
- taskId: 'task-1',
1203
- displayName: 'Attached fallback',
1204
- title: 'Attached fallback',
1205
- docType: 'other',
1206
- sizeBytes: 128,
1207
- updatedAt: '2026-03-19T10:00:00.000Z',
1208
- editable: true,
1209
- attachmentCount: 1,
1210
- },
1211
- {
1212
- id: 'doc-unattached-fallback',
1213
- assetId: 'doc-unattached-fallback',
1214
- fsPath: 'workspaces/default/assets/documents/doc-unattached-fallback.md',
1215
- apiUrl: '/api/taskforce/documents/doc-unattached-fallback/content',
1216
- taskId: null,
1217
- displayName: 'Unattached fallback',
1218
- title: 'Unattached fallback',
1219
- docType: 'other',
1220
- sizeBytes: 64,
1221
- updatedAt: '2026-03-19T11:00:00.000Z',
1222
- editable: true,
1223
- attachmentCount: 0,
1224
- },
1225
- ];
1226
- const { rerender } = renderDocumentWorkspace({
1227
- tasks: [{ id: 'task-1', title: 'Parent task' }],
1228
- documents: docs.filter((doc) => doc.taskId),
1229
- hasActiveFilters: true,
1230
- });
1231
- expect(screen.getByText('Attached fallback')).toBeInTheDocument();
1232
- expect(screen.queryByText('Unattached fallback')).not.toBeInTheDocument();
1233
- rerender(_jsx(DocumentWorkspace, { tasks: [{ id: 'task-1', title: 'Parent task' }], documents: docs.filter((doc) => !doc.taskId), loading: false, error: null, selectedDoc: null, searchQuery: "", hasActiveFilters: true, onSearchChange: () => { }, onSelectDoc: () => { }, onRefresh: () => { }, onDeleteSelected: () => { } }));
1234
- expect(screen.getByText('Unattached fallback')).toBeInTheDocument();
1235
- expect(screen.queryByText('Attached fallback')).not.toBeInTheDocument();
1236
- });
1237
- it('switches the active document when a different index item is selected', async () => {
1238
- const user = userEvent.setup();
1239
- const docs = [
1240
- createDoc({
1241
- id: 'doc-first',
1242
- assetId: 'asset-doc-first',
1243
- apiUrl: '/api/taskforce/documents/asset-doc-first/content',
1244
- displayName: 'First notes',
1245
- title: 'First notes',
1246
- }),
1247
- createDoc({
1248
- id: 'doc-second',
1249
- assetId: 'asset-doc-second',
1250
- apiUrl: '/api/taskforce/documents/asset-doc-second/content',
1251
- displayName: 'Second notes',
1252
- title: 'Second notes',
1253
- fsPath: 'workspaces/default/assets/documents/second-notes.md',
1254
- }),
1255
- ];
1256
- fetchTaskforceApi
1257
- .mockResolvedValueOnce({
1258
- ok: true,
1259
- json: async () => ({ documents: docs }),
1260
- })
1261
- .mockResolvedValueOnce({
1262
- ok: true,
1263
- text: async () => '# First content\n',
1264
- })
1265
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
1266
- .mockResolvedValueOnce({
1267
- ok: true,
1268
- text: async () => '# Second content\n',
1269
- })
1270
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1271
- render(_jsx(DocumentWorkspaceShell, { tasks: [], runtimeMode: "local" }));
1272
- expect(await screen.findByText('First content')).toBeInTheDocument();
1273
- await user.click(screen.getByRole('button', { name: /Second notes/i }));
1274
- expect(await screen.findByText('Second content')).toBeInTheDocument();
1275
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(4, '/api/taskforce/documents/asset-doc-second/content', expect.objectContaining({ credentials: 'same-origin' }), ''));
1276
- });
1277
- it('returns to the remaining filtered document when the active selection is filtered out', async () => {
1278
- const user = userEvent.setup();
1279
- const docs = [
1280
- createDoc({
1281
- id: 'doc-attached-return',
1282
- assetId: 'asset-doc-attached-return',
1283
- apiUrl: '/api/taskforce/documents/asset-doc-attached-return/content',
1284
- displayName: 'Attached return doc',
1285
- title: 'Attached return doc',
1286
- attachmentCount: 1,
1287
- attachmentState: 'attached',
1288
- taskId: 'task-1',
1289
- }),
1290
- createDoc({
1291
- id: 'doc-unattached-return',
1292
- assetId: 'asset-doc-unattached-return',
1293
- apiUrl: '/api/taskforce/documents/asset-doc-unattached-return/content',
1294
- displayName: 'Unattached return doc',
1295
- title: 'Unattached return doc',
1296
- fsPath: 'workspaces/default/assets/documents/unattached-return-doc.md',
1297
- attachmentCount: 0,
1298
- attachmentState: 'unattached',
1299
- taskId: null,
1300
- }),
1301
- ];
1302
- function Harness() {
1303
- const [attachmentFilters, setAttachmentFilters] = useState([]);
1304
- return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => setAttachmentFilters(['attached']), children: "Show attached only" }), _jsx(DocumentWorkspaceShell, { tasks: [{ id: 'task-1', title: 'Parent task' }], runtimeMode: "local", attachmentFilters: attachmentFilters })] }));
1305
- }
1306
- fetchTaskforceApi
1307
- .mockResolvedValueOnce({
1308
- ok: true,
1309
- json: async () => ({ documents: docs }),
1310
- })
1311
- .mockResolvedValueOnce({
1312
- ok: true,
1313
- text: async () => '# Attached return content\n',
1314
- })
1315
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
1316
- .mockResolvedValueOnce({
1317
- ok: true,
1318
- text: async () => '# Unattached return content\n',
1319
- })
1320
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse())
1321
- .mockResolvedValueOnce({
1322
- ok: true,
1323
- text: async () => '# Attached return content\n',
1324
- })
1325
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1326
- render(_jsx(Harness, {}));
1327
- expect(await screen.findByText('Attached return content')).toBeInTheDocument();
1328
- await user.click(screen.getByRole('button', { name: /Unattached return doc/i }));
1329
- expect(await screen.findByText('Unattached return content')).toBeInTheDocument();
1330
- await user.click(screen.getByRole('button', { name: /Show attached only/i }));
1331
- expect(await screen.findByText('Attached return content')).toBeInTheDocument();
1332
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(6, '/api/taskforce/documents/asset-doc-attached-return/content', expect.objectContaining({ credentials: 'same-origin' }), ''));
1333
- });
1334
- it('selects a requested document by asset id when normalized paths differ', async () => {
1335
- const docs = [
1336
- {
1337
- id: 'asset-doc-unattached',
1338
- assetId: 'asset-doc-unattached',
1339
- fsPath: 'workspaces/default/assets/documents/asset-doc-unattached-random.md',
1340
- apiUrl: '/api/taskforce/documents/asset-doc-unattached/content',
1341
- taskId: null,
1342
- displayName: 'Unattached doc',
1343
- title: 'Unattached doc',
1344
- docType: 'other',
1345
- sizeBytes: 10,
1346
- updatedAt: '2026-03-19T10:00:00.000Z',
1347
- editable: true,
1348
- attachmentCount: 0,
1349
- attachmentState: 'unattached',
1350
- },
1351
- {
1352
- id: 'asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
1353
- assetId: 'asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
1354
- fsPath: 'workspaces/default/assets/documents/asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-notes.md',
1355
- apiUrl: '/api/taskforce/documents/asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/content',
1356
- taskId: 'task-1',
1357
- displayName: 'Attached notes',
1358
- title: 'Attached notes',
1359
- docType: 'other',
1360
- sizeBytes: 20,
1361
- updatedAt: '2026-03-19T11:00:00.000Z',
1362
- editable: true,
1363
- attachmentCount: 1,
1364
- attachmentState: 'attached',
1365
- }
1366
- ];
1367
- function Harness() {
1368
- return (_jsx(DocumentWorkspaceShell, { tasks: [{ id: 'task-1', title: 'Parent task' }], runtimeMode: "local", requestedDocPath: "workspaces/default/assets/documents/path-aliases/asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }));
1369
- }
1370
- fetchTaskforceApi
1371
- .mockResolvedValueOnce({
1372
- ok: true,
1373
- json: async () => ({ documents: docs }),
1374
- })
1375
- .mockResolvedValueOnce({
1376
- ok: true,
1377
- text: async () => '# Attached notes\n',
1378
- })
1379
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1380
- render(_jsx(Harness, {}));
1381
- await waitFor(() => {
1382
- expect(screen.getByText('Attached notes')).toBeInTheDocument();
1383
- });
1384
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(2, '/api/taskforce/documents/asset-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/content', expect.objectContaining({ credentials: 'same-origin' }), ''));
1385
- expect(screen.queryByText('Unattached doc')).toBeInTheDocument();
1386
- });
1387
- it('selects a requested document directly by canonical asset id in cloud mode', async () => {
1388
- const docs = [
1389
- {
1390
- id: 'asset-doc-unattached',
1391
- assetId: 'asset-doc-unattached',
1392
- fsPath: 'workspaces/default/assets/documents/asset-doc-unattached-random.md',
1393
- apiUrl: '/api/taskforce/documents/asset-doc-unattached/content',
1394
- taskId: null,
1395
- displayName: 'Unattached doc',
1396
- title: 'Unattached doc',
1397
- docType: 'other',
1398
- sizeBytes: 10,
1399
- updatedAt: '2026-03-19T10:00:00.000Z',
1400
- editable: true,
1401
- attachmentCount: 0,
1402
- attachmentState: 'unattached',
1403
- },
1404
- {
1405
- id: 'asset-doc-cloud-1',
1406
- assetId: 'asset-doc-cloud-1',
1407
- fsPath: 'workspaces/workspace-1/assets/documents/asset-doc-cloud-1-implementation-plan.md',
1408
- apiUrl: '/api/taskforce/documents/asset-doc-cloud-1/content',
1409
- taskId: 'task-1',
1410
- displayName: 'Implementation plan',
1411
- title: 'Implementation plan',
1412
- docType: 'implementation-plan',
1413
- sizeBytes: 20,
1414
- updatedAt: '2026-03-19T11:00:00.000Z',
1415
- editable: true,
1416
- attachmentCount: 1,
1417
- attachmentState: 'attached',
1418
- }
1419
- ];
1420
- function Harness() {
1421
- return (_jsx(DocumentWorkspaceShell, { tasks: [{ id: 'task-1', title: 'Parent task' }], runtimeMode: "cloud", apiBaseUrl: "https://performance-app.taskforcehq.ai", requestedDocAssetId: "asset-doc-cloud-1" }));
1422
- }
1423
- fetchTaskforceApi
1424
- .mockResolvedValueOnce({
1425
- ok: true,
1426
- json: async () => ({ documents: docs }),
1427
- })
1428
- .mockResolvedValueOnce({
1429
- ok: true,
1430
- text: async () => '# Implementation plan\n',
1431
- })
1432
- .mockResolvedValueOnce(createEmptyReviewSessionsResponse());
1433
- render(_jsx(Harness, {}));
1434
- await waitFor(() => {
1435
- expect(screen.getByText('Implementation plan')).toBeInTheDocument();
1436
- });
1437
- await waitFor(() => expect(fetchTaskforceApi).toHaveBeenNthCalledWith(2, '/api/taskforce/documents/asset-doc-cloud-1/content', expect.objectContaining({ credentials: 'same-origin' }), 'https://performance-app.taskforcehq.ai'));
1438
- });
1439
- });