@taskforcehq/taskforce 0.3.305 → 0.3.306
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/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +215 -43
- package/dist/TaskforceCore.test.js +433 -28
- package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/assets/fonts/Noir_medium.woff2 +0 -0
- package/dist/assets/fonts/Noir_regular.woff2 +0 -0
- package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/compat/workspaceSyncCompat.js +46 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +549 -0
- package/dist/components/features/AgentsModule.d.ts +10 -1
- package/dist/components/features/AgentsModule.js +260 -15
- package/dist/components/features/AgentsModule.test.js +255 -5
- package/dist/components/features/DocumentViewer.d.ts +5 -1
- package/dist/components/features/DocumentViewer.js +60 -3
- package/dist/components/features/DocumentWorkspace.js +1 -1
- package/dist/components/features/DocumentWorkspace.test.js +33 -0
- package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
- package/dist/components/task/TaskCard.js +19 -28
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskForm.js +85 -90
- package/dist/components/task/TaskForm.test.js +110 -8
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +94 -13
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +70 -14
- package/dist/components/views/PlanComparisonPage.test.js +238 -0
- package/dist/components/views/PlansPage.js +127 -36
- package/dist/components/views/PlansPage.test.js +294 -12
- package/dist/components/views/StandaloneLayout.js +283 -533
- package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
- package/dist/components/views/panels/PlanningDrawer.js +18 -11
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
- package/dist/components/views/planningScope.d.ts +20 -0
- package/dist/components/views/planningScope.js +25 -0
- package/dist/components/views/planningScope.test.d.ts +1 -0
- package/dist/components/views/planningScope.test.js +60 -0
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
- package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
- package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
- package/dist/core/AiProfileService.d.ts +39 -2
- package/dist/core/AiProfileService.js +639 -73
- package/dist/core/AiProfileService.test.js +19 -1
- package/dist/core/AiProfiles.test.js +566 -7
- package/dist/core/AttachmentLinkService.js +57 -15
- package/dist/core/AuthIdentityService.d.ts +107 -0
- package/dist/core/AuthIdentityService.js +284 -0
- package/dist/core/AuthTokenService.d.ts +4 -0
- package/dist/core/AuthTokenService.js +34 -6
- package/dist/core/AuthTokenService.test.js +3 -3
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +23 -2
- package/dist/core/PlanEntitlementService.js +343 -57
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +35 -2
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +199 -27
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +42 -42
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/Taskforce.d.ts +225 -14
- package/dist/core/Taskforce.js +765 -192
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
- package/dist/core/WorkspaceLifecycleService.js +0 -5
- package/dist/core/WorkspacePermissions.test.js +25 -1
- package/dist/core/WorkspacePlanMode.test.js +117 -16
- package/dist/core/shared.d.ts +1 -1
- package/dist/core/shared.js +5 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/bootstrap.js +2 -0
- package/dist/hooks/sync/controlPlane.d.ts +37 -0
- package/dist/hooks/sync/controlPlane.js +78 -0
- package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
- package/dist/hooks/sync/controlPlane.test.js +121 -0
- package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
- package/dist/hooks/sync/lifecyclePolicy.js +93 -0
- package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
- package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
- package/dist/hooks/sync/orchestratorShared.js +68 -47
- package/dist/hooks/sync/orchestratorShared.test.js +50 -1
- package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
- package/dist/hooks/sync/pullLifecycle.js +24 -0
- package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
- package/dist/hooks/sync/pullLifecycle.test.js +80 -0
- package/dist/hooks/sync/recovery.d.ts +16 -2
- package/dist/hooks/sync/recovery.js +171 -53
- package/dist/hooks/sync/recovery.test.d.ts +1 -0
- package/dist/hooks/sync/recovery.test.js +292 -0
- package/dist/hooks/sync/transfers.d.ts +15 -3
- package/dist/hooks/sync/transfers.js +179 -62
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +396 -0
- package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
- package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
- package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
- package/dist/hooks/sync/useSyncStatusActions.js +88 -0
- package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
- package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
- package/dist/hooks/sync/useSyncStatusControls.js +60 -0
- package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
- package/dist/hooks/useSyncOrchestrator.js +359 -178
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskforce.d.ts +60 -2
- package/dist/hooks/useTaskforce.js +549 -240
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +6 -1
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
- package/dist/localization/locales/en-US.d.ts +0 -1
- package/dist/localization/locales/en-US.js +0 -1
- package/dist/localization/locales/es-419.js +0 -1
- package/dist/localization/locales/pt-BR.js +0 -1
- package/dist/mcp/documentAssetRegistrar.js +3 -3
- package/dist/mcp/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +131 -64
- package/dist/mcp/runtime.test.js +291 -12
- package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +171 -61
- package/dist/migrations/taskSchemaMigrations.test.js +15 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/dist/resources/templates/workflows/collaborate.yaml +1 -1
- package/dist/resources/templates/workflows/evaluate.yaml +2 -2
- package/dist/resources/templates/workflows/plan.yaml +1 -1
- package/dist/resources/templates/workflows/review.yaml +2 -2
- package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
- package/dist/server/auth/providers/apple.d.ts +33 -0
- package/dist/server/auth/providers/apple.js +82 -0
- package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
- package/dist/server/auth/providers/appleClientSecret.js +65 -0
- package/dist/server/auth/providers/github.d.ts +26 -0
- package/dist/server/auth/providers/github.js +86 -0
- package/dist/server/auth/providers/google.d.ts +21 -0
- package/dist/server/auth/providers/google.js +56 -0
- package/dist/server/auth/providers/types.d.ts +21 -0
- package/dist/server/auth/providers/types.js +1 -0
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +6 -3
- package/dist/server/documentReviewRoutes.test.js +36 -3
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +120 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +106 -3
- package/dist/server/routes/admin.d.ts +3 -0
- package/dist/server/routes/admin.js +272 -19
- package/dist/server/routes/auth.d.ts +15 -2
- package/dist/server/routes/auth.js +994 -57
- package/dist/server/routes/authSupport.d.ts +1 -1
- package/dist/server/routes/authSupport.js +1 -1
- package/dist/server/routes/billing.js +361 -35
- package/dist/server/routes/billing.test.js +592 -12
- package/dist/server/routes/documents.d.ts +1 -0
- package/dist/server/routes/documents.js +172 -70
- package/dist/server/routes/shared.d.ts +28 -2
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +61 -605
- package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
- package/dist/server/routes/syncAuxRoutes.js +91 -0
- package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
- package/dist/server/routes/syncAuxRoutes.test.js +158 -0
- package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
- package/dist/server/routes/syncPullApplyRoutes.js +204 -0
- package/dist/server/routes/syncPushRoutes.d.ts +24 -0
- package/dist/server/routes/syncPushRoutes.js +212 -0
- package/dist/server/routes/syncRouteGuards.d.ts +36 -0
- package/dist/server/routes/syncRouteGuards.js +67 -0
- package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
- package/dist/server/routes/syncRouteGuards.test.js +94 -0
- package/dist/server/routes/syncRouteTypes.d.ts +13 -0
- package/dist/server/routes/syncRouteTypes.js +1 -0
- package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
- package/dist/server/routes/syncSnapshotRoutes.js +180 -0
- package/dist/server/routes/tasks.js +43 -1
- package/dist/server/routes/workspaces.js +36 -5
- package/dist/server/routes.js +60 -3
- package/dist/server/routes.test.js +1824 -171
- package/dist/services/workflowExportSnapshots.test.js +2 -0
- package/dist/shared/aiProfileSeatScope.d.ts +5 -0
- package/dist/shared/aiProfileSeatScope.js +21 -0
- package/dist/shared/passwordPolicy.d.ts +13 -0
- package/dist/shared/passwordPolicy.js +84 -0
- package/dist/shared/passwordPolicy.test.d.ts +1 -0
- package/dist/shared/passwordPolicy.test.js +34 -0
- package/dist/storage/workspaceAssetStore.d.ts +15 -3
- package/dist/storage/workspaceAssetStore.js +132 -44
- package/dist/storage/workspaceAssetStore.test.js +93 -6
- package/dist/styles/fonts.css +70 -0
- package/dist/sync/collaborationSyncPayload.d.ts +6 -1
- package/dist/sync/collaborationSyncPayload.js +17 -11
- package/dist/sync/collaborationSyncPayload.test.js +13 -1
- package/dist/sync/contentSyncState.d.ts +4 -8
- package/dist/sync/contentSyncState.js +17 -70
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +70 -3
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +44 -0
- package/dist/sync/syncResourceAdapters.js +77 -0
- package/dist/sync/syncService.d.ts +5 -0
- package/dist/sync/syncService.js +52 -0
- package/dist/sync/syncService.test.js +57 -3
- package/dist/sync/taskSyncChangeKey.d.ts +2 -0
- package/dist/sync/taskSyncChangeKey.js +143 -0
- package/dist/sync/taskSyncPayload.js +3 -8
- package/dist/sync/workspacePullFeed.js +4 -3
- package/dist/sync/workspacePullFeed.test.js +43 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +45 -0
- package/dist/sync/workspaceSyncModel.js +183 -28
- package/dist/sync/workspaceSyncModel.test.js +291 -12
- package/dist/sync/workspaceSyncState.d.ts +17 -0
- package/dist/sync/workspaceSyncState.js +2 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +17 -0
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
- package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
- package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
- package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
- package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
- package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
- package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
- package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
- package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
- package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
- package/dist/ui/assets/index-C6k75jr8.js +6 -0
- package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
- package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/ui/fonts/Noir_medium.otf +0 -0
- package/dist/ui/fonts/Noir_medium.woff2 +0 -0
- package/dist/ui/fonts/Noir_regular.otf +0 -0
- package/dist/ui/fonts/Noir_regular.woff2 +0 -0
- package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/assignees.d.ts +2 -0
- package/dist/utils/assignees.js +2 -0
- package/dist/utils/avatarUpload.d.ts +27 -0
- package/dist/utils/avatarUpload.js +132 -0
- package/dist/utils/avatarUpload.test.d.ts +1 -0
- package/dist/utils/avatarUpload.test.js +40 -0
- package/dist/utils/bootstrapTrace.d.ts +7 -0
- package/dist/utils/bootstrapTrace.js +29 -0
- package/dist/utils/commercialLifecyclePresentation.js +3 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/syncEventDetails.d.ts +3 -0
- package/dist/utils/syncEventDetails.js +64 -0
- package/dist/utils/syncEventDetails.test.d.ts +1 -0
- package/dist/utils/syncEventDetails.test.js +23 -0
- package/dist/utils/syncEventPresentation.d.ts +15 -0
- package/dist/utils/syncEventPresentation.js +129 -0
- package/dist/utils/syncEventPresentation.test.d.ts +1 -0
- package/dist/utils/syncEventPresentation.test.js +64 -0
- package/dist/utils/syncStatusPresentation.d.ts +72 -0
- package/dist/utils/syncStatusPresentation.js +217 -0
- package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
- package/dist/utils/syncStatusPresentation.test.js +164 -0
- package/dist/utils/taskActivity.d.ts +2 -0
- package/dist/utils/taskActivity.js +228 -22
- package/dist/utils/taskActivity.test.js +119 -8
- package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
- package/dist/utils/workspaceSyncPresentation.js +68 -2
- package/dist/utils/workspaceSyncPresentation.test.js +36 -1
- package/package.json +3 -2
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/run-e2e-realtime.sh +17 -3
- package/scripts/run-smoke.sh +17 -5
- package/scripts/run-soak.sh +17 -5
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/src/resources/templates/workflows/collaborate.yaml +1 -1
- package/src/resources/templates/workflows/evaluate.yaml +2 -2
- package/src/resources/templates/workflows/plan.yaml +1 -1
- package/src/resources/templates/workflows/review.yaml +2 -2
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/index-C2-OXZV7.css +0 -1
- package/dist/ui/assets/index-DcSI5F86.js +0 -6
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -7,6 +7,28 @@ import { TaskforceCore } from './TaskforceCore';
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import modalStyles from './components/ui/Modal.module.css';
|
|
10
|
+
vi.mock('react-avatar-editor', async () => {
|
|
11
|
+
const React = await import('react');
|
|
12
|
+
return {
|
|
13
|
+
default: React.forwardRef((_props, ref) => {
|
|
14
|
+
React.useImperativeHandle(ref, () => ({
|
|
15
|
+
getImageScaledToCanvas: () => ({
|
|
16
|
+
width: 1024,
|
|
17
|
+
height: 1024,
|
|
18
|
+
getContext: () => ({
|
|
19
|
+
drawImage: () => undefined
|
|
20
|
+
}),
|
|
21
|
+
toBlob: (callback, type) => {
|
|
22
|
+
callback(new Blob([new Uint8Array([1, 2, 3, 4])], { type: type || 'image/webp' }));
|
|
23
|
+
}
|
|
24
|
+
}),
|
|
25
|
+
getImage: () => document.createElement('canvas'),
|
|
26
|
+
getCroppingRect: () => ({ x: 0, y: 0, width: 1, height: 1 })
|
|
27
|
+
}));
|
|
28
|
+
return _jsx("div", { "data-testid": "avatar-editor" });
|
|
29
|
+
})
|
|
30
|
+
};
|
|
31
|
+
});
|
|
10
32
|
const mockTasks = [
|
|
11
33
|
{
|
|
12
34
|
id: 'task-20260130-abc123',
|
|
@@ -1085,7 +1107,6 @@ describe('TaskforceCore Integration', () => {
|
|
|
1085
1107
|
render(_jsx(MemoryRouter, { initialEntries: ['/'], children: _jsx(TaskforceCore, {}) }));
|
|
1086
1108
|
await waitFor(() => {
|
|
1087
1109
|
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
1088
|
-
expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
|
|
1089
1110
|
});
|
|
1090
1111
|
});
|
|
1091
1112
|
it('redirects authenticated users with a blocking account access gate to the plans page', async () => {
|
|
@@ -1789,11 +1810,12 @@ describe('TaskforceCore Integration', () => {
|
|
|
1789
1810
|
if (url.includes('/api/taskforce/workspace/assignee-options')) {
|
|
1790
1811
|
return jsonResponse({ assignees: [] });
|
|
1791
1812
|
}
|
|
1792
|
-
if (url.includes('/api/taskforce/
|
|
1793
|
-
return jsonResponse(
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1813
|
+
if (url.includes('/api/taskforce/planning/bootstrap')) {
|
|
1814
|
+
return jsonResponse({
|
|
1815
|
+
initiatives: [],
|
|
1816
|
+
workstreams: [],
|
|
1817
|
+
workstreamTaskSummaries: [],
|
|
1818
|
+
});
|
|
1797
1819
|
}
|
|
1798
1820
|
return jsonResponse({});
|
|
1799
1821
|
});
|
|
@@ -1820,7 +1842,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
1820
1842
|
await user.click(screen.getByRole('button', { name: 'Register' }));
|
|
1821
1843
|
await user.type(await screen.findByPlaceholderText('Email'), accounts.newcomer.email);
|
|
1822
1844
|
await user.type(screen.getByPlaceholderText('Display name'), accounts.newcomer.displayName);
|
|
1823
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
1845
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
1846
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
1824
1847
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
1825
1848
|
await waitFor(() => {
|
|
1826
1849
|
expect(screen.getByTitle(/Account/i)).toBeInTheDocument();
|
|
@@ -1902,6 +1925,7 @@ describe('TaskforceCore Integration', () => {
|
|
|
1902
1925
|
expect(screen.getByText('Create your Taskforce account.')).toBeInTheDocument();
|
|
1903
1926
|
expect(screen.getByLabelText('Email')).toBeInTheDocument();
|
|
1904
1927
|
expect(screen.getByLabelText('Display name')).toBeInTheDocument();
|
|
1928
|
+
expect(screen.getByLabelText('Confirm password')).toBeInTheDocument();
|
|
1905
1929
|
expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
|
|
1906
1930
|
expect(screen.getByPlaceholderText('Display name')).toBeInTheDocument();
|
|
1907
1931
|
expect(screen.getByRole('button', { name: 'Create Account' })).toBeInTheDocument();
|
|
@@ -1913,6 +1937,56 @@ describe('TaskforceCore Integration', () => {
|
|
|
1913
1937
|
});
|
|
1914
1938
|
expect(container.querySelector('[aria-pressed]')).not.toBeInTheDocument();
|
|
1915
1939
|
});
|
|
1940
|
+
it('blocks registration when the confirmation does not match', async () => {
|
|
1941
|
+
const user = userEvent.setup();
|
|
1942
|
+
const fetchMock = vi.fn((url) => {
|
|
1943
|
+
if (url.includes('/api/taskforce/auth/session')) {
|
|
1944
|
+
return Promise.resolve({
|
|
1945
|
+
ok: true,
|
|
1946
|
+
json: () => Promise.resolve({
|
|
1947
|
+
authenticated: false,
|
|
1948
|
+
runtimeMode: 'cloud',
|
|
1949
|
+
authRequiredForApi: false
|
|
1950
|
+
})
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
if (url.includes('/api/taskforce/config')) {
|
|
1954
|
+
return Promise.resolve({
|
|
1955
|
+
ok: true,
|
|
1956
|
+
json: () => Promise.resolve({
|
|
1957
|
+
...mockConfig,
|
|
1958
|
+
runtimeMode: 'cloud',
|
|
1959
|
+
authRequiredForApi: false,
|
|
1960
|
+
setupState: {
|
|
1961
|
+
globalSetupState: 'present',
|
|
1962
|
+
workspaceSetupState: 'present',
|
|
1963
|
+
runtimeMode: 'cloud',
|
|
1964
|
+
workspaceId: 'workspace-local-active',
|
|
1965
|
+
mode: 'core',
|
|
1966
|
+
forceSetup: false,
|
|
1967
|
+
forceGlobalSetup: false,
|
|
1968
|
+
forceWorkspaceSetup: false
|
|
1969
|
+
}
|
|
1970
|
+
})
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
if (url.includes('/api/taskforce/workflow-templates')) {
|
|
1974
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve({ templates: [] }) });
|
|
1975
|
+
}
|
|
1976
|
+
return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
|
|
1977
|
+
});
|
|
1978
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
1979
|
+
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
1980
|
+
await user.type(await screen.findByPlaceholderText('Email'), 'mismatch@example.com');
|
|
1981
|
+
await user.type(screen.getByPlaceholderText('Display name'), 'Mismatch User');
|
|
1982
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
1983
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'DifferentPassphrase123!');
|
|
1984
|
+
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
1985
|
+
await waitFor(() => {
|
|
1986
|
+
expect(screen.getByText('Passwords do not match.')).toBeInTheDocument();
|
|
1987
|
+
});
|
|
1988
|
+
expect(fetchMock.mock.calls.some((call) => String(call[0]).includes('/api/taskforce/auth/register'))).toBe(false);
|
|
1989
|
+
});
|
|
1916
1990
|
it('keeps the verification screen focused on verification and lets the user request a fresh email token', async () => {
|
|
1917
1991
|
const fetchMock = vi.fn((url) => {
|
|
1918
1992
|
if (url.includes('/api/taskforce/auth/session')) {
|
|
@@ -2204,7 +2278,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
2204
2278
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
|
|
2205
2279
|
await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
|
|
2206
2280
|
await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
|
|
2207
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
2281
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
2282
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2208
2283
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2209
2284
|
await waitFor(() => {
|
|
2210
2285
|
expect(screen.getByText('Configure your new workspace.')).toBeInTheDocument();
|
|
@@ -2362,11 +2437,11 @@ describe('TaskforceCore Integration', () => {
|
|
|
2362
2437
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2363
2438
|
await user.type(await screen.findByPlaceholderText('Email'), 'pending@example.com');
|
|
2364
2439
|
await user.type(screen.getByPlaceholderText('Display name'), 'Pending User');
|
|
2365
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
2440
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
2441
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2366
2442
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2367
2443
|
await waitFor(() => {
|
|
2368
2444
|
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
2369
|
-
expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
|
|
2370
2445
|
});
|
|
2371
2446
|
expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/api/taskforce/billing/checkout-session'))).toBe(false);
|
|
2372
2447
|
});
|
|
@@ -2478,11 +2553,148 @@ describe('TaskforceCore Integration', () => {
|
|
|
2478
2553
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2479
2554
|
await user.type(await screen.findByPlaceholderText('Email'), 'brandnew@example.com');
|
|
2480
2555
|
await user.type(screen.getByPlaceholderText('Display name'), 'Brand New');
|
|
2481
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
2556
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
2557
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2482
2558
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2483
2559
|
await waitFor(() => {
|
|
2484
2560
|
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
2485
|
-
|
|
2561
|
+
});
|
|
2562
|
+
expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
|
|
2563
|
+
});
|
|
2564
|
+
it('keeps registration on plan selection while the post-auth cloud session is still resolving for a commercially gated user with no workspace', async () => {
|
|
2565
|
+
const user = userEvent.setup();
|
|
2566
|
+
let authenticated = false;
|
|
2567
|
+
let authSessionCallCount = 0;
|
|
2568
|
+
let releaseInitialSession = null;
|
|
2569
|
+
const initialSessionGate = new Promise((resolve) => {
|
|
2570
|
+
releaseInitialSession = resolve;
|
|
2571
|
+
});
|
|
2572
|
+
const fetchMock = vi.fn((input) => {
|
|
2573
|
+
const url = String(input);
|
|
2574
|
+
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
2575
|
+
return jsonResponse({
|
|
2576
|
+
config: {
|
|
2577
|
+
baseUrl: 'https://app.taskforce.example',
|
|
2578
|
+
apiBaseUrl: 'https://app.taskforce.example/api',
|
|
2579
|
+
cloudAuthBaseUrl: 'https://app.taskforce.example',
|
|
2580
|
+
runtimeMode: 'cloud',
|
|
2581
|
+
authSource: 'cloud',
|
|
2582
|
+
workspaceMode: 'multi-cloud',
|
|
2583
|
+
workspaceSwitchingEnabled: true,
|
|
2584
|
+
authRequiredForApi: true
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
if (url.includes('/api/taskforce/auth/session')) {
|
|
2589
|
+
authSessionCallCount += 1;
|
|
2590
|
+
const payload = () => ({
|
|
2591
|
+
authenticated,
|
|
2592
|
+
runtimeMode: 'cloud',
|
|
2593
|
+
authRequiredForApi: true,
|
|
2594
|
+
userId: authenticated ? 'user-gated-pending' : null,
|
|
2595
|
+
workspaceId: null,
|
|
2596
|
+
betaAccess: true
|
|
2597
|
+
});
|
|
2598
|
+
if (authSessionCallCount === 1) {
|
|
2599
|
+
return initialSessionGate.then(() => jsonResponse(payload()));
|
|
2600
|
+
}
|
|
2601
|
+
return jsonResponse(payload());
|
|
2602
|
+
}
|
|
2603
|
+
if (url.includes('/api/taskforce/config')) {
|
|
2604
|
+
return jsonResponse({
|
|
2605
|
+
...mockConfig,
|
|
2606
|
+
runtimeMode: 'cloud',
|
|
2607
|
+
authRequiredForApi: true,
|
|
2608
|
+
workspaceSwitchingEnabled: true,
|
|
2609
|
+
setupState: {
|
|
2610
|
+
globalSetupState: 'present',
|
|
2611
|
+
workspaceSetupState: 'missing',
|
|
2612
|
+
runtimeMode: 'cloud',
|
|
2613
|
+
workspaceId: 'default',
|
|
2614
|
+
mode: 'core',
|
|
2615
|
+
forceSetup: true,
|
|
2616
|
+
forceGlobalSetup: false,
|
|
2617
|
+
forceWorkspaceSetup: true
|
|
2618
|
+
}
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
if (url.includes('/api/taskforce/workspaces')) {
|
|
2622
|
+
return jsonResponse({
|
|
2623
|
+
success: true,
|
|
2624
|
+
workspaces: [],
|
|
2625
|
+
currentWorkspaceId: 'default'
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
if (url.includes('/api/taskforce/account/access-status')) {
|
|
2629
|
+
return jsonResponse({
|
|
2630
|
+
gate: 'plan_selection_required',
|
|
2631
|
+
hasEntitlement: true,
|
|
2632
|
+
canAccessApp: false,
|
|
2633
|
+
canAccessPlans: true,
|
|
2634
|
+
message: 'Choose a plan to continue.'
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
if (url.includes('/api/taskforce/account/profile-summary')) {
|
|
2638
|
+
return jsonResponse({
|
|
2639
|
+
entitlementState: 'pending_plan_selection',
|
|
2640
|
+
gate: 'plan_selection_required',
|
|
2641
|
+
message: 'Choose a plan to continue.',
|
|
2642
|
+
workspaceId: null
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
if (url.includes('/api/taskforce/public/pricing')) {
|
|
2646
|
+
return jsonResponse({ plans: [] });
|
|
2647
|
+
}
|
|
2648
|
+
if (url.includes('/api/taskforce/auth/register')) {
|
|
2649
|
+
authenticated = true;
|
|
2650
|
+
return jsonResponse({
|
|
2651
|
+
success: true,
|
|
2652
|
+
workspaceSetupRequired: true,
|
|
2653
|
+
verificationRequired: false,
|
|
2654
|
+
planSelectionRequired: true,
|
|
2655
|
+
commercialState: 'pending_plan_selection'
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
if (url.includes('/api/taskforce/categories'))
|
|
2659
|
+
return jsonResponse(mockCategories);
|
|
2660
|
+
if (url.includes('/api/taskforce/tasks'))
|
|
2661
|
+
return jsonResponse({ tasks: mockTasks });
|
|
2662
|
+
if (url.includes('/api/taskforce/archive'))
|
|
2663
|
+
return jsonResponse({ archived: [] });
|
|
2664
|
+
if (url.includes('/api/taskforce/data-version'))
|
|
2665
|
+
return jsonResponse({ dataVersion: 1 });
|
|
2666
|
+
if (url.includes('/api/taskforce/types'))
|
|
2667
|
+
return jsonResponse({ types: [] });
|
|
2668
|
+
if (url.includes('/api/taskforce/priorities'))
|
|
2669
|
+
return jsonResponse({ priorities: [] });
|
|
2670
|
+
if (url.includes('/api/taskforce/approaches'))
|
|
2671
|
+
return jsonResponse({ approaches: [] });
|
|
2672
|
+
if (url.includes('/api/taskforce/specialists'))
|
|
2673
|
+
return jsonResponse({ specialists: [] });
|
|
2674
|
+
if (url.includes('/api/taskforce/workflow-templates'))
|
|
2675
|
+
return jsonResponse({ templates: [] });
|
|
2676
|
+
return jsonResponse({});
|
|
2677
|
+
});
|
|
2678
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
2679
|
+
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2680
|
+
await user.type(await screen.findByPlaceholderText('Email'), 'gated-pending@example.com');
|
|
2681
|
+
await user.type(screen.getByPlaceholderText('Display name'), 'Gated Pending');
|
|
2682
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
2683
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2684
|
+
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2685
|
+
await act(async () => {
|
|
2686
|
+
await Promise.resolve();
|
|
2687
|
+
});
|
|
2688
|
+
await waitFor(() => {
|
|
2689
|
+
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
2690
|
+
});
|
|
2691
|
+
expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
|
|
2692
|
+
await act(async () => {
|
|
2693
|
+
releaseInitialSession?.();
|
|
2694
|
+
await Promise.resolve();
|
|
2695
|
+
});
|
|
2696
|
+
await waitFor(() => {
|
|
2697
|
+
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
2486
2698
|
});
|
|
2487
2699
|
expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
|
|
2488
2700
|
});
|
|
@@ -2653,7 +2865,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
2653
2865
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&planId=basic&planVersionId=basic-v1&interval=year&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2654
2866
|
await user.type(await screen.findByPlaceholderText('Email'), 'checkout@example.com');
|
|
2655
2867
|
await user.type(screen.getByPlaceholderText('Display name'), 'Checkout User');
|
|
2656
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
2868
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
2869
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2657
2870
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2658
2871
|
await waitFor(() => {
|
|
2659
2872
|
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
@@ -2786,7 +2999,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
2786
2999
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2787
3000
|
await user.type(await screen.findByPlaceholderText('Email'), 'free-user@example.com');
|
|
2788
3001
|
await user.type(screen.getByPlaceholderText('Display name'), 'Free User');
|
|
2789
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
3002
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
3003
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2790
3004
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2791
3005
|
await act(async () => {
|
|
2792
3006
|
await Promise.resolve();
|
|
@@ -2912,7 +3126,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
2912
3126
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
2913
3127
|
await user.type(await screen.findByPlaceholderText('Email'), 'existing-workspace@example.com');
|
|
2914
3128
|
await user.type(screen.getByPlaceholderText('Display name'), 'Existing Workspace User');
|
|
2915
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
3129
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
3130
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2916
3131
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2917
3132
|
await act(async () => {
|
|
2918
3133
|
await Promise.resolve();
|
|
@@ -2982,7 +3197,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
2982
3197
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
|
|
2983
3198
|
await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
|
|
2984
3199
|
await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
|
|
2985
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
3200
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
3201
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
2986
3202
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
2987
3203
|
await waitFor(() => {
|
|
2988
3204
|
expect(screen.getByPlaceholderText('Verification token')).toBeInTheDocument();
|
|
@@ -2990,6 +3206,156 @@ describe('TaskforceCore Integration', () => {
|
|
|
2990
3206
|
expect(screen.queryByRole('button', { name: 'Create Account' })).not.toBeInTheDocument();
|
|
2991
3207
|
expect(screen.queryByText('Subscription Plans')).not.toBeInTheDocument();
|
|
2992
3208
|
});
|
|
3209
|
+
it('keeps verified registration on plan selection when commercial onboarding is still blocked', async () => {
|
|
3210
|
+
const user = userEvent.setup();
|
|
3211
|
+
let authenticated = false;
|
|
3212
|
+
const fetchMock = vi.fn((input) => {
|
|
3213
|
+
const url = String(input);
|
|
3214
|
+
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
3215
|
+
return jsonResponse({
|
|
3216
|
+
config: {
|
|
3217
|
+
baseUrl: 'https://app.taskforce.example',
|
|
3218
|
+
apiBaseUrl: 'https://app.taskforce.example/api',
|
|
3219
|
+
cloudAuthBaseUrl: 'https://app.taskforce.example',
|
|
3220
|
+
runtimeMode: 'cloud',
|
|
3221
|
+
authSource: 'cloud',
|
|
3222
|
+
workspaceMode: 'multi-cloud',
|
|
3223
|
+
workspaceSwitchingEnabled: true,
|
|
3224
|
+
authRequiredForApi: true
|
|
3225
|
+
}
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
if (url.includes('/api/taskforce/auth/session')) {
|
|
3229
|
+
return jsonResponse({
|
|
3230
|
+
authenticated,
|
|
3231
|
+
runtimeMode: 'cloud',
|
|
3232
|
+
authRequiredForApi: true,
|
|
3233
|
+
userId: authenticated ? 'user-verify' : null,
|
|
3234
|
+
workspaceId: authenticated ? 'default' : null,
|
|
3235
|
+
betaAccess: true
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
if (url.includes('/api/taskforce/config')) {
|
|
3239
|
+
return jsonResponse({
|
|
3240
|
+
...mockConfig,
|
|
3241
|
+
runtimeMode: 'cloud',
|
|
3242
|
+
authRequiredForApi: true,
|
|
3243
|
+
workspaceSwitchingEnabled: true,
|
|
3244
|
+
setupState: {
|
|
3245
|
+
globalSetupState: 'present',
|
|
3246
|
+
workspaceSetupState: authenticated ? 'missing' : 'present',
|
|
3247
|
+
runtimeMode: 'cloud',
|
|
3248
|
+
workspaceId: 'default',
|
|
3249
|
+
mode: 'core',
|
|
3250
|
+
forceSetup: false,
|
|
3251
|
+
forceGlobalSetup: false,
|
|
3252
|
+
forceWorkspaceSetup: false
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
if (url.includes('/api/taskforce/workspaces')) {
|
|
3257
|
+
return jsonResponse({
|
|
3258
|
+
success: true,
|
|
3259
|
+
workspaces: [],
|
|
3260
|
+
currentWorkspaceId: null
|
|
3261
|
+
});
|
|
3262
|
+
}
|
|
3263
|
+
if (url.includes('/api/taskforce/account/access-status')) {
|
|
3264
|
+
return jsonResponse({
|
|
3265
|
+
gate: 'plan_selection_required',
|
|
3266
|
+
hasEntitlement: true,
|
|
3267
|
+
canAccessApp: false,
|
|
3268
|
+
canAccessPlans: true,
|
|
3269
|
+
message: 'Choose a plan to continue.'
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
if (url.includes('/api/taskforce/account/profile-summary')) {
|
|
3273
|
+
return jsonResponse({
|
|
3274
|
+
entitlementState: 'pending_plan_selection',
|
|
3275
|
+
gate: 'plan_selection_required',
|
|
3276
|
+
message: 'Choose a plan to continue.',
|
|
3277
|
+
workspaceId: null
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3280
|
+
if (url.includes('/api/taskforce/public/pricing')) {
|
|
3281
|
+
return jsonResponse({
|
|
3282
|
+
plans: [{
|
|
3283
|
+
planId: 'free',
|
|
3284
|
+
displayName: 'Free',
|
|
3285
|
+
status: 'active',
|
|
3286
|
+
defaultVersion: {
|
|
3287
|
+
planVersionId: 'free-v1',
|
|
3288
|
+
versionNumber: 1,
|
|
3289
|
+
seatLimit: null,
|
|
3290
|
+
metadata: {}
|
|
3291
|
+
},
|
|
3292
|
+
features: [],
|
|
3293
|
+
pricing: {
|
|
3294
|
+
month: {
|
|
3295
|
+
planVersionId: 'free-v1',
|
|
3296
|
+
interval: 'month',
|
|
3297
|
+
pricingType: 'free',
|
|
3298
|
+
stripePriceId: 'free:free-v1:month',
|
|
3299
|
+
displayAmount: '$0.00',
|
|
3300
|
+
unitAmount: 0,
|
|
3301
|
+
currency: 'usd'
|
|
3302
|
+
},
|
|
3303
|
+
year: null
|
|
3304
|
+
}
|
|
3305
|
+
}]
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3308
|
+
if (url.includes('/api/taskforce/auth/register')) {
|
|
3309
|
+
return jsonResponse({
|
|
3310
|
+
success: true,
|
|
3311
|
+
workspaceSetupRequired: true,
|
|
3312
|
+
verificationRequired: true,
|
|
3313
|
+
verificationToken: 'verify-token',
|
|
3314
|
+
emailSent: true,
|
|
3315
|
+
planSelectionRequired: true,
|
|
3316
|
+
commercialState: 'pending_plan_selection'
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
if (url.includes('/api/taskforce/auth/verify-email/confirm')) {
|
|
3320
|
+
authenticated = true;
|
|
3321
|
+
return jsonResponse({ success: true });
|
|
3322
|
+
}
|
|
3323
|
+
if (url.includes('/api/taskforce/categories'))
|
|
3324
|
+
return jsonResponse(mockCategories);
|
|
3325
|
+
if (url.includes('/api/taskforce/tasks'))
|
|
3326
|
+
return jsonResponse({ tasks: mockTasks });
|
|
3327
|
+
if (url.includes('/api/taskforce/archive'))
|
|
3328
|
+
return jsonResponse({ archived: [] });
|
|
3329
|
+
if (url.includes('/api/taskforce/data-version'))
|
|
3330
|
+
return jsonResponse({ dataVersion: 1 });
|
|
3331
|
+
if (url.includes('/api/taskforce/types'))
|
|
3332
|
+
return jsonResponse({ types: [] });
|
|
3333
|
+
if (url.includes('/api/taskforce/priorities'))
|
|
3334
|
+
return jsonResponse({ priorities: [] });
|
|
3335
|
+
if (url.includes('/api/taskforce/approaches'))
|
|
3336
|
+
return jsonResponse({ approaches: [] });
|
|
3337
|
+
if (url.includes('/api/taskforce/specialists'))
|
|
3338
|
+
return jsonResponse({ specialists: [] });
|
|
3339
|
+
if (url.includes('/api/taskforce/workflow-templates'))
|
|
3340
|
+
return jsonResponse({ templates: [] });
|
|
3341
|
+
return jsonResponse({});
|
|
3342
|
+
});
|
|
3343
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
3344
|
+
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
3345
|
+
await user.type(await screen.findByPlaceholderText('Email'), 'verify-gated@example.com');
|
|
3346
|
+
await user.type(screen.getByPlaceholderText('Display name'), 'Verify Gated User');
|
|
3347
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
3348
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
3349
|
+
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
3350
|
+
await waitFor(() => {
|
|
3351
|
+
expect(screen.getByPlaceholderText('Verification token')).toHaveValue('verify-token');
|
|
3352
|
+
});
|
|
3353
|
+
await user.click(screen.getByRole('button', { name: 'Verify Email' }));
|
|
3354
|
+
await waitFor(() => {
|
|
3355
|
+
expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
|
|
3356
|
+
});
|
|
3357
|
+
expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
|
|
3358
|
+
});
|
|
2993
3359
|
it('allows returning to sign-in/register from forgot mode in local runtime', async () => {
|
|
2994
3360
|
const user = userEvent.setup();
|
|
2995
3361
|
const fetchMock = vi.fn((url) => {
|
|
@@ -3291,7 +3657,8 @@ describe('TaskforceCore Integration', () => {
|
|
|
3291
3657
|
render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
|
|
3292
3658
|
await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
|
|
3293
3659
|
await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
|
|
3294
|
-
await user.type(screen.getByPlaceholderText('Password'), '
|
|
3660
|
+
await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
|
|
3661
|
+
await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
|
|
3295
3662
|
await user.click(screen.getByRole('button', { name: 'Create Account' }));
|
|
3296
3663
|
await waitFor(() => {
|
|
3297
3664
|
expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
|
|
@@ -3677,6 +4044,7 @@ describe('TaskforceCore Integration', () => {
|
|
|
3677
4044
|
await waitFor(() => {
|
|
3678
4045
|
expect(screen.getByText('Create your account to join this workspace.')).toBeInTheDocument();
|
|
3679
4046
|
expect(screen.getByPlaceholderText('Create password')).toBeInTheDocument();
|
|
4047
|
+
expect(screen.getByPlaceholderText('Confirm password')).toBeInTheDocument();
|
|
3680
4048
|
expect(screen.getByRole('button', { name: 'Create Account and Join' })).toBeInTheDocument();
|
|
3681
4049
|
});
|
|
3682
4050
|
});
|
|
@@ -5417,6 +5785,15 @@ describe('TaskforceCore Integration', () => {
|
|
|
5417
5785
|
expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/account/')");
|
|
5418
5786
|
expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/billing/')");
|
|
5419
5787
|
});
|
|
5788
|
+
it('keeps plans on a dedicated surface while preserving the faster exit flow', () => {
|
|
5789
|
+
const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
|
|
5790
|
+
const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
|
|
5791
|
+
expect(standaloneLayoutSource).toContain('{!isPlansScreen && <WorkspaceHeaderActions actions={universalFilterActions} />}');
|
|
5792
|
+
expect(standaloneLayoutSource).toContain('{!isPlansScreen && showFilters && workspaceFilterBar}');
|
|
5793
|
+
expect(standaloneLayoutSource).not.toMatch(/\{isPlansScreen \? \([\s\S]*\{workspaceToolRail\}[\s\S]*<PlansPage/);
|
|
5794
|
+
expect(standaloneLayoutSource).toContain('closePlansScreen();');
|
|
5795
|
+
expect(standaloneLayoutSource).toContain('void (async () => {');
|
|
5796
|
+
});
|
|
5420
5797
|
it('uses shared /login flow for local sign in instead of a duplicate modal', async () => {
|
|
5421
5798
|
const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
|
|
5422
5799
|
const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
|
|
@@ -5601,11 +5978,12 @@ describe('TaskforceCore Integration', () => {
|
|
|
5601
5978
|
if (url.includes('/api/taskforce/workflow-templates')) {
|
|
5602
5979
|
return jsonResponse({ templates: [] });
|
|
5603
5980
|
}
|
|
5604
|
-
if (url.includes('/api/taskforce/
|
|
5605
|
-
return jsonResponse(
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5981
|
+
if (url.includes('/api/taskforce/planning/bootstrap')) {
|
|
5982
|
+
return jsonResponse({
|
|
5983
|
+
initiatives: [],
|
|
5984
|
+
workstreams: [],
|
|
5985
|
+
workstreamTaskSummaries: [],
|
|
5986
|
+
});
|
|
5609
5987
|
}
|
|
5610
5988
|
return jsonResponse({});
|
|
5611
5989
|
});
|
|
@@ -5947,9 +6325,9 @@ describe('TaskforceCore Integration', () => {
|
|
|
5947
6325
|
occurredAt: '2026-03-15T14:28:17.000Z',
|
|
5948
6326
|
eventType: 'apply',
|
|
5949
6327
|
status: 'error',
|
|
5950
|
-
errorMessage: 'Detected
|
|
6328
|
+
errorMessage: 'Detected task reference number mismatch during normal sync.',
|
|
5951
6329
|
details: {
|
|
5952
|
-
|
|
6330
|
+
taskId: 'task-1',
|
|
5953
6331
|
existingReferenceNumber: 12,
|
|
5954
6332
|
incomingReferenceNumber: 18
|
|
5955
6333
|
}
|
|
@@ -7183,6 +7561,7 @@ describe('TaskforceCore Integration', () => {
|
|
|
7183
7561
|
const user = userEvent.setup();
|
|
7184
7562
|
let savedDisplayName = 'Original Name';
|
|
7185
7563
|
let savedAvatarUrl = '';
|
|
7564
|
+
const cloudAuthBaseUrl = 'https://performance-app.taskforcehq.ai';
|
|
7186
7565
|
const fetchMock = vi.fn((input, init) => {
|
|
7187
7566
|
const url = String(input);
|
|
7188
7567
|
if (url.includes('/api/taskforce/auth/runtime-config')) {
|
|
@@ -7222,6 +7601,9 @@ describe('TaskforceCore Integration', () => {
|
|
|
7222
7601
|
...mockConfig,
|
|
7223
7602
|
runtimeMode: 'local',
|
|
7224
7603
|
authRequiredForApi: false,
|
|
7604
|
+
apiBaseUrl: cloudAuthBaseUrl,
|
|
7605
|
+
cloudAuthBaseUrl,
|
|
7606
|
+
cloudAuthViaLocalProxy: true,
|
|
7225
7607
|
workspaceId: 'workspace-local-active',
|
|
7226
7608
|
setupState: {
|
|
7227
7609
|
globalSetupState: 'present',
|
|
@@ -7257,13 +7639,12 @@ describe('TaskforceCore Integration', () => {
|
|
|
7257
7639
|
return jsonResponse({
|
|
7258
7640
|
success: true,
|
|
7259
7641
|
draftId: 'avatar-draft-1',
|
|
7260
|
-
uploadUrl: 'https://upload.example.com/avatar-draft-1',
|
|
7261
7642
|
relativePath: 'users/user-1/profile/avatar/avatar-draft-1-avatar.png',
|
|
7262
7643
|
path: '/api/taskforce/context/users%2Fuser-1%2Fprofile%2Favatar%2Favatar-draft-1-avatar.png'
|
|
7263
7644
|
});
|
|
7264
7645
|
}
|
|
7265
|
-
if (url.includes('
|
|
7266
|
-
return
|
|
7646
|
+
if (url.includes('/api/taskforce/auth/profile/avatar/upload')) {
|
|
7647
|
+
return jsonResponse({ success: true, draftId: 'avatar-draft-1' });
|
|
7267
7648
|
}
|
|
7268
7649
|
if (url.includes('/api/taskforce/auth/profile/avatar/finalize')) {
|
|
7269
7650
|
return jsonResponse({
|
|
@@ -7304,8 +7685,21 @@ describe('TaskforceCore Integration', () => {
|
|
|
7304
7685
|
const displayNameInput = within(modal).getByPlaceholderText('Display name');
|
|
7305
7686
|
await user.clear(displayNameInput);
|
|
7306
7687
|
await user.type(displayNameInput, 'Updated Name');
|
|
7307
|
-
const
|
|
7688
|
+
const getContextSpy = vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({
|
|
7689
|
+
drawImage: () => undefined
|
|
7690
|
+
});
|
|
7691
|
+
const toBlobSpy = vi.spyOn(HTMLCanvasElement.prototype, 'toBlob').mockImplementation(function toBlob(callback, type) {
|
|
7692
|
+
callback(new Blob([new Uint8Array([1, 2, 3, 4])], { type: type || 'image/webp' }));
|
|
7693
|
+
});
|
|
7694
|
+
await user.click(within(modal).getByLabelText('Edit profile photo'));
|
|
7695
|
+
const avatarManager = await screen.findByText('Edit Profile Photo');
|
|
7696
|
+
const avatarManagerModal = avatarManager.closest(`.${modalStyles.modal}`);
|
|
7697
|
+
const avatarInput = within(avatarManagerModal).getByLabelText('Choose profile photo');
|
|
7308
7698
|
await user.upload(avatarInput, new File(['avatar'], 'avatar.png', { type: 'image/png' }));
|
|
7699
|
+
await user.click(within(avatarManagerModal).getByRole('button', { name: 'Apply Photo' }));
|
|
7700
|
+
await waitFor(() => {
|
|
7701
|
+
expect(screen.queryByText('Edit Profile Photo')).not.toBeInTheDocument();
|
|
7702
|
+
});
|
|
7309
7703
|
await user.click(within(modal).getByRole('button', { name: 'Save Profile' }));
|
|
7310
7704
|
await waitFor(() => {
|
|
7311
7705
|
const profileCall = fetchMock.mock.calls.find((call) => {
|
|
@@ -7317,6 +7711,17 @@ describe('TaskforceCore Integration', () => {
|
|
|
7317
7711
|
expect(payload.displayName).toBe('Updated Name');
|
|
7318
7712
|
expect(payload.avatarDraftId).toBe('avatar-draft-1');
|
|
7319
7713
|
});
|
|
7714
|
+
expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/init', expect.objectContaining({ method: 'POST' }));
|
|
7715
|
+
const avatarUploadCall = fetchMock.mock.calls.find((call) => String(call[0]) === '/api/taskforce/auth/profile/avatar/upload');
|
|
7716
|
+
expect(avatarUploadCall).toBeTruthy();
|
|
7717
|
+
expect(avatarUploadCall?.[1]).toEqual(expect.objectContaining({ method: 'POST' }));
|
|
7718
|
+
const avatarUploadHeaders = new Headers(avatarUploadCall?.[1]?.headers);
|
|
7719
|
+
expect(avatarUploadHeaders.get('Content-Type')).toBe('image/webp');
|
|
7720
|
+
expect(avatarUploadHeaders.get('x-taskforce-avatar-draft-id')).toBe('avatar-draft-1');
|
|
7721
|
+
expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/finalize', expect.objectContaining({ method: 'POST' }));
|
|
7722
|
+
expect(fetchMock.mock.calls.some((call) => String(call[0]).startsWith(`${cloudAuthBaseUrl}/api/taskforce/auth/profile/avatar/`))).toBe(false);
|
|
7723
|
+
getContextSpy.mockRestore();
|
|
7724
|
+
toBlobSpy.mockRestore();
|
|
7320
7725
|
await waitFor(() => {
|
|
7321
7726
|
expect(screen.queryByText('Edit Profile')).not.toBeInTheDocument();
|
|
7322
7727
|
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|