@hubspot/cli 7.7.31-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/app.js +1 -6
- 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.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 +0 -3
- package/lang/en.d.ts +8 -26
- package/lang/en.js +9 -27
- 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 +2 -10
- 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__/{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/structure.d.ts +2 -2
- package/lib/projects/upload.d.ts +1 -2
- package/lib/projects/upload.js +1 -2
- 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.d.ts +32 -0
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -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/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
- package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -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/index.js +8 -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 +4 -3
- package/types/LocalDev.d.ts +2 -1
- package/types/Projects.d.ts +1 -0
- package/types/Yargs.d.ts +1 -1
- 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/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/commands/app/__tests__/install.test.js +0 -47
- package/commands/app/install.d.ts +0 -8
- package/commands/app/install.js +0 -122
- /package/{commands/app/__tests__/install.test.d.ts → lib/projects/__tests__/deploy.test.d.ts} +0 -0
- /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → platformVersion.test.d.ts} +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { HsListFunctionsTool } from '../HsListFunctionsTool.js';
|
|
3
|
+
import { runCommandInDir } from '../../../utils/project.js';
|
|
4
|
+
import { addFlag } from '../../../utils/command.js';
|
|
5
|
+
vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
|
|
6
|
+
vi.mock('../../../utils/project');
|
|
7
|
+
vi.mock('../../../utils/command');
|
|
8
|
+
vi.mock('../../../utils/toolUsageTracking', () => ({
|
|
9
|
+
trackToolUsage: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
const mockRunCommandInDir = runCommandInDir;
|
|
12
|
+
const mockAddFlag = addFlag;
|
|
13
|
+
describe('HsListFunctionsTool', () => {
|
|
14
|
+
let mockMcpServer;
|
|
15
|
+
let tool;
|
|
16
|
+
let mockRegisteredTool;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
// @ts-expect-error Not mocking the whole server
|
|
20
|
+
mockMcpServer = {
|
|
21
|
+
registerTool: vi.fn(),
|
|
22
|
+
};
|
|
23
|
+
mockRegisteredTool = {};
|
|
24
|
+
mockMcpServer.registerTool.mockReturnValue(mockRegisteredTool);
|
|
25
|
+
tool = new HsListFunctionsTool(mockMcpServer);
|
|
26
|
+
});
|
|
27
|
+
describe('register', () => {
|
|
28
|
+
it('should register the tool with the MCP server', () => {
|
|
29
|
+
const result = tool.register();
|
|
30
|
+
expect(mockMcpServer.registerTool).toHaveBeenCalledWith('list-hubspot-cms-serverless-functions', {
|
|
31
|
+
title: 'List HubSpot CMS Serverless Functions',
|
|
32
|
+
description: 'Get a list of all serverless functions deployed in a HubSpot portal/account. Shows function routes, HTTP methods, secrets, and timestamps.',
|
|
33
|
+
inputSchema: expect.any(Object),
|
|
34
|
+
}, expect.any(Function));
|
|
35
|
+
expect(result).toBe(mockRegisteredTool);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('handler', () => {
|
|
39
|
+
it('should execute hs function list command with no parameters', async () => {
|
|
40
|
+
mockRunCommandInDir.mockResolvedValue({
|
|
41
|
+
stdout: 'Route | Method | Secrets | Created | Updated\n/api/test | GET | | 2023-01-01 | 2023-01-01',
|
|
42
|
+
stderr: '',
|
|
43
|
+
});
|
|
44
|
+
const result = await tool.handler({
|
|
45
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
46
|
+
});
|
|
47
|
+
expect(mockRunCommandInDir).toHaveBeenCalledWith('/test/dir', 'hs function list');
|
|
48
|
+
expect(result.content).toHaveLength(2);
|
|
49
|
+
expect(result.content[0].text).toContain('Route | Method | Secrets');
|
|
50
|
+
expect(result.content[1].text).toBe('');
|
|
51
|
+
});
|
|
52
|
+
it('should execute hs function list command with json flag', async () => {
|
|
53
|
+
mockRunCommandInDir.mockResolvedValue({
|
|
54
|
+
stdout: '[{"route": "/api/test", "method": "GET"}]',
|
|
55
|
+
stderr: '',
|
|
56
|
+
});
|
|
57
|
+
const result = await tool.handler({
|
|
58
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
59
|
+
json: true,
|
|
60
|
+
});
|
|
61
|
+
expect(mockRunCommandInDir).toHaveBeenCalledWith('/test/dir', 'hs function list --json');
|
|
62
|
+
expect(result.content).toHaveLength(2);
|
|
63
|
+
expect(result.content[0].text).toContain('[{"route": "/api/test"');
|
|
64
|
+
expect(result.content[1].text).toBe('');
|
|
65
|
+
});
|
|
66
|
+
it('should execute hs function list command with account parameter', async () => {
|
|
67
|
+
mockAddFlag.mockReturnValue('hs function list --account test-account');
|
|
68
|
+
mockRunCommandInDir.mockResolvedValue({
|
|
69
|
+
stdout: 'account-specific-functions',
|
|
70
|
+
stderr: '',
|
|
71
|
+
});
|
|
72
|
+
const result = await tool.handler({
|
|
73
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
74
|
+
account: 'test-account',
|
|
75
|
+
});
|
|
76
|
+
expect(mockAddFlag).toHaveBeenCalledWith('hs function list', 'account', 'test-account');
|
|
77
|
+
expect(mockRunCommandInDir).toHaveBeenCalledWith('/test/dir', 'hs function list --account test-account');
|
|
78
|
+
expect(result.content).toHaveLength(2);
|
|
79
|
+
expect(result.content[0].text).toContain('account-specific-functions');
|
|
80
|
+
expect(result.content[1].text).toBe('');
|
|
81
|
+
});
|
|
82
|
+
it('should execute hs function list command with both json and account parameters', async () => {
|
|
83
|
+
mockAddFlag.mockReturnValue('hs function list --json --account test-account');
|
|
84
|
+
mockRunCommandInDir.mockResolvedValue({
|
|
85
|
+
stdout: '[{"route": "/api/test"}]',
|
|
86
|
+
stderr: '',
|
|
87
|
+
});
|
|
88
|
+
const result = await tool.handler({
|
|
89
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
90
|
+
json: true,
|
|
91
|
+
account: 'test-account',
|
|
92
|
+
});
|
|
93
|
+
expect(mockAddFlag).toHaveBeenCalledWith('hs function list --json', 'account', 'test-account');
|
|
94
|
+
expect(mockRunCommandInDir).toHaveBeenCalledWith('/test/dir', 'hs function list --json --account test-account');
|
|
95
|
+
expect(result.content).toHaveLength(2);
|
|
96
|
+
expect(result.content[0].text).toContain('[{"route": "/api/test"}]');
|
|
97
|
+
expect(result.content[1].text).toBe('');
|
|
98
|
+
});
|
|
99
|
+
it('should handle command execution errors', async () => {
|
|
100
|
+
mockRunCommandInDir.mockRejectedValue(new Error('Command failed'));
|
|
101
|
+
const result = await tool.handler({
|
|
102
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
103
|
+
});
|
|
104
|
+
expect(result.content).toHaveLength(1);
|
|
105
|
+
expect(result.content[0].text).toContain('Error executing hs function list command: Command failed');
|
|
106
|
+
});
|
|
107
|
+
it('should handle stderr output', async () => {
|
|
108
|
+
mockRunCommandInDir.mockResolvedValue({
|
|
109
|
+
stdout: 'Route | Method | Secrets\n/api/test | GET |',
|
|
110
|
+
stderr: 'Warning: Some warning message',
|
|
111
|
+
});
|
|
112
|
+
const result = await tool.handler({
|
|
113
|
+
absoluteCurrentWorkingDirectory: '/test/dir',
|
|
114
|
+
});
|
|
115
|
+
expect(result.content).toHaveLength(2);
|
|
116
|
+
expect(result.content[0].text).toContain('/api/test | GET');
|
|
117
|
+
expect(result.content[1].text).toContain('Warning: Some warning message');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -9,6 +9,10 @@ import { DocsSearchTool } from './project/DocsSearchTool.js';
|
|
|
9
9
|
import { DocFetchTool } from './project/DocFetchTool.js';
|
|
10
10
|
import { HsListTool } from './cms/HsListTool.js';
|
|
11
11
|
import { HsCreateModuleTool } from './cms/HsCreateModuleTool.js';
|
|
12
|
+
import { HsCreateTemplateTool } from './cms/HsCreateTemplateTool.js';
|
|
13
|
+
import { HsCreateFunctionTool } from './cms/HsCreateFunctionTool.js';
|
|
14
|
+
import { HsListFunctionsTool } from './cms/HsListFunctionsTool.js';
|
|
15
|
+
import { HsFunctionLogsTool } from './cms/HsFunctionLogsTool.js';
|
|
12
16
|
export function registerProjectTools(mcpServer) {
|
|
13
17
|
return [
|
|
14
18
|
new UploadProjectTools(mcpServer).register(),
|
|
@@ -26,5 +30,9 @@ export function registerCmsTools(mcpServer) {
|
|
|
26
30
|
return [
|
|
27
31
|
new HsListTool(mcpServer).register(),
|
|
28
32
|
new HsCreateModuleTool(mcpServer).register(),
|
|
33
|
+
new HsCreateTemplateTool(mcpServer).register(),
|
|
34
|
+
new HsCreateFunctionTool(mcpServer).register(),
|
|
35
|
+
new HsListFunctionsTool(mcpServer).register(),
|
|
36
|
+
new HsFunctionLogsTool(mcpServer).register(),
|
|
29
37
|
];
|
|
30
38
|
}
|
|
@@ -6,19 +6,19 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
6
6
|
addApp: z.ZodBoolean;
|
|
7
7
|
distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
|
|
8
8
|
auth: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>;
|
|
9
|
-
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">]>, "many">>;
|
|
9
|
+
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
11
|
absoluteProjectPath: string;
|
|
12
12
|
addApp: boolean;
|
|
13
13
|
auth?: "oauth" | "static" | undefined;
|
|
14
14
|
distribution?: "marketplace" | "private" | undefined;
|
|
15
|
-
features?: ("card" | "settings" | "app-function" | "webhooks" | "
|
|
15
|
+
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
16
16
|
}, {
|
|
17
17
|
absoluteProjectPath: string;
|
|
18
18
|
addApp: boolean;
|
|
19
19
|
auth?: "oauth" | "static" | undefined;
|
|
20
20
|
distribution?: "marketplace" | "private" | undefined;
|
|
21
|
-
features?: ("card" | "settings" | "app-function" | "webhooks" | "
|
|
21
|
+
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
22
22
|
}>;
|
|
23
23
|
export type AddFeatureInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
24
24
|
export declare class AddFeatureToProjectTool extends Tool<AddFeatureInputSchema> {
|
|
@@ -8,7 +8,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
8
8
|
projectBase: z.ZodUnion<[z.ZodLiteral<"empty">, z.ZodLiteral<"app">]>;
|
|
9
9
|
distribution: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"marketplace">, z.ZodLiteral<"private">]>>;
|
|
10
10
|
auth: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"static">, z.ZodLiteral<"oauth">]>>>;
|
|
11
|
-
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">]>, "many">>;
|
|
11
|
+
features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
12
12
|
}, "strip", z.ZodTypeAny, {
|
|
13
13
|
projectBase: "app" | "empty";
|
|
14
14
|
absoluteCurrentWorkingDirectory: string;
|
|
@@ -16,7 +16,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
16
16
|
name?: string | undefined;
|
|
17
17
|
auth?: "oauth" | "static" | undefined;
|
|
18
18
|
distribution?: "marketplace" | "private" | undefined;
|
|
19
|
-
features?: ("card" | "settings" | "app-function" | "webhooks" | "
|
|
19
|
+
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
20
20
|
}, {
|
|
21
21
|
projectBase: "app" | "empty";
|
|
22
22
|
absoluteCurrentWorkingDirectory: string;
|
|
@@ -24,7 +24,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
|
|
|
24
24
|
name?: string | undefined;
|
|
25
25
|
auth?: "oauth" | "static" | undefined;
|
|
26
26
|
distribution?: "marketplace" | "private" | undefined;
|
|
27
|
-
features?: ("card" | "settings" | "app-function" | "webhooks" | "
|
|
27
|
+
features?: ("card" | "settings" | "workflow-action-tool" | "workflow-action" | "app-function" | "webhooks" | "app-object" | "scim")[] | undefined;
|
|
28
28
|
}>;
|
|
29
29
|
export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
|
|
30
30
|
export declare class CreateProjectTool extends Tool<CreateProjectInputSchema> {
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { formatTextContents } from '../../utils/content.js';
|
|
4
4
|
import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
|
|
5
5
|
import { getAccountId, getConfigPath, loadConfig, } from '@hubspot/local-dev-lib/config';
|
|
6
|
-
import { useV3Api } from '../../../lib/projects/
|
|
6
|
+
import { useV3Api } from '../../../lib/projects/platformVersion.js';
|
|
7
7
|
const inputSchema = {
|
|
8
8
|
platformVersion: z
|
|
9
9
|
.string()
|
|
@@ -43,8 +43,8 @@ export class GetConfigValuesTool extends Tool {
|
|
|
43
43
|
register() {
|
|
44
44
|
return this.mcpServer.registerTool(toolName, {
|
|
45
45
|
title: 'Fetch the JSON Schema for component',
|
|
46
|
-
description: `Fetches and returns the JSON schema for the provided feature 'type' found in -hsmeta.json file.
|
|
47
|
-
This should be called before editing a '-hsmeta.json' file to get the list of possible values and restrictions on those values.
|
|
46
|
+
description: `Fetches and returns the JSON schema for the provided feature 'type' found in -hsmeta.json file.
|
|
47
|
+
This should be called before editing a '-hsmeta.json' file to get the list of possible values and restrictions on those values.
|
|
48
48
|
This will only work for projects with platformVersion 2025.2 and beyond`,
|
|
49
49
|
inputSchema,
|
|
50
50
|
}, this.handler);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
2
|
export declare const absoluteProjectPath: z.ZodString;
|
|
3
3
|
export declare const absoluteCurrentWorkingDirectory: z.ZodString;
|
|
4
|
-
export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">]>, "many">>;
|
|
4
|
+
export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
|
|
5
5
|
export declare const docsSearchQuery: z.ZodString;
|
|
6
6
|
export declare const docUrl: z.ZodString;
|
|
@@ -6,15 +6,17 @@ export const absoluteCurrentWorkingDirectory = z
|
|
|
6
6
|
.string()
|
|
7
7
|
.describe('The absolute path to the current working directory.');
|
|
8
8
|
export const features = z
|
|
9
|
-
.array(z
|
|
10
|
-
.union([
|
|
9
|
+
.array(z.union([
|
|
11
10
|
z.literal('card'),
|
|
12
11
|
z.literal('settings'),
|
|
13
12
|
z.literal('app-function'),
|
|
14
13
|
z.literal('webhooks'),
|
|
15
14
|
z.literal('workflow-action'),
|
|
16
|
-
|
|
17
|
-
.
|
|
15
|
+
z.literal('workflow-action-tool'),
|
|
16
|
+
z.literal('app-object'),
|
|
17
|
+
z.literal('scim'),
|
|
18
|
+
]))
|
|
19
|
+
.describe('The features to include in the project, multiple options can be selected')
|
|
18
20
|
.optional();
|
|
19
21
|
export const docsSearchQuery = z
|
|
20
22
|
.string()
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.33-experimental.0",
|
|
4
4
|
"description": "The official CLI for developing on HubSpot",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": "https://github.com/HubSpot/hubspot-cli",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@hubspot/local-dev-lib": "3.
|
|
10
|
-
"@hubspot/project-parsing-lib": "0.8.
|
|
9
|
+
"@hubspot/local-dev-lib": "3.18.0",
|
|
10
|
+
"@hubspot/project-parsing-lib": "0.8.3",
|
|
11
11
|
"@hubspot/serverless-dev-runtime": "7.0.6",
|
|
12
12
|
"@hubspot/theme-preview-dev-server": "0.0.10",
|
|
13
13
|
"@hubspot/ui-extensions-dev-server": "0.9.8",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"mcp-local": "yarn tsx ./scripts/mcp-local.ts",
|
|
85
85
|
"prettier:write": "prettier --write './**/*.{ts,js,json}'",
|
|
86
86
|
"release": "yarn tsx ./scripts/release.ts release",
|
|
87
|
+
"view-ui": "yarn build && yarn tsx ./scripts/view-ui.ts",
|
|
87
88
|
"test": "vitest run",
|
|
88
89
|
"test-dev": "vitest",
|
|
89
90
|
"test-cli": "yarn build && yarn --cwd 'acceptance-tests' test-ci",
|
package/types/LocalDev.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
1
|
+
import { HSProfileVariables, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
|
|
2
2
|
import { Environment } from '@hubspot/local-dev-lib/types/Config';
|
|
3
3
|
import { ValueOf } from '@hubspot/local-dev-lib/types/Utils';
|
|
4
4
|
import { ProjectConfig } from './Projects.js';
|
|
@@ -16,6 +16,7 @@ export type LocalDevStateConstructorOptions = {
|
|
|
16
16
|
initialProjectNodes: {
|
|
17
17
|
[key: string]: IntermediateRepresentationNodeLocalDev;
|
|
18
18
|
};
|
|
19
|
+
initialProjectProfileData: HSProfileVariables;
|
|
19
20
|
env: Environment;
|
|
20
21
|
};
|
|
21
22
|
export type LocalDevWebsocketMessage = {
|
package/types/Projects.d.ts
CHANGED
package/types/Yargs.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface BoxWithTitleProps {
|
|
2
|
+
title: string;
|
|
3
|
+
message: string;
|
|
4
|
+
titleBackgroundColor?: string;
|
|
5
|
+
borderColor?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getBoxWithTitle(props: BoxWithTitleProps): React.ReactNode;
|
|
8
|
+
export declare function BoxWithTitle({ title, message, titleBackgroundColor, borderColor, }: BoxWithTitleProps): React.ReactNode;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { CONTAINER_STYLES } from '../styles.js';
|
|
4
|
+
export function getBoxWithTitle(props) {
|
|
5
|
+
return _jsx(BoxWithTitle, { ...props });
|
|
6
|
+
}
|
|
7
|
+
export function BoxWithTitle({ title, message, titleBackgroundColor, borderColor, }) {
|
|
8
|
+
return (_jsxs(Box, { ...CONTAINER_STYLES, borderStyle: "round", borderColor: borderColor, children: [_jsx(Box, { position: "absolute", marginTop: -2, paddingX: 0, alignSelf: "flex-start", justifyContent: "center", alignItems: "center", children: _jsx(Text, { backgroundColor: titleBackgroundColor, bold: true, children: ` ${title} ` }) }), _jsx(Box, { justifyContent: "center", alignItems: "center", children: _jsx(Text, { children: message }) })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface HorizontalSelectPromptProps {
|
|
2
|
+
defaultOption?: string;
|
|
3
|
+
options: string[];
|
|
4
|
+
onSelect: (value: string) => void;
|
|
5
|
+
prompt?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getHorizontalSelectPrompt(props: HorizontalSelectPromptProps): React.ReactNode;
|
|
8
|
+
export declare function HorizontalSelectPrompt({ defaultOption, options, onSelect, prompt, }: HorizontalSelectPromptProps): React.ReactNode;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { CONTAINER_STYLES, INK_COLORS } from '../styles.js';
|
|
5
|
+
export function getHorizontalSelectPrompt(props) {
|
|
6
|
+
return _jsx(HorizontalSelectPrompt, { ...props });
|
|
7
|
+
}
|
|
8
|
+
export function HorizontalSelectPrompt({ defaultOption, options, onSelect, prompt, }) {
|
|
9
|
+
const [selectedIndex, setSelectedIndex] = useState(defaultOption && options.indexOf(defaultOption) !== -1
|
|
10
|
+
? options.indexOf(defaultOption)
|
|
11
|
+
: 0);
|
|
12
|
+
const moveRight = () => {
|
|
13
|
+
setSelectedIndex(prev => (prev < options.length - 1 ? prev + 1 : 0));
|
|
14
|
+
};
|
|
15
|
+
const moveLeft = () => {
|
|
16
|
+
setSelectedIndex(prev => (prev > 0 ? prev - 1 : options.length - 1));
|
|
17
|
+
};
|
|
18
|
+
useInput((_, key) => {
|
|
19
|
+
if (key.leftArrow) {
|
|
20
|
+
moveLeft();
|
|
21
|
+
}
|
|
22
|
+
else if (key.rightArrow) {
|
|
23
|
+
moveRight();
|
|
24
|
+
}
|
|
25
|
+
else if (key.return) {
|
|
26
|
+
onSelect(options[selectedIndex]);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return (_jsxs(Box, { ...CONTAINER_STYLES, flexDirection: "column", marginTop: 1, width: "100%", alignSelf: "center", justifyContent: "center", children: [prompt && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: prompt }) })), _jsx(Box, { flexDirection: "row", justifyContent: "center", flexWrap: "wrap", width: "100%", gap: 1, children: options.map((option, index) => (_jsx(Box, { children: _jsx(Text, { backgroundColor: index === selectedIndex ? INK_COLORS.INFO_BLUE : undefined, bold: index === selectedIndex, children: ` ${option} ` }) }, index))) }), _jsx(Box, { marginTop: 1, alignSelf: "center", justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "Use arrow keys to navigate, Enter to select" }) })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface StatusMessageBoxProps {
|
|
2
|
+
title: string;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getWarningBox(props: StatusMessageBoxProps): React.ReactNode;
|
|
6
|
+
export declare function WarningBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
|
|
7
|
+
export declare function getAlertBox(props: StatusMessageBoxProps): React.ReactNode;
|
|
8
|
+
export declare function AlertBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
|
|
9
|
+
export declare function getSuccessBox(props: StatusMessageBoxProps): React.ReactNode;
|
|
10
|
+
export declare function SuccessBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
|
|
11
|
+
export declare function getInfoBox(props: StatusMessageBoxProps): React.ReactNode;
|
|
12
|
+
export declare function InfoBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { INK_COLORS } from '../styles.js';
|
|
3
|
+
import { BoxWithTitle } from './BoxWithTitle.js';
|
|
4
|
+
export function getWarningBox(props) {
|
|
5
|
+
return _jsx(WarningBox, { ...props });
|
|
6
|
+
}
|
|
7
|
+
export function WarningBox({ title, message, }) {
|
|
8
|
+
const color = INK_COLORS.WARNING_YELLOW;
|
|
9
|
+
return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
|
|
10
|
+
}
|
|
11
|
+
export function getAlertBox(props) {
|
|
12
|
+
return _jsx(AlertBox, { ...props });
|
|
13
|
+
}
|
|
14
|
+
export function AlertBox({ title, message, }) {
|
|
15
|
+
const color = INK_COLORS.ALERT_RED;
|
|
16
|
+
return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
|
|
17
|
+
}
|
|
18
|
+
export function getSuccessBox(props) {
|
|
19
|
+
return _jsx(SuccessBox, { ...props });
|
|
20
|
+
}
|
|
21
|
+
export function SuccessBox({ title, message, }) {
|
|
22
|
+
const color = INK_COLORS.SUCCESS_GREEN;
|
|
23
|
+
return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
|
|
24
|
+
}
|
|
25
|
+
export function getInfoBox(props) {
|
|
26
|
+
return _jsx(InfoBox, { ...props });
|
|
27
|
+
}
|
|
28
|
+
export function InfoBox({ title, message, }) {
|
|
29
|
+
const color = INK_COLORS.INFO_BLUE;
|
|
30
|
+
return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ComponentPropPair = {
|
|
2
|
+
component: React.ReactNode;
|
|
3
|
+
signature: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* These components will be used by the sandbox ui. Please add any new components here.
|
|
7
|
+
*/
|
|
8
|
+
export declare const populatedComponents: Record<string, ComponentPropPair>;
|
|
9
|
+
export declare function getComponentOptions(): string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getSuccessBox, getInfoBox, getWarningBox, getAlertBox, } from '../components/StatusMessageBoxes.js';
|
|
2
|
+
import { getBoxWithTitle } from '../components/BoxWithTitle.js';
|
|
3
|
+
import { SuccessBox, InfoBox, WarningBox, AlertBox, } from '../components/StatusMessageBoxes.js';
|
|
4
|
+
import { BoxWithTitle } from '../components/BoxWithTitle.js';
|
|
5
|
+
/**
|
|
6
|
+
* These components will be used by the sandbox ui. Please add any new components here.
|
|
7
|
+
*/
|
|
8
|
+
export const populatedComponents = {
|
|
9
|
+
SuccessBox: {
|
|
10
|
+
component: getSuccessBox({
|
|
11
|
+
title: 'Success',
|
|
12
|
+
message: 'This is a success message',
|
|
13
|
+
}),
|
|
14
|
+
signature: SuccessBox.toString(),
|
|
15
|
+
},
|
|
16
|
+
InfoBox: {
|
|
17
|
+
component: getInfoBox({
|
|
18
|
+
title: 'Info',
|
|
19
|
+
message: 'This is an info message',
|
|
20
|
+
}),
|
|
21
|
+
signature: InfoBox.toString(),
|
|
22
|
+
},
|
|
23
|
+
WarningBox: {
|
|
24
|
+
component: getWarningBox({
|
|
25
|
+
title: 'Warning',
|
|
26
|
+
message: 'This is a warning message',
|
|
27
|
+
}),
|
|
28
|
+
signature: WarningBox.toString(),
|
|
29
|
+
},
|
|
30
|
+
AlertBox: {
|
|
31
|
+
component: getAlertBox({
|
|
32
|
+
title: 'Alert',
|
|
33
|
+
message: 'This is an alert message',
|
|
34
|
+
}),
|
|
35
|
+
signature: AlertBox.toString(),
|
|
36
|
+
},
|
|
37
|
+
BoxWithTitle: {
|
|
38
|
+
component: getBoxWithTitle({
|
|
39
|
+
title: 'Title',
|
|
40
|
+
message: 'This is a box with a title',
|
|
41
|
+
}),
|
|
42
|
+
signature: BoxWithTitle.toString(),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export function getComponentOptions() {
|
|
46
|
+
return Object.keys(populatedComponents);
|
|
47
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This hook is used to get the current terminal size.
|
|
3
|
+
* It will return the current terminal size, and if a component is using this hook to set height/width,
|
|
4
|
+
* it will re-render when the terminal size changes.
|
|
5
|
+
* ONLY USE THIS HOOK WITH SCREENS. THIS HOOK WILL DESTROY ANY PRIOR LOG OUTPUT IF USED TO SET HEIGHT/WIDTH.
|
|
6
|
+
* @param minHeight - The minimum height of the terminal.
|
|
7
|
+
* @param minWidth - The minimum width of the terminal.
|
|
8
|
+
* @returns The current terminal size.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useTerminalSize(minHeight?: number, minWidth?: number): {
|
|
11
|
+
columns: number;
|
|
12
|
+
rows: number;
|
|
13
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useStdout } from 'ink';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* This hook is used to get the current terminal size.
|
|
5
|
+
* It will return the current terminal size, and if a component is using this hook to set height/width,
|
|
6
|
+
* it will re-render when the terminal size changes.
|
|
7
|
+
* ONLY USE THIS HOOK WITH SCREENS. THIS HOOK WILL DESTROY ANY PRIOR LOG OUTPUT IF USED TO SET HEIGHT/WIDTH.
|
|
8
|
+
* @param minHeight - The minimum height of the terminal.
|
|
9
|
+
* @param minWidth - The minimum width of the terminal.
|
|
10
|
+
* @returns The current terminal size.
|
|
11
|
+
*/
|
|
12
|
+
export function useTerminalSize(minHeight, minWidth) {
|
|
13
|
+
const { stdout } = useStdout();
|
|
14
|
+
const [size, setSize] = useState({
|
|
15
|
+
columns: Math.max(stdout.columns, minWidth ?? 0),
|
|
16
|
+
rows: Math.max(stdout.rows, minHeight ?? 0),
|
|
17
|
+
});
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const handleResize = () => {
|
|
20
|
+
setSize({
|
|
21
|
+
columns: Math.max(stdout.columns, minWidth ?? 0),
|
|
22
|
+
rows: Math.max(stdout.rows, minHeight ?? 0),
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
stdout.on('resize', handleResize);
|
|
26
|
+
return () => {
|
|
27
|
+
stdout.off('resize', handleResize);
|
|
28
|
+
};
|
|
29
|
+
}, [stdout]);
|
|
30
|
+
return size;
|
|
31
|
+
}
|
package/ui/styles.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const CONTAINER_STYLES: {
|
|
2
|
+
readonly padding: 1;
|
|
3
|
+
readonly marginY: 0.5;
|
|
4
|
+
readonly flexDirection: "column";
|
|
5
|
+
readonly flexWrap: "wrap";
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Any color that can be used well in both WHITE and BLACK terminals.
|
|
9
|
+
* This is a best effort to ensure that the color is good looking in both
|
|
10
|
+
* light and dark modes.
|
|
11
|
+
*/
|
|
12
|
+
export declare const INK_COLORS: {
|
|
13
|
+
ALERT_RED: string;
|
|
14
|
+
SUCCESS_GREEN: string;
|
|
15
|
+
INFO_BLUE: string;
|
|
16
|
+
WARNING_YELLOW: string;
|
|
17
|
+
WHITE: string;
|
|
18
|
+
};
|
package/ui/styles.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const CONTAINER_STYLES = {
|
|
2
|
+
padding: 1,
|
|
3
|
+
marginY: 0.5,
|
|
4
|
+
flexDirection: 'column',
|
|
5
|
+
flexWrap: 'wrap',
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Any color that can be used well in both WHITE and BLACK terminals.
|
|
9
|
+
* This is a best effort to ensure that the color is good looking in both
|
|
10
|
+
* light and dark modes.
|
|
11
|
+
*/
|
|
12
|
+
export const INK_COLORS = {
|
|
13
|
+
ALERT_RED: '#fc7272',
|
|
14
|
+
SUCCESS_GREEN: '#4deb7a',
|
|
15
|
+
INFO_BLUE: '#4dcbeb',
|
|
16
|
+
WARNING_YELLOW: '#EEB117',
|
|
17
|
+
WHITE: 'white',
|
|
18
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { HorizontalSelectPrompt } from '../components/HorizontalSelectPrompt.js';
|
|
5
|
+
import { getComponentOptions, populatedComponents, } from '../lib/ui-testing-utils.js';
|
|
6
|
+
import { useTerminalSize } from '../lib/useTerminalSize.js';
|
|
7
|
+
import { CONTAINER_STYLES } from '../styles.js';
|
|
8
|
+
export function getUiSandbox(props) {
|
|
9
|
+
return _jsx(UiSandbox, { ...props });
|
|
10
|
+
}
|
|
11
|
+
export function UiSandbox({ componentName }) {
|
|
12
|
+
const componentOptions = getComponentOptions();
|
|
13
|
+
const [selectedComponent, setSelectedComponent] = useState(componentName ? populatedComponents[componentName] : undefined);
|
|
14
|
+
const mapStringToComponent = (str) => {
|
|
15
|
+
setSelectedComponent(populatedComponents[str] || undefined);
|
|
16
|
+
};
|
|
17
|
+
const getFunctionArguments = (signature) => {
|
|
18
|
+
const startIndex = signature.indexOf('(');
|
|
19
|
+
const endIndex = signature.indexOf(')');
|
|
20
|
+
const functionArgs = signature.slice(startIndex, endIndex + 1);
|
|
21
|
+
return functionArgs;
|
|
22
|
+
};
|
|
23
|
+
const size = useTerminalSize(20);
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", height: size.rows, children: [_jsx(HorizontalSelectPrompt, { defaultOption: componentName, options: componentOptions, onSelect: mapStringToComponent }), selectedComponent?.component, selectedComponent?.signature && (_jsxs(Box, { ...CONTAINER_STYLES, gap: 1, borderStyle: "classic", children: [_jsx(Text, { children: "Signature:" }), _jsx(Text, { children: getFunctionArguments(selectedComponent?.signature) })] }))] }));
|
|
25
|
+
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import yargs from 'yargs';
|
|
2
|
-
import { addAccountOptions, addConfigOptions, addUseEnvironmentOptions, addGlobalOptions, addJSONOutputOptions, } from '../../../lib/commonOpts.js';
|
|
3
|
-
import installCommand from '../install.js';
|
|
4
|
-
vi.mock('../../../lib/commonOpts');
|
|
5
|
-
describe('commands/app/install', () => {
|
|
6
|
-
const yargsMock = yargs;
|
|
7
|
-
describe('command', () => {
|
|
8
|
-
it('should have the correct command structure', () => {
|
|
9
|
-
expect(installCommand.command).toEqual('install <test-account-id>');
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
describe('describe', () => {
|
|
13
|
-
it('should provide a description', () => {
|
|
14
|
-
expect(installCommand.describe).not.toBeDefined();
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
describe('builder', () => {
|
|
18
|
-
it('should support the correct options', () => {
|
|
19
|
-
installCommand.builder(yargsMock);
|
|
20
|
-
expect(addGlobalOptions).toHaveBeenCalledTimes(1);
|
|
21
|
-
expect(addGlobalOptions).toHaveBeenCalledWith(yargsMock);
|
|
22
|
-
expect(addAccountOptions).toHaveBeenCalledTimes(1);
|
|
23
|
-
expect(addAccountOptions).toHaveBeenCalledWith(yargsMock);
|
|
24
|
-
expect(addConfigOptions).toHaveBeenCalledTimes(1);
|
|
25
|
-
expect(addConfigOptions).toHaveBeenCalledWith(yargsMock);
|
|
26
|
-
expect(addUseEnvironmentOptions).toHaveBeenCalledTimes(1);
|
|
27
|
-
expect(addUseEnvironmentOptions).toHaveBeenCalledWith(yargsMock);
|
|
28
|
-
expect(addJSONOutputOptions).toHaveBeenCalledTimes(1);
|
|
29
|
-
expect(addJSONOutputOptions).toHaveBeenCalledWith(yargsMock);
|
|
30
|
-
expect(yargsMock.positional).toHaveBeenCalledTimes(1);
|
|
31
|
-
expect(yargsMock.positional).toHaveBeenCalledWith('test-account-id', expect.objectContaining({
|
|
32
|
-
type: 'number',
|
|
33
|
-
required: true,
|
|
34
|
-
describe: expect.any(String),
|
|
35
|
-
}));
|
|
36
|
-
expect(yargsMock.option).toHaveBeenCalledTimes(2);
|
|
37
|
-
expect(yargsMock.option).toHaveBeenCalledWith('app-uid', expect.objectContaining({
|
|
38
|
-
type: 'string',
|
|
39
|
-
describe: expect.any(String),
|
|
40
|
-
}));
|
|
41
|
-
expect(yargsMock.option).toHaveBeenCalledWith('project-name', expect.objectContaining({
|
|
42
|
-
type: 'string',
|
|
43
|
-
describe: expect.any(String),
|
|
44
|
-
}));
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|