@capawesome/cli 1.13.1 → 1.14.0-dev.1f912c9.1755635879

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -3
  3. package/dist/commands/apps/bundles/create.js +215 -233
  4. package/dist/commands/apps/bundles/create.test.js +274 -0
  5. package/dist/commands/apps/bundles/delete.js +42 -47
  6. package/dist/commands/apps/bundles/delete.test.js +140 -0
  7. package/dist/commands/apps/bundles/update.js +69 -72
  8. package/dist/commands/apps/bundles/update.test.js +142 -0
  9. package/dist/commands/apps/channels/create.js +47 -70
  10. package/dist/commands/apps/channels/create.test.js +115 -0
  11. package/dist/commands/apps/channels/delete.js +53 -56
  12. package/dist/commands/apps/channels/delete.test.js +140 -0
  13. package/dist/commands/apps/channels/get.js +27 -61
  14. package/dist/commands/apps/channels/get.test.js +136 -0
  15. package/dist/commands/apps/channels/list.js +26 -63
  16. package/dist/commands/apps/channels/list.test.js +123 -0
  17. package/dist/commands/apps/channels/update.js +48 -67
  18. package/dist/commands/apps/channels/update.test.js +139 -0
  19. package/dist/commands/apps/create.js +38 -38
  20. package/dist/commands/apps/create.test.js +117 -0
  21. package/dist/commands/apps/delete.js +39 -40
  22. package/dist/commands/apps/delete.test.js +119 -0
  23. package/dist/commands/apps/devices/delete.js +42 -47
  24. package/dist/commands/apps/devices/delete.test.js +140 -0
  25. package/dist/commands/doctor.js +12 -29
  26. package/dist/commands/doctor.test.js +52 -0
  27. package/dist/commands/login.js +48 -69
  28. package/dist/commands/login.test.js +122 -0
  29. package/dist/commands/logout.js +13 -31
  30. package/dist/commands/logout.test.js +47 -0
  31. package/dist/commands/manifests/generate.js +20 -38
  32. package/dist/commands/manifests/generate.test.js +60 -0
  33. package/dist/commands/whoami.js +13 -30
  34. package/dist/commands/whoami.test.js +30 -0
  35. package/dist/config/consts.js +4 -5
  36. package/dist/config/index.js +1 -17
  37. package/dist/index.js +50 -80
  38. package/dist/services/app-bundle-files.js +117 -136
  39. package/dist/services/app-bundles.js +22 -41
  40. package/dist/services/app-channels.js +54 -77
  41. package/dist/services/app-devices.js +10 -25
  42. package/dist/services/apps.js +25 -41
  43. package/dist/services/authorization-service.js +4 -8
  44. package/dist/services/config.js +15 -28
  45. package/dist/services/organizations.js +18 -0
  46. package/dist/services/session-code.js +7 -22
  47. package/dist/services/sessions.js +13 -30
  48. package/dist/services/update.js +17 -55
  49. package/dist/services/users.js +11 -26
  50. package/dist/types/app-bundle-file.js +1 -2
  51. package/dist/types/app-bundle.js +1 -2
  52. package/dist/types/app-channel.js +1 -2
  53. package/dist/types/app-device.js +1 -2
  54. package/dist/types/app.js +1 -2
  55. package/dist/types/index.js +8 -23
  56. package/dist/types/npm-package.js +1 -2
  57. package/dist/types/organization.js +1 -0
  58. package/dist/types/session-code.js +1 -2
  59. package/dist/types/session.js +1 -2
  60. package/dist/types/user.js +1 -2
  61. package/dist/utils/buffer.js +12 -43
  62. package/dist/utils/error.js +24 -14
  63. package/dist/utils/file.js +22 -41
  64. package/dist/utils/hash.js +3 -39
  65. package/dist/utils/http-client.js +27 -53
  66. package/dist/utils/manifest.js +11 -24
  67. package/dist/utils/prompt.js +9 -26
  68. package/dist/utils/signature.js +3 -39
  69. package/dist/utils/userConfig.js +5 -9
  70. package/dist/utils/zip.js +11 -27
  71. package/package.json +23 -10
  72. package/dist/utils/ci.js +0 -7
