@hubspot/cli 7.8.0-experimental.0 → 7.8.0
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/bin/cli.js +31 -27
- package/commands/__tests__/auth.test.js +5 -0
- package/commands/__tests__/doctor.test.js +16 -16
- package/commands/__tests__/getStarted.test.js +2 -2
- package/commands/__tests__/mcp.test.js +1 -1
- package/commands/__tests__/project.test.js +2 -3
- package/commands/account/auth.js +1 -0
- package/commands/account/clean.js +18 -27
- package/commands/account/createOverride.js +13 -31
- package/commands/account/info.js +20 -31
- package/commands/account/list.js +16 -22
- package/commands/account/remove.js +12 -20
- package/commands/account/removeOverride.js +11 -21
- package/commands/account/rename.js +6 -9
- package/commands/account/use.js +12 -26
- package/commands/account.js +2 -2
- package/commands/app/__tests__/migrate.test.js +5 -6
- package/commands/app/migrate.js +13 -19
- package/commands/app/secret/add.js +2 -1
- package/commands/app/secret/delete.js +2 -1
- package/commands/app/secret/list.js +2 -1
- package/commands/app/secret/update.js +2 -1
- package/commands/app/secret.js +2 -1
- package/commands/app.js +2 -2
- package/commands/auth.d.ts +1 -0
- package/commands/auth.js +17 -7
- package/commands/cms/convertFields.js +7 -9
- package/commands/cms/getReactModule.js +9 -14
- package/commands/cms/lighthouseScore.js +33 -36
- package/commands/cms.js +2 -2
- package/commands/completion.js +3 -3
- package/commands/config/set.d.ts +1 -1
- package/commands/config/set.js +64 -37
- package/commands/config.js +2 -2
- package/commands/create.js +2 -2
- package/commands/customObject/create.js +10 -12
- package/commands/customObject/schema/create.js +9 -11
- package/commands/customObject/schema/delete.js +16 -16
- package/commands/customObject/schema/fetch-all.js +12 -11
- package/commands/customObject/schema/fetch.js +15 -15
- package/commands/customObject/schema/list.js +4 -4
- package/commands/customObject/schema/update.js +13 -13
- package/commands/customObject/schema.js +2 -2
- package/commands/customObject.js +6 -7
- package/commands/doctor.js +8 -11
- package/commands/feedback.js +8 -13
- package/commands/fetch.js +8 -8
- package/commands/filemanager/fetch.js +7 -7
- package/commands/filemanager/upload.js +15 -34
- package/commands/filemanager.js +2 -2
- package/commands/function/deploy.js +11 -29
- package/commands/function/list.js +8 -8
- package/commands/function/server.js +9 -11
- package/commands/function.d.ts +1 -1
- package/commands/function.js +2 -2
- package/commands/getStarted.d.ts +1 -3
- package/commands/getStarted.js +68 -20
- package/commands/hubdb/clear.js +7 -15
- package/commands/hubdb/create.js +9 -15
- package/commands/hubdb/delete.js +8 -15
- package/commands/hubdb/fetch.js +6 -9
- package/commands/hubdb.d.ts +1 -1
- package/commands/hubdb.js +2 -2
- package/commands/init.js +2 -3
- package/commands/lint.js +16 -16
- package/commands/list.js +8 -14
- package/commands/logs.js +14 -20
- package/commands/mcp/__tests__/setup.test.js +2 -2
- package/commands/mcp/setup.js +11 -2
- package/commands/mcp.js +3 -3
- package/commands/mv.js +6 -17
- package/commands/open.js +5 -5
- package/commands/project/__tests__/add.test.js +15 -13
- package/commands/project/__tests__/create.test.js +6 -6
- package/commands/project/__tests__/deploy.test.js +3 -7
- package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
- package/commands/project/__tests__/installDeps.test.js +8 -8
- package/commands/project/__tests__/list.test.js +31 -0
- package/commands/project/__tests__/logs.test.js +1 -4
- package/commands/project/__tests__/migrate.test.js +7 -7
- package/commands/project/__tests__/migrateApp.test.js +3 -7
- package/commands/project/__tests__/profile.test.js +1 -1
- package/commands/project/__tests__/validate.test.js +98 -0
- package/commands/project/add.d.ts +2 -2
- package/commands/project/add.js +7 -10
- package/commands/project/cloneApp.js +14 -19
- package/commands/project/create.js +4 -11
- package/commands/project/deploy.js +5 -5
- package/commands/project/dev/deprecatedFlow.js +9 -18
- package/commands/project/dev/index.js +21 -18
- package/commands/project/dev/unifiedFlow.js +15 -8
- package/commands/project/download.js +15 -16
- package/commands/project/installDeps.d.ts +2 -2
- package/commands/project/installDeps.js +9 -9
- package/commands/project/list.d.ts +4 -0
- package/commands/project/list.js +62 -0
- package/commands/project/listBuilds.js +12 -21
- package/commands/project/logs.js +21 -24
- package/commands/project/migrate.js +33 -12
- package/commands/project/migrateApp.js +10 -17
- package/commands/project/open.js +6 -14
- package/commands/project/profile/add.js +3 -3
- package/commands/project/profile/delete.js +1 -2
- package/commands/project/profile.js +2 -3
- package/commands/project/upload.js +16 -25
- package/commands/project/validate.js +7 -7
- package/commands/project/watch.js +13 -22
- package/commands/project.js +4 -3
- package/commands/sandbox/__tests__/create.test.js +5 -5
- package/commands/sandbox/create.js +22 -32
- package/commands/sandbox/delete.js +39 -64
- package/commands/sandbox.js +2 -2
- package/commands/secret/addSecret.js +7 -17
- package/commands/secret/deleteSecret.js +10 -20
- package/commands/secret/listSecret.js +8 -10
- package/commands/secret/updateSecret.js +9 -17
- package/commands/secret.js +2 -2
- package/commands/testAccount/__tests__/delete.test.js +2 -4
- package/commands/testAccount/create.js +2 -2
- package/commands/testAccount/delete.d.ts +4 -3
- package/commands/testAccount/delete.js +155 -14
- package/commands/testAccount/importData.d.ts +1 -1
- package/commands/testAccount/importData.js +1 -1
- package/commands/testAccount.js +1 -1
- package/commands/theme/preview.js +1 -4
- package/lang/en.d.ts +365 -111
- package/lang/en.js +409 -158
- package/lang/en.lyaml +4 -4
- package/lib/__tests__/buildAccount.test.js +4 -3
- package/lib/__tests__/commonOpts.test.js +1 -1
- package/lib/__tests__/dependencyManagement.test.js +1 -1
- package/lib/__tests__/developerTestAccounts.test.js +3 -3
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/__tests__/npm.test.js +1 -1
- package/lib/__tests__/oauth.test.js +4 -4
- package/lib/__tests__/process.test.js +10 -5
- package/lib/__tests__/sandboxSync.test.js +8 -8
- package/lib/__tests__/sandboxes.test.js +8 -8
- package/lib/__tests__/serverlessLogs.test.js +1 -1
- package/lib/__tests__/usageTracking.test.js +5 -5
- package/lib/__tests__/validation.test.js +2 -1
- package/lib/__tests__/yargsUtils.test.js +83 -9
- package/lib/app/__tests__/migrate.test.js +19 -56
- package/lib/app/__tests__/migrate_legacy.test.js +1 -1
- package/lib/app/migrate.d.ts +2 -8
- package/lib/app/migrate.js +6 -81
- package/lib/app/migrate_legacy.js +20 -24
- package/lib/buildAccount.d.ts +2 -2
- package/lib/buildAccount.js +32 -64
- package/lib/commonOpts.d.ts +1 -1
- package/lib/commonOpts.js +25 -22
- package/lib/configMigrate.js +88 -9
- package/lib/configOptions.js +7 -0
- package/lib/constants.d.ts +22 -1
- package/lib/constants.js +26 -1
- package/lib/dependencyManagement.d.ts +0 -5
- package/lib/dependencyManagement.js +9 -36
- package/lib/developerTestAccounts.js +9 -23
- package/lib/doctor/Diagnosis.js +11 -23
- package/lib/doctor/DiagnosticInfoBuilder.js +12 -11
- package/lib/doctor/Doctor.js +42 -90
- package/lib/doctor/__tests__/Doctor.test.js +4 -4
- package/lib/errorHandlers/index.js +12 -20
- package/lib/errorHandlers/suppressError.js +11 -18
- package/lib/hasFeature.js +6 -0
- package/lib/lang.js +6 -5
- package/lib/links.d.ts +1 -0
- package/lib/links.js +14 -7
- package/lib/mcp/setup.js +1 -1
- package/lib/middleware/__test__/commandTargetingUtils.test.js +99 -0
- package/lib/middleware/__test__/configMiddleware.test.js +11 -11
- package/lib/middleware/__test__/yargsChecksMiddleware.test.js +6 -8
- package/lib/middleware/commandTargetingUtils.d.ts +8 -0
- package/lib/middleware/commandTargetingUtils.js +74 -0
- package/lib/middleware/configMiddleware.d.ts +1 -1
- package/lib/middleware/configMiddleware.js +21 -81
- package/lib/middleware/fireAlarmMiddleware.js +15 -5
- package/lib/middleware/gitMiddleware.js +5 -1
- package/lib/middleware/notificationsMiddleware.js +5 -11
- package/lib/middleware/yargsChecksMiddleware.js +6 -9
- package/lib/npm.js +2 -2
- package/lib/oauth.js +5 -5
- package/lib/process.js +5 -4
- package/lib/projects/__tests__/AppDevModeInterface.test.js +87 -90
- package/lib/projects/__tests__/LocalDevProcess.test.js +231 -19
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +89 -63
- package/lib/projects/__tests__/deploy.test.js +73 -8
- package/lib/projects/__tests__/localDevProjectHelpers.test.js +6 -2
- package/lib/projects/__tests__/platformVersion.test.js +8 -8
- package/lib/projects/__tests__/projects.test.js +12 -12
- package/lib/projects/__tests__/structure.test.js +3 -3
- package/lib/projects/__tests__/upload.test.d.ts +1 -0
- package/lib/projects/__tests__/upload.test.js +82 -0
- package/lib/projects/add/__tests__/legacyAddComponent.test.js +6 -6
- package/lib/projects/add/__tests__/v2AddComponent.test.d.ts +1 -0
- package/lib/projects/add/__tests__/{v3AddComponent.test.js → v2AddComponent.test.js} +39 -39
- package/lib/projects/add/{v3AddComponent.d.ts → v2AddComponent.d.ts} +1 -1
- package/lib/projects/add/{v3AddComponent.js → v2AddComponent.js} +5 -5
- package/lib/projects/create/__tests__/legacy.test.js +5 -5
- package/lib/projects/create/__tests__/v2.test.d.ts +1 -0
- package/lib/projects/create/__tests__/{v3.test.js → v2.test.js} +82 -7
- package/lib/projects/create/index.js +4 -4
- package/lib/projects/create/legacy.js +2 -2
- package/lib/projects/create/{v3.d.ts → v2.d.ts} +3 -3
- package/lib/projects/create/{v3.js → v2.js} +16 -13
- package/lib/projects/deploy.d.ts +1 -1
- package/lib/projects/deploy.js +2 -2
- package/lib/projects/localDev/AppDevModeInterface.d.ts +10 -1
- package/lib/projects/localDev/AppDevModeInterface.js +118 -89
- package/lib/projects/localDev/DevServerManager.d.ts +11 -29
- package/lib/projects/localDev/DevServerManager.js +19 -61
- package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +40 -0
- package/lib/projects/localDev/DevServerManager_DEPRECATED.js +120 -0
- package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
- package/lib/projects/localDev/LocalDevLogger.js +27 -6
- package/lib/projects/localDev/{LocalDevManager.js → LocalDevManager_DEPRECATED.js} +10 -11
- package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
- package/lib/projects/localDev/LocalDevProcess.js +93 -21
- package/lib/projects/localDev/LocalDevState.d.ts +12 -8
- package/lib/projects/localDev/LocalDevState.js +27 -17
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +6 -1
- package/lib/projects/localDev/LocalDevWebsocketServer.js +94 -33
- package/lib/projects/localDev/helpers/account.d.ts +1 -1
- package/lib/projects/localDev/helpers/account.js +2 -2
- package/lib/projects/localDev/helpers/project.d.ts +3 -2
- package/lib/projects/localDev/helpers/project.js +49 -10
- package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +7 -0
- package/lib/projects/localDev/localDevWebsocketServerUtils.js +19 -0
- package/lib/projects/platformVersion.d.ts +1 -1
- package/lib/projects/platformVersion.js +1 -1
- package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
- package/lib/projects/structure.js +6 -6
- package/lib/projects/upload.d.ts +1 -1
- package/lib/projects/upload.js +17 -8
- package/lib/projects/urls.d.ts +0 -1
- package/lib/projects/urls.js +0 -3
- package/lib/prompts/__tests__/downloadProjectPrompt.test.js +1 -0
- package/lib/prompts/__tests__/projectAddPrompt.test.js +10 -10
- package/lib/prompts/accountNamePrompt.js +14 -19
- package/lib/prompts/accountsPrompt.js +2 -2
- package/lib/prompts/cmsFieldPrompt.js +2 -2
- package/lib/prompts/createApiSamplePrompt.js +5 -5
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +10 -1
- package/lib/prompts/createFunctionPrompt.js +14 -14
- package/lib/prompts/createModulePrompt.js +9 -9
- package/lib/prompts/createTemplatePrompt.js +2 -2
- package/lib/prompts/downloadProjectPrompt.js +5 -8
- package/lib/prompts/installAppPrompt.d.ts +1 -6
- package/lib/prompts/installAppPrompt.js +1 -6
- package/lib/prompts/personalAccessKeyPrompt.js +3 -3
- package/lib/prompts/previewPrompt.js +6 -6
- package/lib/prompts/projectAddPrompt.d.ts +2 -2
- package/lib/prompts/projectAddPrompt.js +9 -2
- package/lib/prompts/projectDevTargetAccountPrompt.js +20 -32
- package/lib/prompts/projectNamePrompt.js +4 -8
- package/lib/prompts/projectsLogsPrompt.js +2 -4
- package/lib/prompts/promptUtils.js +30 -9
- package/lib/prompts/sandboxesPrompt.js +7 -7
- package/lib/prompts/secretPrompt.js +3 -3
- package/lib/prompts/selectAppPrompt.js +3 -3
- package/lib/prompts/selectHubDBTablePrompt.js +9 -13
- package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
- package/lib/prompts/selectPublicAppForMigrationPrompt.js +15 -19
- package/lib/prompts/setAsDefaultAccountPrompt.js +4 -8
- package/lib/prompts/uploadPrompt.js +5 -5
- package/lib/sandboxSync.js +24 -41
- package/lib/sandboxes.js +19 -47
- package/lib/schema.js +3 -3
- package/lib/serverlessLogs.js +11 -13
- package/lib/theme/__tests__/migrate.test.d.ts +1 -0
- package/lib/theme/__tests__/migrate.test.js +233 -0
- package/lib/theme/migrate.d.ts +13 -0
- package/lib/theme/migrate.js +90 -0
- package/lib/ui/SpinniesManager.d.ts +2 -0
- package/lib/ui/SpinniesManager.js +112 -8
- package/lib/ui/boxen.js +1 -2
- package/lib/ui/git.js +13 -10
- package/lib/ui/index.d.ts +4 -0
- package/lib/ui/index.js +47 -38
- package/lib/ui/serverlessFunctionLogs.js +9 -7
- package/lib/ui/uiMessages.d.ts +72 -0
- package/lib/ui/uiMessages.js +75 -0
- package/lib/usageTracking.js +8 -8
- package/lib/validation.js +20 -23
- package/lib/yargsUtils.d.ts +1 -1
- package/lib/yargsUtils.js +12 -5
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
- package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
- package/mcp-server/tools/cms/HsListTool.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
- package/mcp-server/tools/index.js +4 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
- package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/CreateProjectTool.js +5 -5
- package/mcp-server/tools/project/DeployProjectTool.js +1 -1
- package/mcp-server/tools/project/DocFetchTool.js +2 -2
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +7 -7
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +23 -0
- package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +68 -0
- package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +11 -0
- package/mcp-server/tools/project/GetApplicationInfoTool.js +49 -0
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +13 -7
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +2 -2
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
- package/mcp-server/tools/project/UploadProjectTools.js +2 -2
- package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
- package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
- package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetApiUsagePatternsByAppIdTool.test.js +169 -0
- package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.d.ts +1 -0
- package/mcp-server/tools/project/__tests__/GetApplicationInfoTool.test.js +115 -0
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
- package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
- package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
- package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/tools/project/constants.js +9 -3
- package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
- package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
- package/mcp-server/utils/cliConfig.d.ts +1 -0
- package/mcp-server/utils/cliConfig.js +12 -0
- package/mcp-server/utils/toolUsageTracking.js +2 -2
- package/package.json +8 -12
- package/types/LocalDev.d.ts +19 -3
- package/ui/components/HorizontalSelectPrompt.js +1 -1
- package/ui/index.js +1 -1
- package/commands/getStartedV2.d.ts +0 -9
- package/commands/getStartedV2.js +0 -39
- package/lib/middleware/__test__/utils.test.js +0 -51
- package/lib/middleware/utils.d.ts +0 -8
- package/lib/middleware/utils.js +0 -14
- package/lib/projects/localDev/DevServerManagerV2.d.ts +0 -22
- package/lib/projects/localDev/DevServerManagerV2.js +0 -81
- package/ui/components/Ascii.d.ts +0 -10
- package/ui/components/Ascii.js +0 -11
- package/ui/views/GetStarted.d.ts +0 -7
- package/ui/views/GetStarted.js +0 -157
- /package/{lib/middleware/__test__/utils.test.d.ts → commands/project/__tests__/list.test.d.ts} +0 -0
- /package/{lib/projects/add/__tests__/v3AddComponent.test.d.ts → commands/project/__tests__/validate.test.d.ts} +0 -0
- /package/lib/{projects/create/__tests__/v3.test.d.ts → middleware/__test__/commandTargetingUtils.test.d.ts} +0 -0
- /package/lib/projects/localDev/{LocalDevManager.d.ts → LocalDevManager_DEPRECATED.d.ts} +0 -0
|
@@ -99,7 +99,7 @@ describe('lib/projects/deploy', () => {
|
|
|
99
99
|
const targetAccountId = 12345;
|
|
100
100
|
const projectName = 'test-project';
|
|
101
101
|
const buildId = 5;
|
|
102
|
-
const
|
|
102
|
+
const useV2Api = true;
|
|
103
103
|
const force = false;
|
|
104
104
|
it('successfully deploys and returns deploy result', async () => {
|
|
105
105
|
const mockDeployResponseData = {
|
|
@@ -126,9 +126,9 @@ describe('lib/projects/deploy', () => {
|
|
|
126
126
|
data: mockDeployResponseData,
|
|
127
127
|
});
|
|
128
128
|
mockPollDeployStatus.mockResolvedValue(mockDeployResult);
|
|
129
|
-
const
|
|
130
|
-
expect(mockDeployProject).toHaveBeenCalledWith(targetAccountId, projectName, buildId,
|
|
131
|
-
expect(
|
|
129
|
+
const deploy = await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
130
|
+
expect(mockDeployProject).toHaveBeenCalledWith(targetAccountId, projectName, buildId, useV2Api, force);
|
|
131
|
+
expect(deploy).toEqual(mockDeployResult);
|
|
132
132
|
});
|
|
133
133
|
it('handles blocked deploy with warnings', async () => {
|
|
134
134
|
const mockBlockedResponse = {
|
|
@@ -150,15 +150,80 @@ describe('lib/projects/deploy', () => {
|
|
|
150
150
|
mockDeployProject.mockResolvedValue({
|
|
151
151
|
data: mockBlockedResponse,
|
|
152
152
|
});
|
|
153
|
-
|
|
153
|
+
await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
154
154
|
expect(mockUiLogger.warn).toHaveBeenCalledWith(commands.project.deploy.errors.deployWarningsHeader);
|
|
155
|
-
|
|
155
|
+
});
|
|
156
|
+
it('handles blocked deploy with errors (cannot be forced)', async () => {
|
|
157
|
+
const mockBlockedResponse = {
|
|
158
|
+
buildResultType: 'DEPLOY_BLOCKED',
|
|
159
|
+
issues: [
|
|
160
|
+
{
|
|
161
|
+
uid: 'component-1',
|
|
162
|
+
componentTypeName: 'module',
|
|
163
|
+
errorMessages: [],
|
|
164
|
+
blockingMessages: [
|
|
165
|
+
{
|
|
166
|
+
message: 'This is an error',
|
|
167
|
+
isWarning: false,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
mockDeployProject.mockResolvedValue({
|
|
174
|
+
data: mockBlockedResponse,
|
|
175
|
+
});
|
|
176
|
+
await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
177
|
+
expect(mockUiLogger.error).toHaveBeenCalledWith(commands.project.deploy.errors.deployBlockedHeader);
|
|
178
|
+
expect(mockUiLogger.log).toHaveBeenCalledWith(commands.project.deploy.errors.deployIssueComponentWarning('component-1', 'module', 'This is an error'));
|
|
179
|
+
});
|
|
180
|
+
it('handles blocked deploy with no blocking messages', async () => {
|
|
181
|
+
const mockBlockedResponse = {
|
|
182
|
+
buildResultType: 'DEPLOY_BLOCKED',
|
|
183
|
+
issues: [
|
|
184
|
+
{
|
|
185
|
+
uid: 'component-1',
|
|
186
|
+
componentTypeName: 'module',
|
|
187
|
+
errorMessages: [],
|
|
188
|
+
blockingMessages: [],
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
mockDeployProject.mockResolvedValue({
|
|
193
|
+
data: mockBlockedResponse,
|
|
194
|
+
});
|
|
195
|
+
await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
196
|
+
expect(mockUiLogger.warn).toHaveBeenCalledWith(commands.project.deploy.errors.deployWarningsHeader);
|
|
197
|
+
expect(mockUiLogger.log).toHaveBeenCalledWith(commands.project.deploy.errors.deployIssueComponentGeneric('component-1', 'module'));
|
|
156
198
|
});
|
|
157
199
|
it('handles general deploy failure', async () => {
|
|
158
200
|
mockDeployProject.mockResolvedValue({ data: null });
|
|
159
|
-
const
|
|
201
|
+
const deploy = await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
202
|
+
expect(mockUiLogger.error).toHaveBeenCalledWith(commands.project.deploy.errors.deploy);
|
|
203
|
+
expect(deploy).toBeUndefined();
|
|
204
|
+
});
|
|
205
|
+
it('handles undefined deploy response', async () => {
|
|
206
|
+
mockDeployProject.mockResolvedValue({ data: undefined });
|
|
207
|
+
const deploy = await handleProjectDeploy(targetAccountId, projectName, buildId, useV2Api, force);
|
|
160
208
|
expect(mockUiLogger.error).toHaveBeenCalledWith(commands.project.deploy.errors.deploy);
|
|
161
|
-
expect(
|
|
209
|
+
expect(deploy).toBeUndefined();
|
|
210
|
+
});
|
|
211
|
+
it('passes correct parameters to deployProject', async () => {
|
|
212
|
+
const mockDeployResponseData = {
|
|
213
|
+
id: 'deploy-123',
|
|
214
|
+
buildResultType: 'DEPLOY_QUEUED',
|
|
215
|
+
links: {
|
|
216
|
+
status: 'http://status-url',
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
mockDeployProject.mockResolvedValue({
|
|
220
|
+
data: mockDeployResponseData,
|
|
221
|
+
});
|
|
222
|
+
mockPollDeployStatus.mockResolvedValue({});
|
|
223
|
+
await handleProjectDeploy(targetAccountId, projectName, buildId, false, true);
|
|
224
|
+
expect(mockDeployProject).toHaveBeenCalledWith(targetAccountId, projectName, buildId, false, // useV2Api
|
|
225
|
+
true // force
|
|
226
|
+
);
|
|
162
227
|
});
|
|
163
228
|
});
|
|
164
229
|
});
|
|
@@ -30,6 +30,8 @@ describe('isDeployedProjectUpToDateWithLocal', () => {
|
|
|
30
30
|
componentRoot: '/local/path',
|
|
31
31
|
componentConfigPath: '/local/path/config.json',
|
|
32
32
|
configUpdatedSinceLastUpload: false,
|
|
33
|
+
removed: false,
|
|
34
|
+
parsingErrors: [],
|
|
33
35
|
},
|
|
34
36
|
componentDeps: {},
|
|
35
37
|
metaFilePath: '/local/path',
|
|
@@ -99,7 +101,8 @@ describe('isDeployedProjectUpToDateWithLocal', () => {
|
|
|
99
101
|
it('should clean up temp directory even when errors occur', async () => {
|
|
100
102
|
// Mock downloadProject to throw an error after temp dir is created
|
|
101
103
|
downloadProject.mockRejectedValue(new Error('Download Error'));
|
|
102
|
-
await
|
|
104
|
+
const result = await isDeployedProjectUpToDateWithLocal(mockProjectConfig, mockAccountId, mockBuildId, mockLocalProjectNodes);
|
|
105
|
+
expect(result).toBe(false);
|
|
103
106
|
expect(fs.remove).toHaveBeenCalledWith(mockTempDir);
|
|
104
107
|
});
|
|
105
108
|
it('should handle translateForLocalDev errors', async () => {
|
|
@@ -111,7 +114,8 @@ describe('isDeployedProjectUpToDateWithLocal', () => {
|
|
|
111
114
|
extractZipArchive.mockResolvedValue(undefined);
|
|
112
115
|
// Mock translate to throw an error
|
|
113
116
|
translate.mockRejectedValue(new Error('Translation Error'));
|
|
114
|
-
await
|
|
117
|
+
const result = await isDeployedProjectUpToDateWithLocal(mockProjectConfig, mockAccountId, mockBuildId, mockLocalProjectNodes);
|
|
118
|
+
expect(result).toBe(false);
|
|
115
119
|
expect(fs.remove).toHaveBeenCalledWith(mockTempDir);
|
|
116
120
|
});
|
|
117
121
|
});
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isV2Project } from '../platformVersion.js';
|
|
2
2
|
describe('platformVersion', () => {
|
|
3
|
-
describe('
|
|
3
|
+
describe('isV2Project', () => {
|
|
4
4
|
it('returns true if platform version is UNSTABLE', () => {
|
|
5
|
-
expect(
|
|
5
|
+
expect(isV2Project('UNSTABLE')).toBe(true);
|
|
6
6
|
});
|
|
7
7
|
it('returns true if platform version is equal to the minimum', () => {
|
|
8
|
-
expect(
|
|
8
|
+
expect(isV2Project('2025.2')).toBe(true);
|
|
9
9
|
});
|
|
10
10
|
it('returns true if platform version is greater than the minimum', () => {
|
|
11
|
-
expect(
|
|
11
|
+
expect(isV2Project('2026.2')).toBe(true);
|
|
12
12
|
});
|
|
13
13
|
it('returns false if platform version is less than the minimum', () => {
|
|
14
|
-
expect(
|
|
14
|
+
expect(isV2Project('2025.0')).toBe(false);
|
|
15
15
|
});
|
|
16
16
|
it('returns false if platform version is invalid', () => {
|
|
17
|
-
expect(
|
|
17
|
+
expect(isV2Project(null)).toBe(false);
|
|
18
18
|
});
|
|
19
19
|
it('returns false for an invalid platform version', () => {
|
|
20
|
-
expect(
|
|
20
|
+
expect(isV2Project('notplaformversion')).toBe(false);
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
});
|
|
@@ -3,8 +3,8 @@ import os from 'os';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { EXIT_CODES } from '../../enums/exitCodes.js';
|
|
5
5
|
import { validateProjectConfig } from '../../projects/config.js';
|
|
6
|
-
import {
|
|
7
|
-
vi.mock('
|
|
6
|
+
import { uiLogger } from '../../ui/logger.js';
|
|
7
|
+
vi.mock('../../ui/logger.js');
|
|
8
8
|
describe('lib/projects', () => {
|
|
9
9
|
describe('validateProjectConfig()', () => {
|
|
10
10
|
let projectDir;
|
|
@@ -26,58 +26,58 @@ describe('lib/projects', () => {
|
|
|
26
26
|
// @ts-ignore Testing invalid input
|
|
27
27
|
validateProjectConfig(null, projectDir);
|
|
28
28
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
29
|
-
expect(
|
|
29
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*Unable to locate a project configuration file. Try running again from a project directory, or run*/));
|
|
30
30
|
});
|
|
31
31
|
it('rejects configuration with missing name', () => {
|
|
32
32
|
// @ts-ignore Testing invalid input
|
|
33
33
|
validateProjectConfig({ srcDir: '.' }, projectDir);
|
|
34
34
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
35
|
-
expect(
|
|
35
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields*/));
|
|
36
36
|
});
|
|
37
37
|
it('rejects configuration with missing srcDir', () => {
|
|
38
38
|
// @ts-ignore Testing invalid input
|
|
39
39
|
validateProjectConfig({ name: 'hello' }, projectDir);
|
|
40
40
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
41
|
-
expect(
|
|
41
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*missing required fields.*/));
|
|
42
42
|
});
|
|
43
43
|
describe('rejects configuration with srcDir outside project directory', () => {
|
|
44
44
|
it('for parent directory', () => {
|
|
45
45
|
validateProjectConfig({ name: 'hello', srcDir: '..', platformVersion: '' }, projectDir);
|
|
46
46
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
47
|
-
expect(
|
|
47
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: ".."'));
|
|
48
48
|
});
|
|
49
49
|
it('for root directory', () => {
|
|
50
50
|
validateProjectConfig({ name: 'hello', srcDir: '/', platformVersion: '' }, projectDir);
|
|
51
51
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
52
|
-
expect(
|
|
52
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining('srcDir: "/"'));
|
|
53
53
|
});
|
|
54
54
|
it('for complicated directory', () => {
|
|
55
55
|
const srcDir = './src/././../src/../../src';
|
|
56
56
|
validateProjectConfig({ name: 'hello', srcDir, platformVersion: '' }, projectDir);
|
|
57
57
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
58
|
-
expect(
|
|
58
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringContaining(`srcDir: "${srcDir}"`));
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
61
|
it('rejects configuration with srcDir that does not exist', () => {
|
|
62
62
|
validateProjectConfig({ name: 'hello', srcDir: 'foo', platformVersion: '' }, projectDir);
|
|
63
63
|
expect(exitMock).toHaveBeenCalledWith(EXIT_CODES.ERROR);
|
|
64
|
-
expect(
|
|
64
|
+
expect(uiLogger.error).toHaveBeenCalledWith(expect.stringMatching(/.*could not be found in.*/));
|
|
65
65
|
});
|
|
66
66
|
describe('accepts configuration with valid srcDir', () => {
|
|
67
67
|
it('for current directory', () => {
|
|
68
68
|
validateProjectConfig({ name: 'hello', srcDir: '.', platformVersion: '' }, projectDir);
|
|
69
69
|
expect(exitMock).not.toHaveBeenCalled();
|
|
70
|
-
expect(
|
|
70
|
+
expect(uiLogger.error).not.toHaveBeenCalled();
|
|
71
71
|
});
|
|
72
72
|
it('for relative directory', () => {
|
|
73
73
|
validateProjectConfig({ name: 'hello', srcDir: './src', platformVersion: '' }, projectDir);
|
|
74
74
|
expect(exitMock).not.toHaveBeenCalled();
|
|
75
|
-
expect(
|
|
75
|
+
expect(uiLogger.error).not.toHaveBeenCalled();
|
|
76
76
|
});
|
|
77
77
|
it('for implied relative directory', () => {
|
|
78
78
|
validateProjectConfig({ name: 'hello', srcDir: 'src', platformVersion: '' }, projectDir);
|
|
79
79
|
expect(exitMock).not.toHaveBeenCalled();
|
|
80
|
-
expect(
|
|
80
|
+
expect(uiLogger.error).not.toHaveBeenCalled();
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
83
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import * as HSfs from '@hubspot/local-dev-lib/fs';
|
|
3
|
-
import {
|
|
3
|
+
import { uiLogger } from '../../ui/logger.js';
|
|
4
4
|
import { getComponentTypeFromConfigFile, loadConfigFile, getAppCardConfigs, getIsLegacyApp, componentIsApp, findProjectComponents, getProjectComponentTypes, getComponentUid, componentIsPublicApp, } from '../structure.js';
|
|
5
5
|
import { ComponentTypes } from '../../../types/Projects.js';
|
|
6
6
|
vi.mock('fs');
|
|
7
7
|
vi.mock('@hubspot/local-dev-lib/fs');
|
|
8
|
-
vi.mock('
|
|
8
|
+
vi.mock('../../ui/logger.js');
|
|
9
9
|
const mockedReadFileSync = fs.readFileSync;
|
|
10
10
|
const mockedWalk = HSfs.walk;
|
|
11
11
|
const getMockPrivateAppConfig = (cards = []) => ({
|
|
@@ -46,7 +46,7 @@ describe('lib/projects/structure', () => {
|
|
|
46
46
|
throw new Error('File not found');
|
|
47
47
|
});
|
|
48
48
|
expect(loadConfigFile('nonexistent/path/app.json')).toBeNull();
|
|
49
|
-
expect(
|
|
49
|
+
expect(uiLogger.debug).toHaveBeenCalled();
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
describe('getAppCardConfigs()', () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { vi } from 'vitest';
|
|
5
|
+
import { validateSourceDirectory } from '../upload.js';
|
|
6
|
+
import { uiLogger } from '../../ui/logger.js';
|
|
7
|
+
import { lib } from '../../../lang/en.js';
|
|
8
|
+
import { isV2Project } from '../platformVersion.js';
|
|
9
|
+
import ProjectValidationError from '../../errors/ProjectValidationError.js';
|
|
10
|
+
import { walk } from '@hubspot/local-dev-lib/fs';
|
|
11
|
+
// Mock dependencies
|
|
12
|
+
vi.mock('../../ui/logger.js');
|
|
13
|
+
vi.mock('../platformVersion.js');
|
|
14
|
+
vi.mock('@hubspot/local-dev-lib/fs');
|
|
15
|
+
describe('lib/projects/upload', () => {
|
|
16
|
+
describe('validateSourceDirectory', () => {
|
|
17
|
+
let tempDir;
|
|
18
|
+
let srcDir;
|
|
19
|
+
let projectConfig;
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'upload-test-'));
|
|
22
|
+
srcDir = path.join(tempDir, 'src');
|
|
23
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
24
|
+
projectConfig = {
|
|
25
|
+
name: 'test-project',
|
|
26
|
+
srcDir: 'src',
|
|
27
|
+
platformVersion: '2025.2',
|
|
28
|
+
};
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
fs.removeSync(tempDir);
|
|
33
|
+
});
|
|
34
|
+
it('should throw ProjectValidationError when source directory is empty', async () => {
|
|
35
|
+
vi.mocked(walk).mockResolvedValue([]);
|
|
36
|
+
await expect(validateSourceDirectory(srcDir, projectConfig, tempDir)).rejects.toThrow(ProjectValidationError);
|
|
37
|
+
expect(walk).toHaveBeenCalledWith(srcDir, ['node_modules']);
|
|
38
|
+
});
|
|
39
|
+
it('should warn about legacy files in V2 projects', async () => {
|
|
40
|
+
vi.mocked(isV2Project).mockReturnValue(true);
|
|
41
|
+
const legacyFilePath = path.join(srcDir, 'app', 'serverless.json');
|
|
42
|
+
vi.mocked(walk).mockResolvedValue([legacyFilePath]);
|
|
43
|
+
await validateSourceDirectory(srcDir, projectConfig, tempDir);
|
|
44
|
+
expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app/serverless.json', '2025.2'));
|
|
45
|
+
});
|
|
46
|
+
it('should warn about multiple legacy files', async () => {
|
|
47
|
+
vi.mocked(isV2Project).mockReturnValue(true);
|
|
48
|
+
const filePaths = [
|
|
49
|
+
path.join(srcDir, 'app1', 'serverless.json'),
|
|
50
|
+
path.join(srcDir, 'app2', 'app.json'),
|
|
51
|
+
path.join(srcDir, 'app3', 'public-app.json'),
|
|
52
|
+
];
|
|
53
|
+
vi.mocked(walk).mockResolvedValue(filePaths);
|
|
54
|
+
await validateSourceDirectory(srcDir, projectConfig, tempDir);
|
|
55
|
+
expect(uiLogger.warn).toHaveBeenCalledTimes(3);
|
|
56
|
+
expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app1/serverless.json', '2025.2'));
|
|
57
|
+
expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app2/app.json', '2025.2'));
|
|
58
|
+
expect(uiLogger.warn).toHaveBeenCalledWith(lib.projectUpload.handleProjectUpload.legacyFileDetected('src/app3/public-app.json', '2025.2'));
|
|
59
|
+
});
|
|
60
|
+
it('should not warn about non-legacy files', async () => {
|
|
61
|
+
vi.mocked(isV2Project).mockReturnValue(true);
|
|
62
|
+
const filePaths = [
|
|
63
|
+
path.join(srcDir, 'component.js'),
|
|
64
|
+
path.join(srcDir, 'config.json'),
|
|
65
|
+
];
|
|
66
|
+
vi.mocked(walk).mockResolvedValue(filePaths);
|
|
67
|
+
await validateSourceDirectory(srcDir, projectConfig, tempDir);
|
|
68
|
+
expect(uiLogger.warn).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
it('should not warn about legacy files in non-V2 projects', async () => {
|
|
71
|
+
vi.mocked(isV2Project).mockReturnValue(false);
|
|
72
|
+
projectConfig.platformVersion = '2025.1';
|
|
73
|
+
const filePaths = [
|
|
74
|
+
path.join(srcDir, 'app', 'serverless.json'),
|
|
75
|
+
path.join(srcDir, 'app', 'app.json'),
|
|
76
|
+
];
|
|
77
|
+
vi.mocked(walk).mockResolvedValue(filePaths);
|
|
78
|
+
await validateSourceDirectory(srcDir, projectConfig, tempDir);
|
|
79
|
+
expect(uiLogger.warn).not.toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -2,7 +2,7 @@ import { legacyAddComponent } from '../legacyAddComponent.js';
|
|
|
2
2
|
import { findProjectComponents } from '../../structure.js';
|
|
3
3
|
import { getProjectComponentListFromRepo } from '../../create/legacy.js';
|
|
4
4
|
import { projectAddPrompt } from '../../../prompts/projectAddPrompt.js';
|
|
5
|
-
import {
|
|
5
|
+
import { uiLogger } from '../../../ui/logger.js';
|
|
6
6
|
import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
|
|
7
7
|
import { trackCommandUsage } from '../../../usageTracking.js';
|
|
8
8
|
import { ComponentTypes, } from '../../../../types/Projects.js';
|
|
@@ -10,13 +10,13 @@ import { commands } from '../../../../lang/en.js';
|
|
|
10
10
|
vi.mock('../../structure');
|
|
11
11
|
vi.mock('../../create/legacy');
|
|
12
12
|
vi.mock('../../../prompts/projectAddPrompt');
|
|
13
|
-
vi.mock('
|
|
13
|
+
vi.mock('../../../ui/logger.js');
|
|
14
14
|
vi.mock('@hubspot/local-dev-lib/github');
|
|
15
15
|
vi.mock('../../../usageTracking.js');
|
|
16
16
|
const mockedFindProjectComponents = vi.mocked(findProjectComponents);
|
|
17
17
|
const mockedGetProjectComponentListFromRepo = vi.mocked(getProjectComponentListFromRepo);
|
|
18
18
|
const mockedProjectAddPrompt = vi.mocked(projectAddPrompt);
|
|
19
|
-
const
|
|
19
|
+
const mockedUiLogger = vi.mocked(uiLogger);
|
|
20
20
|
const mockedCloneGithubRepo = vi.mocked(cloneGithubRepo);
|
|
21
21
|
const mockedTrackCommandUsage = vi.mocked(trackCommandUsage);
|
|
22
22
|
describe('lib/projects/add/legacyAddComponent', () => {
|
|
@@ -75,8 +75,8 @@ describe('lib/projects/add/legacyAddComponent', () => {
|
|
|
75
75
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
76
76
|
type: 'module',
|
|
77
77
|
}, accountId);
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
78
|
+
expect(mockedUiLogger.log).toHaveBeenCalledWith(commands.project.add.creatingComponent('test-project'));
|
|
79
|
+
expect(mockedUiLogger.success).toHaveBeenCalledWith(commands.project.add.success('new-component'));
|
|
80
80
|
});
|
|
81
81
|
it('throws an error when project contains a public app', async () => {
|
|
82
82
|
const mockComponents = [
|
|
@@ -206,7 +206,7 @@ describe('lib/projects/add/legacyAddComponent', () => {
|
|
|
206
206
|
mockedCloneGithubRepo.mockRejectedValue(new Error('Clone failed'));
|
|
207
207
|
await expect(legacyAddComponent(mockArgs, projectDir, mockProjectConfig, accountId)).rejects.toThrow(commands.project.add.error.failedToDownloadComponent);
|
|
208
208
|
expect(mockedCloneGithubRepo).toHaveBeenCalled();
|
|
209
|
-
expect(
|
|
209
|
+
expect(mockedUiLogger.success).not.toHaveBeenCalled();
|
|
210
210
|
});
|
|
211
211
|
it('calls trackCommandUsage with correct component type', async () => {
|
|
212
212
|
const mockComponents = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import {
|
|
2
|
+
import { v2AddComponent } from '../v2AddComponent.js';
|
|
3
3
|
import { getConfigForPlatformVersion } from '../../create/legacy.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createV2App } from '../../create/v2.js';
|
|
5
5
|
import { confirmPrompt } from '../../../prompts/promptUtils.js';
|
|
6
|
-
import {
|
|
6
|
+
import { projectAddPromptV2 } from '../../../prompts/projectAddPrompt.js';
|
|
7
7
|
import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
|
|
8
|
-
import {
|
|
8
|
+
import { uiLogger } from '../../../ui/logger.js';
|
|
9
9
|
import { getProjectMetadata } from '@hubspot/project-parsing-lib/src/lib/project.js';
|
|
10
10
|
import { trackCommandUsage } from '../../../usageTracking.js';
|
|
11
11
|
import { commands } from '../../../../lang/en.js';
|
|
12
12
|
vi.mock('fs');
|
|
13
13
|
vi.mock('../../../prompts/promptUtils');
|
|
14
14
|
vi.mock('../../create/legacy');
|
|
15
|
-
vi.mock('../../create/
|
|
15
|
+
vi.mock('../../create/v2');
|
|
16
16
|
vi.mock('../../../prompts/projectAddPrompt');
|
|
17
17
|
vi.mock('@hubspot/local-dev-lib/github');
|
|
18
|
-
vi.mock('
|
|
18
|
+
vi.mock('../../../ui/logger.js');
|
|
19
19
|
vi.mock('@hubspot/project-parsing-lib/src/lib/project');
|
|
20
20
|
vi.mock('../../../usageTracking');
|
|
21
21
|
const mockedFs = vi.mocked(fs);
|
|
22
22
|
const mockedGetConfigForPlatformVersion = vi.mocked(getConfigForPlatformVersion);
|
|
23
23
|
const mockedConfirmPrompt = vi.mocked(confirmPrompt);
|
|
24
|
-
const
|
|
25
|
-
const
|
|
24
|
+
const mockedCreateV2App = vi.mocked(createV2App);
|
|
25
|
+
const mockedProjectAddPromptV2 = vi.mocked(projectAddPromptV2);
|
|
26
26
|
const mockedCloneGithubRepo = vi.mocked(cloneGithubRepo);
|
|
27
|
-
const
|
|
27
|
+
const mockedUiLogger = vi.mocked(uiLogger);
|
|
28
28
|
const mockedGetProjectMetadata = vi.mocked(getProjectMetadata);
|
|
29
29
|
const mockedTrackCommandUsage = vi.mocked(trackCommandUsage);
|
|
30
|
-
describe('lib/projects/add/
|
|
30
|
+
describe('lib/projects/add/v2AddComponent', () => {
|
|
31
31
|
const mockProjectConfig = {
|
|
32
32
|
name: 'test-project',
|
|
33
33
|
srcDir: 'src',
|
|
34
|
-
platformVersion: '
|
|
34
|
+
platformVersion: '2025.2',
|
|
35
35
|
};
|
|
36
36
|
const mockArgs = {
|
|
37
37
|
name: 'test-component',
|
|
@@ -66,13 +66,13 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
68
|
beforeEach(() => {
|
|
69
|
-
|
|
69
|
+
mockedCreateV2App.mockResolvedValue({
|
|
70
70
|
authType: 'oauth',
|
|
71
71
|
distribution: 'private',
|
|
72
72
|
});
|
|
73
73
|
mockedTrackCommandUsage.mockResolvedValue();
|
|
74
74
|
});
|
|
75
|
-
describe('
|
|
75
|
+
describe('v2AddComponent()', () => {
|
|
76
76
|
it('successfully adds a component when app already exists', async () => {
|
|
77
77
|
const mockAppMeta = {
|
|
78
78
|
config: {
|
|
@@ -86,21 +86,21 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
86
86
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
87
87
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadata);
|
|
88
88
|
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockAppMeta));
|
|
89
|
-
|
|
89
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
90
90
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
91
|
-
await
|
|
92
|
-
expect(mockedGetConfigForPlatformVersion).toHaveBeenCalledWith('
|
|
91
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
92
|
+
expect(mockedGetConfigForPlatformVersion).toHaveBeenCalledWith('2025.2');
|
|
93
93
|
expect(mockedGetProjectMetadata).toHaveBeenCalledWith('/path/to/project/src');
|
|
94
|
-
expect(
|
|
94
|
+
expect(mockedProjectAddPromptV2).toHaveBeenCalled();
|
|
95
95
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
96
96
|
type: 'module',
|
|
97
97
|
}, mockAccountId);
|
|
98
98
|
expect(mockedCloneGithubRepo).toHaveBeenCalledWith(expect.any(String), projectDir, expect.objectContaining({
|
|
99
|
-
sourceDir: ['
|
|
99
|
+
sourceDir: ['2025.2/test-component'],
|
|
100
100
|
hideLogs: true,
|
|
101
101
|
branch: 'main',
|
|
102
102
|
}));
|
|
103
|
-
expect(
|
|
103
|
+
expect(mockedUiLogger.success).toHaveBeenCalled();
|
|
104
104
|
});
|
|
105
105
|
it('creates an app when no app exists and user confirms', async () => {
|
|
106
106
|
const mockProjectMetadataNoApps = {
|
|
@@ -116,15 +116,15 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
116
116
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
117
117
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadataNoApps);
|
|
118
118
|
mockedConfirmPrompt.mockResolvedValue(true);
|
|
119
|
-
|
|
119
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
120
120
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
121
|
-
await
|
|
122
|
-
expect(
|
|
121
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
122
|
+
expect(mockedCreateV2App).toHaveBeenCalled();
|
|
123
123
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
124
124
|
type: 'module',
|
|
125
125
|
}, mockAccountId);
|
|
126
126
|
expect(mockedCloneGithubRepo).toHaveBeenCalledWith(expect.any(String), projectDir, expect.objectContaining({
|
|
127
|
-
sourceDir: ['
|
|
127
|
+
sourceDir: ['2025.2/test-component', '2025.2/app-template'],
|
|
128
128
|
}));
|
|
129
129
|
});
|
|
130
130
|
it('should not call clone', async () => {
|
|
@@ -145,10 +145,10 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
145
145
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
146
146
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadataNoApps);
|
|
147
147
|
mockedConfirmPrompt.mockResolvedValue(true);
|
|
148
|
-
|
|
148
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
149
149
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
150
|
-
await
|
|
151
|
-
expect(
|
|
150
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
151
|
+
expect(mockedCreateV2App).not.toHaveBeenCalled();
|
|
152
152
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
153
153
|
type: '',
|
|
154
154
|
}, mockAccountId);
|
|
@@ -164,7 +164,7 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
164
164
|
};
|
|
165
165
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
166
166
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadataMaxApps);
|
|
167
|
-
await expect(
|
|
167
|
+
await expect(v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId)).rejects.toThrow('This project currently has the maximum number of apps: 1');
|
|
168
168
|
});
|
|
169
169
|
it('throws an error when components list is empty', async () => {
|
|
170
170
|
const mockEmptyConfig = {
|
|
@@ -172,7 +172,7 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
172
172
|
parentComponents: [],
|
|
173
173
|
};
|
|
174
174
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockEmptyConfig);
|
|
175
|
-
await expect(
|
|
175
|
+
await expect(v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId)).rejects.toThrow(commands.project.add.error.failedToFetchComponentList);
|
|
176
176
|
});
|
|
177
177
|
it('throws an error when app meta file cannot be parsed', async () => {
|
|
178
178
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
@@ -180,7 +180,7 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
180
180
|
mockedFs.readFileSync.mockImplementation(() => {
|
|
181
181
|
throw new Error('File read error');
|
|
182
182
|
});
|
|
183
|
-
await expect(
|
|
183
|
+
await expect(v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId)).rejects.toThrow('Unable to parse app file');
|
|
184
184
|
});
|
|
185
185
|
it('throws an error when cloning fails', async () => {
|
|
186
186
|
const mockAppMeta = {
|
|
@@ -195,9 +195,9 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
195
195
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
196
196
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadata);
|
|
197
197
|
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockAppMeta));
|
|
198
|
-
|
|
198
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
199
199
|
mockedCloneGithubRepo.mockRejectedValue(new Error('Clone failed'));
|
|
200
|
-
await expect(
|
|
200
|
+
await expect(v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId)).rejects.toThrow(commands.project.add.error.failedToDownloadComponent);
|
|
201
201
|
});
|
|
202
202
|
it('should track usage with multiple component types', async () => {
|
|
203
203
|
const mockAppMeta = {
|
|
@@ -219,9 +219,9 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
219
219
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
220
220
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadata);
|
|
221
221
|
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockAppMeta));
|
|
222
|
-
|
|
222
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
223
223
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
224
|
-
await
|
|
224
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
225
225
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
226
226
|
type: 'module,card',
|
|
227
227
|
}, mockAccountId);
|
|
@@ -243,8 +243,8 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
243
243
|
};
|
|
244
244
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
245
245
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadataNoApps);
|
|
246
|
-
|
|
247
|
-
await
|
|
246
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
247
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
248
248
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
249
249
|
type: '',
|
|
250
250
|
}, mockAccountId);
|
|
@@ -270,9 +270,9 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
270
270
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
271
271
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadata);
|
|
272
272
|
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockAppMeta));
|
|
273
|
-
|
|
273
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
274
274
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
275
|
-
await
|
|
275
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
276
276
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
277
277
|
type: 'workflow-action-tool',
|
|
278
278
|
}, mockAccountId);
|
|
@@ -308,9 +308,9 @@ describe('lib/projects/add/v3AddComponent', () => {
|
|
|
308
308
|
mockedGetConfigForPlatformVersion.mockResolvedValue(mockConfig);
|
|
309
309
|
mockedGetProjectMetadata.mockResolvedValue(mockProjectMetadata);
|
|
310
310
|
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockAppMeta));
|
|
311
|
-
|
|
311
|
+
mockedProjectAddPromptV2.mockResolvedValue(mockPromptResponse);
|
|
312
312
|
mockedCloneGithubRepo.mockResolvedValue(true);
|
|
313
|
-
await
|
|
313
|
+
await v2AddComponent(mockArgs, projectDir, mockProjectConfig, mockAccountId);
|
|
314
314
|
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', {
|
|
315
315
|
type: 'workflow-action-tool,module',
|
|
316
316
|
}, mockAccountId);
|