@taskforcehq/taskforce 0.3.169 → 0.3.300
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.
- package/README.md +52 -21
- package/dist/Taskforce.module.css +3394 -5981
- package/dist/TaskforceCore.CategoryFallback.test.js +5 -2
- package/dist/TaskforceCore.filter-persistence.test.js +402 -0
- package/dist/TaskforceCore.js +388 -71
- package/dist/TaskforceCore.routing.test.js +311 -0
- package/dist/TaskforceCore.test.js +3344 -615
- package/dist/TaskforceWidget.js +3 -2
- package/dist/__tests__/localizationCatalogs.test.js +48 -31
- package/dist/__tests__/workflowSimulation.test.js +2 -2
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.d.ts +60 -0
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +324 -0
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +278 -0
- package/dist/annotatedAttachments/payload.d.ts +2 -0
- package/dist/annotatedAttachments/payload.js +54 -0
- package/dist/annotatedAttachments/payload.test.js +67 -0
- package/dist/annotatedAttachments/service.d.ts +49 -0
- package/dist/annotatedAttachments/service.js +121 -0
- package/dist/annotatedAttachments/types.d.ts +86 -0
- package/dist/cli.js +74 -25
- package/dist/compat/deploymentCompat.d.ts +12 -0
- package/dist/compat/deploymentCompat.js +43 -0
- package/dist/compat/workspaceSyncCompat.d.ts +6 -0
- package/dist/compat/workspaceSyncCompat.js +79 -0
- package/dist/compat/workspaceSyncCompat.test.js +87 -0
- package/dist/components/features/AgentsModule.d.ts +5 -0
- package/dist/components/features/AgentsModule.js +131 -0
- package/dist/components/features/AgentsModule.test.js +102 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +24 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +1942 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +2831 -0
- package/dist/components/features/DocumentGenerateModal.js +8 -16
- package/dist/components/features/DocumentIndex.d.ts +3 -4
- package/dist/components/features/DocumentIndex.js +22 -10
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +1000 -18
- package/dist/components/features/DocumentWorkspace.d.ts +40 -2
- package/dist/components/features/DocumentWorkspace.js +96 -16
- package/dist/components/features/DocumentWorkspace.test.js +1202 -0
- package/dist/components/features/InitiativesModule.d.ts +22 -0
- package/dist/components/features/InitiativesModule.js +33 -0
- package/dist/components/features/TaskSettings.d.ts +41 -32
- package/dist/components/features/TaskSettings.js +798 -313
- package/dist/components/features/TaskSettings.test.js +1221 -47
- package/dist/components/features/WorkflowsModule.d.ts +27 -0
- package/dist/components/features/WorkflowsModule.js +21 -0
- package/dist/components/features/documentWorkspaceFilters.d.ts +10 -0
- package/dist/components/features/documentWorkspaceFilters.js +11 -0
- package/dist/components/features/settings/CategoryManager.d.ts +2 -0
- package/dist/components/features/settings/CategoryManager.js +15 -8
- package/dist/components/features/settings/PriorityManager.d.ts +18 -0
- package/dist/components/features/settings/PriorityManager.js +51 -0
- package/dist/components/features/settings/TaxonomyEditor.d.ts +2 -1
- package/dist/components/features/settings/TaxonomyEditor.js +41 -18
- package/dist/components/features/settings/TaxonomyLibraryManager.d.ts +26 -0
- package/dist/components/features/settings/TaxonomyLibraryManager.js +301 -0
- package/dist/components/features/settings/TaxonomyLibraryManager.test.js +141 -0
- package/dist/components/features/settings/TaxonomyManager.d.ts +35 -12
- package/dist/components/features/settings/TaxonomyManager.js +59 -27
- package/dist/components/features/settings/TypeManager.d.ts +11 -0
- package/dist/components/features/settings/TypeManager.js +48 -11
- package/dist/components/features/settings/{WorkflowEditorPrototype.d.ts → WorkflowEditor.d.ts} +2 -2
- package/dist/components/features/settings/{WorkflowEditorPrototype.js → WorkflowEditor.js} +3 -3
- package/dist/components/task/TaskActionHeader.d.ts +12 -2
- package/dist/components/task/TaskActionHeader.js +64 -8
- package/dist/components/task/TaskActionHeader.test.js +73 -0
- package/dist/components/task/TaskCard.d.ts +14 -16
- package/dist/components/task/TaskCard.js +144 -174
- package/dist/components/task/TaskCard.test.js +416 -64
- package/dist/components/task/TaskContextUpload.d.ts +6 -5
- package/dist/components/task/TaskContextUpload.js +199 -64
- package/dist/components/task/TaskContextUpload.test.js +331 -14
- package/dist/components/task/TaskForm.d.ts +23 -29
- package/dist/components/task/TaskForm.js +348 -169
- package/dist/components/task/TaskForm.test.js +419 -23
- package/dist/components/task/TaskKanban.d.ts +20 -21
- package/dist/components/task/TaskKanban.js +176 -316
- package/dist/components/task/TaskKanban.planning-drop.test.js +93 -0
- package/dist/components/task/TaskList.d.ts +22 -11
- package/dist/components/task/TaskList.js +98 -12
- package/dist/components/task/TaskList.test.js +206 -7
- package/dist/components/task/TaskReferenceBadge.d.ts +12 -0
- package/dist/components/task/TaskReferenceBadge.js +5 -0
- package/dist/components/task/TaskStatusActions.d.ts +12 -0
- package/dist/components/task/TaskStatusActions.js +18 -0
- package/dist/components/task/TaskStatusActions.test.js +45 -0
- package/dist/components/ui/ImageReferenceBadge.d.ts +12 -0
- package/dist/components/ui/ImageReferenceBadge.js +5 -0
- package/dist/components/ui/LevelSelector.d.ts +2 -1
- package/dist/components/ui/LevelSelector.js +2 -2
- package/dist/components/ui/Markdown.d.ts +1 -2
- package/dist/components/ui/Markdown.js +2 -13
- package/dist/components/ui/Modal.d.ts +3 -2
- package/dist/components/ui/Modal.js +4 -3
- package/dist/components/ui/MultiSelectFilter.d.ts +3 -1
- package/dist/components/ui/MultiSelectFilter.js +2 -2
- package/dist/components/ui/ReferenceBadgeButton.d.ts +13 -0
- package/dist/components/ui/ReferenceBadgeButton.js +6 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +7 -1
- package/dist/components/ui/TaxonomyDropdown.js +58 -23
- package/dist/components/ui/TopNoticeLayer.d.ts +7 -0
- package/dist/components/ui/TopNoticeLayer.js +14 -0
- package/dist/components/ui/TopNoticeLayer.test.js +25 -0
- package/dist/components/views/AppShellHeader.d.ts +13 -0
- package/dist/components/views/AppShellHeader.js +19 -0
- package/dist/components/views/AppShellHeader.test.js +21 -0
- package/dist/components/views/PlanComparisonPage.d.ts +73 -0
- package/dist/components/views/PlanComparisonPage.js +196 -0
- package/dist/components/views/PlanComparisonPage.test.js +251 -0
- package/dist/components/views/PlansPage.d.ts +14 -0
- package/dist/components/views/PlansPage.js +355 -0
- package/dist/components/views/StandaloneLayout.d.ts +2 -2
- package/dist/components/views/StandaloneLayout.js +2212 -1194
- package/dist/components/views/WidgetView.d.ts +2 -2
- package/dist/components/views/WidgetView.js +149 -43
- package/dist/components/views/chrome/WorkspaceChromeSections.d.ts +57 -0
- package/dist/components/views/chrome/WorkspaceChromeSections.js +38 -0
- package/dist/components/views/chrome/WorkspaceChromeSections.test.js +54 -0
- package/dist/components/views/chrome/workspaceChrome.d.ts +31 -0
- package/dist/components/views/chrome/workspaceChrome.js +108 -0
- package/dist/components/views/chrome/workspaceChrome.test.js +52 -0
- package/dist/components/views/panels/DocumentFilterToolbar.d.ts +10 -0
- package/dist/components/views/panels/DocumentFilterToolbar.js +7 -0
- package/dist/components/views/panels/DocumentFilterToolbar.test.js +19 -0
- package/dist/components/views/panels/FilterToolbar.d.ts +66 -0
- package/dist/components/views/panels/FilterToolbar.js +21 -0
- package/dist/components/views/panels/FilterToolbar.test.d.ts +1 -0
- package/dist/components/views/panels/FilterToolbar.test.js +45 -0
- package/dist/components/views/panels/FilterToolbarControls.d.ts +46 -0
- package/dist/components/views/panels/FilterToolbarControls.js +61 -0
- package/dist/components/views/panels/PlanningDrawer.d.ts +115 -0
- package/dist/components/views/panels/PlanningDrawer.js +220 -0
- package/dist/components/views/panels/ScheduleSidebar.d.ts +52 -0
- package/dist/components/views/panels/ScheduleSidebar.js +127 -0
- package/dist/components/views/panels/scheduleUtils.d.ts +14 -0
- package/dist/components/views/panels/scheduleUtils.js +79 -0
- package/dist/components/views/teamManagementAccess.d.ts +6 -0
- package/dist/components/views/teamManagementAccess.js +18 -0
- package/dist/components/views/teamManagementAccess.test.d.ts +1 -0
- package/dist/components/views/teamManagementAccess.test.js +44 -0
- package/dist/config/clientDeployment.d.ts +2 -0
- package/dist/config/clientDeployment.js +13 -0
- package/dist/config/cloudEnvironment.d.ts +15 -0
- package/dist/config/cloudEnvironment.js +60 -0
- package/dist/config/deployment.d.ts +20 -0
- package/dist/config/deployment.js +67 -0
- package/dist/config/deployment.test.d.ts +1 -0
- package/dist/config/deployment.test.js +88 -0
- package/dist/config/envSchema.js +36 -8
- package/dist/config/envSchema.test.js +28 -0
- package/dist/core/AiProfileService.d.ts +66 -0
- package/dist/core/AiProfileService.js +320 -0
- package/dist/core/AiProfileService.test.d.ts +1 -0
- package/dist/core/AiProfileService.test.js +164 -0
- package/dist/core/AiProfiles.test.d.ts +1 -0
- package/dist/core/AiProfiles.test.js +449 -0
- package/dist/core/AttachmentLinkService.d.ts +15 -0
- package/dist/core/AttachmentLinkService.js +157 -0
- package/dist/core/AuthTokenService.d.ts +44 -0
- package/dist/core/AuthTokenService.js +207 -0
- package/dist/core/CreatedBy.test.js +134 -6
- package/dist/core/DeletedTaskLifecycleService.d.ts +33 -0
- package/dist/core/DeletedTaskLifecycleService.js +158 -0
- package/dist/core/DeletedTaskTrash.test.d.ts +1 -0
- package/dist/core/DeletedTaskTrash.test.js +104 -0
- package/dist/core/EntitlementsPolicy.test.js +78 -5
- package/dist/core/GlobalSettingsService.d.ts +16 -0
- package/dist/core/GlobalSettingsService.js +370 -0
- package/dist/core/JsonBackupRuntime.test.d.ts +1 -0
- package/dist/core/JsonBackupRuntime.test.js +155 -0
- package/dist/core/McpTokenService.d.ts +118 -0
- package/dist/core/McpTokenService.js +921 -0
- package/dist/core/PlanEntitlementService.d.ts +201 -0
- package/dist/core/PlanEntitlementService.js +1442 -0
- package/dist/core/PlanVersionPolicy.test.d.ts +1 -0
- package/dist/core/PlanVersionPolicy.test.js +178 -0
- package/dist/core/PlanningEntities.test.d.ts +1 -0
- package/dist/core/PlanningEntities.test.js +95 -0
- package/dist/core/ScheduleFields.test.js +15 -19
- package/dist/core/SignupMonetizationSettings.test.d.ts +1 -0
- package/dist/core/SignupMonetizationSettings.test.js +159 -0
- package/dist/core/Stats.test.d.ts +1 -0
- package/dist/core/Stats.test.js +36 -0
- package/dist/core/SyncReconciliationService.d.ts +93 -0
- package/dist/core/SyncReconciliationService.js +406 -0
- package/dist/core/SystemAdmin.test.js +578 -13
- package/dist/core/TaskActivityService.d.ts +21 -0
- package/dist/core/TaskActivityService.js +130 -0
- package/dist/core/TaskAttachmentsCanonical.test.d.ts +1 -0
- package/dist/core/TaskAttachmentsCanonical.test.js +306 -0
- package/dist/core/TaskAuditTimeline.test.d.ts +1 -0
- package/dist/core/TaskAuditTimeline.test.js +132 -0
- package/dist/core/TaskTaxonomyValidation.test.js +338 -15
- package/dist/core/Taskforce.d.ts +930 -183
- package/dist/core/Taskforce.ids.test.js +252 -0
- package/dist/core/Taskforce.js +3848 -2532
- package/dist/core/Taskforce.mcpAuth.test.d.ts +1 -0
- package/dist/core/Taskforce.mcpAuth.test.js +343 -0
- package/dist/core/Taskforce.realtimeMutation.test.js +57 -2
- package/dist/core/TaxonomySettingsService.d.ts +18 -0
- package/dist/core/TaxonomySettingsService.js +162 -0
- package/dist/core/WorkspaceDelete.test.js +32 -13
- package/dist/core/WorkspaceLifecycleService.d.ts +11 -0
- package/dist/core/WorkspaceLifecycleService.js +88 -0
- package/dist/core/WorkspacePermissions.test.js +95 -0
- package/dist/core/WorkspacePlanMode.test.js +114 -1
- package/dist/core/WorkspacePreferenceService.d.ts +41 -0
- package/dist/core/WorkspacePreferenceService.js +246 -0
- package/dist/core/WorkspaceSyncSettings.test.d.ts +1 -0
- package/dist/core/WorkspaceSyncSettings.test.js +65 -0
- package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +1 -0
- package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +212 -0
- package/dist/core/shared.d.ts +36 -0
- package/dist/core/shared.js +128 -0
- package/dist/core/types.d.ts +614 -0
- package/dist/core/types.js +27 -0
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +56 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +230 -0
- package/dist/documentReviews/DocumentReviewSessionStore.d.ts +52 -0
- package/dist/documentReviews/DocumentReviewSessionStore.js +193 -0
- package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +1 -0
- package/dist/documentReviews/DocumentReviewSessionStore.test.js +127 -0
- package/dist/documentReviews/service.d.ts +68 -0
- package/dist/documentReviews/service.js +210 -0
- package/dist/documentReviews/types.d.ts +58 -0
- package/dist/documentReviews/types.js +1 -0
- package/dist/hooks/auth/useAuthGuardPolicy.d.ts +2 -0
- package/dist/hooks/auth/useAuthGuardPolicy.js +1 -0
- package/dist/hooks/tasks/useCurrentTaskRelations.d.ts +15 -0
- package/dist/hooks/tasks/useCurrentTaskRelations.js +37 -0
- package/dist/hooks/tasks/useTaskActions.d.ts +3 -0
- package/dist/hooks/tasks/useTaskActions.js +2 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +65 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.js +235 -0
- package/dist/hooks/tasks/useTaskFilters.d.ts +2 -0
- package/dist/hooks/tasks/useTaskFilters.js +2 -0
- package/dist/hooks/tasks/useTaskFormActions.d.ts +58 -0
- package/dist/hooks/tasks/useTaskFormActions.js +365 -0
- package/dist/hooks/tasks/useTaskRelationships.d.ts +16 -0
- package/dist/hooks/tasks/useTaskRelationships.js +64 -0
- package/dist/hooks/ui/useResourceExport.d.ts +19 -0
- package/dist/hooks/ui/useResourceExport.js +84 -0
- package/dist/hooks/ui/useSettingsModel.d.ts +203 -0
- package/dist/hooks/ui/useSettingsModel.js +125 -0
- package/dist/hooks/ui/useSettingsPersistence.d.ts +36 -0
- package/dist/hooks/ui/useSettingsPersistence.js +254 -0
- package/dist/hooks/ui/useSettingsTaxonomyManagement.d.ts +119 -0
- package/dist/hooks/ui/useSettingsTaxonomyManagement.js +536 -0
- package/dist/hooks/ui/useUiNotice.d.ts +1 -0
- package/dist/hooks/ui/useUiNotice.js +1 -0
- package/dist/hooks/ui/useWorkflowTemplates.d.ts +37 -0
- package/dist/hooks/ui/useWorkflowTemplates.js +111 -0
- package/dist/hooks/ui/useZenMode.d.ts +5 -0
- package/dist/hooks/ui/useZenMode.js +32 -0
- package/dist/hooks/useFilterSort.d.ts +60 -0
- package/dist/hooks/useFilterSort.js +362 -0
- package/dist/hooks/useFilterSort.test.d.ts +1 -0
- package/dist/hooks/useFilterSort.test.js +333 -0
- package/dist/hooks/useRealtimeSync.d.ts +1 -0
- package/dist/hooks/useRealtimeSync.js +24 -9
- package/dist/hooks/useRealtimeSync.test.js +28 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +34 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +65 -103
- package/dist/hooks/useSyncOrchestrator.js +1538 -670
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +700 -174
- package/dist/hooks/useTaskData.d.ts +35 -0
- package/dist/hooks/useTaskData.js +239 -0
- package/dist/hooks/useTaskData.test.d.ts +1 -0
- package/dist/hooks/useTaskData.test.js +119 -0
- package/dist/hooks/useTaskMutations.d.ts +46 -0
- package/dist/hooks/useTaskMutations.js +472 -0
- package/dist/hooks/useTaskMutations.test.d.ts +1 -0
- package/dist/hooks/useTaskMutations.test.js +80 -0
- package/dist/hooks/useTaskforce.contract.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.contract.test.js +7 -0
- package/dist/hooks/useTaskforce.d.ts +260 -295
- package/dist/hooks/useTaskforce.js +1954 -2623
- package/dist/hooks/useTaskforce.priority-filters.test.js +6 -11
- package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +1120 -0
- package/dist/hooks/useUiNotice.d.ts +16 -0
- package/dist/hooks/useUiNotice.js +49 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +23 -46
- package/dist/hooks/useWorkspaceSyncController.js +280 -461
- package/dist/hooks/useWorkspaceSyncController.test.js +106 -199
- package/dist/hooks/workspace/useTaskforceApiRouting.d.ts +8 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.js +91 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +60 -0
- package/dist/hooks/workspace/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/workspace/useWorkspaceSyncController.js +1 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +30 -0
- package/dist/hooks/workspace/workspaceLocalState.js +101 -0
- package/dist/localization/locales/en-US.d.ts +1 -8
- package/dist/localization/locales/en-US.js +4 -11
- package/dist/localization/locales/es-419.js +2 -9
- package/dist/localization/locales/pt-BR.js +2 -9
- package/dist/localization/localizationCatalogs.test.d.ts +1 -0
- package/dist/localization/localizationCatalogs.test.js +37 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +9 -15
- package/dist/mcp/clientIntegrations.d.ts +33 -0
- package/dist/mcp/clientIntegrations.js +199 -0
- package/dist/mcp/clientIntegrations.test.d.ts +1 -0
- package/dist/mcp/clientIntegrations.test.js +110 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/optionResolution.test.js +6 -2
- package/dist/mcp/runtime.d.ts +120 -0
- package/dist/mcp/runtime.js +5668 -0
- package/dist/mcp/runtime.test.d.ts +1 -0
- package/dist/mcp/runtime.test.js +3293 -0
- package/dist/mcp/server.js +2 -1793
- package/dist/mcp/structuredCommentValidation.test.d.ts +1 -0
- package/dist/{__tests__ → mcp}/structuredCommentValidation.test.js +1 -1
- package/dist/mcp/structuredDocValidation.js +1 -2
- package/dist/mcp/structuredDocValidation.test.d.ts +1 -0
- package/dist/{__tests__ → mcp}/structuredDocValidation.test.js +9 -4
- package/dist/mcp/toolRegistry.d.ts +570 -0
- package/dist/mcp/toolRegistry.js +90 -0
- package/dist/mcp/toolRegistry.test.d.ts +1 -0
- package/dist/mcp/toolRegistry.test.js +30 -0
- package/dist/migrations/billingSchemaParity.test.js +111 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
- package/dist/migrations/taskSchemaMigrations.js +681 -39
- package/dist/migrations/taskSchemaMigrations.test.d.ts +1 -0
- package/dist/migrations/taskSchemaMigrations.test.js +210 -0
- package/dist/plugins/vite.js +13 -7
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/archive/phoenix/apple-touch-icon.png +0 -0
- package/dist/public/archive/phoenix/favicon.ico +0 -0
- package/dist/public/archive/phoenix/site.webmanifest +1 -0
- package/dist/public/favicon-96x96.png +0 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/favicon.svg +17 -0
- package/dist/public/site.webmanifest +21 -1
- package/dist/public/web-app-manifest-192x192.png +0 -0
- package/dist/public/web-app-manifest-512x512.png +0 -0
- package/dist/releaseVersion.test.d.ts +1 -0
- package/dist/{__tests__/releaseVersion.test.js → releaseVersion.test.js} +1 -1
- package/dist/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
- package/dist/resources/templates/workflows/collaborate.yaml +11 -5
- package/dist/resources/templates/workflows/do.yaml +8 -2
- package/dist/resources/templates/workflows/evaluate.yaml +13 -7
- package/dist/resources/templates/workflows/execute.yaml +40 -23
- package/dist/resources/templates/workflows/plan.yaml +15 -8
- package/dist/resources/templates/workflows/review.yaml +14 -8
- package/dist/runtime/appGateDefinitions.d.ts +32 -0
- package/dist/runtime/appGateDefinitions.js +32 -0
- package/dist/runtime/appGates.d.ts +29 -0
- package/dist/runtime/appGates.js +53 -0
- package/dist/runtime/appGates.test.d.ts +1 -0
- package/dist/runtime/appGates.test.js +42 -0
- package/dist/runtime/migrationFlags.d.ts +1 -0
- package/dist/runtime/migrationFlags.js +1 -0
- package/dist/server/annotatedAttachmentsRoutes.test.d.ts +1 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +473 -0
- package/dist/server/auth.js +14 -2
- package/dist/server/auth.test.js +21 -1
- package/dist/server/cors.js +11 -5
- package/dist/server/cors.test.js +6 -0
- package/dist/server/documentReviewRoutes.test.d.ts +1 -0
- package/dist/server/documentReviewRoutes.test.js +157 -0
- package/dist/server/index.cookieProxy.test.js +46 -1
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +168 -41
- package/dist/server/index.rateLimit.test.js +6 -0
- package/dist/server/index.test.js +133 -2
- package/dist/server/mockStripe.d.ts +72 -0
- package/dist/server/mockStripe.js +357 -0
- package/dist/server/realtimeSyncWs.js +4 -0
- package/dist/server/routes/admin.d.ts +114 -0
- package/dist/server/routes/admin.js +2226 -0
- package/dist/server/routes/annotatedAttachments.d.ts +24 -0
- package/dist/server/routes/annotatedAttachments.js +235 -0
- package/dist/server/routes/auth.d.ts +61 -0
- package/dist/server/routes/auth.js +1296 -0
- package/dist/server/{routes.billing.d.ts → routes/billing.d.ts} +3 -2
- package/dist/server/routes/billing.js +2001 -0
- package/dist/server/routes/billing.test.d.ts +1 -0
- package/dist/server/routes/billing.test.js +1631 -0
- package/dist/server/routes/documentReviews.d.ts +11 -0
- package/dist/server/routes/documentReviews.js +191 -0
- package/dist/server/routes/documents.d.ts +118 -0
- package/dist/server/routes/documents.js +1113 -0
- package/dist/server/routes/resources.d.ts +25 -0
- package/dist/server/routes/resources.js +225 -0
- package/dist/server/routes/shared.d.ts +35 -0
- package/dist/server/routes/shared.js +305 -0
- package/dist/server/routes/shared.test.d.ts +1 -0
- package/dist/server/routes/shared.test.js +32 -0
- package/dist/server/{routes.sync.d.ts → routes/sync.d.ts} +5 -5
- package/dist/server/routes/sync.integration.test.d.ts +1 -0
- package/dist/server/routes/sync.integration.test.js +2003 -0
- package/dist/server/routes/sync.js +3143 -0
- package/dist/server/routes/tasks.d.ts +72 -0
- package/dist/server/routes/tasks.js +1252 -0
- package/dist/server/routes/workspaces.d.ts +32 -0
- package/dist/server/routes/workspaces.js +307 -0
- package/dist/server/routes.d.ts +8 -9
- package/dist/server/routes.js +968 -4092
- package/dist/server/routes.test.js +9043 -2415
- package/dist/server/runtimeMode.d.ts +1 -1
- package/dist/server/runtimeMode.js +3 -18
- package/dist/server/runtimeMode.test.js +3 -3
- package/dist/server/securityHeaders.d.ts +1 -1
- package/dist/server/securityHeaders.js +65 -6
- package/dist/server/securityHeaders.test.js +40 -2
- package/dist/services/templateEngine.test.d.ts +1 -0
- package/dist/{__tests__ → services}/templateEngine.test.js +1 -1
- package/dist/services/templates.test.d.ts +1 -0
- package/dist/{__tests__ → services}/templates.test.js +1 -1
- package/dist/services/workflowDocumentFixtures.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowDocumentFixtures.test.js +4 -4
- package/dist/services/workflowExportMatrix.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowExportMatrix.test.js +1 -1
- package/dist/services/workflowExportSnapshots.test.d.ts +1 -0
- package/dist/services/workflowGeneration.test.d.ts +1 -0
- package/dist/services/workflowSimulation.test.d.ts +1 -0
- package/dist/services/workflowSimulation.test.js +113 -0
- package/dist/services/workflowV2Policy.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowV2Policy.test.js +2 -2
- package/dist/shared/aiProfileSurfaceType.d.ts +9 -0
- package/dist/shared/aiProfileSurfaceType.js +49 -0
- package/dist/shared/customTaxonomyLibrary.d.ts +11 -0
- package/dist/shared/customTaxonomyLibrary.js +276 -0
- package/dist/shared/customTaxonomyLibrary.test.d.ts +1 -0
- package/dist/shared/customTaxonomyLibrary.test.js +20 -0
- package/dist/shared/taxonomyCatalog.d.ts +19 -0
- package/dist/shared/taxonomyCatalog.js +36 -0
- package/dist/shared/taxonomyDefaults.d.ts +29 -0
- package/dist/shared/taxonomyDefaults.js +26 -0
- package/dist/shared/taxonomyLibrary.d.ts +15 -0
- package/dist/shared/taxonomyLibrary.js +98 -0
- package/dist/shared/taxonomyLibrary.test.d.ts +1 -0
- package/dist/shared/taxonomyLibrary.test.js +26 -0
- package/dist/shared/taxonomyState.d.ts +36 -0
- package/dist/shared/taxonomyState.js +73 -0
- package/dist/shared/taxonomyState.test.d.ts +1 -0
- package/dist/shared/taxonomyState.test.js +71 -0
- package/dist/shared/workflowDefaults.d.ts +9 -0
- package/dist/shared/workflowDefaults.js +11 -0
- package/dist/storage/documentIntegrity.d.ts +2 -0
- package/dist/storage/documentIntegrity.js +34 -17
- package/dist/storage/documentPurge.d.ts +2 -1
- package/dist/storage/documentPurge.js +35 -8
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/objectStorage.d.ts +1 -1
- package/dist/storage/objectStorageClient.d.ts +6 -4
- package/dist/storage/objectStorageClient.js +77 -29
- package/dist/storage/objectStorageClient.test.d.ts +1 -0
- package/dist/storage/objectStorageClient.test.js +57 -0
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.d.ts +1 -0
- package/dist/storage/objectStorageConfig.test.js +38 -0
- package/dist/storage/postgresAdapter.d.ts +5 -0
- package/dist/storage/postgresAdapter.js +2 -2
- package/dist/storage/postgresAdapter.test.d.ts +1 -0
- package/dist/storage/postgresAdapter.test.js +29 -0
- package/dist/storage/taskAssetStore.d.ts +7 -4
- package/dist/storage/taskAssetStore.js +65 -60
- package/dist/storage/workspaceAssetStore.d.ts +122 -0
- package/dist/storage/workspaceAssetStore.js +496 -0
- package/dist/storage/workspaceAssetStore.test.d.ts +1 -0
- package/dist/storage/workspaceAssetStore.test.js +501 -0
- package/dist/sync/cloudSyncApi.d.ts +33 -2
- package/dist/sync/cloudSyncApi.js +37 -15
- package/dist/sync/cloudSyncApi.test.js +60 -3
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +3 -0
- package/dist/sync/syncService.d.ts +25 -0
- package/dist/sync/syncService.js +184 -16
- package/dist/sync/syncService.test.js +289 -25
- package/dist/sync/workspaceRepair.d.ts +114 -0
- package/dist/sync/workspaceRepair.js +566 -0
- package/dist/sync/workspaceSyncModel.d.ts +230 -2
- package/dist/sync/workspaceSyncModel.js +456 -17
- package/dist/sync/workspaceSyncModel.test.js +642 -0
- package/dist/sync/workspaceSyncState.d.ts +12 -0
- package/dist/sync/workspaceSyncState.js +12 -0
- package/dist/sync/workspaceSyncSurface.d.ts +33 -0
- package/dist/sync/workspaceSyncSurface.js +74 -0
- package/dist/types.d.ts +143 -18
- package/dist/types.js +9 -24
- package/dist/ui/assets/AgentsModule-TVmCgizS.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CAyhnPeR.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-BOCw5e4D.js +250 -0
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +1 -0
- package/dist/ui/assets/InitiativesModule-CN0r02Oi.js +1 -0
- package/dist/ui/assets/PlansPage-BlVC_lRq.css +1 -0
- package/dist/ui/assets/PlansPage-FcQSNLZd.js +1 -0
- package/dist/ui/assets/TaskSettings-BwB9NtmJ.css +1 -0
- package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +8 -0
- package/dist/ui/assets/WorkflowsModule-CY9R2zPB.js +5 -0
- package/dist/ui/assets/index-Bzqy5QGm.js +6 -0
- package/dist/ui/assets/index-DxRiI3tY.css +1 -0
- package/dist/ui/assets/vendor-dnd-DRzYolkg.js +5 -0
- package/dist/ui/assets/{vendor-icons-ZFG3SGwo.js → vendor-icons-QZyhEwgT.js} +1 -1
- package/dist/ui/assets/{vendor-markdown-CKYkQHfj.js → vendor-markdown-BUxTU7dS.js} +1 -1
- package/dist/ui/assets/vendor-react-CKJs5o3c.js +61 -0
- package/dist/ui/assets/vendor-router-BbWMxlnO.js +3 -0
- package/dist/ui/branding/logos/logo.png +0 -0
- package/dist/ui/branding/logos/logo_dark.svg +27 -0
- package/dist/ui/branding/logos/logo_favicon.svg +35 -0
- package/dist/ui/branding/logos/logo_light.svg +27 -0
- package/dist/ui/favicon/apple-touch-icon.png +0 -0
- package/dist/ui/favicon/archive/phoenix/apple-touch-icon.png +0 -0
- package/dist/ui/favicon/archive/phoenix/favicon.ico +0 -0
- package/dist/ui/favicon/archive/phoenix/site.webmanifest +1 -0
- package/dist/ui/favicon/favicon-96x96.png +0 -0
- package/dist/ui/favicon/favicon.ico +0 -0
- package/dist/ui/favicon/favicon.svg +17 -0
- package/dist/ui/favicon/site.webmanifest +21 -1
- package/dist/ui/favicon/web-app-manifest-192x192.png +0 -0
- package/dist/ui/favicon/web-app-manifest-512x512.png +0 -0
- package/dist/ui/index.html +10 -7
- package/dist/ui/product/screen_01.png +0 -0
- package/dist/ui/product/screen_02.png +0 -0
- package/dist/ui/product/screen_03.png +0 -0
- package/dist/utils/annotatedAttachments.d.ts +20 -0
- package/dist/utils/annotatedAttachments.js +74 -0
- package/dist/utils/annotatedAttachments.test.d.ts +1 -0
- package/dist/utils/annotatedAttachments.test.js +108 -0
- package/dist/utils/assignees.d.ts +26 -0
- package/dist/utils/assignees.js +132 -0
- package/dist/utils/canonicalAssetKind.d.ts +16 -0
- package/dist/utils/canonicalAssetKind.js +39 -0
- package/dist/utils/canonicalAssetKind.test.d.ts +1 -0
- package/dist/utils/canonicalAssetKind.test.js +24 -0
- package/dist/utils/coalescedRefresh.d.ts +5 -0
- package/dist/utils/coalescedRefresh.js +16 -0
- package/dist/utils/coalescedRefresh.test.d.ts +1 -0
- package/dist/utils/coalescedRefresh.test.js +30 -0
- package/dist/utils/constants.d.ts +16 -3
- package/dist/utils/constants.icons.test.js +2 -1
- package/dist/utils/constants.js +70 -13
- package/dist/utils/contextAssetEvents.d.ts +7 -0
- package/dist/utils/contextAssetEvents.js +6 -0
- package/dist/utils/contextFiles.d.ts +14 -3
- package/dist/utils/contextFiles.js +40 -8
- package/dist/utils/documentManagerApi.d.ts +6 -0
- package/dist/utils/documentManagerApi.js +19 -0
- package/dist/utils/documentNames.d.ts +24 -0
- package/dist/utils/documentNames.js +91 -0
- package/dist/utils/documentReferences.d.ts +9 -0
- package/dist/utils/documentReferences.js +11 -0
- package/dist/utils/entityReferences.d.ts +9 -0
- package/dist/utils/entityReferences.js +32 -0
- package/dist/utils/imageReferences.d.ts +9 -0
- package/dist/utils/imageReferences.js +11 -0
- package/dist/utils/planParser.d.ts +2 -2
- package/dist/utils/planParser.js +11 -11
- package/dist/utils/planParser.test.d.ts +1 -0
- package/dist/{__tests__ → utils}/planParser.test.js +22 -22
- package/dist/utils/planningReferences.d.ts +17 -0
- package/dist/utils/planningReferences.js +30 -0
- package/dist/utils/planningReferences.test.d.ts +1 -0
- package/dist/utils/planningReferences.test.js +17 -0
- package/dist/utils/taskActivity.d.ts +9 -0
- package/dist/utils/taskActivity.js +147 -0
- package/dist/utils/taskActivity.test.d.ts +1 -0
- package/dist/utils/taskActivity.test.js +34 -0
- package/dist/utils/taskDraftDefaults.d.ts +21 -0
- package/dist/utils/taskDraftDefaults.js +40 -0
- package/dist/utils/taskDraftDefaults.test.d.ts +1 -0
- package/dist/utils/taskDraftDefaults.test.js +34 -0
- package/dist/utils/taskNormalization.d.ts +25 -0
- package/dist/utils/taskNormalization.js +161 -0
- package/dist/utils/taskNormalization.test.d.ts +1 -0
- package/dist/utils/taskNormalization.test.js +158 -0
- package/dist/utils/taskReferences.d.ts +5 -0
- package/dist/utils/taskReferences.js +11 -0
- package/dist/utils/taskforceApiClient.d.ts +2 -0
- package/dist/utils/taskforceApiClient.js +101 -3
- package/dist/utils/taskforceApiClient.test.d.ts +1 -0
- package/dist/utils/taskforceApiClient.test.js +44 -0
- package/dist/utils/taxonomyFilterOptions.d.ts +2 -0
- package/dist/utils/taxonomyFilterOptions.js +17 -0
- package/dist/utils/taxonomyFormDefaults.d.ts +3 -0
- package/dist/utils/taxonomyFormDefaults.js +28 -0
- package/dist/utils/taxonomyFormDefaults.test.d.ts +1 -0
- package/dist/utils/taxonomyFormDefaults.test.js +63 -0
- package/dist/utils/taxonomyLifecycle.d.ts +3 -0
- package/dist/utils/taxonomyLifecycle.js +17 -0
- package/dist/utils/taxonomySorting.d.ts +7 -0
- package/dist/utils/taxonomySorting.js +74 -0
- package/dist/utils/theme.d.ts +15 -0
- package/dist/utils/theme.js +23 -0
- package/dist/utils/uiNotice.d.ts +7 -0
- package/dist/utils/uiNotice.js +6 -0
- package/dist/utils/uiNotice.test.d.ts +1 -0
- package/dist/utils/uiNotice.test.js +18 -0
- package/dist/utils/workspaceIdentity.d.ts +14 -0
- package/dist/utils/workspaceIdentity.js +21 -0
- package/dist/utils/workspaceIdentity.test.js +30 -1
- package/dist/utils/workspaceSyncPresentation.d.ts +20 -0
- package/dist/utils/workspaceSyncPresentation.js +21 -0
- package/dist/utils/workspaceSyncPresentation.test.d.ts +1 -0
- package/dist/utils/workspaceSyncPresentation.test.js +36 -0
- package/package.json +28 -23
- package/scripts/bootstrap-playwright-auth.mjs +155 -0
- package/scripts/check-compat-boundaries.mjs +41 -0
- package/scripts/check-env.ts +9 -2
- package/scripts/debug-login.mjs +32 -0
- package/scripts/migrate-planning-model.ts +233 -0
- package/scripts/migrate-task-data-assets.mjs +408 -0
- package/scripts/playwright-auth.sh +11 -0
- package/scripts/run-dev-proxy.sh +2 -0
- package/scripts/update-homebrew-formula.sh +20 -3
- package/scripts/with-esbuild.sh +11 -0
- package/src/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
- package/src/resources/templates/workflows/collaborate.yaml +11 -5
- package/src/resources/templates/workflows/do.yaml +8 -2
- package/src/resources/templates/workflows/evaluate.yaml +13 -7
- package/src/resources/templates/workflows/execute.yaml +40 -23
- package/src/resources/templates/workflows/plan.yaml +15 -8
- package/src/resources/templates/workflows/review.yaml +14 -8
- package/dist/TaskforceScreenshot.d.ts +0 -11
- package/dist/TaskforceScreenshot.js +0 -113
- package/dist/compat/priorityCompat.d.ts +0 -2
- package/dist/compat/priorityCompat.js +0 -24
- package/dist/components/features/settings/TaxonomyManager 2.d.ts +0 -52
- package/dist/components/features/settings/TaxonomyManager 2.js +0 -75
- package/dist/components/task/TaskKanban 2.d.ts +0 -76
- package/dist/components/task/TaskKanban 2.js +0 -1004
- package/dist/components/task/TaskScreenshotSection.d.ts +0 -15
- package/dist/components/task/TaskScreenshotSection.js +0 -22
- package/dist/components/views/StandaloneLayout 2.d.ts +0 -8
- package/dist/components/views/StandaloneLayout 2.js +0 -1660
- package/dist/core/InitiativeTemplates.test.js +0 -50
- package/dist/core/ParentLifecycle.test.js +0 -98
- package/dist/migrations/clientMigrations.d.ts +0 -9
- package/dist/migrations/clientMigrations.js +0 -74
- package/dist/migrations/flags.d.ts +0 -6
- package/dist/migrations/flags.js +0 -8
- package/dist/migrations/taskMigrations.d.ts +0 -71
- package/dist/migrations/taskMigrations.js +0 -236
- package/dist/migrations/taskMigrations.test.js +0 -34
- package/dist/server/routes.billing.js +0 -991
- package/dist/server/routes.billing.test.js +0 -387
- package/dist/server/routes.sync.integration.test.js +0 -470
- package/dist/server/routes.sync.js +0 -1141
- package/dist/soak/syncSoakHarness.d.ts +0 -6
- package/dist/soak/syncSoakHarness.js +0 -10
- package/dist/ui/assets/index-0W5vyCpn.css +0 -1
- package/dist/ui/assets/index-MCZuNNfn.js +0 -23
- package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
- package/dist/ui/assets/vendor-dnd-BI1K77C1.js +0 -5
- package/dist/ui/assets/vendor-react-DdIDsh7f.js +0 -9
- package/dist/ui/assets/vendor-router-BtiNU7Dl.js +0 -3
- package/dist/utils/taskHierarchy.d.ts +0 -4
- package/dist/utils/taskHierarchy.js +0 -69
- package/dist/utils/taskHierarchy.test.js +0 -38
- package/dist/utils/taskParenting.d.ts +0 -3
- package/dist/utils/taskParenting.js +0 -24
- package/dist/utils/taskParenting.test.js +0 -21
- package/dist/utils/taskRelations.d.ts +0 -6
- package/dist/utils/taskRelations.js +0 -24
- package/dist/utils/taskRelations.test.js +0 -34
- package/scripts/check-compat-boundaries.js +0 -48
- /package/dist/{__tests__/planParser.test.d.ts → TaskforceCore.filter-persistence.test.d.ts} +0 -0
- /package/dist/{__tests__/releaseVersion.test.d.ts → TaskforceCore.routing.test.d.ts} +0 -0
- /package/dist/{__tests__/structuredCommentValidation.test.d.ts → annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts} +0 -0
- /package/dist/{__tests__/structuredDocValidation.test.d.ts → annotatedAttachments/payload.test.d.ts} +0 -0
- /package/dist/{__tests__/templateEngine.test.d.ts → annotatedAttachments/types.js} +0 -0
- /package/dist/{__tests__/templates.test.d.ts → compat/workspaceSyncCompat.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowDocumentFixtures.test.d.ts → components/features/AgentsModule.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowExportMatrix.test.d.ts → components/features/AnnotatedAttachmentWorkspace.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowExportSnapshots.test.d.ts → components/features/DocumentWorkspace.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowGeneration.test.d.ts → components/features/settings/TaxonomyLibraryManager.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowV2Policy.test.d.ts → components/task/TaskActionHeader.test.d.ts} +0 -0
- /package/dist/{core/InitiativeTemplates.test.d.ts → components/task/TaskKanban.planning-drop.test.d.ts} +0 -0
- /package/dist/{core/ParentLifecycle.test.d.ts → components/task/TaskStatusActions.test.d.ts} +0 -0
- /package/dist/{migrations/taskMigrations.test.d.ts → components/ui/TopNoticeLayer.test.d.ts} +0 -0
- /package/dist/{server/routes.billing.test.d.ts → components/views/AppShellHeader.test.d.ts} +0 -0
- /package/dist/{server/routes.sync.integration.test.d.ts → components/views/PlanComparisonPage.test.d.ts} +0 -0
- /package/dist/{utils/taskHierarchy.test.d.ts → components/views/chrome/WorkspaceChromeSections.test.d.ts} +0 -0
- /package/dist/{utils/taskParenting.test.d.ts → components/views/chrome/workspaceChrome.test.d.ts} +0 -0
- /package/dist/{utils/taskRelations.test.d.ts → components/views/panels/DocumentFilterToolbar.test.d.ts} +0 -0
- /package/dist/public/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
- /package/dist/public/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
- /package/dist/public/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
- /package/dist/public/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
- /package/dist/public/{favicon.png → archive/phoenix/favicon.png} +0 -0
- /package/dist/{__tests__ → services}/workflowExportSnapshots.test.js +0 -0
- /package/dist/{__tests__ → services}/workflowGeneration.test.js +0 -0
- /package/dist/ui/favicon/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
- /package/dist/ui/favicon/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
- /package/dist/ui/favicon/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
- /package/dist/ui/favicon/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
- /package/dist/ui/favicon/{favicon.png → archive/phoenix/favicon.png} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { describe, it, expect, vi } from 'vitest';
|
|
5
5
|
import { TaskSettings } from './TaskSettings';
|
|
6
|
+
import { TASKFORCE_THEME_OPTIONS } from '../../utils/theme';
|
|
6
7
|
describe('TaskSettings Component', () => {
|
|
7
8
|
// Default mock props
|
|
8
9
|
const defaultSettingsModel = {
|
|
@@ -13,13 +14,17 @@ describe('TaskSettings Component', () => {
|
|
|
13
14
|
jsonBackupEnabled: false,
|
|
14
15
|
globalJsonBackupEnabled: false,
|
|
15
16
|
jsonBackupUseGlobalDefault: false,
|
|
17
|
+
showTaskCardStatusLabel: true,
|
|
16
18
|
exportWorkflowsPath: '.agent/workflows',
|
|
17
|
-
exportSpecialistsPath: '.agent/specialists',
|
|
18
19
|
exportingResource: null,
|
|
19
20
|
exportResult: null,
|
|
20
21
|
pathSaved: false,
|
|
21
22
|
initialSection: null,
|
|
22
23
|
exportEnvironment: 'antigravity',
|
|
24
|
+
fetchCloudAuthApi: vi.fn(async () => ({
|
|
25
|
+
ok: true,
|
|
26
|
+
json: async () => ({ items: [] })
|
|
27
|
+
})),
|
|
23
28
|
// Mock Callbacks
|
|
24
29
|
onThemeChange: vi.fn(),
|
|
25
30
|
onSaveTheme: vi.fn(),
|
|
@@ -28,11 +33,12 @@ describe('TaskSettings Component', () => {
|
|
|
28
33
|
onKeyShortcutChange: vi.fn(),
|
|
29
34
|
onJsonBackupEnabledChange: vi.fn(),
|
|
30
35
|
onSaveGlobalJsonBackupEnabled: vi.fn(),
|
|
36
|
+
onSaveGlobalWeekStartsOn: vi.fn(),
|
|
37
|
+
onChecklistDropdownEnabledChange: vi.fn(),
|
|
38
|
+
onShowTaskCardStatusLabelChange: vi.fn(async () => true),
|
|
31
39
|
onSaveSettings: vi.fn(),
|
|
32
40
|
onExportWorkflowsPathChange: vi.fn(),
|
|
33
|
-
onExportSpecialistsPathChange: vi.fn(),
|
|
34
41
|
onExportEnvironmentChange: vi.fn(),
|
|
35
|
-
onExportResources: vi.fn(),
|
|
36
42
|
onShowFolderBrowserChange: vi.fn(),
|
|
37
43
|
onBrowserTargetChange: vi.fn(),
|
|
38
44
|
onFetchFolders: vi.fn(),
|
|
@@ -53,11 +59,12 @@ describe('TaskSettings Component', () => {
|
|
|
53
59
|
onUpdateCategoryColor: vi.fn(),
|
|
54
60
|
// Type Manager Props
|
|
55
61
|
types: [
|
|
56
|
-
{ value: '
|
|
57
|
-
{ value: '
|
|
62
|
+
{ value: 'task', label: 'Task' },
|
|
63
|
+
{ value: 'issue', label: 'Issue' }
|
|
58
64
|
],
|
|
59
65
|
onSaveType: vi.fn(),
|
|
60
66
|
onRemoveType: vi.fn(),
|
|
67
|
+
onUpdateType: vi.fn(),
|
|
61
68
|
// Taxonomy Props
|
|
62
69
|
taxonomies: [],
|
|
63
70
|
onUpdateTaxonomies: vi.fn(),
|
|
@@ -65,9 +72,14 @@ describe('TaskSettings Component', () => {
|
|
|
65
72
|
{ value: 1, label: 'Low', color: 'teal-500', icon: 'ArrowDown' },
|
|
66
73
|
{ value: 2, label: 'Medium', color: 'blue-500', icon: 'Minus' },
|
|
67
74
|
{ value: 3, label: 'High', color: 'amber-500', icon: 'ArrowUp' },
|
|
68
|
-
{ value: 4, label: 'Critical', color: 'red-500', icon: 'AlertTriangle' },
|
|
69
75
|
],
|
|
70
76
|
onUpdatePriorities: vi.fn(),
|
|
77
|
+
onAnalyzeSystemTaxonomyPack: vi.fn(() => ({
|
|
78
|
+
unmatchedCategoryValues: [],
|
|
79
|
+
unmatchedTypeValues: [],
|
|
80
|
+
incompatiblePriorityValues: []
|
|
81
|
+
})),
|
|
82
|
+
onApplySystemTaxonomyPack: vi.fn(async () => ({ success: true })),
|
|
71
83
|
approaches: [
|
|
72
84
|
{ value: 'evaluate', label: 'Evaluate', color: 'amber-500', icon: 'Search' },
|
|
73
85
|
{ value: 'collaborate', label: 'Collaborate', color: 'blue-500', icon: 'Users' },
|
|
@@ -95,32 +107,89 @@ describe('TaskSettings Component', () => {
|
|
|
95
107
|
};
|
|
96
108
|
return render(_jsx(TaskSettings, { ...props }));
|
|
97
109
|
};
|
|
110
|
+
const originalFetch = global.fetch;
|
|
98
111
|
it('4.1 Open settings - Renders correctly', () => {
|
|
99
112
|
renderComponent();
|
|
100
|
-
expect(screen.getByText('
|
|
101
|
-
expect(screen.
|
|
113
|
+
expect(screen.getByText('Sections')).toBeInTheDocument();
|
|
114
|
+
expect(screen.getByRole('button', { name: /general/i })).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
it('4.1a Sidebar renders all primary settings sections', () => {
|
|
117
|
+
renderComponent();
|
|
118
|
+
expect(screen.getByRole('button', { name: /general/i })).toBeInTheDocument();
|
|
119
|
+
expect(screen.getByRole('button', { name: /appearance/i })).toBeInTheDocument();
|
|
120
|
+
expect(screen.getByRole('button', { name: /application/i })).toBeInTheDocument();
|
|
121
|
+
expect(screen.getByRole('button', { name: /task cards/i })).toBeInTheDocument();
|
|
122
|
+
expect(screen.getByRole('button', { name: /task forms/i })).toBeInTheDocument();
|
|
123
|
+
expect(screen.queryByRole('button', { name: /documents/i })).not.toBeInTheDocument();
|
|
124
|
+
expect(screen.queryByRole('button', { name: /commands/i })).not.toBeInTheDocument();
|
|
125
|
+
expect(screen.queryByRole('button', { name: /initiatives/i })).not.toBeInTheDocument();
|
|
126
|
+
expect(screen.queryByRole('button', { name: /workflows/i })).not.toBeInTheDocument();
|
|
127
|
+
expect(screen.queryByRole('button', { name: /agents/i })).not.toBeInTheDocument();
|
|
128
|
+
expect(screen.getByRole('button', { name: /taxonomy/i })).toBeInTheDocument();
|
|
129
|
+
expect(screen.getByRole('button', { name: /^mcp$/i })).toBeInTheDocument();
|
|
130
|
+
expect(screen.getByRole('button', { name: /integrations/i })).toBeInTheDocument();
|
|
131
|
+
});
|
|
132
|
+
it('4.1b General tab removes old profile/account chrome', () => {
|
|
133
|
+
renderComponent();
|
|
134
|
+
expect(screen.queryByText('Workspace Profile')).not.toBeInTheDocument();
|
|
135
|
+
expect(screen.queryByText('Account Plan')).not.toBeInTheDocument();
|
|
136
|
+
expect(screen.queryByText('Project Settings')).not.toBeInTheDocument();
|
|
137
|
+
expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument();
|
|
138
|
+
expect(screen.getByRole('button', { name: 'Reset to Global' })).toBeInTheDocument();
|
|
102
139
|
});
|
|
103
|
-
it('4.
|
|
140
|
+
it('4.1c Danger zone is hidden in local mode', () => {
|
|
141
|
+
renderComponent({
|
|
142
|
+
settingsModel: {
|
|
143
|
+
runtimeMode: 'local',
|
|
144
|
+
workspaceSwitchingEnabled: true,
|
|
145
|
+
isAuthenticated: true,
|
|
146
|
+
currentWorkspaceRole: 'owner'
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
expect(screen.queryByText('Danger Zone')).not.toBeInTheDocument();
|
|
150
|
+
});
|
|
151
|
+
it('4.1d Global tab no longer shows operating mode', async () => {
|
|
152
|
+
const user = userEvent.setup();
|
|
104
153
|
renderComponent();
|
|
105
|
-
|
|
154
|
+
await user.click(screen.getByRole('button', { name: 'Global' }));
|
|
155
|
+
expect(screen.queryByText('Operating Mode')).not.toBeInTheDocument();
|
|
106
156
|
});
|
|
107
157
|
it('4.3 Theme toggle', async () => {
|
|
108
158
|
const user = userEvent.setup();
|
|
109
159
|
renderComponent();
|
|
160
|
+
await user.click(screen.getByRole('button', { name: /application/i }));
|
|
110
161
|
const lightBtn = screen.getByRole('button', { name: /^light$/i });
|
|
111
162
|
await user.click(lightBtn);
|
|
112
163
|
expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('light');
|
|
113
164
|
expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('light');
|
|
114
165
|
});
|
|
166
|
+
it('4.3a Renders all registered themes in the picker', async () => {
|
|
167
|
+
const user = userEvent.setup();
|
|
168
|
+
renderComponent();
|
|
169
|
+
await user.click(screen.getByRole('button', { name: /application/i }));
|
|
170
|
+
for (const themeOption of TASKFORCE_THEME_OPTIONS) {
|
|
171
|
+
expect(screen.getByRole('button', { name: new RegExp(`^${themeOption.label}$`, 'i') })).toBeInTheDocument();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
115
174
|
it('4.4 Theme persists', async () => {
|
|
116
175
|
const user = userEvent.setup();
|
|
117
176
|
// This tests that the callback is invoked, persistence is handled by parent
|
|
118
177
|
renderComponent();
|
|
178
|
+
await user.click(screen.getByRole('button', { name: /application/i }));
|
|
119
179
|
const midnightBtn = screen.getByRole('button', { name: /^midnight$/i });
|
|
120
180
|
await user.click(midnightBtn);
|
|
121
181
|
expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('midnight');
|
|
122
182
|
expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('midnight');
|
|
123
183
|
});
|
|
184
|
+
it('4.4a Dawn theme can be selected', async () => {
|
|
185
|
+
const user = userEvent.setup();
|
|
186
|
+
renderComponent();
|
|
187
|
+
await user.click(screen.getByRole('button', { name: /application/i }));
|
|
188
|
+
const dawnBtn = screen.getByRole('button', { name: /^dawn$/i });
|
|
189
|
+
await user.click(dawnBtn);
|
|
190
|
+
expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('dawn');
|
|
191
|
+
expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('dawn');
|
|
192
|
+
});
|
|
124
193
|
it('4.4b JSON backup toggle updates setting', async () => {
|
|
125
194
|
const user = userEvent.setup();
|
|
126
195
|
renderComponent();
|
|
@@ -128,6 +197,37 @@ describe('TaskSettings Component', () => {
|
|
|
128
197
|
await user.click(backupToggle);
|
|
129
198
|
expect(defaultSettingsModel.onJsonBackupEnabledChange).toHaveBeenCalledWith(true);
|
|
130
199
|
});
|
|
200
|
+
it('4.4c JSON backup controls are hidden in cloud mode', async () => {
|
|
201
|
+
const user = userEvent.setup();
|
|
202
|
+
renderComponent({
|
|
203
|
+
settingsModel: {
|
|
204
|
+
runtimeMode: 'cloud'
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
expect(screen.queryByRole('checkbox', { name: /enable automatic json backup/i })).not.toBeInTheDocument();
|
|
208
|
+
await user.click(screen.getByRole('button', { name: 'Global' }));
|
|
209
|
+
expect(screen.queryByRole('checkbox', { name: /enable global json backup default/i })).not.toBeInTheDocument();
|
|
210
|
+
});
|
|
211
|
+
it('4.4d Task forms section owns checklist dropdown setting', async () => {
|
|
212
|
+
const user = userEvent.setup();
|
|
213
|
+
renderComponent();
|
|
214
|
+
await user.click(screen.getByRole('button', { name: /task forms/i }));
|
|
215
|
+
const checklistToggle = screen.getByRole('checkbox', { name: /enable checklist dropdown in task form/i });
|
|
216
|
+
await user.click(checklistToggle);
|
|
217
|
+
expect(defaultSettingsModel.onChecklistDropdownEnabledChange).toHaveBeenCalledWith(false);
|
|
218
|
+
});
|
|
219
|
+
it('4.4e Task cards section owns the status label setting', async () => {
|
|
220
|
+
const user = userEvent.setup();
|
|
221
|
+
renderComponent();
|
|
222
|
+
await user.click(screen.getByRole('button', { name: /task cards/i }));
|
|
223
|
+
const statusLabelToggle = screen.getByRole('checkbox', { name: /show status label/i });
|
|
224
|
+
await user.click(statusLabelToggle);
|
|
225
|
+
expect(defaultSettingsModel.onShowTaskCardStatusLabelChange).toHaveBeenCalledWith(false);
|
|
226
|
+
});
|
|
227
|
+
it('4.4f Initial appearance section opens directly', () => {
|
|
228
|
+
renderComponent({ initialSection: 'appearance-task-forms' });
|
|
229
|
+
expect(screen.getByRole('checkbox', { name: /enable checklist dropdown in task form/i })).toBeInTheDocument();
|
|
230
|
+
});
|
|
131
231
|
// 4.5 Open category manager - It's always visible in the new design
|
|
132
232
|
it('4.5 Categories section is visible', () => {
|
|
133
233
|
renderComponent({ initialSection: 'categories' });
|
|
@@ -165,7 +265,7 @@ describe('TaskSettings Component', () => {
|
|
|
165
265
|
await user.tab(); // Trigger blur
|
|
166
266
|
expect(defaultSettingsModel.onUpdateCategory).toHaveBeenCalledWith(expect.objectContaining({ label: 'Backend API' }), 'Backend');
|
|
167
267
|
});
|
|
168
|
-
it('4.8
|
|
268
|
+
it('4.8 Retire category', async () => {
|
|
169
269
|
// Mock window.confirm
|
|
170
270
|
vi.spyOn(window, 'confirm').mockImplementation(() => true);
|
|
171
271
|
const user = userEvent.setup();
|
|
@@ -175,11 +275,28 @@ describe('TaskSettings Component', () => {
|
|
|
175
275
|
await user.click(dropdown);
|
|
176
276
|
const option = screen.getByText('Backend');
|
|
177
277
|
await user.click(option);
|
|
178
|
-
// Click
|
|
179
|
-
const
|
|
180
|
-
await user.click(
|
|
278
|
+
// Click retire
|
|
279
|
+
const retireBtn = screen.getByTitle('Retire Category');
|
|
280
|
+
await user.click(retireBtn);
|
|
181
281
|
expect(defaultSettingsModel.onRemoveCategory).toHaveBeenCalledWith('backend');
|
|
182
282
|
});
|
|
283
|
+
it('4.8a Protected fallback category cannot be retired', async () => {
|
|
284
|
+
const user = userEvent.setup();
|
|
285
|
+
renderComponent({
|
|
286
|
+
initialSection: 'categories',
|
|
287
|
+
settingsModel: {
|
|
288
|
+
categories: [
|
|
289
|
+
{ value: 'default', label: 'General', disabled: false },
|
|
290
|
+
{ value: 'backend', label: 'Backend', disabled: false }
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
const dropdown = screen.getByText('Select a category to configure...').closest('button');
|
|
295
|
+
await user.click(dropdown);
|
|
296
|
+
await user.click(screen.getAllByText('General').at(-1));
|
|
297
|
+
expect(screen.queryByTitle('Retire Category')).not.toBeInTheDocument();
|
|
298
|
+
expect(screen.getByText('General is the protected fallback category and cannot be retired.')).toBeInTheDocument();
|
|
299
|
+
});
|
|
183
300
|
it('4.9 Category icon', async () => {
|
|
184
301
|
const user = userEvent.setup();
|
|
185
302
|
renderComponent({ initialSection: 'categories' });
|
|
@@ -244,27 +361,459 @@ describe('TaskSettings Component', () => {
|
|
|
244
361
|
await user.click(createBtn);
|
|
245
362
|
expect(defaultSettingsModel.onSaveType).toHaveBeenCalledWith('Refactor');
|
|
246
363
|
});
|
|
247
|
-
it('4.14
|
|
364
|
+
it('4.14 Retire type', async () => {
|
|
248
365
|
vi.spyOn(window, 'confirm').mockImplementation(() => true);
|
|
249
366
|
const user = userEvent.setup();
|
|
250
367
|
renderComponent({ initialSection: 'types' });
|
|
251
368
|
const dropdown = screen.getByText('Select a type to configure...').closest('button');
|
|
252
369
|
await user.click(dropdown);
|
|
253
|
-
const option = screen.
|
|
370
|
+
const option = screen.getAllByText('Issue').at(-1);
|
|
254
371
|
await user.click(option);
|
|
255
|
-
const
|
|
256
|
-
await user.click(
|
|
257
|
-
expect(defaultSettingsModel.onRemoveType).toHaveBeenCalledWith('
|
|
372
|
+
const retireBtn = screen.getByTitle('Retire Type');
|
|
373
|
+
await user.click(retireBtn);
|
|
374
|
+
expect(defaultSettingsModel.onRemoveType).toHaveBeenCalledWith('issue');
|
|
258
375
|
});
|
|
259
|
-
it('4.
|
|
376
|
+
it('4.14ab Retired types can be restored', async () => {
|
|
377
|
+
const user = userEvent.setup();
|
|
378
|
+
renderComponent({
|
|
379
|
+
initialSection: 'types',
|
|
380
|
+
settingsModel: {
|
|
381
|
+
types: [
|
|
382
|
+
{ value: 'task', label: 'Task', color: 'blue-500', icon: 'CheckSquare', status: 'active' },
|
|
383
|
+
{ value: 'issue', label: 'Issue', color: 'red-500', icon: 'AlertCircle', status: 'retired' }
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
const dropdown = screen.getByText('Select a type to configure...').closest('button');
|
|
388
|
+
await user.click(dropdown);
|
|
389
|
+
await user.click(screen.getByText('Issue (Retired)'));
|
|
390
|
+
const restoreBtn = screen.getByTitle('Restore Type');
|
|
391
|
+
await user.click(restoreBtn);
|
|
392
|
+
expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('issue', { status: 'active' });
|
|
393
|
+
});
|
|
394
|
+
it('4.14aa Protected fallback type cannot be retired', async () => {
|
|
395
|
+
const user = userEvent.setup();
|
|
396
|
+
renderComponent({
|
|
397
|
+
initialSection: 'types',
|
|
398
|
+
settingsModel: {
|
|
399
|
+
types: [
|
|
400
|
+
{ value: 'task', label: 'Task', color: 'blue-500', icon: 'CheckSquare' },
|
|
401
|
+
{ value: 'review', label: 'Review', color: 'violet-500', icon: 'Search' }
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
const dropdown = screen.getByText('Select a type to configure...').closest('button');
|
|
406
|
+
await user.click(dropdown);
|
|
407
|
+
await user.click(screen.getByText('Task'));
|
|
408
|
+
expect(screen.queryByTitle('Retire Type')).not.toBeInTheDocument();
|
|
409
|
+
expect(screen.getByText('Task is the protected fallback type and cannot be retired.')).toBeInTheDocument();
|
|
410
|
+
});
|
|
411
|
+
it('4.14a Rename type', async () => {
|
|
412
|
+
const user = userEvent.setup();
|
|
413
|
+
renderComponent({ initialSection: 'types' });
|
|
414
|
+
const dropdown = screen.getByText('Select a type to configure...').closest('button');
|
|
415
|
+
await user.click(dropdown);
|
|
416
|
+
const option = screen.getByText('Task');
|
|
417
|
+
await user.click(option);
|
|
418
|
+
const labelInput = screen.getByDisplayValue('Task');
|
|
419
|
+
await user.clear(labelInput);
|
|
420
|
+
await user.type(labelInput, 'Action Item');
|
|
421
|
+
await user.tab();
|
|
422
|
+
expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('task', { label: 'Action Item' });
|
|
423
|
+
});
|
|
424
|
+
it('4.14b Update type color', async () => {
|
|
260
425
|
const user = userEvent.setup();
|
|
426
|
+
renderComponent({ initialSection: 'types' });
|
|
427
|
+
const dropdown = screen.getByText('Select a type to configure...').closest('button');
|
|
428
|
+
await user.click(dropdown);
|
|
429
|
+
await user.click(screen.getByText('Task'));
|
|
430
|
+
await user.click(screen.getByTitle('green-500'));
|
|
431
|
+
expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('task', { color: 'green-500' });
|
|
432
|
+
});
|
|
433
|
+
it('4.15 Export workflows', async () => {
|
|
261
434
|
renderComponent({ initialSection: 'commands' });
|
|
262
|
-
expect(screen.getByText('
|
|
435
|
+
expect(screen.getByText('Command Workflows')).toBeInTheDocument();
|
|
436
|
+
});
|
|
437
|
+
it('4.14c Priority editor hides generic taxonomy controls', async () => {
|
|
438
|
+
const user = userEvent.setup();
|
|
439
|
+
renderComponent();
|
|
440
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
441
|
+
const priorityRow = screen.getByText('Priority Levels');
|
|
442
|
+
await user.click(priorityRow.closest('button'));
|
|
443
|
+
const dropdown = screen.getByText('Select a priority level to configure...').closest('button');
|
|
444
|
+
await user.click(dropdown);
|
|
445
|
+
await user.click(screen.getByText('Medium'));
|
|
446
|
+
expect(screen.queryByText('Mark as Required')).not.toBeInTheDocument();
|
|
447
|
+
expect(screen.queryByText('Allow Multi-select')).not.toBeInTheDocument();
|
|
448
|
+
expect(screen.queryByText('Widget Type')).not.toBeInTheDocument();
|
|
449
|
+
expect(screen.getByText(/fixed ordered scale/i)).toBeInTheDocument();
|
|
450
|
+
});
|
|
451
|
+
it('4.14c1 Taxonomy libraries are visible from the taxonomy settings list', async () => {
|
|
452
|
+
const user = userEvent.setup();
|
|
453
|
+
renderComponent();
|
|
454
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
455
|
+
expect(screen.getByRole('button', { name: /system taxonomy library/i })).toBeInTheDocument();
|
|
456
|
+
expect(screen.getByText(/browse curated packs for categories, task types, and priority scales/i)).toBeInTheDocument();
|
|
457
|
+
expect(screen.getByRole('button', { name: /custom taxonomy library/i })).toBeInTheDocument();
|
|
458
|
+
});
|
|
459
|
+
it('4.14c2 System Taxonomy Library opens preview and apply controls', async () => {
|
|
460
|
+
const user = userEvent.setup();
|
|
461
|
+
renderComponent();
|
|
462
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
463
|
+
await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
|
|
464
|
+
expect(screen.getByText('Browse starter packs before you replace anything.')).toBeInTheDocument();
|
|
465
|
+
expect(screen.getByText('Software Development')).toBeInTheDocument();
|
|
466
|
+
expect(screen.getByText('Apply Selected Sections')).toBeInTheDocument();
|
|
467
|
+
expect(screen.getByRole('checkbox', { name: 'Categories' })).toBeInTheDocument();
|
|
468
|
+
expect(screen.getByRole('checkbox', { name: 'Types' })).toBeInTheDocument();
|
|
469
|
+
expect(screen.getByRole('checkbox', { name: 'Priorities' })).toBeInTheDocument();
|
|
470
|
+
expect(screen.getByRole('checkbox', { name: /remap unmatched existing values to default/i })).toBeInTheDocument();
|
|
471
|
+
});
|
|
472
|
+
it('4.14c2b System Taxonomy Library applies a selected pack', async () => {
|
|
473
|
+
const user = userEvent.setup();
|
|
474
|
+
const onApplySystemTaxonomyPack = vi.fn(async () => ({ success: true }));
|
|
475
|
+
renderComponent({
|
|
476
|
+
settingsModel: {
|
|
477
|
+
onApplySystemTaxonomyPack
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
481
|
+
await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
|
|
482
|
+
await user.click(screen.getByRole('button', { name: 'Apply Pack' }));
|
|
483
|
+
expect(onApplySystemTaxonomyPack).toHaveBeenCalledWith(expect.objectContaining({
|
|
484
|
+
pack: expect.objectContaining({ id: 'general' }),
|
|
485
|
+
sections: {
|
|
486
|
+
categories: true,
|
|
487
|
+
types: true,
|
|
488
|
+
priorities: true
|
|
489
|
+
},
|
|
490
|
+
remapExistingValuesToDefault: false
|
|
491
|
+
}));
|
|
492
|
+
expect(await screen.findByText(/was applied to the selected taxonomy sections/i)).toBeInTheDocument();
|
|
493
|
+
});
|
|
494
|
+
it('4.14c2c System Taxonomy Library surfaces preflight warnings for unmatched values', async () => {
|
|
495
|
+
const user = userEvent.setup();
|
|
496
|
+
renderComponent({
|
|
497
|
+
settingsModel: {
|
|
498
|
+
onAnalyzeSystemTaxonomyPack: vi.fn(() => ({
|
|
499
|
+
unmatchedCategoryValues: ['marketing'],
|
|
500
|
+
unmatchedTypeValues: ['request', 'incident'],
|
|
501
|
+
incompatiblePriorityValues: [4]
|
|
502
|
+
}))
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
506
|
+
await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
|
|
507
|
+
expect(screen.getByText(/1 category value will be preserved as legacy/i)).toBeInTheDocument();
|
|
508
|
+
expect(screen.getByText(/2 type values will be preserved as legacy/i)).toBeInTheDocument();
|
|
509
|
+
expect(screen.getByText(/priority replacement will be blocked unless you remap/i)).toBeInTheDocument();
|
|
263
510
|
});
|
|
264
|
-
it('4.
|
|
511
|
+
it('4.14c3 Custom Taxonomy Library opens the custom pack browser', async () => {
|
|
265
512
|
const user = userEvent.setup();
|
|
513
|
+
renderComponent();
|
|
514
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
515
|
+
await user.click(screen.getByRole('button', { name: /custom taxonomy library/i }));
|
|
516
|
+
expect(screen.getByText('Browse reusable custom taxonomy packs.')).toBeInTheDocument();
|
|
517
|
+
expect(screen.getByText('Choose Taxonomies to Install')).toBeInTheDocument();
|
|
518
|
+
expect(screen.getByRole('button', { name: /install selected taxonomies/i })).toBeInTheDocument();
|
|
519
|
+
});
|
|
520
|
+
it('4.14d Retires custom taxonomy options instead of deleting them', async () => {
|
|
521
|
+
const user = userEvent.setup();
|
|
522
|
+
renderComponent({
|
|
523
|
+
settingsModel: {
|
|
524
|
+
taxonomies: [{
|
|
525
|
+
id: 'review-status',
|
|
526
|
+
label: 'Review Status',
|
|
527
|
+
widgetType: 'select',
|
|
528
|
+
isSystem: false,
|
|
529
|
+
multiSelect: false,
|
|
530
|
+
options: [
|
|
531
|
+
{ value: 'pending', label: 'Pending', status: 'active' },
|
|
532
|
+
{ value: 'legal-review', label: 'Legal Review', status: 'active' }
|
|
533
|
+
]
|
|
534
|
+
}]
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
538
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
539
|
+
const dropdown = screen.getByText('Select an option to configure...').closest('button');
|
|
540
|
+
await user.click(dropdown);
|
|
541
|
+
await user.click(screen.getByText('Legal Review'));
|
|
542
|
+
await user.click(screen.getByTitle('Retire Option'));
|
|
543
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
|
|
544
|
+
expect.objectContaining({
|
|
545
|
+
id: 'review-status',
|
|
546
|
+
options: [
|
|
547
|
+
expect.objectContaining({ value: 'pending', status: 'active' }),
|
|
548
|
+
expect.objectContaining({ value: 'legal-review', status: 'retired' })
|
|
549
|
+
]
|
|
550
|
+
})
|
|
551
|
+
]);
|
|
552
|
+
});
|
|
553
|
+
it('4.14e Toggles custom taxonomy filter visibility', async () => {
|
|
554
|
+
const user = userEvent.setup();
|
|
555
|
+
renderComponent({
|
|
556
|
+
settingsModel: {
|
|
557
|
+
taxonomies: [{
|
|
558
|
+
id: 'review-status',
|
|
559
|
+
label: 'Review Status',
|
|
560
|
+
widgetType: 'select',
|
|
561
|
+
isSystem: false,
|
|
562
|
+
multiSelect: false,
|
|
563
|
+
filterEnabled: true,
|
|
564
|
+
options: [
|
|
565
|
+
{ value: 'pending', label: 'Pending', status: 'active' }
|
|
566
|
+
]
|
|
567
|
+
}]
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
571
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
572
|
+
const filterToggle = screen.getByRole('checkbox', { name: 'Show in Filters' });
|
|
573
|
+
await user.click(filterToggle);
|
|
574
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
|
|
575
|
+
expect.objectContaining({
|
|
576
|
+
id: 'review-status',
|
|
577
|
+
filterEnabled: false
|
|
578
|
+
})
|
|
579
|
+
]);
|
|
580
|
+
});
|
|
581
|
+
it('4.14e1 Toggles custom taxonomy task-form visibility', async () => {
|
|
582
|
+
const user = userEvent.setup();
|
|
583
|
+
renderComponent({
|
|
584
|
+
settingsModel: {
|
|
585
|
+
taxonomies: [{
|
|
586
|
+
id: 'review-status',
|
|
587
|
+
label: 'Review Status',
|
|
588
|
+
widgetType: 'select',
|
|
589
|
+
isSystem: false,
|
|
590
|
+
multiSelect: false,
|
|
591
|
+
formEnabled: true,
|
|
592
|
+
options: [
|
|
593
|
+
{ value: 'pending', label: 'Pending', status: 'active' }
|
|
594
|
+
]
|
|
595
|
+
}]
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
599
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
600
|
+
const formToggle = screen.getByRole('checkbox', { name: 'Show in Task Form' });
|
|
601
|
+
await user.click(formToggle);
|
|
602
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
|
|
603
|
+
expect.objectContaining({
|
|
604
|
+
id: 'review-status',
|
|
605
|
+
formEnabled: false
|
|
606
|
+
})
|
|
607
|
+
]);
|
|
608
|
+
});
|
|
609
|
+
it('4.14f Updates custom taxonomy description', async () => {
|
|
610
|
+
const user = userEvent.setup();
|
|
611
|
+
renderComponent({
|
|
612
|
+
settingsModel: {
|
|
613
|
+
taxonomies: [{
|
|
614
|
+
id: 'review-status',
|
|
615
|
+
label: 'Review Status',
|
|
616
|
+
widgetType: 'select',
|
|
617
|
+
isSystem: false,
|
|
618
|
+
multiSelect: false,
|
|
619
|
+
options: [
|
|
620
|
+
{ value: 'pending', label: 'Pending', status: 'active' }
|
|
621
|
+
]
|
|
622
|
+
}]
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
626
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
627
|
+
const descriptionInput = screen.getByPlaceholderText('Optional guidance for when and how to use this taxonomy...');
|
|
628
|
+
fireEvent.change(descriptionInput, { target: { value: 'Use this to track review routing.' } });
|
|
629
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
630
|
+
expect.objectContaining({
|
|
631
|
+
id: 'review-status',
|
|
632
|
+
description: 'Use this to track review routing.'
|
|
633
|
+
})
|
|
634
|
+
]);
|
|
635
|
+
});
|
|
636
|
+
it('4.14g Updates custom taxonomy aliases', async () => {
|
|
637
|
+
const user = userEvent.setup();
|
|
638
|
+
renderComponent({
|
|
639
|
+
settingsModel: {
|
|
640
|
+
taxonomies: [{
|
|
641
|
+
id: 'review-status',
|
|
642
|
+
label: 'Review Status',
|
|
643
|
+
widgetType: 'select',
|
|
644
|
+
isSystem: false,
|
|
645
|
+
multiSelect: false,
|
|
646
|
+
options: [
|
|
647
|
+
{ value: 'pending', label: 'Pending', status: 'active' }
|
|
648
|
+
]
|
|
649
|
+
}]
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
653
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
654
|
+
const aliasInputs = screen.getAllByPlaceholderText('Optional aliases, comma-separated');
|
|
655
|
+
fireEvent.change(aliasInputs[0], { target: { value: 'review route, routing' } });
|
|
656
|
+
fireEvent.blur(aliasInputs[0]);
|
|
657
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
658
|
+
expect.objectContaining({
|
|
659
|
+
id: 'review-status',
|
|
660
|
+
aliases: ['review route', 'routing']
|
|
661
|
+
})
|
|
662
|
+
]);
|
|
663
|
+
});
|
|
664
|
+
it('4.14h Updates custom taxonomy option aliases', async () => {
|
|
665
|
+
const user = userEvent.setup();
|
|
666
|
+
renderComponent({
|
|
667
|
+
settingsModel: {
|
|
668
|
+
taxonomies: [{
|
|
669
|
+
id: 'review-status',
|
|
670
|
+
label: 'Review Status',
|
|
671
|
+
widgetType: 'select',
|
|
672
|
+
isSystem: false,
|
|
673
|
+
multiSelect: false,
|
|
674
|
+
options: [
|
|
675
|
+
{ value: 'pending', label: 'Pending', status: 'active' }
|
|
676
|
+
]
|
|
677
|
+
}]
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
681
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
682
|
+
const dropdown = screen.getByText('Select an option to configure...').closest('button');
|
|
683
|
+
await user.click(dropdown);
|
|
684
|
+
await user.click(screen.getByText('Pending'));
|
|
685
|
+
const aliasInputs = screen.getAllByPlaceholderText('Optional aliases, comma-separated');
|
|
686
|
+
fireEvent.change(aliasInputs[1], { target: { value: 'queued, awaiting review' } });
|
|
687
|
+
fireEvent.blur(aliasInputs[1]);
|
|
688
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
689
|
+
expect.objectContaining({
|
|
690
|
+
id: 'review-status',
|
|
691
|
+
options: [
|
|
692
|
+
expect.objectContaining({
|
|
693
|
+
value: 'pending',
|
|
694
|
+
aliases: ['queued', 'awaiting review']
|
|
695
|
+
})
|
|
696
|
+
]
|
|
697
|
+
})
|
|
698
|
+
]);
|
|
699
|
+
});
|
|
700
|
+
it('4.14i Retires whole custom taxonomies instead of deleting them', async () => {
|
|
701
|
+
vi.spyOn(window, 'confirm').mockImplementation(() => true);
|
|
702
|
+
const user = userEvent.setup();
|
|
703
|
+
renderComponent({
|
|
704
|
+
settingsModel: {
|
|
705
|
+
taxonomies: [{
|
|
706
|
+
id: 'review-status',
|
|
707
|
+
label: 'Review Status',
|
|
708
|
+
widgetType: 'select',
|
|
709
|
+
isSystem: false,
|
|
710
|
+
multiSelect: false,
|
|
711
|
+
options: [{ value: 'pending', label: 'Pending', status: 'active' }]
|
|
712
|
+
}]
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
716
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
717
|
+
await user.click(screen.getByRole('button', { name: /retire taxonomy/i }));
|
|
718
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
719
|
+
expect.objectContaining({
|
|
720
|
+
id: 'review-status',
|
|
721
|
+
status: 'retired'
|
|
722
|
+
})
|
|
723
|
+
]);
|
|
724
|
+
});
|
|
725
|
+
it('4.14j Restores retired custom taxonomies', async () => {
|
|
726
|
+
const user = userEvent.setup();
|
|
727
|
+
renderComponent({
|
|
728
|
+
settingsModel: {
|
|
729
|
+
taxonomies: [{
|
|
730
|
+
id: 'review-status',
|
|
731
|
+
label: 'Review Status',
|
|
732
|
+
status: 'retired',
|
|
733
|
+
widgetType: 'select',
|
|
734
|
+
isSystem: false,
|
|
735
|
+
multiSelect: false,
|
|
736
|
+
options: [{ value: 'pending', label: 'Pending', status: 'active' }]
|
|
737
|
+
}]
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
741
|
+
await user.click(screen.getByText(/Review Status \(Retired\)/).closest('button'));
|
|
742
|
+
await user.click(screen.getByRole('button', { name: /restore taxonomy/i }));
|
|
743
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
744
|
+
expect.objectContaining({
|
|
745
|
+
id: 'review-status',
|
|
746
|
+
status: 'active'
|
|
747
|
+
})
|
|
748
|
+
]);
|
|
749
|
+
});
|
|
750
|
+
it('4.14k Reorders custom taxonomies from the list view', async () => {
|
|
751
|
+
const user = userEvent.setup();
|
|
752
|
+
renderComponent({
|
|
753
|
+
settingsModel: {
|
|
754
|
+
taxonomies: [
|
|
755
|
+
{
|
|
756
|
+
id: 'alpha',
|
|
757
|
+
label: 'Alpha',
|
|
758
|
+
widgetType: 'select',
|
|
759
|
+
isSystem: false,
|
|
760
|
+
multiSelect: false,
|
|
761
|
+
options: []
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
id: 'beta',
|
|
765
|
+
label: 'Beta',
|
|
766
|
+
widgetType: 'select',
|
|
767
|
+
isSystem: false,
|
|
768
|
+
multiSelect: false,
|
|
769
|
+
options: []
|
|
770
|
+
}
|
|
771
|
+
]
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
775
|
+
await user.click(screen.getAllByTitle('Move taxonomy down').find((button) => !button.hasAttribute('disabled')));
|
|
776
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
777
|
+
expect.objectContaining({ id: 'beta' }),
|
|
778
|
+
expect.objectContaining({ id: 'alpha' })
|
|
779
|
+
]);
|
|
780
|
+
});
|
|
781
|
+
it('4.14l Reorders custom taxonomy options from the detail view', async () => {
|
|
782
|
+
const user = userEvent.setup();
|
|
783
|
+
renderComponent({
|
|
784
|
+
settingsModel: {
|
|
785
|
+
taxonomies: [{
|
|
786
|
+
id: 'review-status',
|
|
787
|
+
label: 'Review Status',
|
|
788
|
+
widgetType: 'select',
|
|
789
|
+
isSystem: false,
|
|
790
|
+
multiSelect: false,
|
|
791
|
+
options: [
|
|
792
|
+
{ value: 'pending', label: 'Pending', status: 'active' },
|
|
793
|
+
{ value: 'approved', label: 'Approved', status: 'active' }
|
|
794
|
+
]
|
|
795
|
+
}]
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
await user.click(screen.getByRole('button', { name: /taxonomy/i }));
|
|
799
|
+
await user.click(screen.getByText('Review Status').closest('button'));
|
|
800
|
+
const dropdown = screen.getByText('Select an option to configure...').closest('button');
|
|
801
|
+
await user.click(dropdown);
|
|
802
|
+
await user.click(screen.getByText('Approved'));
|
|
803
|
+
await user.click(screen.getByTitle('Move option up'));
|
|
804
|
+
expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
|
|
805
|
+
expect.objectContaining({
|
|
806
|
+
id: 'review-status',
|
|
807
|
+
options: [
|
|
808
|
+
expect.objectContaining({ value: 'approved' }),
|
|
809
|
+
expect.objectContaining({ value: 'pending' })
|
|
810
|
+
]
|
|
811
|
+
})
|
|
812
|
+
]);
|
|
813
|
+
});
|
|
814
|
+
it('4.16 Agents section shows registered AI profiles', async () => {
|
|
266
815
|
renderComponent({ initialSection: 'agents' });
|
|
267
|
-
expect(screen.getByText('
|
|
816
|
+
expect(screen.getByText('Registered AI Profiles')).toBeInTheDocument();
|
|
268
817
|
});
|
|
269
818
|
it('4.17 initialSection navigates to detail view', () => {
|
|
270
819
|
renderComponent({ initialSection: 'categories' });
|
|
@@ -272,37 +821,662 @@ describe('TaskSettings Component', () => {
|
|
|
272
821
|
expect(screen.getByText('Categories')).toBeInTheDocument();
|
|
273
822
|
expect(screen.queryByText('System Taxonomies')).not.toBeInTheDocument();
|
|
274
823
|
});
|
|
275
|
-
it('4.18 documents
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
await user.click(screen.getByRole('button', { name: /Documents/i }));
|
|
280
|
-
expect(onSectionChange).toHaveBeenCalledWith('documents');
|
|
824
|
+
it('4.18 legacy documents initialSection falls back to general tab', () => {
|
|
825
|
+
renderComponent({ initialSection: 'documents' });
|
|
826
|
+
expect(screen.getByRole('button', { name: /General/i })).toBeInTheDocument();
|
|
827
|
+
expect(screen.queryByText('Cloud Document Storage')).not.toBeInTheDocument();
|
|
281
828
|
});
|
|
282
|
-
it('4.19 document
|
|
829
|
+
it('4.19 document settings copy is not rendered in settings', () => {
|
|
283
830
|
renderComponent();
|
|
284
831
|
expect(screen.queryByText('Document Storage (Admin)')).not.toBeInTheDocument();
|
|
832
|
+
expect(screen.queryByText('Local Documents')).not.toBeInTheDocument();
|
|
833
|
+
expect(screen.queryByText('Cloud Document Storage')).not.toBeInTheDocument();
|
|
834
|
+
expect(screen.queryByRole('button', { name: 'Open App Admin' })).not.toBeInTheDocument();
|
|
285
835
|
});
|
|
286
|
-
it('4.20
|
|
287
|
-
const
|
|
836
|
+
it('4.20 Cloud runtime shows cloud MCP only', async () => {
|
|
837
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
838
|
+
ok: true,
|
|
839
|
+
json: async () => ({ items: [] })
|
|
840
|
+
}));
|
|
841
|
+
const user = userEvent.setup();
|
|
842
|
+
renderComponent({
|
|
843
|
+
settingsModel: {
|
|
844
|
+
runtimeMode: 'cloud',
|
|
845
|
+
isAuthenticated: true,
|
|
846
|
+
cloudAuthBaseUrl: 'https://cloud.example.com',
|
|
847
|
+
fetchCloudAuthApi
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
851
|
+
expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
|
|
852
|
+
expect(screen.getByRole('button', { name: 'Generate Token' })).toBeInTheDocument();
|
|
853
|
+
expect(screen.queryByRole('button', { name: 'Local MCP' })).not.toBeInTheDocument();
|
|
854
|
+
expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
|
|
855
|
+
expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
|
|
856
|
+
method: 'GET',
|
|
857
|
+
credentials: 'include'
|
|
858
|
+
}));
|
|
859
|
+
}, 10000);
|
|
860
|
+
it('4.21 Local authenticated runtime defaults MCP tab to cloud with toggle', async () => {
|
|
861
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
288
862
|
ok: true,
|
|
289
|
-
json: async () => ({
|
|
863
|
+
json: async () => ({ items: [] })
|
|
290
864
|
}));
|
|
291
|
-
vi.stubGlobal('fetch', fetchMock);
|
|
292
865
|
const user = userEvent.setup();
|
|
293
|
-
renderComponent({
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
866
|
+
renderComponent({
|
|
867
|
+
settingsModel: {
|
|
868
|
+
runtimeMode: 'local',
|
|
869
|
+
isAuthenticated: true,
|
|
870
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
871
|
+
cloudAuthBaseUrl: 'https://cloud.example.com',
|
|
872
|
+
fetchCloudAuthApi
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
876
|
+
expect(await screen.findByRole('button', { name: 'Cloud MCP' })).toBeInTheDocument();
|
|
877
|
+
expect(screen.getByRole('button', { name: 'Local MCP' })).toBeInTheDocument();
|
|
878
|
+
expect(screen.getByText('Cloud MCP Configuration')).toBeInTheDocument();
|
|
879
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
880
|
+
expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
|
|
881
|
+
await user.click(screen.getByRole('button', { name: 'Local MCP' }));
|
|
882
|
+
expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
|
|
883
|
+
await user.click(screen.getByRole('button', { name: 'Advanced' }));
|
|
884
|
+
expect(screen.getByText('MCP Path Mapping (Host Root)')).toBeInTheDocument();
|
|
885
|
+
expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
|
|
886
|
+
method: 'GET',
|
|
887
|
+
credentials: 'include'
|
|
888
|
+
}));
|
|
889
|
+
}, 10000);
|
|
890
|
+
it('4.23b MCP token creation uses the provided cloud auth helper', async () => {
|
|
891
|
+
const fetchCloudAuthApi = vi.fn(async (path, init) => {
|
|
892
|
+
if (path === '/api/taskforce/settings/mcp/tokens' && init?.method === 'POST') {
|
|
893
|
+
return {
|
|
894
|
+
ok: true,
|
|
895
|
+
json: async () => ({
|
|
896
|
+
token: 'tfmcp_example_secret',
|
|
897
|
+
record: {
|
|
898
|
+
id: 'mcp-token-1',
|
|
899
|
+
name: 'Workspace Alpha MCP',
|
|
900
|
+
tokenPrefix: 'tfmcp_example',
|
|
901
|
+
scopes: ['tasks:read', 'tasks:write'],
|
|
902
|
+
status: 'active',
|
|
903
|
+
createdAt: new Date().toISOString()
|
|
904
|
+
}
|
|
905
|
+
})
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
return {
|
|
909
|
+
ok: true,
|
|
910
|
+
json: async () => ({ items: [] })
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
const user = userEvent.setup();
|
|
914
|
+
renderComponent({
|
|
915
|
+
settingsModel: {
|
|
916
|
+
runtimeMode: 'local',
|
|
917
|
+
isAuthenticated: true,
|
|
918
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
919
|
+
fetchCloudAuthApi
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
923
|
+
await user.click(await screen.findByRole('button', { name: 'Generate Token' }));
|
|
924
|
+
expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
|
|
925
|
+
method: 'POST',
|
|
926
|
+
credentials: 'include'
|
|
927
|
+
}));
|
|
928
|
+
expect((await screen.findAllByText('tfmcp_example_secret')).length).toBeGreaterThan(0);
|
|
929
|
+
}, 10000);
|
|
930
|
+
it('4.23c MCP token name defaults to the workspace name', async () => {
|
|
931
|
+
const user = userEvent.setup();
|
|
932
|
+
renderComponent({
|
|
933
|
+
settingsModel: {
|
|
934
|
+
runtimeMode: 'local',
|
|
935
|
+
isAuthenticated: true,
|
|
936
|
+
currentWorkspaceName: 'Workspace Alpha'
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
940
|
+
expect(await screen.findByDisplayValue('Workspace Alpha MCP')).toBeInTheDocument();
|
|
300
941
|
});
|
|
301
|
-
it('4.
|
|
942
|
+
it('4.23d MCP test connection uses the provided cloud auth helper', async () => {
|
|
943
|
+
const fetchCloudAuthApi = vi.fn(async (path, init) => {
|
|
944
|
+
if (path === '/api/taskforce/settings/mcp/test-connection') {
|
|
945
|
+
return {
|
|
946
|
+
ok: true,
|
|
947
|
+
json: async () => ({
|
|
948
|
+
result: {
|
|
949
|
+
workspaceName: 'Workspace Alpha',
|
|
950
|
+
workspaceId: 'workspace-alpha',
|
|
951
|
+
scopes: ['tasks:read', 'tasks:write']
|
|
952
|
+
}
|
|
953
|
+
})
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
ok: true,
|
|
958
|
+
json: async () => ({ items: [] })
|
|
959
|
+
};
|
|
960
|
+
});
|
|
302
961
|
const user = userEvent.setup();
|
|
303
|
-
renderComponent({
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
962
|
+
renderComponent({
|
|
963
|
+
settingsModel: {
|
|
964
|
+
runtimeMode: 'local',
|
|
965
|
+
isAuthenticated: true,
|
|
966
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
967
|
+
fetchCloudAuthApi
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
971
|
+
await user.click(screen.getByRole('button', { name: 'Test Connection' }));
|
|
972
|
+
await user.type(await screen.findByPlaceholderText('tfmcp_xxxxx_replace-me'), 'tfmcp_example_secret');
|
|
973
|
+
await user.click(screen.getAllByRole('button', { name: 'Test Connection' })[1]);
|
|
974
|
+
expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/test-connection', expect.objectContaining({
|
|
975
|
+
method: 'POST',
|
|
976
|
+
credentials: 'include'
|
|
977
|
+
}));
|
|
978
|
+
expect(await screen.findByText('Connection looks good for this workspace.')).toBeInTheDocument();
|
|
979
|
+
expect(screen.getByText(/Granted scopes: tasks:read, tasks:write/i)).toBeInTheDocument();
|
|
980
|
+
}, 10000);
|
|
981
|
+
it('4.23e Antigravity uses serverUrl instead of transport in the cloud snippet', async () => {
|
|
982
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
983
|
+
ok: true,
|
|
984
|
+
json: async () => ({ items: [] })
|
|
985
|
+
}));
|
|
986
|
+
const user = userEvent.setup();
|
|
987
|
+
renderComponent({
|
|
988
|
+
settingsModel: {
|
|
989
|
+
runtimeMode: 'local',
|
|
990
|
+
isAuthenticated: true,
|
|
991
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
992
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
993
|
+
fetchCloudAuthApi
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
997
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'antigravity');
|
|
998
|
+
const snippetMatches = await screen.findAllByText(/serverUrl/);
|
|
999
|
+
expect(snippetMatches.length).toBeGreaterThan(0);
|
|
1000
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1001
|
+
expect(screen.queryByText(/"transport"/)).not.toBeInTheDocument();
|
|
1002
|
+
}, 10000);
|
|
1003
|
+
it('4.23e1 falls back to the dedicated MCP hostname when only the cloud app host is configured', async () => {
|
|
1004
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1005
|
+
ok: true,
|
|
1006
|
+
json: async () => ({ items: [] })
|
|
1007
|
+
}));
|
|
1008
|
+
const user = userEvent.setup();
|
|
1009
|
+
renderComponent({
|
|
1010
|
+
settingsModel: {
|
|
1011
|
+
runtimeMode: 'local',
|
|
1012
|
+
isAuthenticated: true,
|
|
1013
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1014
|
+
cloudAuthBaseUrl: 'https://performance-app.taskforcehq.ai',
|
|
1015
|
+
cloudMcpBaseUrl: '',
|
|
1016
|
+
fetchCloudAuthApi
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1020
|
+
expect(await screen.findByText(/https:\/\/performance-mcp\.taskforcehq\.ai\/mcp/)).toBeInTheDocument();
|
|
1021
|
+
expect(screen.queryByText(/https:\/\/performance-app\.taskforcehq\.ai\/mcp/)).not.toBeInTheDocument();
|
|
1022
|
+
}, 10000);
|
|
1023
|
+
it('4.23e1b prefers the dedicated MCP hostname even when cloudMcpBaseUrl is stale and points at the app host', async () => {
|
|
1024
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1025
|
+
ok: true,
|
|
1026
|
+
json: async () => ({ items: [] })
|
|
1027
|
+
}));
|
|
1028
|
+
const user = userEvent.setup();
|
|
1029
|
+
renderComponent({
|
|
1030
|
+
settingsModel: {
|
|
1031
|
+
runtimeMode: 'local',
|
|
1032
|
+
isAuthenticated: true,
|
|
1033
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1034
|
+
cloudAuthBaseUrl: 'https://performance-app.taskforcehq.ai',
|
|
1035
|
+
cloudMcpBaseUrl: 'https://performance-app.taskforcehq.ai',
|
|
1036
|
+
fetchCloudAuthApi
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1040
|
+
expect(await screen.findByText(/https:\/\/performance-mcp\.taskforcehq\.ai\/mcp/)).toBeInTheDocument();
|
|
1041
|
+
expect(screen.queryByText(/https:\/\/performance-app\.taskforcehq\.ai\/mcp/)).not.toBeInTheDocument();
|
|
1042
|
+
}, 10000);
|
|
1043
|
+
it('4.23e2 Cursor uses top-level url instead of transport in the cloud config', async () => {
|
|
1044
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1045
|
+
ok: true,
|
|
1046
|
+
json: async () => ({ items: [] })
|
|
1047
|
+
}));
|
|
1048
|
+
const user = userEvent.setup();
|
|
1049
|
+
renderComponent({
|
|
1050
|
+
settingsModel: {
|
|
1051
|
+
runtimeMode: 'local',
|
|
1052
|
+
isAuthenticated: true,
|
|
1053
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1054
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1055
|
+
fetchCloudAuthApi
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1059
|
+
const urlMatches = await screen.findAllByText(/"url"/);
|
|
1060
|
+
expect(urlMatches.length).toBeGreaterThan(0);
|
|
1061
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1062
|
+
expect(screen.queryByText(/"transport"/)).not.toBeInTheDocument();
|
|
1063
|
+
expect(screen.queryByText(/serverUrl/)).not.toBeInTheDocument();
|
|
1064
|
+
}, 10000);
|
|
1065
|
+
it('4.23e2b Cline uses streamableHttp in the cloud config', async () => {
|
|
1066
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1067
|
+
ok: true,
|
|
1068
|
+
json: async () => ({ items: [] })
|
|
1069
|
+
}));
|
|
1070
|
+
const user = userEvent.setup();
|
|
1071
|
+
renderComponent({
|
|
1072
|
+
settingsModel: {
|
|
1073
|
+
runtimeMode: 'local',
|
|
1074
|
+
isAuthenticated: true,
|
|
1075
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1076
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1077
|
+
fetchCloudAuthApi
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1081
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'cline');
|
|
1082
|
+
expect(await screen.findByText(/Cline Configuration Instructions/i)).toBeInTheDocument();
|
|
1083
|
+
expect(screen.getByText(/"type"/)).toBeInTheDocument();
|
|
1084
|
+
expect(screen.getByText(/streamableHttp/)).toBeInTheDocument();
|
|
1085
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1086
|
+
}, 10000);
|
|
1087
|
+
it('4.23e2c VS Code uses servers with http transport in the cloud config', async () => {
|
|
1088
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1089
|
+
ok: true,
|
|
1090
|
+
json: async () => ({ items: [] })
|
|
1091
|
+
}));
|
|
1092
|
+
const user = userEvent.setup();
|
|
1093
|
+
renderComponent({
|
|
1094
|
+
settingsModel: {
|
|
1095
|
+
runtimeMode: 'local',
|
|
1096
|
+
isAuthenticated: true,
|
|
1097
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1098
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1099
|
+
fetchCloudAuthApi
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1103
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'vscode');
|
|
1104
|
+
expect(await screen.findByText(/VS Code Configuration Instructions/i)).toBeInTheDocument();
|
|
1105
|
+
expect(screen.getByText(/"servers"/)).toBeInTheDocument();
|
|
1106
|
+
expect(screen.getByText(/"type"/)).toBeInTheDocument();
|
|
1107
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1108
|
+
}, 10000);
|
|
1109
|
+
it('4.23e3 Codex shows config.toml MCP output', async () => {
|
|
1110
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1111
|
+
ok: true,
|
|
1112
|
+
json: async () => ({ items: [] })
|
|
1113
|
+
}));
|
|
1114
|
+
const user = userEvent.setup();
|
|
1115
|
+
renderComponent({
|
|
1116
|
+
settingsModel: {
|
|
1117
|
+
runtimeMode: 'local',
|
|
1118
|
+
isAuthenticated: true,
|
|
1119
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1120
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1121
|
+
fetchCloudAuthApi
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1125
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
|
|
1126
|
+
expect((await screen.findAllByText(/\.codex\/config\.toml/i)).length).toBeGreaterThan(0);
|
|
1127
|
+
expect(screen.getByText(/\[mcp_servers\./)).toBeInTheDocument();
|
|
1128
|
+
expect(screen.getByText(/http_headers = \{ Authorization = "Bearer/i)).toBeInTheDocument();
|
|
1129
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1130
|
+
}, 10000);
|
|
1131
|
+
it('4.23e3a Codex MCP output includes optional automation identity headers', async () => {
|
|
1132
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1133
|
+
ok: true,
|
|
1134
|
+
json: async () => ({ items: [] })
|
|
1135
|
+
}));
|
|
1136
|
+
const user = userEvent.setup();
|
|
1137
|
+
renderComponent({
|
|
1138
|
+
settingsModel: {
|
|
1139
|
+
runtimeMode: 'local',
|
|
1140
|
+
isAuthenticated: true,
|
|
1141
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1142
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1143
|
+
fetchCloudAuthApi
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1147
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
|
|
1148
|
+
await user.type(screen.getByPlaceholderText('Automation Codex'), 'Automation Codex');
|
|
1149
|
+
await user.type(screen.getByPlaceholderText('tfp_...'), 'tfp_automation_codex');
|
|
1150
|
+
expect(await screen.findByText(/X-Taskforce-AI-Name = "Automation Codex"/i)).toBeInTheDocument();
|
|
1151
|
+
expect(screen.getByText(/X-Taskforce-AI-Profile-Token = "tfp_automation_codex"/i)).toBeInTheDocument();
|
|
1152
|
+
}, 10000);
|
|
1153
|
+
it('4.23e3aa MCP tab can prefill automation identity from an existing AI profile', async () => {
|
|
1154
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1155
|
+
ok: true,
|
|
1156
|
+
json: async () => ({ items: [] })
|
|
1157
|
+
}));
|
|
1158
|
+
const fetchMock = vi.fn(async (input) => {
|
|
1159
|
+
const url = String(input);
|
|
1160
|
+
if (url.includes('/api/taskforce/workspace/assignee-options')) {
|
|
1161
|
+
return {
|
|
1162
|
+
ok: true,
|
|
1163
|
+
json: async () => ({
|
|
1164
|
+
assignees: [
|
|
1165
|
+
{
|
|
1166
|
+
value: 'ai-profile-123',
|
|
1167
|
+
label: 'Automation Codex',
|
|
1168
|
+
profileToken: 'tfp_automation_codex',
|
|
1169
|
+
username: 'automation-codex',
|
|
1170
|
+
icon: 'Bot',
|
|
1171
|
+
color: '#6B7280',
|
|
1172
|
+
kind: 'agent',
|
|
1173
|
+
}
|
|
1174
|
+
]
|
|
1175
|
+
})
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
throw new Error(`Unexpected fetch call: ${url}`);
|
|
1179
|
+
});
|
|
1180
|
+
global.fetch = fetchMock;
|
|
1181
|
+
const user = userEvent.setup();
|
|
1182
|
+
renderComponent({
|
|
1183
|
+
settingsModel: {
|
|
1184
|
+
runtimeMode: 'local',
|
|
1185
|
+
isAuthenticated: true,
|
|
1186
|
+
workspaceId: 'workspace-alpha',
|
|
1187
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1188
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1189
|
+
fetchCloudAuthApi
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1193
|
+
await user.selectOptions(await screen.findByRole('combobox', { name: 'Use existing AI profile' }), 'ai-profile-123');
|
|
1194
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
|
|
1195
|
+
expect(await screen.findByDisplayValue('Automation Codex')).toBeInTheDocument();
|
|
1196
|
+
expect(screen.getByDisplayValue('tfp_automation_codex')).toBeInTheDocument();
|
|
1197
|
+
expect(screen.getByText(/X-Taskforce-AI-Name = "Automation Codex"/i)).toBeInTheDocument();
|
|
1198
|
+
expect(screen.getByText(/X-Taskforce-AI-Profile-Token = "tfp_automation_codex"/i)).toBeInTheDocument();
|
|
1199
|
+
global.fetch = originalFetch;
|
|
1200
|
+
}, 10000);
|
|
1201
|
+
it('4.23e3b Claude Code shows a remote MCP add command', async () => {
|
|
1202
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1203
|
+
ok: true,
|
|
1204
|
+
json: async () => ({ items: [] })
|
|
1205
|
+
}));
|
|
1206
|
+
const user = userEvent.setup();
|
|
1207
|
+
renderComponent({
|
|
1208
|
+
settingsModel: {
|
|
1209
|
+
runtimeMode: 'local',
|
|
1210
|
+
isAuthenticated: true,
|
|
1211
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1212
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1213
|
+
fetchCloudAuthApi
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1217
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'claude-code');
|
|
1218
|
+
expect(await screen.findByText(/Claude Code Configuration Instructions/i)).toBeInTheDocument();
|
|
1219
|
+
expect(screen.getByText(/claude mcp add --transport http/i)).toBeInTheDocument();
|
|
1220
|
+
expect(screen.getByText(/claude mcp add --transport http taskforce-/i)).toBeInTheDocument();
|
|
1221
|
+
expect(screen.getByText(/--header "Authorization: Bearer/i)).toBeInTheDocument();
|
|
1222
|
+
}, 10000);
|
|
1223
|
+
it('4.23e3c Gemini CLI shows a remote MCP add command', async () => {
|
|
1224
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1225
|
+
ok: true,
|
|
1226
|
+
json: async () => ({ items: [] })
|
|
1227
|
+
}));
|
|
1228
|
+
const user = userEvent.setup();
|
|
1229
|
+
renderComponent({
|
|
1230
|
+
settingsModel: {
|
|
1231
|
+
runtimeMode: 'local',
|
|
1232
|
+
isAuthenticated: true,
|
|
1233
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1234
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1235
|
+
fetchCloudAuthApi
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1239
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'gemini-cli');
|
|
1240
|
+
expect(await screen.findByText(/Gemini CLI Configuration Instructions/i)).toBeInTheDocument();
|
|
1241
|
+
expect(screen.getByText(/gemini mcp add taskforce-/i)).toBeInTheDocument();
|
|
1242
|
+
expect(screen.getByText(/--transport http/i)).toBeInTheDocument();
|
|
1243
|
+
expect(screen.getByText(/--header "Authorization: Bearer/i)).toBeInTheDocument();
|
|
1244
|
+
expect(screen.getByText(/Add --scope user if you want it available across all projects/i)).toBeInTheDocument();
|
|
1245
|
+
}, 10000);
|
|
1246
|
+
it('4.23e3d OpenClaw shows an openclaw.json MCP snippet', async () => {
|
|
1247
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1248
|
+
ok: true,
|
|
1249
|
+
json: async () => ({ items: [] })
|
|
1250
|
+
}));
|
|
1251
|
+
const user = userEvent.setup();
|
|
1252
|
+
renderComponent({
|
|
1253
|
+
settingsModel: {
|
|
1254
|
+
runtimeMode: 'local',
|
|
1255
|
+
isAuthenticated: true,
|
|
1256
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1257
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1258
|
+
fetchCloudAuthApi
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1262
|
+
await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'openclaw');
|
|
1263
|
+
expect(await screen.findByText(/OpenClaw Configuration Instructions/i)).toBeInTheDocument();
|
|
1264
|
+
expect((await screen.findAllByText(/~\/\.openclaw\/openclaw\.json/i)).length).toBeGreaterThan(0);
|
|
1265
|
+
const configBlocks = Array.from(document.querySelectorAll('pre code'));
|
|
1266
|
+
const openClawConfigBlock = configBlocks.find((block) => block.textContent?.includes('https://mcp.taskforcehq.ai/mcp'));
|
|
1267
|
+
expect(openClawConfigBlock?.textContent).toContain('"mcp"');
|
|
1268
|
+
expect(openClawConfigBlock?.textContent).toContain('"servers"');
|
|
1269
|
+
expect(openClawConfigBlock?.textContent).toContain('"transport": "streamable-http"');
|
|
1270
|
+
expect(openClawConfigBlock?.textContent).toContain('"Authorization": "Bearer');
|
|
1271
|
+
expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
|
|
1272
|
+
}, 10000);
|
|
1273
|
+
it('4.23e4 Cloud selector excludes desktop OAuth clients from the MCP client list', async () => {
|
|
1274
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1275
|
+
ok: true,
|
|
1276
|
+
json: async () => ({ items: [] })
|
|
1277
|
+
}));
|
|
1278
|
+
const user = userEvent.setup();
|
|
1279
|
+
renderComponent({
|
|
1280
|
+
settingsModel: {
|
|
1281
|
+
runtimeMode: 'local',
|
|
1282
|
+
isAuthenticated: true,
|
|
1283
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1284
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1285
|
+
fetchCloudAuthApi
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1289
|
+
expect(screen.queryByRole('option', { name: /Claude Desktop/i })).not.toBeInTheDocument();
|
|
1290
|
+
expect(screen.queryByRole('option', { name: /ChatGPT Desktop/i })).not.toBeInTheDocument();
|
|
1291
|
+
expect(await screen.findByText('Cursor Configuration Instructions')).toBeInTheDocument();
|
|
1292
|
+
}, 10000);
|
|
1293
|
+
it('4.23e4b Integrations tab shows ChatGPT and Claude connector management separately from PAT tokens', async () => {
|
|
1294
|
+
const fetchCloudAuthApi = vi.fn(async (path) => {
|
|
1295
|
+
if (path.startsWith('/api/taskforce/settings/mcp/connectors')) {
|
|
1296
|
+
return {
|
|
1297
|
+
ok: true,
|
|
1298
|
+
json: async () => ({
|
|
1299
|
+
items: [
|
|
1300
|
+
{
|
|
1301
|
+
id: 'grant-1',
|
|
1302
|
+
provider: 'chatgpt',
|
|
1303
|
+
clientId: 'client-1',
|
|
1304
|
+
clientName: 'ChatGPT Connector',
|
|
1305
|
+
workspaceId: 'workspace-alpha',
|
|
1306
|
+
userId: 'user-1',
|
|
1307
|
+
scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
1308
|
+
status: 'active',
|
|
1309
|
+
createdAt: '2026-03-14T12:00:00.000Z',
|
|
1310
|
+
lastUsedAt: null,
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
id: 'grant-2',
|
|
1314
|
+
provider: 'claude',
|
|
1315
|
+
clientId: 'client-2',
|
|
1316
|
+
clientName: 'Claude Desktop Connector',
|
|
1317
|
+
workspaceId: 'workspace-alpha',
|
|
1318
|
+
userId: 'user-1',
|
|
1319
|
+
scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
|
|
1320
|
+
status: 'active',
|
|
1321
|
+
createdAt: '2026-03-14T13:00:00.000Z',
|
|
1322
|
+
lastUsedAt: null,
|
|
1323
|
+
}
|
|
1324
|
+
]
|
|
1325
|
+
})
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
ok: true,
|
|
1330
|
+
json: async () => ({ items: [] })
|
|
1331
|
+
};
|
|
1332
|
+
});
|
|
1333
|
+
const user = userEvent.setup();
|
|
1334
|
+
renderComponent({
|
|
1335
|
+
settingsModel: {
|
|
1336
|
+
runtimeMode: 'local',
|
|
1337
|
+
isAuthenticated: true,
|
|
1338
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1339
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1340
|
+
fetchCloudAuthApi
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
await user.click(screen.getByRole('button', { name: /Integrations/i }));
|
|
1344
|
+
expect(await screen.findByRole('heading', { name: 'Integrations' })).toBeInTheDocument();
|
|
1345
|
+
expect(await screen.findByText('ChatGPT Connector')).toBeInTheDocument();
|
|
1346
|
+
expect(await screen.findByText('Claude Desktop Connector')).toBeInTheDocument();
|
|
1347
|
+
expect(screen.getAllByText(/Taskforce handles OAuth and binds each connector authorization/i)).toHaveLength(2);
|
|
1348
|
+
expect(screen.getAllByRole('button', { name: /Disconnect/i })).toHaveLength(2);
|
|
1349
|
+
}, 10000);
|
|
1350
|
+
it('4.23e4c MCP tab no longer shows connector management cards', async () => {
|
|
1351
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1352
|
+
ok: true,
|
|
1353
|
+
json: async () => ({ items: [] })
|
|
1354
|
+
}));
|
|
1355
|
+
const user = userEvent.setup();
|
|
1356
|
+
renderComponent({
|
|
1357
|
+
settingsModel: {
|
|
1358
|
+
runtimeMode: 'local',
|
|
1359
|
+
isAuthenticated: true,
|
|
1360
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1361
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1362
|
+
fetchCloudAuthApi
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1366
|
+
expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
|
|
1367
|
+
expect(screen.queryByText('ChatGPT Connector')).not.toBeInTheDocument();
|
|
1368
|
+
expect(screen.queryByText(/^Connectors$/)).not.toBeInTheDocument();
|
|
1369
|
+
}, 10000);
|
|
1370
|
+
it('4.23g Cloud MCP configuration offers snippet and full-config copy actions', async () => {
|
|
1371
|
+
const user = userEvent.setup();
|
|
1372
|
+
renderComponent({
|
|
1373
|
+
settingsModel: {
|
|
1374
|
+
runtimeMode: 'local',
|
|
1375
|
+
isAuthenticated: true,
|
|
1376
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1377
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1381
|
+
expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
|
|
1382
|
+
expect(screen.getAllByRole('button', { name: /copy/i }).length).toBeGreaterThan(0);
|
|
1383
|
+
}, 10000);
|
|
1384
|
+
it('4.23g1 Cloud MCP copy warns before a token is generated', async () => {
|
|
1385
|
+
const user = userEvent.setup();
|
|
1386
|
+
renderComponent({
|
|
1387
|
+
settingsModel: {
|
|
1388
|
+
runtimeMode: 'local',
|
|
1389
|
+
isAuthenticated: true,
|
|
1390
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1391
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1395
|
+
await screen.findByText('Cloud MCP Configuration');
|
|
1396
|
+
await user.click(screen.getByTitle('Copy full configuration file'));
|
|
1397
|
+
expect(screen.getByText('Generate a token before copying the Cloud MCP configuration.')).toBeInTheDocument();
|
|
1398
|
+
}, 10000);
|
|
1399
|
+
it('4.23h Active tokens sort first and revoked tokens do not show revoke or regenerate actions', async () => {
|
|
1400
|
+
const user = userEvent.setup();
|
|
1401
|
+
const fetchCloudAuthApi = vi.fn(async () => ({
|
|
1402
|
+
ok: true,
|
|
1403
|
+
json: async () => ({
|
|
1404
|
+
items: [
|
|
1405
|
+
{
|
|
1406
|
+
id: 'token-revoked',
|
|
1407
|
+
name: 'Old Token',
|
|
1408
|
+
scopes: ['tasks:read'],
|
|
1409
|
+
tokenPrefix: 'tfmcp_old',
|
|
1410
|
+
status: 'revoked',
|
|
1411
|
+
createdAt: '2026-03-01T00:00:00.000Z',
|
|
1412
|
+
lastUsedAt: null,
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
id: 'token-active',
|
|
1416
|
+
name: 'Current Token',
|
|
1417
|
+
scopes: ['tasks:read', 'tasks:write'],
|
|
1418
|
+
tokenPrefix: 'tfmcp_new',
|
|
1419
|
+
status: 'active',
|
|
1420
|
+
createdAt: '2026-03-02T00:00:00.000Z',
|
|
1421
|
+
lastUsedAt: null,
|
|
1422
|
+
}
|
|
1423
|
+
]
|
|
1424
|
+
})
|
|
1425
|
+
}));
|
|
1426
|
+
renderComponent({
|
|
1427
|
+
settingsModel: {
|
|
1428
|
+
runtimeMode: 'local',
|
|
1429
|
+
isAuthenticated: true,
|
|
1430
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1431
|
+
fetchCloudAuthApi
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1435
|
+
expect(await screen.findByText('Current Token')).toBeInTheDocument();
|
|
1436
|
+
expect(screen.getByText('Old Token')).toBeInTheDocument();
|
|
1437
|
+
const tokenLabels = screen.getAllByText(/^(Current Token|Old Token)$/);
|
|
1438
|
+
expect(tokenLabels[0]).toHaveTextContent('Current Token');
|
|
1439
|
+
expect(tokenLabels[1]).toHaveTextContent('Old Token');
|
|
1440
|
+
expect(screen.getAllByRole('button', { name: 'Regenerate' })).toHaveLength(1);
|
|
1441
|
+
expect(screen.getAllByRole('button', { name: 'Revoke' })).toHaveLength(1);
|
|
1442
|
+
}, 10000);
|
|
1443
|
+
it('4.23f Cloud MCP snippet keeps the server id short enough for client tool-name limits', async () => {
|
|
1444
|
+
const user = userEvent.setup();
|
|
1445
|
+
renderComponent({
|
|
1446
|
+
settingsModel: {
|
|
1447
|
+
runtimeMode: 'local',
|
|
1448
|
+
isAuthenticated: true,
|
|
1449
|
+
workspaceId: 'workspace-019c771a-b6d7-7bc8-b837-c1133f3ba7a2',
|
|
1450
|
+
currentWorkspaceName: 'Workspace Alpha',
|
|
1451
|
+
cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1455
|
+
await screen.findByText('Cloud MCP Configuration');
|
|
1456
|
+
const codeNodes = Array.from(document.querySelectorAll('code'));
|
|
1457
|
+
const snippetNode = codeNodes.find((node) => node.textContent?.includes('"mcpServers"'));
|
|
1458
|
+
expect(snippetNode).toBeTruthy();
|
|
1459
|
+
const parsed = JSON.parse(snippetNode?.textContent || '{}');
|
|
1460
|
+
const serverId = Object.keys(parsed?.mcpServers || {})[0] || '';
|
|
1461
|
+
expect(serverId.startsWith('taskforce-')).toBe(true);
|
|
1462
|
+
expect(serverId.length).toBeLessThanOrEqual(33);
|
|
1463
|
+
expect(`mcp_${serverId}_set_schedule_preferences`.length).toBeLessThanOrEqual(64);
|
|
1464
|
+
}, 10000);
|
|
1465
|
+
it('4.24 Local signed-out runtime shows local MCP only', async () => {
|
|
1466
|
+
const user = userEvent.setup();
|
|
1467
|
+
renderComponent({
|
|
1468
|
+
settingsModel: {
|
|
1469
|
+
runtimeMode: 'local',
|
|
1470
|
+
isAuthenticated: false
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
await user.click(screen.getByRole('button', { name: /MCP/i }));
|
|
1474
|
+
expect(screen.getByText('Local MCP')).toBeInTheDocument();
|
|
1475
|
+
expect(screen.getByText(/Claude Code CLI, Gemini CLI/i)).toBeInTheDocument();
|
|
1476
|
+
expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
|
|
1477
|
+
await user.click(screen.getByRole('button', { name: 'Advanced' }));
|
|
1478
|
+
expect(screen.getByText('MCP Path Mapping (Host Root)')).toBeInTheDocument();
|
|
1479
|
+
expect(screen.queryByText('Cloud MCP Configuration')).not.toBeInTheDocument();
|
|
1480
|
+
expect(screen.queryByRole('button', { name: 'Cloud MCP' })).not.toBeInTheDocument();
|
|
307
1481
|
});
|
|
308
1482
|
});
|