@@ -1,60 +1,59 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const citty_1 = require("citty");
16
- const consola_1 = __importDefault(require("consola"));
17
- const apps_1 = __importDefault(require("../../services/apps"));
18
- const error_1 = require("../../utils/error");
19
- const prompt_1 = require("../../utils/prompt");
20
- exports.default = (0, citty_1.defineCommand)({
21
- meta: {
22
- description: 'Delete an app.',
23
- },
24
- args: {
25
- appId: {
26
- type: 'string',
27
- description: 'ID of the app.',
28
- },
29
- },
30
- run: (ctx) => __awaiter(void 0, void 0, void 0, function* () {
31
- let appId = ctx.args.appId;
1
+ import { defineCommand, defineOptions } from '@robingenz/zli';
2
+ import consola from 'consola';
3
+ import { z } from 'zod';
4
+ import appsService from '../../services/apps.js';
5
+ import organizationsService from '../../services/organizations.js';
6
+ import { getMessageFromUnknownError } from '../../utils/error.js';
7
+ import { prompt } from '../../utils/prompt.js';
8
+ export default defineCommand({
9
+ description: 'Delete an app.',
10
+ options: defineOptions(z.object({
11
+ appId: z.string().optional().describe('ID of the app.'),
12
+ })),
13
+ action: async (options, args) => {
14
+ let { appId } = options;
32
15
  if (!appId) {
33
- const apps = yield apps_1.default.findAll();
16
+ const organizations = await organizationsService.findAll();
17
+ if (organizations.length === 0) {
18
+ consola.error('You must create an organization before deleting an app.');
19
+ process.exit(1);
20
+ }
21
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
22
+ const organizationId = await prompt('Which organization do you want to delete the app from?', {
23
+ type: 'select',
24
+ options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
25
+ });
26
+ if (!organizationId) {
27
+ consola.error('You must select an organization to delete the app from.');
28
+ process.exit(1);
29
+ }
30
+ const apps = await appsService.findAll({
31
+ organizationId,
32
+ });
34
33
  if (!apps.length) {
35
- consola_1.default.error('You must create an app before deleting it.');
34
+ consola.error('You must create an app before deleting it.');
36
35
  process.exit(1);
37
36
  }
38
37
  // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
39
- appId = yield (0, prompt_1.prompt)('Which app do you want to delete?', {
38
+ appId = await prompt('Which app do you want to delete?', {
40
39
  type: 'select',
41
40
  options: apps.map((app) => ({ label: app.name, value: app.id })),
42
41
  });
43
42
  }
44
- const confirmed = yield (0, prompt_1.prompt)('Are you sure you want to delete this app?', {
43
+ const confirmed = await prompt('Are you sure you want to delete this app?', {
45
44
  type: 'confirm',
46
45
  });
47
46
  if (!confirmed) {
48
47
  return;
49
48
  }
50
49
  try {
51
- yield apps_1.default.delete({ id: appId });
52
- consola_1.default.success('App deleted successfully.');
50
+ await appsService.delete({ id: appId });
51
+ consola.success('App deleted successfully.');
53
52
  }
54
53
  catch (error) {
55
- const message = (0, error_1.getMessageFromUnknownError)(error);
56
- consola_1.default.error(message);
54
+ const message = getMessageFromUnknownError(error);
55
+ consola.error(message);
57
56
  process.exit(1);
58
57
  }
59
- }),
58
+ },
60
59
  });
@@ -0,0 +1,119 @@
1
+ import { DEFAULT_API_BASE_URL } from '../../config/consts.js';
2
+ import authorizationService from '../../services/authorization-service.js';
3
+ import { prompt } from '../../utils/prompt.js';
4
+ import userConfig from '../../utils/userConfig.js';
5
+ import consola from 'consola';
6
+ import nock from 'nock';
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
+ import deleteAppCommand from './delete.js';
9
+ // Mock dependencies
10
+ vi.mock('@/utils/userConfig.js');
11
+ vi.mock('@/utils/prompt.js');
12
+ vi.mock('@/services/authorization-service.js');
13
+ vi.mock('consola');
14
+ describe('apps-delete', () => {
15
+ const mockUserConfig = vi.mocked(userConfig);
16
+ const mockPrompt = vi.mocked(prompt);
17
+ const mockConsola = vi.mocked(consola);
18
+ const mockAuthorizationService = vi.mocked(authorizationService);
19
+ beforeEach(() => {
20
+ vi.clearAllMocks();
21
+ mockUserConfig.read.mockReturnValue({ token: 'test-token' });
22
+ mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
23
+ vi.spyOn(process, 'exit').mockImplementation(() => undefined);
24
+ });
25
+ afterEach(() => {
26
+ nock.cleanAll();
27
+ vi.restoreAllMocks();
28
+ });
29
+ it('should delete app with provided appId after confirmation', async () => {
30
+ const appId = 'app-123';
31
+ const testToken = 'test-token';
32
+ const options = { appId };
33
+ mockPrompt.mockResolvedValueOnce(true); // confirmation
34
+ const scope = nock(DEFAULT_API_BASE_URL)
35
+ .delete(`/v1/apps/${appId}`)
36
+ .matchHeader('Authorization', `Bearer ${testToken}`)
37
+ .reply(200);
38
+ await deleteAppCommand.action(options, undefined);
39
+ expect(scope.isDone()).toBe(true);
40
+ expect(mockPrompt).toHaveBeenCalledWith('Are you sure you want to delete this app?', {
41
+ type: 'confirm',
42
+ });
43
+ expect(mockConsola.success).toHaveBeenCalledWith('App deleted successfully.');
44
+ });
45
+ it('should not delete app when confirmation is declined', async () => {
46
+ const appId = 'app-123';
47
+ const options = { appId };
48
+ mockPrompt.mockResolvedValueOnce(false); // declined confirmation
49
+ await deleteAppCommand.action(options, undefined);
50
+ expect(mockPrompt).toHaveBeenCalledWith('Are you sure you want to delete this app?', {
51
+ type: 'confirm',
52
+ });
53
+ expect(mockConsola.success).not.toHaveBeenCalled();
54
+ });
55
+ it('should prompt for app selection when appId not provided', async () => {
56
+ const orgId = 'org-1';
57
+ const appId = 'app-1';
58
+ const testToken = 'test-token';
59
+ const organization = { id: orgId, name: 'Org 1' };
60
+ const app = { id: appId, name: 'App 1' };
61
+ const options = {};
62
+ const orgsScope = nock(DEFAULT_API_BASE_URL)
63
+ .get('/v1/organizations')
64
+ .matchHeader('Authorization', `Bearer ${testToken}`)
65
+ .reply(200, [organization]);
66
+ const appsScope = nock(DEFAULT_API_BASE_URL)
67
+ .get('/v1/apps')
68
+ .query({ organizationId: orgId })
69
+ .matchHeader('Authorization', `Bearer ${testToken}`)
70
+ .reply(200, [app]);
71
+ const deleteScope = nock(DEFAULT_API_BASE_URL)
72
+ .delete(`/v1/apps/${appId}`)
73
+ .matchHeader('Authorization', `Bearer ${testToken}`)
74
+ .reply(200);
75
+ mockPrompt
76
+ .mockResolvedValueOnce(orgId) // organization selection
77
+ .mockResolvedValueOnce(appId) // app selection
78
+ .mockResolvedValueOnce(true); // confirmation
79
+ await deleteAppCommand.action(options, undefined);
80
+ expect(orgsScope.isDone()).toBe(true);
81
+ expect(appsScope.isDone()).toBe(true);
82
+ expect(deleteScope.isDone()).toBe(true);
83
+ expect(mockConsola.success).toHaveBeenCalledWith('App deleted successfully.');
84
+ });
85
+ it('should handle error when no organizations exist', async () => {
86
+ const testToken = 'test-token';
87
+ const options = {};
88
+ const scope = nock(DEFAULT_API_BASE_URL)
89
+ .get('/v1/organizations')
90
+ .matchHeader('Authorization', `Bearer ${testToken}`)
91
+ .reply(200, []);
92
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => {
93
+ throw new Error(`process.exit called with code ${code}`);
94
+ });
95
+ try {
96
+ await deleteAppCommand.action(options, undefined);
97
+ }
98
+ catch (error) {
99
+ expect(error.message).toBe('process.exit called with code 1');
100
+ }
101
+ expect(scope.isDone()).toBe(true);
102
+ expect(mockConsola.error).toHaveBeenCalledWith('You must create an organization before deleting an app.');
103
+ expect(exitSpy).toHaveBeenCalledWith(1);
104
+ });
105
+ it('should handle API error during deletion', async () => {
106
+ const appId = 'app-123';
107
+ const testToken = 'test-token';
108
+ const options = { appId };
109
+ mockPrompt.mockResolvedValueOnce(true);
110
+ const scope = nock(DEFAULT_API_BASE_URL)
111
+ .delete(`/v1/apps/${appId}`)
112
+ .matchHeader('Authorization', `Bearer ${testToken}`)
113
+ .reply(404, { message: 'App not found' });
114
+ await deleteAppCommand.action(options, undefined);
115
+ expect(scope.isDone()).toBe(true);
116
+ expect(mockConsola.error).toHaveBeenCalled();
117
+ expect(process.exit).toHaveBeenCalledWith(1);
118
+ });
119
+ });
@@ -1,74 +1,69 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const citty_1 = require("citty");
16
- const consola_1 = __importDefault(require("consola"));
17
- const app_devices_1 = __importDefault(require("../../../services/app-devices"));
18
- const apps_1 = __importDefault(require("../../../services/apps"));
19
- const error_1 = require("../../../utils/error");
20
- const prompt_1 = require("../../../utils/prompt");
21
- exports.default = (0, citty_1.defineCommand)({
22
- meta: {
23
- description: 'Delete an app device.',
24
- },
25
- args: {
26
- appId: {
27
- type: 'string',
28
- description: 'ID of the app.',
29
- },
30
- deviceId: {
31
- type: 'string',
32
- description: 'ID of the device.',
33
- },
34
- },
35
- run: (ctx) => __awaiter(void 0, void 0, void 0, function* () {
36
- let appId = ctx.args.appId;
1
+ import { defineCommand, defineOptions } from '@robingenz/zli';
2
+ import consola from 'consola';
3
+ import { z } from 'zod';
4
+ import appDevicesService from '../../../services/app-devices.js';
5
+ import appsService from '../../../services/apps.js';
6
+ import organizationsService from '../../../services/organizations.js';
7
+ import { getMessageFromUnknownError } from '../../../utils/error.js';
8
+ import { prompt } from '../../../utils/prompt.js';
9
+ export default defineCommand({
10
+ description: 'Delete an app device.',
11
+ options: defineOptions(z.object({
12
+ appId: z.string().optional().describe('ID of the app.'),
13
+ deviceId: z.string().optional().describe('ID of the device.'),
14
+ })),
15
+ action: async (options, args) => {
16
+ let { appId, deviceId } = options;
37
17
  if (!appId) {
38
- const apps = yield apps_1.default.findAll();
18
+ const organizations = await organizationsService.findAll();
19
+ if (organizations.length === 0) {
20
+ consola.error('You must create an organization before deleting a device.');
21
+ process.exit(1);
22
+ }
23
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
24
+ const organizationId = await prompt('Select the organization of the app from which you want to delete a device.', {
25
+ type: 'select',
26
+ options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
27
+ });
28
+ if (!organizationId) {
29
+ consola.error('You must select the organization of an app from which you want to delete a device.');
30
+ process.exit(1);
31
+ }
32
+ const apps = await appsService.findAll({
33
+ organizationId,
34
+ });
39
35
  if (!apps.length) {
40
- consola_1.default.error('You must create an app before deleting a device.');
36
+ consola.error('You must create an app before deleting a device.');
41
37
  process.exit(1);
42
38
  }
43
39
  // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
44
- appId = yield (0, prompt_1.prompt)('Which app do you want to delete the device from?', {
40
+ appId = await prompt('Which app do you want to delete the device from?', {
45
41
  type: 'select',
46
42
  options: apps.map((app) => ({ label: app.name, value: app.id })),
47
43
  });
48
44
  }
49
- let deviceId = ctx.args.deviceId;
50
45
  if (!deviceId) {
51
- deviceId = yield (0, prompt_1.prompt)('Enter the device ID:', {
46
+ deviceId = await prompt('Enter the device ID:', {
52
47
  type: 'text',
53
48
  });
54
49
  }
55
- const confirmed = yield (0, prompt_1.prompt)('Are you sure you want to delete this device?', {
50
+ const confirmed = await prompt('Are you sure you want to delete this device?', {
56
51
  type: 'confirm',
57
52
  });
58
53
  if (!confirmed) {
59
54
  return;
60
55
  }
61
56
  try {
62
- yield app_devices_1.default.delete({
57
+ await appDevicesService.delete({
63
58
  appId,
64
59
  deviceId,
65
60
  });
66
- consola_1.default.success('Device deleted successfully.');
61
+ consola.success('Device deleted successfully.');
67
62
  }
68
63
  catch (error) {
69
- const message = (0, error_1.getMessageFromUnknownError)(error);
70
- consola_1.default.error(message);
64
+ const message = getMessageFromUnknownError(error);
65
+ consola.error(message);
71
66
  process.exit(1);
72
67
  }
73
- }),
68
+ },
74
69
  });
@@ -0,0 +1,140 @@
1
+ import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
2
+ import authorizationService from '../../../services/authorization-service.js';
3
+ import { prompt } from '../../../utils/prompt.js';
4
+ import userConfig from '../../../utils/userConfig.js';
5
+ import consola from 'consola';
6
+ import nock from 'nock';
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
+ import deleteDeviceCommand from './delete.js';
9
+ // Mock dependencies
10
+ vi.mock('@/utils/userConfig.js');
11
+ vi.mock('@/utils/prompt.js');
12
+ vi.mock('@/services/authorization-service.js');
13
+ vi.mock('consola');
14
+ describe('apps-devices-delete', () => {
15
+ const mockUserConfig = vi.mocked(userConfig);
16
+ const mockPrompt = vi.mocked(prompt);
17
+ const mockConsola = vi.mocked(consola);
18
+ const mockAuthorizationService = vi.mocked(authorizationService);
19
+ beforeEach(() => {
20
+ vi.clearAllMocks();
21
+ mockUserConfig.read.mockReturnValue({ token: 'test-token' });
22
+ mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
23
+ vi.spyOn(process, 'exit').mockImplementation(() => undefined);
24
+ });
25
+ afterEach(() => {
26
+ nock.cleanAll();
27
+ vi.restoreAllMocks();
28
+ });
29
+ it('should delete device with provided appId and deviceId after confirmation', async () => {
30
+ const appId = 'app-123';
31
+ const deviceId = 'device-456';
32
+ const testToken = 'test-token';
33
+ const options = { appId, deviceId };
34
+ mockPrompt.mockResolvedValueOnce(true); // confirmation
35
+ const scope = nock(DEFAULT_API_BASE_URL)
36
+ .delete(`/v1/apps/${appId}/devices/${deviceId}`)
37
+ .matchHeader('Authorization', `Bearer ${testToken}`)
38
+ .reply(200);
39
+ await deleteDeviceCommand.action(options, undefined);
40
+ expect(scope.isDone()).toBe(true);
41
+ expect(mockPrompt).toHaveBeenCalledWith('Are you sure you want to delete this device?', {
42
+ type: 'confirm',
43
+ });
44
+ expect(mockConsola.success).toHaveBeenCalledWith('Device deleted successfully.');
45
+ });
46
+ it('should not delete device when confirmation is declined', async () => {
47
+ const appId = 'app-123';
48
+ const deviceId = 'device-456';
49
+ const options = { appId, deviceId };
50
+ mockPrompt.mockResolvedValueOnce(false); // declined confirmation
51
+ await deleteDeviceCommand.action(options, undefined);
52
+ expect(mockPrompt).toHaveBeenCalledWith('Are you sure you want to delete this device?', {
53
+ type: 'confirm',
54
+ });
55
+ expect(mockConsola.success).not.toHaveBeenCalled();
56
+ });
57
+ it('should prompt for app selection when appId not provided', async () => {
58
+ const orgId = 'org-1';
59
+ const appId = 'app-1';
60
+ const deviceId = 'device-456';
61
+ const testToken = 'test-token';
62
+ const organization = { id: orgId, name: 'Org 1' };
63
+ const app = { id: appId, name: 'App 1' };
64
+ const options = { deviceId };
65
+ const orgsScope = nock(DEFAULT_API_BASE_URL)
66
+ .get('/v1/organizations')
67
+ .matchHeader('Authorization', `Bearer ${testToken}`)
68
+ .reply(200, [organization]);
69
+ const appsScope = nock(DEFAULT_API_BASE_URL)
70
+ .get('/v1/apps')
71
+ .query({ organizationId: orgId })
72
+ .matchHeader('Authorization', `Bearer ${testToken}`)
73
+ .reply(200, [app]);
74
+ const deleteScope = nock(DEFAULT_API_BASE_URL)
75
+ .delete(`/v1/apps/${appId}/devices/${deviceId}`)
76
+ .matchHeader('Authorization', `Bearer ${testToken}`)
77
+ .reply(200);
78
+ mockPrompt
79
+ .mockResolvedValueOnce(orgId) // organization selection
80
+ .mockResolvedValueOnce(appId) // app selection
81
+ .mockResolvedValueOnce(true); // confirmation
82
+ await deleteDeviceCommand.action(options, undefined);
83
+ expect(orgsScope.isDone()).toBe(true);
84
+ expect(appsScope.isDone()).toBe(true);
85
+ expect(deleteScope.isDone()).toBe(true);
86
+ expect(mockConsola.success).toHaveBeenCalledWith('Device deleted successfully.');
87
+ });
88
+ it('should prompt for deviceId when not provided', async () => {
89
+ const appId = 'app-123';
90
+ const deviceId = 'device-456';
91
+ const testToken = 'test-token';
92
+ const options = { appId };
93
+ const scope = nock(DEFAULT_API_BASE_URL)
94
+ .delete(`/v1/apps/${appId}/devices/${deviceId}`)
95
+ .matchHeader('Authorization', `Bearer ${testToken}`)
96
+ .reply(200);
97
+ mockPrompt
98
+ .mockResolvedValueOnce(deviceId) // device ID input
99
+ .mockResolvedValueOnce(true); // confirmation
100
+ await deleteDeviceCommand.action(options, undefined);
101
+ expect(scope.isDone()).toBe(true);
102
+ expect(mockPrompt).toHaveBeenCalledWith('Enter the device ID:', { type: 'text' });
103
+ expect(mockConsola.success).toHaveBeenCalledWith('Device deleted successfully.');
104
+ });
105
+ it('should handle error when no organizations exist', async () => {
106
+ const testToken = 'test-token';
107
+ const options = {};
108
+ const scope = nock(DEFAULT_API_BASE_URL)
109
+ .get('/v1/organizations')
110
+ .matchHeader('Authorization', `Bearer ${testToken}`)
111
+ .reply(200, []);
112
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => {
113
+ throw new Error(`process.exit called with code ${code}`);
114
+ });
115
+ try {
116
+ await deleteDeviceCommand.action(options, undefined);
117
+ }
118
+ catch (error) {
119
+ expect(error.message).toBe('process.exit called with code 1');
120
+ }
121
+ expect(scope.isDone()).toBe(true);
122
+ expect(mockConsola.error).toHaveBeenCalledWith('You must create an organization before deleting a device.');
123
+ expect(exitSpy).toHaveBeenCalledWith(1);
124
+ });
125
+ it('should handle API error during deletion', async () => {
126
+ const appId = 'app-123';
127
+ const deviceId = 'device-456';
128
+ const testToken = 'test-token';
129
+ const options = { appId, deviceId };
130
+ mockPrompt.mockResolvedValueOnce(true);
131
+ const scope = nock(DEFAULT_API_BASE_URL)
132
+ .delete(`/v1/apps/${appId}/devices/${deviceId}`)
133
+ .matchHeader('Authorization', `Bearer ${testToken}`)
134
+ .reply(404, { message: 'Device not found' });
135
+ await deleteDeviceCommand.action(options, undefined);
136
+ expect(scope.isDone()).toBe(true);
137
+ expect(mockConsola.error).toHaveBeenCalled();
138
+ expect(process.exit).toHaveBeenCalledWith(1);
139
+ });
140
+ });
@@ -1,34 +1,17 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const citty_1 = require("citty");
16
- const consola_1 = __importDefault(require("consola"));
17
- const package_json_1 = __importDefault(require("../../package.json"));
18
- const systeminformation_1 = __importDefault(require("systeminformation"));
19
- exports.default = (0, citty_1.defineCommand)({
20
- meta: {
21
- name: 'doctor',
22
- description: 'Prints out neccessary information for debugging',
23
- },
24
- run: () => __awaiter(void 0, void 0, void 0, function* () {
25
- const osInfo = yield systeminformation_1.default.osInfo();
26
- const versions = yield systeminformation_1.default.versions('npm, node');
27
- consola_1.default.box([
1
+ import { defineCommand } from '@robingenz/zli';
2
+ import consola from 'consola';
3
+ import systeminformation from 'systeminformation';
4
+ import pkg from '../../package.json' with { type: 'json' };
5
+ export default defineCommand({
6
+ description: 'Prints out neccessary information for debugging',
7
+ action: async (options, args) => {
8
+ const osInfo = await systeminformation.osInfo();
9
+ const versions = await systeminformation.versions('npm, node');
10
+ consola.box([
28
11
  `NodeJS version: ${versions.node}`,
29
12
  `NPM version: ${versions.npm}`,
30
- `CLI version: ${package_json_1.default.version}`,
13
+ `CLI version: ${pkg.version}`,
31
14
  `OS: ${osInfo.distro} ${osInfo.release} ${osInfo.codename ? `(${osInfo.codename})` : ''}`,
32
15
  ].join('\n'));
33
- }),
16
+ },
34
17
  });
@@ -0,0 +1,52 @@
1
+ import consola from 'consola';
2
+ import systeminformation from 'systeminformation';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import doctorCommand from './doctor.js';
5
+ // Mock dependencies
6
+ vi.mock('consola');
7
+ vi.mock('systeminformation');
8
+ describe('doctor', () => {
9
+ const mockConsola = vi.mocked(consola);
10
+ const mockSystemInformation = vi.mocked(systeminformation);
11
+ beforeEach(() => {
12
+ vi.clearAllMocks();
13
+ });
14
+ afterEach(() => {
15
+ vi.restoreAllMocks();
16
+ });
17
+ it('should display system information', async () => {
18
+ const mockOsInfo = {
19
+ distro: 'macOS',
20
+ release: '14.0',
21
+ codename: 'Sonoma',
22
+ };
23
+ const mockVersions = {
24
+ node: '18.17.0',
25
+ npm: '9.6.7',
26
+ };
27
+ mockSystemInformation.osInfo.mockResolvedValue(mockOsInfo);
28
+ mockSystemInformation.versions.mockResolvedValue(mockVersions);
29
+ await doctorCommand.action({}, undefined);
30
+ expect(mockSystemInformation.osInfo).toHaveBeenCalled();
31
+ expect(mockSystemInformation.versions).toHaveBeenCalledWith('npm, node');
32
+ expect(mockConsola.box).toHaveBeenCalledWith(expect.stringContaining('NodeJS version: 18.17.0'));
33
+ expect(mockConsola.box).toHaveBeenCalledWith(expect.stringContaining('NPM version: 9.6.7'));
34
+ expect(mockConsola.box).toHaveBeenCalledWith(expect.stringContaining('OS: macOS 14.0 (Sonoma)'));
35
+ });
36
+ it('should handle OS info without codename', async () => {
37
+ const mockOsInfo = {
38
+ distro: 'Ubuntu',
39
+ release: '22.04',
40
+ codename: '',
41
+ };
42
+ const mockVersions = {
43
+ node: '20.0.0',
44
+ npm: '10.0.0',
45
+ };
46
+ mockSystemInformation.osInfo.mockResolvedValue(mockOsInfo);
47
+ mockSystemInformation.versions.mockResolvedValue(mockVersions);
48
+ await doctorCommand.action({}, undefined);
49
+ expect(mockConsola.box).toHaveBeenCalledWith(expect.stringContaining('OS: Ubuntu 22.04'));
50
+ expect(mockConsola.box).toHaveBeenCalledWith(expect.not.stringContaining('()'));
51
+ });
52
+ });