@hubspot/cli 7.7.32-experimental.0 → 7.7.33-experimental.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/commands/getStarted.js +5 -4
- package/commands/project/__tests__/add.test.js +3 -5
- package/commands/project/__tests__/deploy.test.js +3 -2
- package/commands/project/add.js +2 -4
- package/commands/project/deploy.js +9 -61
- package/commands/project/dev/index.js +1 -1
- package/commands/project/dev/unifiedFlow.js +3 -0
- package/commands/project/upload.js +2 -2
- package/commands/project/validate.js +1 -1
- package/commands/project/watch.js +2 -2
- package/lang/en.d.ts +7 -3
- package/lang/en.js +8 -4
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/__tests__/importData.test.js +1 -1
- package/lib/app/migrate.js +9 -2
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +2 -0
- package/lib/errorHandlers/index.d.ts +4 -0
- package/lib/errorHandlers/index.js +1 -1
- package/lib/hasFeature.js +6 -0
- package/lib/importData.js +1 -1
- package/lib/projects/__tests__/AppDevModeInterface.test.js +61 -44
- package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
- package/lib/projects/__tests__/deploy.test.js +164 -0
- package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
- package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
- package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
- package/lib/projects/add/__tests__/v3AddComponent.test.js +71 -1
- package/lib/projects/add/legacyAddComponent.d.ts +1 -1
- package/lib/projects/add/legacyAddComponent.js +5 -1
- package/lib/projects/add/v3AddComponent.d.ts +1 -0
- package/lib/projects/add/v3AddComponent.js +2 -2
- package/lib/projects/create/__tests__/v3.test.js +97 -9
- package/lib/projects/create/index.js +2 -2
- package/lib/projects/create/legacy.js +1 -1
- package/lib/projects/create/v3.d.ts +2 -2
- package/lib/projects/create/v3.js +35 -12
- package/lib/projects/deploy.d.ts +13 -0
- package/lib/projects/deploy.js +63 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +0 -2
- package/lib/projects/localDev/AppDevModeInterface.js +65 -36
- package/lib/projects/localDev/DevServerManagerV2.js +1 -0
- package/lib/projects/localDev/LocalDevProcess.js +3 -1
- package/lib/projects/localDev/LocalDevState.d.ts +5 -2
- package/lib/projects/localDev/LocalDevState.js +9 -1
- package/lib/projects/localDev/helpers/project.js +1 -1
- package/lib/projects/platformVersion.d.ts +1 -0
- package/lib/projects/platformVersion.js +10 -0
- package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
- package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
- package/lib/projects/upload.js +1 -1
- package/lib/projects/urls.d.ts +1 -0
- package/lib/projects/urls.js +3 -0
- package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
- package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
- package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
- package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
- package/lib/prompts/importDataFilePathPrompt.js +4 -2
- package/lib/prompts/installAppPrompt.d.ts +6 -1
- package/lib/prompts/installAppPrompt.js +6 -1
- package/lib/prompts/projectAddPrompt.js +1 -1
- package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
- package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
- package/mcp-server/tools/index.js +2 -0
- package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
- package/mcp-server/tools/project/GetConfigValuesTool.js +3 -3
- package/mcp-server/tools/project/constants.d.ts +1 -1
- package/mcp-server/tools/project/constants.js +6 -4
- package/package.json +3 -3
- package/types/LocalDev.d.ts +2 -1
- package/types/Projects.d.ts +1 -0
- /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
package/commands/getStarted.js
CHANGED
|
@@ -16,7 +16,8 @@ import { handleProjectUpload } from '../lib/projects/upload.js';
|
|
|
16
16
|
import { PROJECT_CONFIG_FILE, GET_STARTED_OPTIONS, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, } from '../lib/constants.js';
|
|
17
17
|
import { writeProjectConfig, getProjectConfig, validateProjectConfig, } from '../lib/projects/config.js';
|
|
18
18
|
import { getProjectPackageJsonLocations, installPackages, } from '../lib/dependencyManagement.js';
|
|
19
|
-
import { pollProjectBuildAndDeploy
|
|
19
|
+
import { pollProjectBuildAndDeploy } from '../lib/projects/pollProjectBuildAndDeploy.js';
|
|
20
|
+
import { useV3Api } from '../lib/projects/platformVersion.js';
|
|
20
21
|
import { openLink } from '../lib/links.js';
|
|
21
22
|
import { getStaticAuthAppInstallUrl } from '../lib/app/urls.js';
|
|
22
23
|
import { getEnv } from '@hubspot/local-dev-lib/config';
|
|
@@ -28,10 +29,12 @@ async function handler(args) {
|
|
|
28
29
|
const { derivedAccountId } = args;
|
|
29
30
|
const env = getEnv(derivedAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
|
|
30
31
|
await trackCommandUsage('get-started', {}, derivedAccountId);
|
|
32
|
+
const accountName = uiAccountDescription(derivedAccountId);
|
|
31
33
|
// TODO: Put this in constants.ts once we have a defined place for the template before INBOUND
|
|
32
34
|
const templateSource = 'robrown-hubspot/hubspot-project-components-ua-app-objects-beta';
|
|
33
35
|
uiInfoSection(commands.getStarted.startTitle, () => {
|
|
34
36
|
uiLogger.log(commands.getStarted.startDescription);
|
|
37
|
+
uiLogger.log(commands.getStarted.guideOverview(accountName));
|
|
35
38
|
});
|
|
36
39
|
const { default: selectedOption } = await promptUser([
|
|
37
40
|
{
|
|
@@ -163,7 +166,6 @@ async function handler(args) {
|
|
|
163
166
|
uiLogger.log(' ');
|
|
164
167
|
}
|
|
165
168
|
// 6. Ask user if they want to upload the project
|
|
166
|
-
const accountName = uiAccountDescription(derivedAccountId);
|
|
167
169
|
const { shouldUpload } = await promptUser([
|
|
168
170
|
{
|
|
169
171
|
type: 'confirm',
|
|
@@ -192,12 +194,11 @@ async function handler(args) {
|
|
|
192
194
|
process.exit(EXIT_CODES.ERROR);
|
|
193
195
|
}
|
|
194
196
|
validateProjectConfig(newProjectConfig, newProjectDir);
|
|
195
|
-
const targetAccountId = derivedAccountId;
|
|
196
197
|
uiLogger.log(' ');
|
|
197
198
|
uiLogger.log(commands.getStarted.logs.uploadingProject);
|
|
198
199
|
uiLogger.log(' ');
|
|
199
200
|
const { result, uploadError } = await handleProjectUpload({
|
|
200
|
-
accountId:
|
|
201
|
+
accountId: derivedAccountId,
|
|
201
202
|
projectConfig: newProjectConfig,
|
|
202
203
|
projectDir: newProjectDir,
|
|
203
204
|
callbackFunc: pollProjectBuildAndDeploy,
|
|
@@ -4,13 +4,13 @@ import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from
|
|
|
4
4
|
import { v3AddComponent } from '../../../lib/projects/add/v3AddComponent.js';
|
|
5
5
|
import { legacyAddComponent } from '../../../lib/projects/add/legacyAddComponent.js';
|
|
6
6
|
import { getProjectConfig } from '../../../lib/projects/config.js';
|
|
7
|
-
import { useV3Api } from '../../../lib/projects/
|
|
7
|
+
import { useV3Api } from '../../../lib/projects/platformVersion.js';
|
|
8
8
|
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
9
9
|
vi.mock('../../../lib/commonOpts');
|
|
10
10
|
vi.mock('../../../lib/projects/add/v3AddComponent');
|
|
11
11
|
vi.mock('../../../lib/projects/add/legacyAddComponent');
|
|
12
12
|
vi.mock('../../../lib/projects/config');
|
|
13
|
-
vi.mock('../../../lib/projects/
|
|
13
|
+
vi.mock('../../../lib/projects/platformVersion');
|
|
14
14
|
vi.mock('../../../lib/usageTracking');
|
|
15
15
|
const mockedV3AddComponent = vi.mocked(v3AddComponent);
|
|
16
16
|
const mockedLegacyAddComponent = vi.mocked(legacyAddComponent);
|
|
@@ -82,15 +82,13 @@ describe('commands/project/add', () => {
|
|
|
82
82
|
it('should call v3AddComponent with accountId for v3 projects', async () => {
|
|
83
83
|
mockedUseV3Api.mockReturnValue(true);
|
|
84
84
|
await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
|
|
85
|
-
expect(mockedTrackCommandUsage).toHaveBeenCalledWith('project-add', undefined, 123);
|
|
86
85
|
expect(mockedV3AddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
|
|
87
86
|
expect(mockedLegacyAddComponent).not.toHaveBeenCalled();
|
|
88
87
|
});
|
|
89
88
|
it('should call legacyAddComponent for non-v3 projects', async () => {
|
|
90
89
|
mockedUseV3Api.mockReturnValue(false);
|
|
91
90
|
await expect(projectAddCommand.handler(mockArgs)).rejects.toThrow('process.exit called');
|
|
92
|
-
expect(
|
|
93
|
-
expect(mockedLegacyAddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig);
|
|
91
|
+
expect(mockedLegacyAddComponent).toHaveBeenCalledWith(mockArgs, mockProjectDir, mockProjectConfig, 123);
|
|
94
92
|
expect(mockedV3AddComponent).not.toHaveBeenCalled();
|
|
95
93
|
});
|
|
96
94
|
it('should exit with error when project config is not found', async () => {
|
|
@@ -8,7 +8,7 @@ import * as ui from '../../../lib/ui/index.js';
|
|
|
8
8
|
import { addAccountOptions, addConfigOptions, addJSONOutputOptions, addUseEnvironmentOptions, } from '../../../lib/commonOpts.js';
|
|
9
9
|
import * as projectUtils from '../../../lib/projects/config.js';
|
|
10
10
|
import * as projectUrlUtils from '../../../lib/projects/urls.js';
|
|
11
|
-
import { pollDeployStatus } from '../../../lib/projects/
|
|
11
|
+
import { pollDeployStatus } from '../../../lib/projects/pollProjectBuildAndDeploy.js';
|
|
12
12
|
import * as projectNamePrompt from '../../../lib/prompts/projectNamePrompt.js';
|
|
13
13
|
import * as promptUtils from '../../../lib/prompts/promptUtils.js';
|
|
14
14
|
import { trackCommandUsage } from '../../../lib/usageTracking.js';
|
|
@@ -23,7 +23,8 @@ vi.mock('../../../lib/commonOpts');
|
|
|
23
23
|
vi.mock('../../../lib/validation');
|
|
24
24
|
vi.mock('../../../lib/projects/config');
|
|
25
25
|
vi.mock('../../../lib/projects/urls');
|
|
26
|
-
vi.mock('../../../lib/projects/
|
|
26
|
+
vi.mock('../../../lib/projects/pollProjectBuildAndDeploy');
|
|
27
|
+
vi.mock('../../../lib/projects/platformVersion');
|
|
27
28
|
vi.mock('../../../lib/prompts/projectNamePrompt');
|
|
28
29
|
vi.mock('../../../lib/prompts/promptUtils');
|
|
29
30
|
vi.mock('../../../lib/usageTracking');
|
package/commands/project/add.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { logError } from '../../lib/errorHandlers/index.js';
|
|
2
|
-
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
3
2
|
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
4
3
|
import { uiBetaTag } from '../../lib/ui/index.js';
|
|
5
4
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
6
5
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
7
6
|
import { commands } from '../../lang/en.js';
|
|
8
|
-
import { useV3Api } from '../../lib/projects/
|
|
7
|
+
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
9
8
|
import { legacyAddComponent } from '../../lib/projects/add/legacyAddComponent.js';
|
|
10
9
|
import { v3AddComponent } from '../../lib/projects/add/v3AddComponent.js';
|
|
11
10
|
import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../lib/constants.js';
|
|
@@ -15,7 +14,6 @@ const describe = uiBetaTag(commands.project.add.describe, false);
|
|
|
15
14
|
async function handler(args) {
|
|
16
15
|
try {
|
|
17
16
|
const { derivedAccountId } = args;
|
|
18
|
-
trackCommandUsage('project-add', undefined, derivedAccountId);
|
|
19
17
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
20
18
|
if (!projectDir || !projectConfig) {
|
|
21
19
|
uiLogger.error(commands.project.add.error.locationInProject);
|
|
@@ -26,7 +24,7 @@ async function handler(args) {
|
|
|
26
24
|
await v3AddComponent(args, projectDir, projectConfig, derivedAccountId);
|
|
27
25
|
}
|
|
28
26
|
else {
|
|
29
|
-
await legacyAddComponent(args, projectDir, projectConfig);
|
|
27
|
+
await legacyAddComponent(args, projectDir, projectConfig, derivedAccountId);
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
catch (e) {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
|
|
2
2
|
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
3
3
|
import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
|
|
4
|
-
import { useV3Api } from '../../lib/projects/
|
|
4
|
+
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
5
5
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
6
6
|
import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
|
|
7
7
|
import { getProjectConfig } from '../../lib/projects/config.js';
|
|
8
|
-
import { pollDeployStatus } from '../../lib/projects/buildAndDeploy.js';
|
|
9
8
|
import { projectNamePrompt } from '../../lib/prompts/projectNamePrompt.js';
|
|
10
9
|
import { promptUser } from '../../lib/prompts/promptUtils.js';
|
|
11
10
|
import { uiBetaTag, uiLine } from '../../lib/ui/index.js';
|
|
@@ -13,54 +12,11 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
|
13
12
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
14
13
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
|
15
14
|
import { loadProfile, logProfileFooter, logProfileHeader, exitIfUsingProfiles, } from '../../lib/projectProfiles.js';
|
|
16
|
-
import {
|
|
15
|
+
import { PROJECT_DEPLOY_TEXT } from '../../lib/constants.js';
|
|
17
16
|
import { commands } from '../../lang/en.js';
|
|
17
|
+
import { handleProjectDeploy, validateBuildIdForDeploy, logDeployErrors, } from '../../lib/projects/deploy.js';
|
|
18
18
|
const command = 'deploy';
|
|
19
19
|
const describe = uiBetaTag(commands.project.deploy.describe, false);
|
|
20
|
-
function validateBuildId(buildId, deployedBuildId, latestBuildId, projectName, accountId) {
|
|
21
|
-
if (Number(buildId) > latestBuildId) {
|
|
22
|
-
return commands.project.deploy.errors.buildIdDoesNotExist(accountId, buildId, projectName);
|
|
23
|
-
}
|
|
24
|
-
if (Number(buildId) === deployedBuildId) {
|
|
25
|
-
return commands.project.deploy.errors.buildAlreadyDeployed(accountId, buildId, projectName);
|
|
26
|
-
}
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
function logDeployErrors(errorData) {
|
|
30
|
-
uiLogger.error(errorData.message);
|
|
31
|
-
errorData.errors.forEach(err => {
|
|
32
|
-
// This is how the pre-deploy check manifests itself in < 2025.2 projects
|
|
33
|
-
if (err.subCategory === PROJECT_ERROR_TYPES.DEPLOY_CONTAINS_REMOVALS) {
|
|
34
|
-
uiLogger.log(commands.project.deploy.errors.deployContainsRemovals(err.context.COMPONENT_NAME));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
uiLogger.log(err.message);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function handleBlockedDeploy(deployResp) {
|
|
42
|
-
const deployCanBeForced = deployResp.issues.every(issue => issue.blockingMessages.every(message => message.isWarning));
|
|
43
|
-
uiLogger.log('');
|
|
44
|
-
if (deployCanBeForced) {
|
|
45
|
-
uiLogger.warn(commands.project.deploy.errors.deployWarningsHeader);
|
|
46
|
-
uiLogger.log('');
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
uiLogger.error(commands.project.deploy.errors.deployBlockedHeader);
|
|
50
|
-
uiLogger.log('');
|
|
51
|
-
}
|
|
52
|
-
deployResp.issues.forEach(issue => {
|
|
53
|
-
if (issue.blockingMessages.length > 0) {
|
|
54
|
-
issue.blockingMessages.forEach(message => {
|
|
55
|
-
uiLogger.log(commands.project.deploy.errors.deployIssueComponentWarning(issue.uid, issue.componentTypeName, message.message));
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
uiLogger.log(commands.project.deploy.errors.deployIssueComponentGeneric(issue.uid, issue.componentTypeName));
|
|
60
|
-
}
|
|
61
|
-
uiLogger.log('');
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
20
|
async function handler(args) {
|
|
65
21
|
const { derivedAccountId, project: projectOption, buildId: buildIdOption, force: forceOption, deployLatestBuild: deployLatestBuildOption, json: formatOutputAsJson, } = args;
|
|
66
22
|
const accountConfig = getAccountConfig(derivedAccountId);
|
|
@@ -105,7 +61,7 @@ async function handler(args) {
|
|
|
105
61
|
return process.exit(EXIT_CODES.ERROR);
|
|
106
62
|
}
|
|
107
63
|
if (buildIdToDeploy) {
|
|
108
|
-
const validationResult =
|
|
64
|
+
const validationResult = validateBuildIdForDeploy(buildIdToDeploy, deployedBuildId, latestBuild.buildId, projectName, targetAccountId);
|
|
109
65
|
if (validationResult !== true) {
|
|
110
66
|
uiLogger.error(validationResult.toString());
|
|
111
67
|
return process.exit(EXIT_CODES.ERROR);
|
|
@@ -122,7 +78,7 @@ async function handler(args) {
|
|
|
122
78
|
default: latestBuild.buildId === deployedBuildId
|
|
123
79
|
? undefined
|
|
124
80
|
: latestBuild.buildId,
|
|
125
|
-
validate: buildId =>
|
|
81
|
+
validate: buildId => validateBuildIdForDeploy(buildId, deployedBuildId, latestBuild.buildId, projectName, targetAccountId),
|
|
126
82
|
});
|
|
127
83
|
buildIdToDeploy = deployBuildIdPromptResponse.buildId;
|
|
128
84
|
}
|
|
@@ -131,21 +87,13 @@ async function handler(args) {
|
|
|
131
87
|
uiLogger.error(commands.project.deploy.errors.noBuildId);
|
|
132
88
|
return process.exit(EXIT_CODES.ERROR);
|
|
133
89
|
}
|
|
134
|
-
const
|
|
135
|
-
if (!
|
|
136
|
-
if (deployResp?.buildResultType === 'DEPLOY_BLOCKED') {
|
|
137
|
-
handleBlockedDeploy(deployResp);
|
|
138
|
-
process.exit(EXIT_CODES.ERROR);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
uiLogger.error(commands.project.deploy.errors.deploy);
|
|
142
|
-
}
|
|
90
|
+
const deployResult = await handleProjectDeploy(targetAccountId, projectName, buildIdToDeploy, useV3Api(projectConfig?.platformVersion), forceOption);
|
|
91
|
+
if (!deployResult) {
|
|
143
92
|
return process.exit(EXIT_CODES.ERROR);
|
|
144
93
|
}
|
|
145
94
|
else if (formatOutputAsJson) {
|
|
146
|
-
jsonOutput.deployId =
|
|
95
|
+
jsonOutput.deployId = deployResult.deployId;
|
|
147
96
|
}
|
|
148
|
-
const deployResult = await pollDeployStatus(targetAccountId, projectName, Number(deployResp.id), buildIdToDeploy);
|
|
149
97
|
if (deployResult.status === PROJECT_DEPLOY_TEXT.STATES.SUCCESS) {
|
|
150
98
|
deploySuccess = true;
|
|
151
99
|
}
|
|
@@ -5,7 +5,7 @@ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
|
|
|
5
5
|
import { uiBetaTag, uiLine } from '../../../lib/ui/index.js';
|
|
6
6
|
import { deprecatedProjectDevFlow } from './deprecatedFlow.js';
|
|
7
7
|
import { unifiedProjectDevFlow } from './unifiedFlow.js';
|
|
8
|
-
import { useV3Api } from '../../../lib/projects/
|
|
8
|
+
import { useV3Api } from '../../../lib/projects/platformVersion.js';
|
|
9
9
|
import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
|
|
10
10
|
import { loadProfile, exitIfUsingProfiles, } from '../../../lib/projectProfiles.js';
|
|
11
11
|
import { commands } from '../../../lang/en.js';
|
|
@@ -22,6 +22,7 @@ import LocalDevWebsocketServer from '../../../lib/projects/localDev/LocalDevWebs
|
|
|
22
22
|
export async function unifiedProjectDevFlow({ args, targetProjectAccountId, providedTargetTestingAccountId, projectConfig, projectDir, }) {
|
|
23
23
|
const env = getValidEnv(getEnv(targetProjectAccountId));
|
|
24
24
|
let projectNodes;
|
|
25
|
+
let projectProfileData;
|
|
25
26
|
// Get IR
|
|
26
27
|
try {
|
|
27
28
|
const intermediateRepresentation = await translateForLocalDev({
|
|
@@ -30,6 +31,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
|
|
|
30
31
|
accountId: targetProjectAccountId,
|
|
31
32
|
}, { profile: args.profile });
|
|
32
33
|
projectNodes = intermediateRepresentation.intermediateNodesIndexedByUid;
|
|
34
|
+
projectProfileData = intermediateRepresentation.profileData;
|
|
33
35
|
uiLogger.debug(util.inspect(projectNodes, false, null, true));
|
|
34
36
|
}
|
|
35
37
|
catch (e) {
|
|
@@ -116,6 +118,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
|
|
|
116
118
|
// End setup, start local dev process
|
|
117
119
|
const localDevProcess = new LocalDevProcess({
|
|
118
120
|
initialProjectNodes: projectNodes,
|
|
121
|
+
initialProjectProfileData: projectProfileData,
|
|
119
122
|
debug: args.debug,
|
|
120
123
|
profile: args.profile,
|
|
121
124
|
targetProjectAccountId,
|
|
@@ -2,14 +2,14 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { logger } from '@hubspot/local-dev-lib/logger';
|
|
3
3
|
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
4
4
|
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
5
|
-
import { useV3Api } from '../../lib/projects/
|
|
5
|
+
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
6
6
|
import { uiBetaTag, uiCommandReference } from '../../lib/ui/index.js';
|
|
7
7
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
8
8
|
import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
|
|
9
9
|
import { logFeedbackMessage } from '../../lib/projects/ui.js';
|
|
10
10
|
import { handleProjectUpload } from '../../lib/projects/upload.js';
|
|
11
11
|
import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
|
|
12
|
-
import { displayWarnLogs, pollProjectBuildAndDeploy, } from '../../lib/projects/
|
|
12
|
+
import { displayWarnLogs, pollProjectBuildAndDeploy, } from '../../lib/projects/pollProjectBuildAndDeploy.js';
|
|
13
13
|
import { i18n } from '../../lib/lang.js';
|
|
14
14
|
import { PROJECT_ERROR_TYPES } from '../../lib/constants.js';
|
|
15
15
|
import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { getAccountConfig } from '@hubspot/local-dev-lib/config';
|
|
3
|
-
import { useV3Api } from '../../lib/projects/
|
|
3
|
+
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
4
4
|
import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
5
5
|
import { uiLogger } from '../../lib/ui/logger.js';
|
|
6
6
|
import { getProjectConfig, validateProjectConfig as validateProjectConfig, } from '../../lib/projects/config.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cancelStagedBuild, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
|
|
2
2
|
import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
|
|
3
|
-
import { useV3Api } from '../../lib/projects/
|
|
3
|
+
import { useV3Api } from '../../lib/projects/platformVersion.js';
|
|
4
4
|
import { uiCommandReference, uiLink, uiBetaTag } from '../../lib/ui/index.js';
|
|
5
5
|
import { i18n } from '../../lib/lang.js';
|
|
6
6
|
import { createWatcher } from '../../lib/projects/watch.js';
|
|
@@ -11,7 +11,7 @@ import { trackCommandUsage } from '../../lib/usageTracking.js';
|
|
|
11
11
|
import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
|
|
12
12
|
import { logFeedbackMessage } from '../../lib/projects/ui.js';
|
|
13
13
|
import { handleProjectUpload } from '../../lib/projects/upload.js';
|
|
14
|
-
import { pollBuildStatus, pollDeployStatus, } from '../../lib/projects/
|
|
14
|
+
import { pollBuildStatus, pollDeployStatus, } from '../../lib/projects/pollProjectBuildAndDeploy.js';
|
|
15
15
|
import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
|
|
16
16
|
import { handleKeypress, handleExit } from '../../lib/process.js';
|
|
17
17
|
import { makeYargsBuilder } from '../../lib/yargsUtils.js';
|
package/lang/en.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ export declare const commands: {
|
|
|
23
23
|
};
|
|
24
24
|
readonly startTitle: "Welcome to HubSpot Development!";
|
|
25
25
|
readonly verboseDescribe: "A step-by-step command to get you started with a HubSpot project.";
|
|
26
|
-
readonly startDescription: "You can use the HubSpot CLI to build apps, CMS themes, and more
|
|
26
|
+
readonly startDescription: "You can use the HubSpot CLI to build apps, CMS themes, and more.\n";
|
|
27
|
+
readonly guideOverview: (accountName: string) => string;
|
|
27
28
|
readonly designManager: "To onboard with CMS, please visit the HubSpot Design Manager in your account and follow the checklist items.";
|
|
28
29
|
readonly openDesignManager: "Click here to go to the HubSpot Design Manager";
|
|
29
30
|
readonly openDesignManagerPrompt: "Open Design Manager in your browser?";
|
|
@@ -1188,9 +1189,11 @@ ${string}`;
|
|
|
1188
1189
|
readonly success: (componentName: string, multiple?: boolean) => string;
|
|
1189
1190
|
readonly error: {
|
|
1190
1191
|
readonly failedToDownloadComponent: "Failed to download project. Please try again later.";
|
|
1192
|
+
readonly invalidComponentType: (componentType: string) => string;
|
|
1191
1193
|
readonly maxExceeded: (maxCount: number) => string;
|
|
1192
1194
|
readonly authTypeNotAllowed: (authType: string) => string;
|
|
1193
1195
|
readonly distributionNotAllowed: (dist: string) => string;
|
|
1196
|
+
readonly portalDoesNotHaveAccessToThisFeature: (accountId: number) => string;
|
|
1194
1197
|
readonly locationInProject: "This command must be run from within a project directory.";
|
|
1195
1198
|
readonly failedToFetchComponentList: "Failed to fetch the list of available features. Please try again later.";
|
|
1196
1199
|
readonly projectContainsPublicApp: "This project contains a public app. This command is currently only compatible with projects that contain private apps.";
|
|
@@ -3160,8 +3163,9 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3160
3163
|
};
|
|
3161
3164
|
};
|
|
3162
3165
|
readonly installAppPrompt: {
|
|
3163
|
-
readonly explanation: "Local development requires this app to be installed in the target test account";
|
|
3166
|
+
readonly explanation: "Local development requires this app to be installed in the target test account.";
|
|
3164
3167
|
readonly reinstallExplanation: "This app's required scopes have been updated since it was last installed on the target test account. To avoid issues with local development, we recommend reinstalling the app with the updated scopes.";
|
|
3168
|
+
readonly staticAuthExplanation: (projectAccountId: number, testingAccountId: number, projectName: string, appUid: string) => string;
|
|
3165
3169
|
readonly prompt: "Open HubSpot to install this app?";
|
|
3166
3170
|
readonly autoPrompt: "Install this app in your target test account?";
|
|
3167
3171
|
readonly reinstallPrompt: "Open HubSpot to reinstall this app?";
|
|
@@ -3397,7 +3401,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3397
3401
|
readonly themesAndAppsNotAllowed: "Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.";
|
|
3398
3402
|
readonly multipleApps: "Multiple apps found in project, this is not allowed in 2025.2";
|
|
3399
3403
|
readonly alreadyExists: (projectName: string) => string;
|
|
3400
|
-
readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that
|
|
3404
|
+
readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.";
|
|
3401
3405
|
readonly failedToUpdateProjectConfig: "Failed to update project config file. Please update the platformVersion in the project config file manually.";
|
|
3402
3406
|
};
|
|
3403
3407
|
readonly unmigratableReasons: {
|
package/lang/en.js
CHANGED
|
@@ -4,7 +4,7 @@ import { PLATFORM_VERSIONS } from '@hubspot/local-dev-lib/constants/projects';
|
|
|
4
4
|
import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constants/auth';
|
|
5
5
|
import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
|
|
6
6
|
import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
|
|
7
|
-
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, } from '../lib/projects/urls.js';
|
|
7
|
+
import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
|
|
8
8
|
import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
|
|
9
9
|
import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
|
|
10
10
|
export const commands = {
|
|
@@ -31,7 +31,8 @@ export const commands = {
|
|
|
31
31
|
},
|
|
32
32
|
startTitle: 'Welcome to HubSpot Development!',
|
|
33
33
|
verboseDescribe: 'A step-by-step command to get you started with a HubSpot project.',
|
|
34
|
-
startDescription: 'You can use the HubSpot CLI to build apps, CMS themes, and more
|
|
34
|
+
startDescription: 'You can use the HubSpot CLI to build apps, CMS themes, and more.\n',
|
|
35
|
+
guideOverview: (accountName) => `This guide will walk you through deploying your first project to ${chalk.bold(accountName)}.\nTo target a different account, exit this guide ${chalk.bold('(ctrl + c)')} and run ${uiCommandReference('hs account use')} before trying again.`,
|
|
35
36
|
designManager: 'To onboard with CMS, please visit the HubSpot Design Manager in your account and follow the checklist items.',
|
|
36
37
|
openDesignManager: 'Click here to go to the HubSpot Design Manager',
|
|
37
38
|
openDesignManagerPrompt: 'Open Design Manager in your browser?',
|
|
@@ -1187,9 +1188,11 @@ export const commands = {
|
|
|
1187
1188
|
success: (componentName, multiple = false) => `${componentName || 'An app'} ${multiple ? 'were' : 'was'} successfully added to your ${componentName ? 'app' : 'project'}.`,
|
|
1188
1189
|
error: {
|
|
1189
1190
|
failedToDownloadComponent: 'Failed to download project. Please try again later.',
|
|
1191
|
+
invalidComponentType: (componentType) => `'${componentType}' is not a valid project component type.`,
|
|
1190
1192
|
maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
|
|
1191
1193
|
authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
|
|
1192
1194
|
distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
|
|
1195
|
+
portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature`,
|
|
1193
1196
|
locationInProject: 'This command must be run from within a project directory.',
|
|
1194
1197
|
failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
|
|
1195
1198
|
projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
|
|
@@ -3154,8 +3157,9 @@ export const lib = {
|
|
|
3154
3157
|
},
|
|
3155
3158
|
},
|
|
3156
3159
|
installAppPrompt: {
|
|
3157
|
-
explanation: 'Local development requires this app to be installed in the target test account',
|
|
3160
|
+
explanation: 'Local development requires this app to be installed in the target test account.',
|
|
3158
3161
|
reinstallExplanation: "This app's required scopes have been updated since it was last installed on the target test account. To avoid issues with local development, we recommend reinstalling the app with the updated scopes.",
|
|
3162
|
+
staticAuthExplanation: (projectAccountId, testingAccountId, projectName, appUid) => `To install this static auth app, your testing account ${uiAccountDescription(testingAccountId)} must be on ${uiLink("this app's allowlist", getAppAllowlistUrl(projectAccountId, projectName, appUid))}.`,
|
|
3159
3163
|
prompt: 'Open HubSpot to install this app?',
|
|
3160
3164
|
autoPrompt: 'Install this app in your target test account?',
|
|
3161
3165
|
reinstallPrompt: 'Open HubSpot to reinstall this app?',
|
|
@@ -3391,7 +3395,7 @@ export const lib = {
|
|
|
3391
3395
|
themesAndAppsNotAllowed: 'Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.',
|
|
3392
3396
|
multipleApps: 'Multiple apps found in project, this is not allowed in 2025.2',
|
|
3393
3397
|
alreadyExists: (projectName) => `A project with name ${projectName} already exists. Please choose another name.`,
|
|
3394
|
-
failedToMigrateThemes: 'Failed to migrate project themes. Please verify that
|
|
3398
|
+
failedToMigrateThemes: 'Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.',
|
|
3395
3399
|
failedToUpdateProjectConfig: 'Failed to update project config file. Please update the platformVersion in the project config file manually.',
|
|
3396
3400
|
},
|
|
3397
3401
|
unmigratableReasons: {
|
|
@@ -1,35 +1,173 @@
|
|
|
1
1
|
import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
2
|
-
import {
|
|
2
|
+
import { http } from '@hubspot/local-dev-lib/http';
|
|
3
|
+
import { hasFeature, hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
4
|
+
import { FEATURES } from '../constants.js';
|
|
3
5
|
vi.mock('@hubspot/local-dev-lib/api/localDevAuth');
|
|
6
|
+
vi.mock('@hubspot/local-dev-lib/http');
|
|
4
7
|
const mockedFetchEnabledFeatures = fetchEnabledFeatures;
|
|
8
|
+
const mockedHttp = http;
|
|
5
9
|
describe('lib/hasFeature', () => {
|
|
6
10
|
describe('hasFeature()', () => {
|
|
7
11
|
const accountId = 123;
|
|
8
|
-
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
it('should return true if the feature is enabled', async () => {
|
|
9
16
|
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
10
17
|
data: {
|
|
11
18
|
enabledFeatures: {
|
|
12
19
|
'feature-1': true,
|
|
13
|
-
'feature-2': false,
|
|
14
|
-
'feature-3': true,
|
|
15
20
|
},
|
|
16
21
|
},
|
|
17
22
|
});
|
|
18
|
-
});
|
|
19
|
-
it('should return true if the feature is enabled', async () => {
|
|
20
23
|
// @ts-expect-error test data
|
|
21
24
|
const result = await hasFeature(accountId, 'feature-1');
|
|
22
25
|
expect(result).toBe(true);
|
|
23
26
|
});
|
|
24
|
-
it('should return false if the feature is
|
|
27
|
+
it('should return false if the feature is disabled', async () => {
|
|
28
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
29
|
+
data: {
|
|
30
|
+
enabledFeatures: {
|
|
31
|
+
'feature-2': false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
25
35
|
// @ts-expect-error test data
|
|
26
36
|
const result = await hasFeature(accountId, 'feature-2');
|
|
27
37
|
expect(result).toBe(false);
|
|
28
38
|
});
|
|
29
39
|
it('should return false if the feature is not present', async () => {
|
|
40
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
41
|
+
data: {
|
|
42
|
+
enabledFeatures: {},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
30
45
|
// @ts-expect-error test data
|
|
31
46
|
const result = await hasFeature(accountId, 'feature-4');
|
|
32
47
|
expect(result).toBe(false);
|
|
33
48
|
});
|
|
49
|
+
it('should return true for APPS_HOME feature when not present in enabled features (defaults on)', async () => {
|
|
50
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
51
|
+
data: {
|
|
52
|
+
enabledFeatures: {},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const result = await hasFeature(accountId, FEATURES.APPS_HOME);
|
|
56
|
+
expect(result).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('should respect explicit setting for APPS_HOME feature even when it defaults on', async () => {
|
|
59
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
60
|
+
data: {
|
|
61
|
+
enabledFeatures: {
|
|
62
|
+
[FEATURES.APPS_HOME]: false,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const result = await hasFeature(accountId, FEATURES.APPS_HOME);
|
|
67
|
+
expect(result).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('should handle truthy values correctly', async () => {
|
|
70
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
71
|
+
data: {
|
|
72
|
+
enabledFeatures: {
|
|
73
|
+
'feature-truthy': 'yes',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// @ts-expect-error test data
|
|
78
|
+
const truthyResult = await hasFeature(accountId, 'feature-truthy');
|
|
79
|
+
expect(truthyResult).toBe(true);
|
|
80
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
81
|
+
data: {
|
|
82
|
+
enabledFeatures: {
|
|
83
|
+
'feature-number': 1,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// @ts-expect-error test data
|
|
88
|
+
const numberResult = await hasFeature(accountId, 'feature-number');
|
|
89
|
+
expect(numberResult).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it('should handle falsy values correctly', async () => {
|
|
92
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
93
|
+
data: {
|
|
94
|
+
enabledFeatures: {
|
|
95
|
+
'feature-null': null,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
// @ts-expect-error test data
|
|
100
|
+
const nullResult = await hasFeature(accountId, 'feature-null');
|
|
101
|
+
expect(nullResult).toBe(false);
|
|
102
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
103
|
+
data: {
|
|
104
|
+
enabledFeatures: {
|
|
105
|
+
'feature-zero': 0,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// @ts-expect-error test data
|
|
110
|
+
const zeroResult = await hasFeature(accountId, 'feature-zero');
|
|
111
|
+
expect(zeroResult).toBe(false);
|
|
112
|
+
mockedFetchEnabledFeatures.mockResolvedValueOnce({
|
|
113
|
+
data: {
|
|
114
|
+
enabledFeatures: {
|
|
115
|
+
'feature-empty': '',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
// @ts-expect-error test data
|
|
120
|
+
const emptyResult = await hasFeature(accountId, 'feature-empty');
|
|
121
|
+
expect(emptyResult).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
it('should propagate errors from fetchEnabledFeatures', async () => {
|
|
124
|
+
const error = new Error('API error');
|
|
125
|
+
mockedFetchEnabledFeatures.mockRejectedValueOnce(error);
|
|
126
|
+
await expect(hasFeature(accountId, FEATURES.UNIFIED_APPS)).rejects.toThrow('API error');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('hasUnfiedAppsAccess()', () => {
|
|
130
|
+
const accountId = 123;
|
|
131
|
+
afterEach(() => {
|
|
132
|
+
vi.clearAllMocks();
|
|
133
|
+
});
|
|
134
|
+
it('should return true when API returns true', async () => {
|
|
135
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
136
|
+
mockedHttp.get.mockResolvedValueOnce({ data: true });
|
|
137
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
138
|
+
expect(result).toBe(true);
|
|
139
|
+
expect(mockedHttp.get).toHaveBeenCalledWith(accountId, {
|
|
140
|
+
url: 'developer-tooling/external/developer-portal/has-unified-dev-platform-access',
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
it('should return false when API returns false', async () => {
|
|
144
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
145
|
+
mockedHttp.get.mockResolvedValueOnce({ data: false });
|
|
146
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
147
|
+
expect(result).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
it('should handle truthy values correctly', async () => {
|
|
150
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
151
|
+
mockedHttp.get.mockResolvedValueOnce({ data: 'yes' });
|
|
152
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
153
|
+
expect(result).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('should handle falsy values correctly', async () => {
|
|
156
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
157
|
+
mockedHttp.get.mockResolvedValueOnce({ data: null });
|
|
158
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
159
|
+
expect(result).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
it('should handle undefined response data', async () => {
|
|
162
|
+
// @ts-expect-error Don't want to mock the full response object
|
|
163
|
+
mockedHttp.get.mockResolvedValueOnce({ data: undefined });
|
|
164
|
+
const result = await hasUnfiedAppsAccess(accountId);
|
|
165
|
+
expect(result).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
it('should propagate errors from http.get', async () => {
|
|
168
|
+
const error = new Error('Network error');
|
|
169
|
+
mockedHttp.get.mockRejectedValueOnce(error);
|
|
170
|
+
await expect(hasUnfiedAppsAccess(accountId)).rejects.toThrow('Network error');
|
|
171
|
+
});
|
|
34
172
|
});
|
|
35
173
|
});
|
|
@@ -43,7 +43,7 @@ describe('lib/importData', () => {
|
|
|
43
43
|
data: { id: '123' },
|
|
44
44
|
});
|
|
45
45
|
await handleImportData(targetAccountId, dataFileNames, importRequest);
|
|
46
|
-
expect(mockUiLogger.
|
|
46
|
+
expect(mockUiLogger.info).toHaveBeenCalledWith(lib.importData.viewImportLink('https://app.hubspot.com', targetAccountId, '123'));
|
|
47
47
|
});
|
|
48
48
|
it('should log the correct error message', async () => {
|
|
49
49
|
mockCreateImport.mockRejectedValue(new Error('test-error'));
|