@hubspot/cli 7.6.0-beta.9 → 7.6.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/api/__tests__/migrate.test.js +5 -5
- package/api/migrate.d.ts +4 -5
- package/api/migrate.js +2 -10
- package/commands/__tests__/getStarted.test.js +2 -2
- package/commands/__tests__/mcp.test.js +1 -1
- package/commands/__tests__/project.test.js +0 -3
- package/commands/app/__tests__/migrate.test.js +1 -1
- package/commands/app/migrate.js +4 -5
- 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/config/set.js +0 -1
- package/commands/getStarted.d.ts +0 -2
- package/commands/getStarted.js +72 -24
- package/commands/mcp/__tests__/setup.test.js +2 -2
- package/commands/mcp/setup.d.ts +0 -1
- package/commands/mcp/setup.js +14 -13
- package/commands/mcp.js +3 -3
- package/commands/project/__tests__/add.test.js +64 -0
- package/commands/project/__tests__/create.test.js +57 -0
- package/commands/project/__tests__/deploy.test.js +3 -5
- package/commands/project/__tests__/devUnifiedFlow.test.js +20 -11
- package/commands/project/__tests__/logs.test.js +0 -3
- package/commands/project/__tests__/migrate.test.js +1 -2
- package/commands/project/__tests__/migrateApp.test.js +1 -2
- package/commands/project/__tests__/profile.test.js +1 -1
- package/commands/project/add.d.ts +1 -1
- package/commands/project/add.js +4 -10
- package/commands/project/create.js +10 -11
- package/commands/project/deploy.js +11 -63
- package/commands/project/dev/deprecatedFlow.js +2 -1
- package/commands/project/dev/index.js +36 -15
- package/commands/project/dev/unifiedFlow.js +14 -10
- package/commands/project/download.js +1 -2
- package/commands/project/installDeps.js +1 -2
- package/commands/project/listBuilds.js +2 -2
- package/commands/project/logs.js +2 -2
- package/commands/project/migrate.js +41 -13
- package/commands/project/migrateApp.js +1 -2
- package/commands/project/open.js +1 -2
- 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 +4 -4
- package/commands/project/validate.js +2 -2
- package/commands/project/watch.js +4 -4
- package/commands/project.js +1 -2
- package/commands/sandbox/delete.js +1 -1
- package/commands/testAccount/importData.d.ts +1 -1
- package/commands/testAccount/importData.js +1 -1
- package/commands/testAccount.js +1 -1
- package/lang/en.d.ts +104 -56
- package/lang/en.js +118 -68
- package/lang/en.lyaml +12 -12
- package/lib/__tests__/hasFeature.test.js +145 -7
- package/lib/__tests__/importData.test.js +1 -1
- package/lib/app/__tests__/migrate.test.js +26 -31
- package/lib/app/migrate.d.ts +3 -10
- package/lib/app/migrate.js +14 -25
- package/lib/constants.d.ts +9 -0
- package/lib/constants.js +9 -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/links.d.ts +1 -0
- package/lib/links.js +10 -3
- package/lib/mcp/setup.d.ts +0 -2
- package/lib/mcp/setup.js +4 -29
- package/lib/middleware/fireAlarmMiddleware.js +15 -5
- package/lib/projects/__tests__/AppDevModeInterface.test.js +72 -44
- package/lib/projects/__tests__/LocalDevProcess.test.js +228 -16
- package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
- package/lib/projects/__tests__/components.test.js +164 -7
- package/lib/projects/__tests__/deploy.test.js +229 -0
- package/lib/projects/__tests__/{localDevHelpers.test.js → localDevProjectHelpers.test.js} +5 -3
- 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 +142 -8
- package/lib/projects/add/legacyAddComponent.d.ts +1 -1
- package/lib/projects/add/legacyAddComponent.js +5 -1
- package/lib/projects/add/v3AddComponent.d.ts +2 -1
- package/lib/projects/add/v3AddComponent.js +22 -5
- package/lib/projects/components.d.ts +1 -0
- package/lib/projects/components.js +27 -1
- package/lib/projects/create/__tests__/v3.test.js +174 -11
- 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 +38 -13
- package/lib/projects/deploy.d.ts +13 -0
- package/lib/projects/deploy.js +63 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
- package/lib/projects/localDev/AppDevModeInterface.js +132 -48
- package/lib/projects/localDev/DevServerManagerV2.js +1 -0
- package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
- package/lib/projects/localDev/LocalDevLogger.js +22 -0
- package/lib/projects/localDev/LocalDevManager.js +1 -1
- package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
- package/lib/projects/localDev/LocalDevProcess.js +93 -20
- package/lib/projects/localDev/LocalDevState.d.ts +13 -9
- package/lib/projects/localDev/LocalDevState.js +26 -17
- package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
- package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
- package/lib/projects/localDev/{helpers.d.ts → helpers/account.d.ts} +1 -14
- package/lib/projects/localDev/helpers/account.js +233 -0
- package/lib/projects/localDev/helpers/project.d.ts +12 -0
- package/lib/projects/localDev/helpers/project.js +176 -0
- package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
- package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
- 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} +4 -14
- package/lib/projects/upload.js +1 -1
- package/lib/projects/urls.d.ts +2 -0
- package/lib/projects/urls.js +7 -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 +3 -2
- package/lib/prompts/projectDevTargetAccountPrompt.js +1 -0
- package/lib/prompts/promptUtils.d.ts +7 -1
- package/lib/prompts/promptUtils.js +17 -1
- package/lib/prompts/selectProjectTemplatePrompt.js +3 -1
- 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/index.js +3 -6
- package/lib/usageTracking.js +2 -2
- package/mcp-server/server.js +2 -1
- package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
- package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
- package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
- 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.d.ts +23 -0
- package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
- package/mcp-server/tools/cms/HsListTool.d.ts +23 -0
- package/mcp-server/tools/cms/HsListTool.js +58 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -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/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
- package/mcp-server/tools/cms/__tests__/HsListTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsListTool.test.js +120 -0
- package/mcp-server/tools/index.d.ts +1 -0
- package/mcp-server/tools/index.js +16 -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 +3 -3
- package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
- package/mcp-server/tools/project/DocsSearchTool.js +8 -8
- package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
- package/mcp-server/tools/project/GetConfigValuesTool.js +14 -8
- 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 +3 -3
- package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +15 -13
- 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 +14 -6
- 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/package.json +5 -4
- package/types/LocalDev.d.ts +21 -4
- package/types/Projects.d.ts +1 -0
- package/types/Prompts.d.ts +1 -0
- package/ui/components/BoxWithTitle.d.ts +8 -0
- package/ui/components/BoxWithTitle.js +9 -0
- package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
- package/ui/components/HorizontalSelectPrompt.js +30 -0
- package/ui/components/StatusMessageBoxes.d.ts +12 -0
- package/ui/components/StatusMessageBoxes.js +31 -0
- package/ui/index.js +1 -1
- package/ui/lib/ui-testing-utils.d.ts +9 -0
- package/ui/lib/ui-testing-utils.js +47 -0
- package/ui/lib/useTerminalSize.d.ts +13 -0
- package/ui/lib/useTerminalSize.js +31 -0
- package/ui/styles.d.ts +18 -0
- package/ui/styles.js +18 -0
- package/ui/views/UiSandbox.d.ts +5 -0
- package/ui/views/UiSandbox.js +25 -0
- package/lib/projects/localDev/helpers.js +0 -388
- /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
- /package/lib/projects/__tests__/{localDevHelpers.test.d.ts → localDevProjectHelpers.test.d.ts} +0 -0
package/lib/mcp/setup.js
CHANGED
|
@@ -4,17 +4,15 @@ import { promptUser } from '../prompts/promptUtils.js';
|
|
|
4
4
|
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
5
5
|
import { logError } from '../errorHandlers/index.js';
|
|
6
6
|
import { execAsync } from '../../mcp-server/utils/command.js';
|
|
7
|
-
import { spawn } from 'node:child_process';
|
|
8
7
|
import path from 'path';
|
|
9
8
|
import os from 'os';
|
|
10
9
|
import fs from 'fs-extra';
|
|
11
10
|
import { existsSync } from 'fs';
|
|
12
|
-
const mcpServerName = '
|
|
11
|
+
const mcpServerName = 'HubSpotDev';
|
|
13
12
|
const claudeCode = 'claude';
|
|
14
13
|
const windsurf = 'windsurf';
|
|
15
14
|
const cursor = 'cursor';
|
|
16
15
|
const vscode = 'vscode';
|
|
17
|
-
const supportedMintlifyClients = [windsurf, cursor];
|
|
18
16
|
export const supportedTools = [
|
|
19
17
|
{ name: commands.mcp.setup.claudeCode, value: claudeCode },
|
|
20
18
|
{ name: commands.mcp.setup.cursor, value: cursor },
|
|
@@ -25,28 +23,6 @@ const defaultMcpCommand = {
|
|
|
25
23
|
command: 'hs',
|
|
26
24
|
args: ['mcp', 'start'],
|
|
27
25
|
};
|
|
28
|
-
export async function addMintlifyMcpServer(installTargets) {
|
|
29
|
-
await runSetupFunction(() => setupMintlify(installTargets));
|
|
30
|
-
}
|
|
31
|
-
export async function setupMintlify(derivedTargets = supportedMintlifyClients) {
|
|
32
|
-
uiLogger.info(commands.mcp.setup.installingDocSearch);
|
|
33
|
-
uiLogger.log('');
|
|
34
|
-
return new Promise(resolve => {
|
|
35
|
-
const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
|
|
36
|
-
const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
|
|
37
|
-
const childProcess = spawn(`npx`, docsSearchClients && docsSearchClients.length
|
|
38
|
-
? [...subcommands, '--client', ...docsSearchClients]
|
|
39
|
-
: subcommands, {
|
|
40
|
-
stdio: 'inherit',
|
|
41
|
-
});
|
|
42
|
-
childProcess.on('exit', code => {
|
|
43
|
-
if (code !== 0) {
|
|
44
|
-
resolve(false);
|
|
45
|
-
}
|
|
46
|
-
resolve(true);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
26
|
export async function addMcpServerToConfig(targets) {
|
|
51
27
|
try {
|
|
52
28
|
let derivedTargets = [];
|
|
@@ -164,7 +140,7 @@ export async function setupVsCode(mcpCommand = defaultMcpCommand) {
|
|
|
164
140
|
name: mcpServerName,
|
|
165
141
|
...buildCommandWithAgentString(mcpCommand, vscode),
|
|
166
142
|
});
|
|
167
|
-
await execAsync(`code --add-mcp
|
|
143
|
+
await execAsync(`code --add-mcp ${JSON.stringify(mcpConfig)}`);
|
|
168
144
|
SpinniesManager.succeed('vsCode', {
|
|
169
145
|
text: commands.mcp.setup.spinners.configuredVsCode,
|
|
170
146
|
});
|
|
@@ -206,15 +182,14 @@ export async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
|
|
|
206
182
|
});
|
|
207
183
|
await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
|
|
208
184
|
}
|
|
209
|
-
await execAsync(`claude mcp add-json "${mcpServerName}"
|
|
185
|
+
await execAsync(`claude mcp add-json "${mcpServerName}" ${JSON.stringify(mcpConfig)} --scope user`);
|
|
210
186
|
SpinniesManager.succeed('claudeCode', {
|
|
211
187
|
text: commands.mcp.setup.spinners.configuredClaudeCode,
|
|
212
188
|
});
|
|
213
189
|
return true;
|
|
214
190
|
}
|
|
215
191
|
catch (error) {
|
|
216
|
-
if (error instanceof Error &&
|
|
217
|
-
error.message.includes('claude: command not found')) {
|
|
192
|
+
if (error instanceof Error && error.message.includes('claude')) {
|
|
218
193
|
SpinniesManager.fail('claudeCode', {
|
|
219
194
|
text: commands.mcp.setup.spinners.claudeCodeNotFound,
|
|
220
195
|
});
|
|
@@ -3,6 +3,8 @@ import { fetchFireAlarms } from '@hubspot/local-dev-lib/api/fireAlarm';
|
|
|
3
3
|
import { debugError } from '../errorHandlers/index.js';
|
|
4
4
|
import pkg from '../../package.json' with { type: 'json' };
|
|
5
5
|
import { logInBox } from '../ui/boxen.js';
|
|
6
|
+
import { renderInline } from '../../ui/index.js';
|
|
7
|
+
import { getWarningBox } from '../../ui/components/StatusMessageBoxes.js';
|
|
6
8
|
/*
|
|
7
9
|
* Versions can be formatted like this:
|
|
8
10
|
* =7.2.2 -> targets the exact version 7.2.2
|
|
@@ -98,12 +100,20 @@ async function logFireAlarms(accountId, command, version) {
|
|
|
98
100
|
}
|
|
99
101
|
return acc;
|
|
100
102
|
}, '');
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
if (!process.env.HUBSPOT_ENABLE_INK) {
|
|
104
|
+
await logInBox({
|
|
105
|
+
contents: notifications,
|
|
106
|
+
options: {
|
|
107
|
+
title: 'Notifications',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await renderInline(getWarningBox({
|
|
104
113
|
title: 'Notifications',
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
message: notifications,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
export async function checkFireAlarms(argv) {
|
|
@@ -37,6 +37,7 @@ vi.mock('../../ui/logger');
|
|
|
37
37
|
vi.mock('../../errorHandlers/index');
|
|
38
38
|
vi.mock('../localDev/LocalDevState');
|
|
39
39
|
vi.mock('../localDev/LocalDevLogger');
|
|
40
|
+
vi.mock('../../ui/SpinniesManager');
|
|
40
41
|
describe('AppDevModeInterface', () => {
|
|
41
42
|
let appDevModeInterface;
|
|
42
43
|
let mockLocalDevState;
|
|
@@ -99,6 +100,7 @@ describe('AppDevModeInterface', () => {
|
|
|
99
100
|
setAppDataForUid: vi.fn(),
|
|
100
101
|
addListener: vi.fn(),
|
|
101
102
|
addUploadWarning: vi.fn(),
|
|
103
|
+
removeListener: vi.fn(),
|
|
102
104
|
};
|
|
103
105
|
mockLocalDevLogger = {};
|
|
104
106
|
// Mock constructors
|
|
@@ -238,16 +240,21 @@ describe('AppDevModeInterface', () => {
|
|
|
238
240
|
await newAppDevModeInterface.setup({});
|
|
239
241
|
expect(process.exit).toHaveBeenCalledWith(0);
|
|
240
242
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
// });
|
|
251
258
|
it('should open browser for OAuth app installation', async () => {
|
|
252
259
|
const oauthAppNode = {
|
|
253
260
|
...mockAppNode,
|
|
@@ -286,7 +293,12 @@ describe('AppDevModeInterface', () => {
|
|
|
286
293
|
},
|
|
287
294
|
});
|
|
288
295
|
await appDevModeInterface.setup({});
|
|
289
|
-
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
|
+
});
|
|
290
302
|
});
|
|
291
303
|
it('should handle errors during setup', async () => {
|
|
292
304
|
const error = new Error('Setup failed');
|
|
@@ -316,39 +328,46 @@ describe('AppDevModeInterface', () => {
|
|
|
316
328
|
await appDevModeInterface.setup({});
|
|
317
329
|
expect(process.exit).toHaveBeenCalledWith(1);
|
|
318
330
|
});
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
+
// });
|
|
352
371
|
});
|
|
353
372
|
describe('start()', () => {
|
|
354
373
|
it('should return early if no app node exists', async () => {
|
|
@@ -386,6 +405,15 @@ describe('AppDevModeInterface', () => {
|
|
|
386
405
|
await appDevModeInterface.cleanup();
|
|
387
406
|
expect(UIEDevModeInterface.cleanup).toHaveBeenCalled();
|
|
388
407
|
});
|
|
408
|
+
it('should remove state listeners', async () => {
|
|
409
|
+
await appDevModeInterface.cleanup();
|
|
410
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('devServerMessage',
|
|
411
|
+
// @ts-expect-error access private method for testing
|
|
412
|
+
appDevModeInterface.onDevServerMessage);
|
|
413
|
+
expect(mockLocalDevState.removeListener).toHaveBeenCalledWith('projectNodes',
|
|
414
|
+
// @ts-expect-error
|
|
415
|
+
appDevModeInterface.onChangeProjectNodes);
|
|
416
|
+
});
|
|
389
417
|
});
|
|
390
418
|
describe('isAutomaticallyInstallable()', () => {
|
|
391
419
|
it('should return true for static auth app on test account with correct parent', () => {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { translateForLocalDev } from '@hubspot/project-parsing-lib';
|
|
3
3
|
import { handleProjectUpload } from '../upload.js';
|
|
4
|
+
import { handleProjectDeploy } from '../deploy.js';
|
|
4
5
|
import { getProjectConfig } from '../config.js';
|
|
6
|
+
import { fetchProject } from '@hubspot/local-dev-lib/api/projects';
|
|
7
|
+
import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
|
|
5
8
|
import LocalDevProcess from '../localDev/LocalDevProcess.js';
|
|
6
9
|
import LocalDevLogger from '../localDev/LocalDevLogger.js';
|
|
7
10
|
import DevServerManagerV2 from '../localDev/DevServerManagerV2.js';
|
|
@@ -19,7 +22,10 @@ vi.mock('@hubspot/ui-extensions-dev-server', () => ({
|
|
|
19
22
|
vi.mock('open');
|
|
20
23
|
vi.mock('@hubspot/project-parsing-lib');
|
|
21
24
|
vi.mock('../upload');
|
|
25
|
+
vi.mock('../deploy');
|
|
22
26
|
vi.mock('../config');
|
|
27
|
+
vi.mock('@hubspot/local-dev-lib/api/projects');
|
|
28
|
+
vi.mock('@hubspot/local-dev-lib/errors/index');
|
|
23
29
|
vi.mock('../localDev/LocalDevLogger');
|
|
24
30
|
vi.mock('../localDev/DevServerManagerV2');
|
|
25
31
|
// Tests for LocalDevProcess and LocalDevState
|
|
@@ -37,10 +43,35 @@ describe('LocalDevProcess', () => {
|
|
|
37
43
|
projectConfig: mockProjectConfig,
|
|
38
44
|
targetProjectAccountId: 123,
|
|
39
45
|
targetTestingAccountId: 456,
|
|
40
|
-
|
|
46
|
+
projectData: {
|
|
47
|
+
id: 789,
|
|
48
|
+
name: 'test-project',
|
|
49
|
+
portalId: 123,
|
|
50
|
+
createdAt: 0,
|
|
51
|
+
deletedAt: 0,
|
|
52
|
+
isLocked: false,
|
|
53
|
+
updatedAt: 0,
|
|
54
|
+
latestBuild: {
|
|
55
|
+
activitySource: { type: 'HUBSPOT_USER', userId: 456 },
|
|
56
|
+
buildId: 123,
|
|
57
|
+
createdAt: '2023-01-01T00:00:00Z',
|
|
58
|
+
deployableState: 'DEPLOYABLE',
|
|
59
|
+
deployStatusTaskLocator: { id: 'task-123', links: [] },
|
|
60
|
+
enqueuedAt: '2023-01-01T00:00:00Z',
|
|
61
|
+
finishedAt: '2023-01-01T00:05:00Z',
|
|
62
|
+
isAutoDeployEnabled: false,
|
|
63
|
+
portalId: 123,
|
|
64
|
+
projectName: 'test-project',
|
|
65
|
+
startedAt: '2023-01-01T00:01:00Z',
|
|
66
|
+
status: 'SUCCESS',
|
|
67
|
+
subbuildStatuses: [],
|
|
68
|
+
uploadMessage: 'Build completed',
|
|
69
|
+
autoDeployId: 0,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
41
72
|
initialProjectNodes: {},
|
|
73
|
+
initialProjectProfileData: {},
|
|
42
74
|
env: ENVIRONMENTS.PROD,
|
|
43
|
-
projectName: 'test-project',
|
|
44
75
|
};
|
|
45
76
|
beforeEach(() => {
|
|
46
77
|
vi.clearAllMocks();
|
|
@@ -61,6 +92,9 @@ describe('LocalDevProcess', () => {
|
|
|
61
92
|
uploadSuccess: vi.fn(),
|
|
62
93
|
fileChangeError: vi.fn(),
|
|
63
94
|
uploadWarning: vi.fn(),
|
|
95
|
+
deployInitiated: vi.fn(),
|
|
96
|
+
deployError: vi.fn(),
|
|
97
|
+
deploySuccess: vi.fn(),
|
|
64
98
|
};
|
|
65
99
|
mockDevServerManager = {
|
|
66
100
|
setup: vi.fn().mockResolvedValue(undefined),
|
|
@@ -71,6 +105,8 @@ describe('LocalDevProcess', () => {
|
|
|
71
105
|
// Mock constructors
|
|
72
106
|
LocalDevLogger.mockImplementation(() => mockLocalDevLogger);
|
|
73
107
|
DevServerManagerV2.mockImplementation(() => mockDevServerManager);
|
|
108
|
+
// Mock external functions
|
|
109
|
+
isHubSpotHttpError.mockReturnValue(false);
|
|
74
110
|
// Create process instance
|
|
75
111
|
process = new LocalDevProcess(mockOptions);
|
|
76
112
|
// Mock process.exit
|
|
@@ -140,9 +176,14 @@ describe('LocalDevProcess', () => {
|
|
|
140
176
|
handleProjectUpload.mockResolvedValue({
|
|
141
177
|
uploadError: new Error('Upload failed'),
|
|
142
178
|
});
|
|
143
|
-
const
|
|
179
|
+
const result = await process.uploadProject();
|
|
144
180
|
expect(mockLocalDevLogger.uploadError).toHaveBeenCalledWith(new Error('Upload failed'));
|
|
145
|
-
expect(
|
|
181
|
+
expect(result).toEqual({
|
|
182
|
+
uploadSuccess: false,
|
|
183
|
+
buildSuccess: false,
|
|
184
|
+
deploySuccess: false,
|
|
185
|
+
deployId: undefined,
|
|
186
|
+
});
|
|
146
187
|
});
|
|
147
188
|
it('should handle successful upload', async () => {
|
|
148
189
|
await process.handleConfigFileChange();
|
|
@@ -151,38 +192,120 @@ describe('LocalDevProcess', () => {
|
|
|
151
192
|
});
|
|
152
193
|
handleProjectUpload.mockResolvedValue({
|
|
153
194
|
uploadError: null,
|
|
195
|
+
result: {
|
|
196
|
+
deployResult: {
|
|
197
|
+
id: 'deploy-123',
|
|
198
|
+
deployId: 123,
|
|
199
|
+
status: 'SUCCESS',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
fetchProject.mockResolvedValue({
|
|
204
|
+
data: {
|
|
205
|
+
id: 789,
|
|
206
|
+
name: 'test-project',
|
|
207
|
+
portalId: 123,
|
|
208
|
+
createdAt: 0,
|
|
209
|
+
deletedAt: 0,
|
|
210
|
+
isLocked: false,
|
|
211
|
+
updatedAt: 0,
|
|
212
|
+
latestBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
213
|
+
deployedBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
214
|
+
},
|
|
154
215
|
});
|
|
155
|
-
const
|
|
216
|
+
const result = await process.uploadProject();
|
|
217
|
+
expect(fetchProject).toHaveBeenCalledWith(mockOptions.targetProjectAccountId, mockOptions.projectConfig.name);
|
|
156
218
|
expect(mockLocalDevLogger.uploadSuccess).toHaveBeenCalled();
|
|
157
219
|
// @ts-expect-error accessing private property for testing
|
|
158
220
|
expect(process.state.uploadWarnings.size).toBe(0);
|
|
159
|
-
expect(
|
|
221
|
+
expect(result).toEqual({
|
|
222
|
+
uploadSuccess: true,
|
|
223
|
+
buildSuccess: true,
|
|
224
|
+
deploySuccess: true,
|
|
225
|
+
deployId: 123,
|
|
226
|
+
});
|
|
160
227
|
});
|
|
161
|
-
it('should reset projectNodesAtLastUpload', async () => {
|
|
162
|
-
const
|
|
163
|
-
|
|
228
|
+
it('should reset projectNodesAtLastUpload if deploy is successful', async () => {
|
|
229
|
+
const mockInitialNodes = {
|
|
230
|
+
node1: {
|
|
231
|
+
uid: 'node1',
|
|
232
|
+
componentType: 'APP',
|
|
233
|
+
localDev: {
|
|
234
|
+
componentRoot: '/test/path',
|
|
235
|
+
componentConfigPath: '/test/path/config.json',
|
|
236
|
+
configUpdatedSinceLastUpload: false,
|
|
237
|
+
},
|
|
238
|
+
componentDeps: {},
|
|
239
|
+
metaFilePath: '/test/path',
|
|
240
|
+
config: { name: 'Node 1' },
|
|
241
|
+
files: [],
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
const mockNewNodes = {
|
|
245
|
+
node1: {
|
|
246
|
+
uid: 'node2',
|
|
247
|
+
componentType: 'APP',
|
|
248
|
+
localDev: {
|
|
249
|
+
componentRoot: '/test/path',
|
|
250
|
+
componentConfigPath: '/test/path/config.json',
|
|
251
|
+
configUpdatedSinceLastUpload: false,
|
|
252
|
+
},
|
|
253
|
+
componentDeps: {},
|
|
254
|
+
metaFilePath: '/test/path',
|
|
255
|
+
config: { name: 'Node 2' },
|
|
256
|
+
files: [],
|
|
257
|
+
},
|
|
258
|
+
};
|
|
164
259
|
// @ts-expect-error accessing private property for testing
|
|
165
|
-
process.state.
|
|
260
|
+
process.state.projectNodesAtLastDeploy = mockInitialNodes;
|
|
166
261
|
getProjectConfig.mockResolvedValue({
|
|
167
262
|
projectConfig: mockOptions.projectConfig,
|
|
168
263
|
});
|
|
169
264
|
handleProjectUpload.mockResolvedValue({
|
|
170
265
|
uploadError: null,
|
|
266
|
+
result: {
|
|
267
|
+
deployResult: {
|
|
268
|
+
id: 'deploy-123',
|
|
269
|
+
deployId: 456,
|
|
270
|
+
status: 'SUCCESS',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
171
273
|
});
|
|
172
274
|
translateForLocalDev.mockResolvedValue({
|
|
173
|
-
intermediateNodesIndexedByUid:
|
|
275
|
+
intermediateNodesIndexedByUid: mockNewNodes,
|
|
174
276
|
});
|
|
175
|
-
|
|
176
|
-
|
|
277
|
+
fetchProject.mockResolvedValue({
|
|
278
|
+
data: {
|
|
279
|
+
id: 789,
|
|
280
|
+
name: 'test-project',
|
|
281
|
+
portalId: 123,
|
|
282
|
+
createdAt: 0,
|
|
283
|
+
deletedAt: 0,
|
|
284
|
+
isLocked: false,
|
|
285
|
+
updatedAt: 0,
|
|
286
|
+
latestBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
287
|
+
deployedBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
const result = await process.uploadProject();
|
|
291
|
+
// Verify translateForLocalDev was called during updateProjectNodesAfterDeploy
|
|
177
292
|
expect(translateForLocalDev).toHaveBeenCalledWith({
|
|
178
293
|
projectSourceDir: path.join(mockOptions.projectDir, mockOptions.projectConfig.srcDir),
|
|
179
294
|
platformVersion: mockOptions.projectConfig.platformVersion,
|
|
180
295
|
accountId: mockOptions.targetProjectAccountId,
|
|
181
|
-
}, {
|
|
296
|
+
}, {
|
|
297
|
+
profile: undefined,
|
|
298
|
+
projectNodesAtLastUpload: undefined,
|
|
299
|
+
});
|
|
182
300
|
// Verify projectNodesAtLastUpload was reset to the new nodes
|
|
183
301
|
// @ts-expect-error accessing private property for testing
|
|
184
|
-
expect(process.state.
|
|
185
|
-
expect(
|
|
302
|
+
expect(process.state.projectNodesAtLastDeploy).toEqual(mockNewNodes);
|
|
303
|
+
expect(result).toEqual({
|
|
304
|
+
uploadSuccess: true,
|
|
305
|
+
buildSuccess: true,
|
|
306
|
+
deploySuccess: true,
|
|
307
|
+
deployId: 456,
|
|
308
|
+
});
|
|
186
309
|
});
|
|
187
310
|
});
|
|
188
311
|
describe('handleFileChange()', () => {
|
|
@@ -251,4 +374,93 @@ describe('LocalDevProcess', () => {
|
|
|
251
374
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
252
375
|
});
|
|
253
376
|
});
|
|
377
|
+
describe('deployLatestBuild()', () => {
|
|
378
|
+
beforeEach(() => {
|
|
379
|
+
vi.clearAllMocks();
|
|
380
|
+
});
|
|
381
|
+
it('should successfully deploy latest build', async () => {
|
|
382
|
+
const mockDeploy = {
|
|
383
|
+
deployId: 456,
|
|
384
|
+
buildId: 123,
|
|
385
|
+
status: 'SUCCESS',
|
|
386
|
+
enqueuedAt: '2023-01-01T00:00:00Z',
|
|
387
|
+
startedAt: '2023-01-01T00:01:00Z',
|
|
388
|
+
finishedAt: '2023-01-01T00:05:00Z',
|
|
389
|
+
portalId: 123,
|
|
390
|
+
projectName: 'test-project',
|
|
391
|
+
userId: 789,
|
|
392
|
+
source: 'HUBSPOT_USER',
|
|
393
|
+
subdeployStatuses: [],
|
|
394
|
+
};
|
|
395
|
+
handleProjectDeploy.mockResolvedValue(mockDeploy);
|
|
396
|
+
const result = await process.deployLatestBuild();
|
|
397
|
+
expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
|
|
398
|
+
expect(handleProjectDeploy).toHaveBeenCalledWith(123, // targetProjectAccountId
|
|
399
|
+
'test-project', // projectName
|
|
400
|
+
123, // buildId
|
|
401
|
+
true, // useV3Api
|
|
402
|
+
false // force
|
|
403
|
+
);
|
|
404
|
+
expect(mockLocalDevLogger.deploySuccess).toHaveBeenCalled();
|
|
405
|
+
expect(result).toEqual({
|
|
406
|
+
success: true,
|
|
407
|
+
deployId: 456,
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
it('should deploy with force parameter', async () => {
|
|
411
|
+
const mockDeploy = {
|
|
412
|
+
deployId: 456,
|
|
413
|
+
buildId: 123,
|
|
414
|
+
status: 'SUCCESS',
|
|
415
|
+
enqueuedAt: '2023-01-01T00:00:00Z',
|
|
416
|
+
startedAt: '2023-01-01T00:01:00Z',
|
|
417
|
+
finishedAt: '2023-01-01T00:05:00Z',
|
|
418
|
+
portalId: 123,
|
|
419
|
+
projectName: 'test-project',
|
|
420
|
+
userId: 789,
|
|
421
|
+
source: 'HUBSPOT_USER',
|
|
422
|
+
subdeployStatuses: [],
|
|
423
|
+
};
|
|
424
|
+
handleProjectDeploy.mockResolvedValue(mockDeploy);
|
|
425
|
+
const result = await process.deployLatestBuild(true);
|
|
426
|
+
expect(handleProjectDeploy).toHaveBeenCalledWith(123, // targetProjectAccountId
|
|
427
|
+
'test-project', // projectName
|
|
428
|
+
123, // buildId
|
|
429
|
+
true, // useV3Api
|
|
430
|
+
true // force
|
|
431
|
+
);
|
|
432
|
+
expect(result).toEqual({
|
|
433
|
+
success: true,
|
|
434
|
+
deployId: 456,
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
it('should return error when no build exists', async () => {
|
|
438
|
+
// Create a process without latestBuild
|
|
439
|
+
const optionsWithoutBuild = {
|
|
440
|
+
...mockOptions,
|
|
441
|
+
projectData: {
|
|
442
|
+
...mockOptions.projectData,
|
|
443
|
+
latestBuild: undefined,
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
const processWithoutBuild = new LocalDevProcess(optionsWithoutBuild);
|
|
447
|
+
const result = await processWithoutBuild.deployLatestBuild();
|
|
448
|
+
expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
|
|
449
|
+
expect(mockLocalDevLogger.deployError).toHaveBeenCalledWith('Error deploying project. No build was found to deploy.');
|
|
450
|
+
expect(result).toEqual({
|
|
451
|
+
success: false,
|
|
452
|
+
});
|
|
453
|
+
expect(handleProjectDeploy).not.toHaveBeenCalled();
|
|
454
|
+
});
|
|
455
|
+
it('should handle deploy failure when no deploy object returned', async () => {
|
|
456
|
+
handleProjectDeploy.mockResolvedValue(undefined);
|
|
457
|
+
const result = await process.deployLatestBuild();
|
|
458
|
+
expect(mockLocalDevLogger.deployInitiated).toHaveBeenCalled();
|
|
459
|
+
expect(handleProjectDeploy).toHaveBeenCalled();
|
|
460
|
+
expect(result).toEqual({
|
|
461
|
+
success: false,
|
|
462
|
+
});
|
|
463
|
+
expect(mockLocalDevLogger.deploySuccess).not.toHaveBeenCalled();
|
|
464
|
+
});
|
|
465
|
+
});
|
|
254
466
|
});
|
|
@@ -30,8 +30,16 @@ describe('LocalDevWebsocketServer', () => {
|
|
|
30
30
|
mockLocalDevProcess = {
|
|
31
31
|
addStateListener: vi.fn(),
|
|
32
32
|
removeStateListener: vi.fn(),
|
|
33
|
-
uploadProject: vi.fn(),
|
|
33
|
+
uploadProject: vi.fn().mockResolvedValue({}),
|
|
34
34
|
sendDevServerMessage: vi.fn(),
|
|
35
|
+
projectData: {
|
|
36
|
+
name: 'test-project',
|
|
37
|
+
id: 123,
|
|
38
|
+
latestBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
39
|
+
deployedBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
40
|
+
},
|
|
41
|
+
targetProjectAccountId: 456,
|
|
42
|
+
targetTestingAccountId: 789,
|
|
35
43
|
};
|
|
36
44
|
// Mock WebSocketServer constructor
|
|
37
45
|
WebSocketServer.mockImplementation(() => mockWebSocketServer);
|
|
@@ -225,23 +233,6 @@ describe('LocalDevWebsocketServer', () => {
|
|
|
225
233
|
expect(mockWebSocket3.close).not.toHaveBeenCalled();
|
|
226
234
|
});
|
|
227
235
|
it('should send project data to each connection independently', () => {
|
|
228
|
-
// Setup mock project data properties as getters
|
|
229
|
-
Object.defineProperty(mockLocalDevProcess, 'projectName', {
|
|
230
|
-
get: () => 'test-project',
|
|
231
|
-
configurable: true,
|
|
232
|
-
});
|
|
233
|
-
Object.defineProperty(mockLocalDevProcess, 'projectId', {
|
|
234
|
-
get: () => 123,
|
|
235
|
-
configurable: true,
|
|
236
|
-
});
|
|
237
|
-
Object.defineProperty(mockLocalDevProcess, 'targetProjectAccountId', {
|
|
238
|
-
get: () => 456,
|
|
239
|
-
configurable: true,
|
|
240
|
-
});
|
|
241
|
-
Object.defineProperty(mockLocalDevProcess, 'targetTestingAccountId', {
|
|
242
|
-
get: () => 789,
|
|
243
|
-
configurable: true,
|
|
244
|
-
});
|
|
245
236
|
// Establish multiple connections
|
|
246
237
|
connectionCallback(mockWebSocket1, {
|
|
247
238
|
headers: { origin: 'https://app.hubspot.com' },
|
|
@@ -255,6 +246,8 @@ describe('LocalDevWebsocketServer', () => {
|
|
|
255
246
|
data: {
|
|
256
247
|
projectName: 'test-project',
|
|
257
248
|
projectId: 123,
|
|
249
|
+
latestBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
250
|
+
deployedBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
258
251
|
targetProjectAccountId: 456,
|
|
259
252
|
targetTestingAccountId: 789,
|
|
260
253
|
},
|
|
@@ -264,6 +257,8 @@ describe('LocalDevWebsocketServer', () => {
|
|
|
264
257
|
data: {
|
|
265
258
|
projectName: 'test-project',
|
|
266
259
|
projectId: 123,
|
|
260
|
+
latestBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
261
|
+
deployedBuild: { id: 'build-1', status: 'SUCCESS' },
|
|
267
262
|
targetProjectAccountId: 456,
|
|
268
263
|
targetTestingAccountId: 789,
|
|
269
264
|
},
|
|
@@ -284,11 +279,11 @@ describe('LocalDevWebsocketServer', () => {
|
|
|
284
279
|
const closeCallbacks2 = mockWebSocket2.on.mock.calls
|
|
285
280
|
.filter(call => call[0] === 'close')
|
|
286
281
|
.map(call => call[1]);
|
|
287
|
-
expect(closeCallbacks1).toHaveLength(3); // projectNodes and
|
|
288
|
-
expect(closeCallbacks2).toHaveLength(3); // projectNodes and
|
|
282
|
+
expect(closeCallbacks1).toHaveLength(3); // projectNodes, appData, and uploadWarnings listeners
|
|
283
|
+
expect(closeCallbacks2).toHaveLength(3); // projectNodes, appData, and uploadWarnings listeners
|
|
289
284
|
// Simulate first connection closing (call all close callbacks)
|
|
290
285
|
closeCallbacks1.forEach(callback => callback());
|
|
291
|
-
// Should have removed listeners for first connection (
|
|
286
|
+
// Should have removed listeners for first connection (3 listeners: projectNodes, appData, and uploadWarnings)
|
|
292
287
|
expect(mockLocalDevProcess.removeStateListener).toHaveBeenCalledTimes(3);
|
|
293
288
|
// Simulate second connection closing
|
|
294
289
|
closeCallbacks2.forEach(callback => callback());
|