@hubspot/cli 7.7.32-experimental.0 → 7.7.34-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/app/__tests__/install.test.js +47 -0
- package/commands/app/install.d.ts +8 -0
- package/commands/app/install.js +122 -0
- package/commands/app.js +6 -1
- package/commands/getStarted.js +7 -20
- 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 +4 -1
- package/commands/project/upload.d.ts +2 -2
- package/commands/project/upload.js +3 -3
- package/commands/project/validate.js +1 -1
- package/commands/project/watch.js +2 -2
- package/commands/testAccount/create.js +3 -0
- package/lang/en.d.ts +30 -4
- package/lang/en.js +31 -5
- 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/mcp/setup.js +1 -1
- package/lib/projectProfiles.d.ts +1 -1
- package/lib/projectProfiles.js +10 -2
- package/lib/projects/__tests__/AppDevModeInterface.test.js +61 -44
- package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
- package/lib/projects/__tests__/deploy.test.d.ts +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.d.ts +2 -2
- package/lib/projects/localDev/helpers/project.js +6 -7
- 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/structure.d.ts +2 -2
- package/lib/projects/upload.d.ts +2 -1
- package/lib/projects/upload.js +2 -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/HsCreateFunctionTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
- package/mcp-server/tools/cms/HsCreateTemplateTool.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/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.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
- 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 +2 -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.js +2 -2
- package/mcp-server/tools/project/GetConfigValuesTool.js +4 -4
- 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 +1 -1
- 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 +2 -2
- package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
- 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 +14 -6
- package/package.json +3 -3
- package/types/LocalDev.d.ts +2 -1
- package/types/Projects.d.ts +1 -0
- package/types/Yargs.d.ts +1 -1
- /package/{lib/projects/__tests__/buildAndDeploy.test.d.ts → commands/app/__tests__/install.test.d.ts} +0 -0
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.";
|
|
@@ -1550,6 +1553,28 @@ ${string}`;
|
|
|
1550
1553
|
readonly app: {
|
|
1551
1554
|
readonly describe: "Commands for managing apps.";
|
|
1552
1555
|
readonly subcommands: {
|
|
1556
|
+
readonly install: {
|
|
1557
|
+
readonly describe: "Install an OAuth app into a test account.";
|
|
1558
|
+
readonly options: {
|
|
1559
|
+
readonly appUid: "The uid of the app to install";
|
|
1560
|
+
readonly projectName: "The name of the project that contains the app";
|
|
1561
|
+
};
|
|
1562
|
+
readonly positionals: {
|
|
1563
|
+
readonly testAccountId: "The id of the test account to install the app into";
|
|
1564
|
+
};
|
|
1565
|
+
readonly errors: {
|
|
1566
|
+
readonly mustSpecifyProjectName: `You must specify a project name. Use the ${string} flag to specify the project name or run this command from within a project directory.`;
|
|
1567
|
+
readonly noAppUidFound: `No app uid found. Please specify the app uid with the ${string} flag or run this command from within a project that contains an app.`;
|
|
1568
|
+
readonly appMustBeOauth: "This command only supports installing oauth apps. Please specify an app with oauth auth type.";
|
|
1569
|
+
};
|
|
1570
|
+
readonly polling: {
|
|
1571
|
+
readonly start: "Installing app...";
|
|
1572
|
+
readonly success: "App installed successfully";
|
|
1573
|
+
readonly failure: "App installation failed";
|
|
1574
|
+
readonly error: "Error installing app";
|
|
1575
|
+
};
|
|
1576
|
+
readonly example: "Install the app with uid my-app-uid from the project named \"my-project\" into the target account with id 1234567890";
|
|
1577
|
+
};
|
|
1553
1578
|
readonly secret: {
|
|
1554
1579
|
readonly describe: "Commands for managing secrets.";
|
|
1555
1580
|
readonly subcommands: {
|
|
@@ -2598,7 +2623,7 @@ export declare const lib: {
|
|
|
2598
2623
|
readonly checking: "Checking if your deployed build is up to date...";
|
|
2599
2624
|
readonly upToDate: "Deployed build is up to date.";
|
|
2600
2625
|
readonly notUpToDate: "Your project contains undeployed local changes.";
|
|
2601
|
-
readonly notUpToDateExplanation:
|
|
2626
|
+
readonly notUpToDateExplanation: (profile?: string) => string;
|
|
2602
2627
|
};
|
|
2603
2628
|
readonly createNewProjectForLocalDev: {
|
|
2604
2629
|
readonly projectMustExistExplanation: (projectName: string, accountId: number) => string;
|
|
@@ -3160,8 +3185,9 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3160
3185
|
};
|
|
3161
3186
|
};
|
|
3162
3187
|
readonly installAppPrompt: {
|
|
3163
|
-
readonly explanation: "Local development requires this app to be installed in the target test account";
|
|
3188
|
+
readonly explanation: "Local development requires this app to be installed in the target test account.";
|
|
3164
3189
|
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.";
|
|
3190
|
+
readonly staticAuthExplanation: (projectAccountId: number, testingAccountId: number, projectName: string, appUid: string) => string;
|
|
3165
3191
|
readonly prompt: "Open HubSpot to install this app?";
|
|
3166
3192
|
readonly autoPrompt: "Install this app in your target test account?";
|
|
3167
3193
|
readonly reinstallPrompt: "Open HubSpot to reinstall this app?";
|
|
@@ -3397,7 +3423,7 @@ Run ${string} to upgrade to version ${string}`;
|
|
|
3397
3423
|
readonly themesAndAppsNotAllowed: "Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.";
|
|
3398
3424
|
readonly multipleApps: "Multiple apps found in project, this is not allowed in 2025.2";
|
|
3399
3425
|
readonly alreadyExists: (projectName: string) => string;
|
|
3400
|
-
readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that
|
|
3426
|
+
readonly failedToMigrateThemes: "Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.";
|
|
3401
3427
|
readonly failedToUpdateProjectConfig: "Failed to update project config file. Please update the platformVersion in the project config file manually.";
|
|
3402
3428
|
};
|
|
3403
3429
|
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.',
|
|
@@ -1547,6 +1550,28 @@ export const commands = {
|
|
|
1547
1550
|
app: {
|
|
1548
1551
|
describe: 'Commands for managing apps.',
|
|
1549
1552
|
subcommands: {
|
|
1553
|
+
install: {
|
|
1554
|
+
describe: 'Install an OAuth app into a test account.',
|
|
1555
|
+
options: {
|
|
1556
|
+
appUid: 'The uid of the app to install',
|
|
1557
|
+
projectName: 'The name of the project that contains the app',
|
|
1558
|
+
},
|
|
1559
|
+
positionals: {
|
|
1560
|
+
testAccountId: 'The id of the test account to install the app into',
|
|
1561
|
+
},
|
|
1562
|
+
errors: {
|
|
1563
|
+
mustSpecifyProjectName: `You must specify a project name. Use the ${uiCommandReference('--project-name')} flag to specify the project name or run this command from within a project directory.`,
|
|
1564
|
+
noAppUidFound: `No app uid found. Please specify the app uid with the ${uiCommandReference('--app-uid')} flag or run this command from within a project that contains an app.`,
|
|
1565
|
+
appMustBeOauth: 'This command only supports installing oauth apps. Please specify an app with oauth auth type.',
|
|
1566
|
+
},
|
|
1567
|
+
polling: {
|
|
1568
|
+
start: 'Installing app...',
|
|
1569
|
+
success: 'App installed successfully',
|
|
1570
|
+
failure: 'App installation failed',
|
|
1571
|
+
error: 'Error installing app',
|
|
1572
|
+
},
|
|
1573
|
+
example: 'Install the app with uid my-app-uid from the project named "my-project" into the target account with id 1234567890',
|
|
1574
|
+
},
|
|
1550
1575
|
secret: {
|
|
1551
1576
|
describe: 'Commands for managing secrets.',
|
|
1552
1577
|
subcommands: {
|
|
@@ -2595,7 +2620,7 @@ export const lib = {
|
|
|
2595
2620
|
checking: 'Checking if your deployed build is up to date...',
|
|
2596
2621
|
upToDate: 'Deployed build is up to date.',
|
|
2597
2622
|
notUpToDate: `Your project contains undeployed local changes.`,
|
|
2598
|
-
notUpToDateExplanation: `Run ${uiCommandReference(
|
|
2623
|
+
notUpToDateExplanation: (profile) => `Run ${uiCommandReference(`hs project upload ${profile ? `--profile ${profile}` : ''}`)} to upload these changes to HubSpot, then re-run ${uiCommandReference(`hs project dev ${profile ? `--profile ${profile}` : ''}`)} to continue local development.`,
|
|
2599
2624
|
},
|
|
2600
2625
|
createNewProjectForLocalDev: {
|
|
2601
2626
|
projectMustExistExplanation: (projectName, accountId) => `The project ${projectName} does not exist in the target account ${uiAccountDescription(accountId)}. This command requires the project to exist in the target account.`,
|
|
@@ -3154,8 +3179,9 @@ export const lib = {
|
|
|
3154
3179
|
},
|
|
3155
3180
|
},
|
|
3156
3181
|
installAppPrompt: {
|
|
3157
|
-
explanation: 'Local development requires this app to be installed in the target test account',
|
|
3182
|
+
explanation: 'Local development requires this app to be installed in the target test account.',
|
|
3158
3183
|
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.",
|
|
3184
|
+
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
3185
|
prompt: 'Open HubSpot to install this app?',
|
|
3160
3186
|
autoPrompt: 'Install this app in your target test account?',
|
|
3161
3187
|
reinstallPrompt: 'Open HubSpot to reinstall this app?',
|
|
@@ -3391,7 +3417,7 @@ export const lib = {
|
|
|
3391
3417
|
themesAndAppsNotAllowed: 'Support for migrating projects containing both themes and apps to the latest platform version is coming soon. Try again later.',
|
|
3392
3418
|
multipleApps: 'Multiple apps found in project, this is not allowed in 2025.2',
|
|
3393
3419
|
alreadyExists: (projectName) => `A project with name ${projectName} already exists. Please choose another name.`,
|
|
3394
|
-
failedToMigrateThemes: 'Failed to migrate project themes. Please verify that
|
|
3420
|
+
failedToMigrateThemes: 'Failed to migrate project themes. Please verify that your themes are properly formatted before trying again.',
|
|
3395
3421
|
failedToUpdateProjectConfig: 'Failed to update project config file. Please update the platformVersion in the project config file manually.',
|
|
3396
3422
|
},
|
|
3397
3423
|
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'));
|
package/lib/app/migrate.js
CHANGED
|
@@ -22,7 +22,7 @@ import { hasUnfiedAppsAccess } from '../hasFeature.js';
|
|
|
22
22
|
import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
|
|
23
23
|
import { uiLogger } from '../ui/logger.js';
|
|
24
24
|
import { debugError } from '../errorHandlers/index.js';
|
|
25
|
-
import { useV3Api } from '../projects/
|
|
25
|
+
import { useV3Api } from '../projects/platformVersion.js';
|
|
26
26
|
export function getUnmigratableReason(reasonCode, projectName, accountId) {
|
|
27
27
|
switch (reasonCode) {
|
|
28
28
|
case UNMIGRATABLE_REASONS.UP_TO_DATE:
|
|
@@ -236,13 +236,20 @@ export async function handleThemesMigration(projectConfig, platformVersion) {
|
|
|
236
236
|
throw new Error(lib.migrate.errors.project.invalidConfig);
|
|
237
237
|
}
|
|
238
238
|
const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
239
|
+
let migrated = false;
|
|
240
|
+
let failureReason;
|
|
239
241
|
try {
|
|
240
|
-
await migrateThemes(projectConfig.projectDir, projectSrcDir);
|
|
242
|
+
const migrationResult = await migrateThemes(projectConfig.projectDir, projectSrcDir);
|
|
243
|
+
migrated = migrationResult.migrated;
|
|
244
|
+
failureReason = migrationResult.failureReason;
|
|
241
245
|
}
|
|
242
246
|
catch (error) {
|
|
243
247
|
debugError(error);
|
|
244
248
|
throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
|
|
245
249
|
}
|
|
250
|
+
if (!migrated) {
|
|
251
|
+
throw new Error(failureReason || lib.migrate.errors.project.failedToMigrateThemes);
|
|
252
|
+
}
|
|
246
253
|
const newProjectConfig = { ...projectConfig.projectConfig };
|
|
247
254
|
newProjectConfig.platformVersion = platformVersion;
|
|
248
255
|
const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
|
package/lib/constants.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ export declare const FEATURES: {
|
|
|
80
80
|
readonly UNIFIED_APPS: "Developers:UnifiedApps:PrivateBeta";
|
|
81
81
|
readonly SANDBOXES_V2: "sandboxes:v2:enabled";
|
|
82
82
|
readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
|
|
83
|
+
readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
|
|
84
|
+
readonly APPS_HOME: "UIE:AppHome";
|
|
83
85
|
};
|
|
84
86
|
export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
|
|
85
87
|
UPLOAD_SUCCESS: string;
|
package/lib/constants.js
CHANGED
|
@@ -72,6 +72,8 @@ export const FEATURES = {
|
|
|
72
72
|
UNIFIED_APPS: 'Developers:UnifiedApps:PrivateBeta',
|
|
73
73
|
SANDBOXES_V2: 'sandboxes:v2:enabled',
|
|
74
74
|
SANDBOXES_V2_CLI: 'sandboxes:v2:cliEnabled',
|
|
75
|
+
APP_EVENTS: 'Developers:UnifiedApps:AppEventsAccess',
|
|
76
|
+
APPS_HOME: 'UIE:AppHome',
|
|
75
77
|
};
|
|
76
78
|
export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
|
|
77
79
|
UPLOAD_SUCCESS: 'server:uploadSuccess',
|
|
@@ -85,7 +85,7 @@ export class ApiErrorContext {
|
|
|
85
85
|
this.projectName = props.projectName || '';
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
function isErrorWithMessageOrReason(error) {
|
|
88
|
+
export function isErrorWithMessageOrReason(error) {
|
|
89
89
|
return (typeof error === 'object' &&
|
|
90
90
|
error !== null &&
|
|
91
91
|
('message' in error || 'reason' in error));
|
package/lib/hasFeature.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { http } from '@hubspot/local-dev-lib/http';
|
|
2
2
|
import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
|
|
3
|
+
import { FEATURES } from './constants.js';
|
|
4
|
+
const FEATURES_THAT_DEFAULT_ON = [FEATURES.APPS_HOME];
|
|
3
5
|
export async function hasFeature(accountId, feature) {
|
|
4
6
|
const { data: { enabledFeatures }, } = await fetchEnabledFeatures(accountId);
|
|
7
|
+
if (enabledFeatures[feature] === undefined &&
|
|
8
|
+
FEATURES_THAT_DEFAULT_ON.includes(feature)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
5
11
|
return Boolean(enabledFeatures[feature]);
|
|
6
12
|
}
|
|
7
13
|
export async function hasUnfiedAppsAccess(accountId) {
|
package/lib/importData.js
CHANGED
|
@@ -10,7 +10,7 @@ export async function handleImportData(targetAccountId, dataFileNames, importReq
|
|
|
10
10
|
const baseUrl = getHubSpotWebsiteOrigin(getEnv());
|
|
11
11
|
const response = await createImport(targetAccountId, importRequest, dataFileNames);
|
|
12
12
|
const importId = response.data.id;
|
|
13
|
-
uiLogger.
|
|
13
|
+
uiLogger.info(lib.importData.viewImportLink(baseUrl, targetAccountId, importId));
|
|
14
14
|
}
|
|
15
15
|
catch (error) {
|
|
16
16
|
uiLogger.error(lib.importData.errors.failedToImportData);
|
package/lib/mcp/setup.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from 'path';
|
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
10
|
import { existsSync } from 'fs';
|
|
11
|
-
const mcpServerName = '
|
|
11
|
+
const mcpServerName = 'HubSpot';
|
|
12
12
|
const claudeCode = 'claude';
|
|
13
13
|
const windsurf = 'windsurf';
|
|
14
14
|
const cursor = 'cursor';
|
package/lib/projectProfiles.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export declare function logProfileHeader(profileName: string): void;
|
|
|
4
4
|
export declare function logProfileFooter(profile: HsProfileFile, includeVariables?: boolean): void;
|
|
5
5
|
export declare function loadProfile(projectConfig: ProjectConfig | null, projectDir: string | null, profileName: string): HsProfileFile | undefined;
|
|
6
6
|
export declare function exitIfUsingProfiles(projectConfig: ProjectConfig | null, projectDir: string | null): Promise<void>;
|
|
7
|
-
export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined): Promise<number | undefined>;
|
|
7
|
+
export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined, useEnv?: boolean): Promise<number | undefined>;
|
package/lib/projectProfiles.js
CHANGED
|
@@ -38,7 +38,12 @@ export function loadProfile(projectConfig, projectDir, profileName) {
|
|
|
38
38
|
uiLogger.error(lib.projectProfiles.loadProfile.errors.missingAccountId(profileFilename));
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
return
|
|
41
|
+
return {
|
|
42
|
+
...profile,
|
|
43
|
+
accountId: process.env.HUBSPOT_ACCOUNT_ID
|
|
44
|
+
? Number(process.env.HUBSPOT_ACCOUNT_ID)
|
|
45
|
+
: profile.accountId,
|
|
46
|
+
};
|
|
42
47
|
}
|
|
43
48
|
catch (e) {
|
|
44
49
|
uiLogger.error(lib.projectProfiles.loadProfile.errors.failedToLoadProfile(profileFilename));
|
|
@@ -54,7 +59,7 @@ export async function exitIfUsingProfiles(projectConfig, projectDir) {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile) {
|
|
62
|
+
export async function loadAndValidateProfile(projectConfig, projectDir, argsProfile, useEnv = false) {
|
|
58
63
|
if (argsProfile) {
|
|
59
64
|
logProfileHeader(argsProfile);
|
|
60
65
|
const profile = loadProfile(projectConfig, projectDir, argsProfile);
|
|
@@ -63,6 +68,9 @@ export async function loadAndValidateProfile(projectConfig, projectDir, argsProf
|
|
|
63
68
|
process.exit(EXIT_CODES.ERROR);
|
|
64
69
|
}
|
|
65
70
|
logProfileFooter(profile, true);
|
|
71
|
+
if (useEnv) {
|
|
72
|
+
return Number(process.env.HUBSPOT_ACCOUNT_ID);
|
|
73
|
+
}
|
|
66
74
|
return profile.accountId;
|
|
67
75
|
}
|
|
68
76
|
else {
|
|
@@ -240,16 +240,21 @@ describe('AppDevModeInterface', () => {
|
|
|
240
240
|
await newAppDevModeInterface.setup({});
|
|
241
241
|
expect(process.exit).toHaveBeenCalledWith(0);
|
|
242
242
|
});
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
243
|
+
// @TODO: Restore test account auto install functionality
|
|
244
|
+
// it('should auto-install static auth app on test account', async () => {
|
|
245
|
+
// (fetchAppInstallationData as Mock).mockResolvedValue({
|
|
246
|
+
// data: {
|
|
247
|
+
// isInstalledWithScopeGroups: false,
|
|
248
|
+
// previouslyAuthorizedScopeGroups: [],
|
|
249
|
+
// },
|
|
250
|
+
// });
|
|
251
|
+
// await appDevModeInterface.setup({});
|
|
252
|
+
// expect(installStaticAuthAppOnTestAccount).toHaveBeenCalledWith(
|
|
253
|
+
// 123,
|
|
254
|
+
// 67890,
|
|
255
|
+
// [1, 2, 3]
|
|
256
|
+
// );
|
|
257
|
+
// });
|
|
253
258
|
it('should open browser for OAuth app installation', async () => {
|
|
254
259
|
const oauthAppNode = {
|
|
255
260
|
...mockAppNode,
|
|
@@ -288,7 +293,12 @@ describe('AppDevModeInterface', () => {
|
|
|
288
293
|
},
|
|
289
294
|
});
|
|
290
295
|
await appDevModeInterface.setup({});
|
|
291
|
-
expect(installAppBrowserPrompt).toHaveBeenCalledWith('http://static-install-url', true
|
|
296
|
+
expect(installAppBrowserPrompt).toHaveBeenCalledWith('http://static-install-url', true, {
|
|
297
|
+
appUid: 'test-app-uid',
|
|
298
|
+
projectAccountId: 12345,
|
|
299
|
+
projectName: 'test-project',
|
|
300
|
+
testingAccountId: 67890,
|
|
301
|
+
});
|
|
292
302
|
});
|
|
293
303
|
it('should handle errors during setup', async () => {
|
|
294
304
|
const error = new Error('Setup failed');
|
|
@@ -318,39 +328,46 @@ describe('AppDevModeInterface', () => {
|
|
|
318
328
|
await appDevModeInterface.setup({});
|
|
319
329
|
expect(process.exit).toHaveBeenCalledWith(1);
|
|
320
330
|
});
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
331
|
+
// @TODO: Restore test account auto install functionality
|
|
332
|
+
// it('should exit if user declines auto-install', async () => {
|
|
333
|
+
// // Set up conditions for automatic installation
|
|
334
|
+
// (getAccountConfig as Mock).mockReturnValue({
|
|
335
|
+
// parentAccountId: 12345, // matches targetProjectAccountId
|
|
336
|
+
// });
|
|
337
|
+
// (isDeveloperTestAccount as Mock).mockReturnValue(true);
|
|
338
|
+
// (fetchAppInstallationData as Mock).mockResolvedValue({
|
|
339
|
+
// data: {
|
|
340
|
+
// isInstalledWithScopeGroups: false,
|
|
341
|
+
// previouslyAuthorizedScopeGroups: [],
|
|
342
|
+
// },
|
|
343
|
+
// });
|
|
344
|
+
// (installAppAutoPrompt as Mock).mockResolvedValue(false);
|
|
345
|
+
// // Create a new instance to trigger the exit during setup
|
|
346
|
+
// const newAppDevModeInterface = new AppDevModeInterface({
|
|
347
|
+
// localDevState: mockLocalDevState,
|
|
348
|
+
// localDevLogger: mockLocalDevLogger,
|
|
349
|
+
// });
|
|
350
|
+
// // The setup method catches the error, so we check that process.exit was called
|
|
351
|
+
// await newAppDevModeInterface.setup({});
|
|
352
|
+
// expect(process.exit).toHaveBeenCalledWith(0);
|
|
353
|
+
// });
|
|
354
|
+
// @TODO: Restore test account auto install functionality
|
|
355
|
+
// it('should fallback to browser install if auto-install fails', async () => {
|
|
356
|
+
// (fetchAppInstallationData as Mock).mockResolvedValue({
|
|
357
|
+
// data: {
|
|
358
|
+
// isInstalledWithScopeGroups: false,
|
|
359
|
+
// previouslyAuthorizedScopeGroups: [],
|
|
360
|
+
// },
|
|
361
|
+
// });
|
|
362
|
+
// (installStaticAuthAppOnTestAccount as Mock).mockRejectedValue(
|
|
363
|
+
// new Error('Install failed')
|
|
364
|
+
// );
|
|
365
|
+
// await appDevModeInterface.setup({});
|
|
366
|
+
// expect(installAppBrowserPrompt).toHaveBeenCalledWith(
|
|
367
|
+
// 'http://static-install-url',
|
|
368
|
+
// false
|
|
369
|
+
// );
|
|
370
|
+
// });
|
|
354
371
|
});
|
|
355
372
|
describe('start()', () => {
|
|
356
373
|
it('should return early if no app node exists', async () => {
|