From 8a70b35dd4fe81441586a50580492b4e10c15383 Mon Sep 17 00:00:00 2001 From: Ross Stenersen Date: Tue, 7 Oct 2025 10:29:32 -0500 Subject: [PATCH] chore: remove rest of old inquirer calls and old inquirer dependencies --- package-lock.json | 206 ++---------------- package.json | 2 - .../util/apps-user-input-create.test.ts | 21 +- .../command/util/capabilities-choose.test.ts | 46 ++-- .../lib/command/util/hub-drivers.test.ts | 35 ++- src/commands/edge/channels/create.ts | 26 +-- src/commands/edge/channels/invites/create.ts | 34 +-- .../command/util/apps-user-input-create.ts | 12 +- src/lib/command/util/capabilities-choose.ts | 17 +- src/lib/command/util/devices-commands.ts | 44 +--- src/lib/command/util/hub-drivers.ts | 11 +- 11 files changed, 98 insertions(+), 356 deletions(-) diff --git a/package-lock.json b/package-lock.json index e77f54e3..438de260 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "eventsource": "^4.0.0", "express": "^5.1.0", "get-port-please": "^3.2.0", - "inquirer": "^9.3.7", "js-yaml": "^4.1.0", "jszip": "^3.10.1", "lodash.at": "^4.6.0", @@ -48,7 +47,6 @@ "@stylistic/eslint-plugin": "^2.8.0", "@types/archiver": "^6.0.3", "@types/express": "^5.0.3", - "@types/inquirer": "^9.0.7", "@types/jest": "^29.5.14", "@types/js-yaml": "^4.0.9", "@types/lodash.at": "^4.6.9", @@ -4951,17 +4949,6 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, - "node_modules/@types/inquirer": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.8.tgz", - "integrity": "sha512-CgPD5kFGWsb8HJ5K7rfWlifao87m4ph8uioU7OTncJevmE/VLIqAAjfQtko578JZg7/f69K4FgqYym3gNr7DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/through": "*", - "rxjs": "^7.2.0" - } - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -5130,16 +5117,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/through": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", - "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -5851,6 +5828,7 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -5865,6 +5843,7 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, "engines": { "node": ">=10" }, @@ -6436,6 +6415,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -6476,6 +6456,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -6487,6 +6468,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -6978,6 +6960,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -7075,6 +7058,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, "engines": { "node": ">=0.8" } @@ -8453,6 +8437,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, "dependencies": { "clone": "^1.0.2" }, @@ -11026,6 +11011,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -11260,6 +11246,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -11375,159 +11362,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/inquirer": { - "version": "9.3.8", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.8.tgz", - "integrity": "sha512-pFGGdaHrmRKMh4WoDDSowddgjT1Vkl90atobmTeSmcPGdYiwikch/m/Ef5wRaiamHejtw0cUUMMerzDUXCci2w==", - "license": "MIT", - "dependencies": { - "@inquirer/external-editor": "^1.0.2", - "@inquirer/figures": "^1.0.3", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/inquirer/node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/inquirer/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -14765,6 +14599,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "engines": { "node": ">=6" } @@ -15599,6 +15434,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -16517,6 +16353,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -16776,6 +16613,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -16887,15 +16725,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -16934,6 +16763,7 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -17280,7 +17110,8 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, "node_modules/sisteransi": { "version": "1.0.5", @@ -17598,6 +17429,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -17883,6 +17715,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -19150,6 +18983,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, "dependencies": { "defaults": "^1.0.3" } diff --git a/package.json b/package.json index 569a734f..32466bba 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "eventsource": "^4.0.0", "express": "^5.1.0", "get-port-please": "^3.2.0", - "inquirer": "^9.3.7", "js-yaml": "^4.1.0", "jszip": "^3.10.1", "lodash.at": "^4.6.0", @@ -77,7 +76,6 @@ "@stylistic/eslint-plugin": "^2.8.0", "@types/archiver": "^6.0.3", "@types/express": "^5.0.3", - "@types/inquirer": "^9.0.7", "@types/jest": "^29.5.14", "@types/js-yaml": "^4.0.9", "@types/lodash.at": "^4.6.9", diff --git a/src/__tests__/lib/command/util/apps-user-input-create.test.ts b/src/__tests__/lib/command/util/apps-user-input-create.test.ts index 815f390a..7c192213 100644 --- a/src/__tests__/lib/command/util/apps-user-input-create.test.ts +++ b/src/__tests__/lib/command/util/apps-user-input-create.test.ts @@ -1,6 +1,6 @@ import { jest } from '@jest/globals' -import type inquirer from 'inquirer' +import { select } from '@inquirer/prompts' import type { v4 as uuid } from 'uuid' import type { @@ -31,11 +31,9 @@ import type { import { buildInputDefMock } from '../../../test-lib/input-type-mock.js' -const promptMock = jest.fn() -jest.unstable_mockModule('inquirer', () => ({ - default: { - prompt: promptMock, - }, +const selectMock = jest.fn() +jest.unstable_mockModule('@inquirer/prompts', () => ({ + select: selectMock, })) const uuidMock = jest.fn().mockReturnValue('generated-uuid' as unknown as Uint8Array) @@ -146,15 +144,12 @@ describe('getAppCreateRequestFromUser', () => { it('queries user for app for oauth-in app', async () => { const appRequest = { appName: 'App Name' } as AppCreateRequest - promptMock.mockResolvedValueOnce({ action: 'oauth-in' }) + selectMock.mockResolvedValueOnce('oauth-in') createFromUserInputMock.mockResolvedValueOnce(appRequest) expect(await getAppCreateRequestFromUser(command)).toBe(appRequest) - expect(promptMock).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ - type: 'list', - name: 'action', - })) + expect(selectMock).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ default: 'oauth-in' })) expect(createFromUserInputMock).toHaveBeenCalledExactlyOnceWith( command, expect.objectContaining({ name: 'OAuth-In SmartApp Mock' }), @@ -165,11 +160,11 @@ describe('getAppCreateRequestFromUser', () => { }) it('allows cancel at top level', async () => { - promptMock.mockResolvedValueOnce({ action: 'cancel' }) + selectMock.mockResolvedValueOnce('cancel') await getAppCreateRequestFromUser(command) - promptMock.mockResolvedValueOnce({ action: 'oauth-in' }) + selectMock.mockResolvedValueOnce('oauth-in') expect(cancelCommandMock).toHaveBeenCalledExactlyOnceWith() expect(createFromUserInputMock).not.toHaveBeenCalled() diff --git a/src/__tests__/lib/command/util/capabilities-choose.test.ts b/src/__tests__/lib/command/util/capabilities-choose.test.ts index 62249201..13ac3aa7 100644 --- a/src/__tests__/lib/command/util/capabilities-choose.test.ts +++ b/src/__tests__/lib/command/util/capabilities-choose.test.ts @@ -1,9 +1,8 @@ import { jest } from '@jest/globals' -import type inquirer from 'inquirer' - import { CapabilitiesEndpoint, type SmartThingsClient } from '@smartthings/core-sdk' +import { stringInput } from '../../../../lib/user-query.js' import type { APICommand } from '../../../../lib/command/api-command.js' import type { selectFromList, SelectFromListFlags } from '../../../../lib/command/select.js' import type { @@ -16,11 +15,9 @@ import type { import type { Sorting } from '../../../../lib/command/io-defs.js' -const promptMock = jest.fn() -jest.unstable_mockModule('inquirer', () => ({ - default: { - prompt: promptMock, - }, +const stringInputMock = jest.fn() +jest.unstable_mockModule('../../../../lib/user-query.js', () => ({ + stringInput: stringInputMock, })) const selectFromListMock = jest.fn() @@ -56,74 +53,75 @@ describe('getIdFromUser', () => { const fieldInfo = {} as Sorting it('returns selected id with version', async () => { - promptMock.mockResolvedValueOnce({ idOrIndex: 'chosen-id' }) + stringInputMock.mockResolvedValueOnce('chosen-id') convertToIdMock.mockReturnValueOnce('converted-chosen-id') expect(await getIdFromUser(fieldInfo, capabilities)) .toStrictEqual({ id: 'converted-chosen-id', version: 1 }) - expect(promptMock).toHaveBeenCalledExactlyOnceWith( - expect.objectContaining({ type: 'input', message: 'Enter id or index' }), + expect(stringInputMock).toHaveBeenCalledExactlyOnceWith( + 'Enter id or index', + expect.objectContaining({ validate: expect.any(Function) }), ) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('chosen-id', capabilities) }) it('passes prompt message to inquirer', async () => { - promptMock.mockResolvedValueOnce({ idOrIndex: 'chosen-id' }) + stringInputMock.mockResolvedValueOnce('chosen-id') convertToIdMock.mockReturnValueOnce('converted-chosen-id') expect(await getIdFromUser(fieldInfo, capabilities, 'user prompt')) .toStrictEqual({ id: 'converted-chosen-id', version: 1 }) - expect(promptMock).toHaveBeenCalledExactlyOnceWith( - expect.objectContaining({ type: 'input', message: 'user prompt' }), + expect(stringInputMock).toHaveBeenCalledExactlyOnceWith( + 'user prompt', + expect.objectContaining({ validate: expect.any(Function) }), ) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('chosen-id', capabilities) }) it('throws error when convertToId fails', async () => { - promptMock.mockResolvedValueOnce({ idOrIndex: 'invalid' }) + stringInputMock.mockResolvedValueOnce('invalid') convertToIdMock.mockReturnValueOnce(false) await expect(getIdFromUser(fieldInfo, capabilities)) .rejects.toThrow() - expect(promptMock).toHaveBeenCalledExactlyOnceWith( - expect.objectContaining({ type: 'input', message: 'Enter id or index' }), + expect(stringInputMock).toHaveBeenCalledWith( + 'Enter id or index', + expect.objectContaining({ validate: expect.any(Function) }), ) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('invalid', capabilities) }) describe('validation function', () => { it('returns true when convertToId returns truthy', async () => { - promptMock.mockResolvedValueOnce({ idOrIndex: 'chosen-id' }) + stringInputMock.mockResolvedValueOnce('chosen-id') convertToIdMock.mockReturnValueOnce('converted-chosen-id') expect(await getIdFromUser(fieldInfo, capabilities, 'user prompt')) .toStrictEqual({ id: 'converted-chosen-id', version: 1 }) - const validateFunction = (promptMock.mock.calls[0][0] as - { validate: (input: string) => true | string }).validate + const validateFunction = stringInputMock.mock.calls[0][1]?.validate convertToIdMock.mockReset() // reset to clear calls made getting the function above convertToIdMock.mockReturnValueOnce('truthy-value') - expect(validateFunction('user-input')).toBe(true) + expect(validateFunction?.('user-input')).toBe(true) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('user-input', capabilities) }) it('returns error string when convertToId returns false', async () => { - promptMock.mockResolvedValueOnce({ idOrIndex: 'chosen-id' }) + stringInputMock.mockResolvedValueOnce('chosen-id') convertToIdMock.mockReturnValueOnce('converted-chosen-id') expect(await getIdFromUser(fieldInfo, capabilities, 'user prompt')) .toStrictEqual({ id: 'converted-chosen-id', version: 1 }) - const validateFunction = (promptMock.mock.calls[0][0] as - { validate: (input: string) => true | string }).validate + const validateFunction = stringInputMock.mock.calls[0][1]?.validate convertToIdMock.mockReset() // reset to clear calls made getting the function above convertToIdMock.mockReturnValueOnce(false) - expect(validateFunction('user-input')) + expect(validateFunction?.('user-input')) .toBe('Invalid id or index user-input. Please enter an index or valid id.') expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('user-input', capabilities) }) diff --git a/src/__tests__/lib/command/util/hub-drivers.test.ts b/src/__tests__/lib/command/util/hub-drivers.test.ts index 41d9427a..467274a3 100644 --- a/src/__tests__/lib/command/util/hub-drivers.test.ts +++ b/src/__tests__/lib/command/util/hub-drivers.test.ts @@ -3,11 +3,8 @@ import { jest } from '@jest/globals' import { promises as fs } from 'node:fs' import type { PeerCertificate } from 'node:tls' -import type inquirer from 'inquirer' -import type { Question } from 'inquirer' - import type { DriverInfo } from '../../../../lib/live-logging.js' -import type { booleanInput } from '../../../../lib/user-query.js' +import type { stringInput, booleanInput } from '../../../../lib/user-query.js' import type { fatalError } from '../../../../lib/util.js' import { convertToId, stringTranslateToId } from '../../../../lib/command/command-util.js' import type { selectFromList } from '../../../../lib/command/select.js' @@ -24,16 +21,11 @@ jest.unstable_mockModule('node:fs', () => ({ }, })) -const promptMock = jest.fn() -jest.unstable_mockModule('inquirer', () => ({ - default: { - prompt: promptMock, - }, -})) - const booleanInputMock = jest.fn() +const stringInputMock = jest.fn() jest.unstable_mockModule('../../../../lib/user-query.js', () => ({ booleanInput: booleanInputMock, + stringInput: stringInputMock, })) const fatalErrorMock = jest.fn().mockReturnValue('never return' as never) @@ -109,15 +101,14 @@ describe('chooseHubDrivers', () => { const getIdFromUser = selectFromListMock.mock.calls[0][2].getIdFromUser expect(getIdFromUser).toBeDefined() - promptMock.mockResolvedValueOnce({ itemIdOrIndex: 'prompt-answer' }) + stringInputMock.mockResolvedValueOnce('prompt-answer') convertToIdMock.mockReturnValueOnce('converted-id') expect(await getIdFromUser?.({ primaryKeyName: 'driver_id' }, hubDriverList)).toBe('converted-id') - expect(promptMock).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ - type: 'input', - message: 'Enter id or index', - })) + expect(stringInputMock).toHaveBeenCalledExactlyOnceWith( + 'Enter id or index', + expect.objectContaining({ default: 'all' })) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('prompt-answer', 'driver_id', hubDriverList) }) @@ -128,7 +119,7 @@ describe('chooseHubDrivers', () => { const getIdFromUser = selectFromListMock.mock.calls[0][2].getIdFromUser expect(getIdFromUser).toBeDefined() - promptMock.mockResolvedValueOnce({ itemIdOrIndex: 'all' }) + stringInputMock.mockResolvedValueOnce('all') expect(await getIdFromUser?.({ primaryKeyName: 'driver_id' }, hubDriverList)).toBe('all') @@ -139,13 +130,13 @@ describe('chooseHubDrivers', () => { expect(await chooseHubDrivers(command, hubDriverList)).toBe('chosen-id') const getIdFromUser = selectFromListMock.mock.calls[0][2].getIdFromUser - promptMock.mockResolvedValueOnce({ itemIdOrIndex: 'valid input' }) + stringInputMock.mockResolvedValueOnce('valid input') convertToIdMock.mockReturnValueOnce('converted-id') expect(await getIdFromUser?.({ primaryKeyName: 'driver_id' }, hubDriverList)).toBe('converted-id') convertToIdMock.mockClear() convertToIdMock.mockReturnValueOnce('converted-id') - const validate = (promptMock.mock.calls[0][0] as Question).validate + const validate = stringInputMock.mock.calls[0][1]?.validate expect(validate).toBeDefined() expect(validate?.('valid input')).toBe(true) expect(convertToIdMock).toHaveBeenCalledExactlyOnceWith('valid input', 'driver_id', hubDriverList) @@ -155,13 +146,13 @@ describe('chooseHubDrivers', () => { expect(await chooseHubDrivers(command, hubDriverList)).toBe('chosen-id') const getIdFromUser = selectFromListMock.mock.calls[0][2].getIdFromUser - promptMock.mockResolvedValueOnce({ itemIdOrIndex: 'invalid input' }) + stringInputMock.mockResolvedValueOnce('invalid input') convertToIdMock.mockReturnValueOnce('converted-id') expect(await getIdFromUser?.({ primaryKeyName: 'driver_id' }, hubDriverList)).toBe('converted-id') convertToIdMock.mockClear() convertToIdMock.mockReturnValueOnce(undefined) - const validate = (promptMock.mock.calls[0][0] as Question).validate + const validate = stringInputMock.mock.calls[0][1]?.validate expect(validate).toBeDefined() expect(validate?.('invalid input')) .toBe('Invalid id or index "invalid input". Please enter an index or valid id.') @@ -172,7 +163,7 @@ describe('chooseHubDrivers', () => { expect(await chooseHubDrivers(command, hubDriverList)).toBe('chosen-id') const getIdFromUser = selectFromListMock.mock.calls[0][2].getIdFromUser - promptMock.mockResolvedValueOnce({ itemIdOrIndex: 'invalid input' }) + stringInputMock.mockResolvedValueOnce('invalid input') convertToIdMock.mockReturnValueOnce(false) await expect(getIdFromUser?.({ primaryKeyName: 'driver_id' }, hubDriverList)).rejects.toThrow('invalid state') }) diff --git a/src/commands/edge/channels/create.ts b/src/commands/edge/channels/create.ts index 87ec4e82..226bd1cf 100644 --- a/src/commands/edge/channels/create.ts +++ b/src/commands/edge/channels/create.ts @@ -1,8 +1,9 @@ -import inquirer from 'inquirer' import { type ArgumentsCamelCase, type Argv, type CommandModule } from 'yargs' import { type Channel, type ChannelCreate } from '@smartthings/core-sdk' +import { stringInput } from '../../../lib/user-query.js' +import { urlValidate } from '../../../lib/validate-util.js' import { apiCommand, apiCommandBuilder, type APICommandFlags, apiDocsURL } from '../../../lib/command/api-command.js' import { inputAndOutputItem, @@ -42,26 +43,9 @@ const handler = async (argv: ArgumentsCamelCase): Promise => const command = await apiCommand(argv) const getInputFromUser = async (): Promise => { - const name = (await inquirer.prompt({ - type: 'input', - name: 'name', - message: 'Channel name:', - validate: input => input ? true : 'name is required', - })).name as string - - const description = (await inquirer.prompt({ - type: 'input', - name: 'description', - message: 'Channel description:', - validate: input => input ? true : 'description is required', - })).description as string - - const termsOfServiceUrl = (await inquirer.prompt({ - type: 'input', - name: 'termsOfServiceUrl', - message: 'Channel terms of service URL:', - validate: input => input ? true : 'termsOfServiceUrl is required', - })).termsOfServiceUrl as string + const name = await stringInput('Channel name:') + const description = await stringInput('Channel description:') + const termsOfServiceUrl = await stringInput('Channel terms of service URL:', { validate: urlValidate }) return { name, description, termsOfServiceUrl, type: 'DRIVER' } } diff --git a/src/commands/edge/channels/invites/create.ts b/src/commands/edge/channels/invites/create.ts index f2cf493d..7a960fd3 100644 --- a/src/commands/edge/channels/invites/create.ts +++ b/src/commands/edge/channels/invites/create.ts @@ -1,7 +1,7 @@ -import inquirer from 'inquirer' import { type ArgumentsCamelCase, type Argv, type CommandModule } from 'yargs' import { type TableFieldDefinition } from '../../../../lib/table-generator.js' +import { stringInput } from '../../../../lib/user-query.js' import { apiCommand, apiCommandBuilder, type APICommandFlags } from '../../../../lib/command/api-command.js' import { edgeCommand } from '../../../../lib/command/edge-command.js' import { @@ -12,6 +12,7 @@ import { import { userInputProcessor } from '../../../../lib/command/input-processor.js' import { chooseChannel } from '../../../../lib/command/util/edge/channels-choose.js' import { type Invitation, type InvitationCreate } from '../../../../lib/edge/endpoints/invites.js' +import { urlValidate } from '../../../../lib/validate-util.js' export type CommandArgs = @@ -48,33 +49,10 @@ const handler = async (argv: ArgumentsCamelCase): Promise => const channelId = await chooseChannel(command, argv.channel, { useConfigDefault: true }) - const name = (await inquirer.prompt({ - type: 'input', - name: 'name', - message: 'Invitation name:', - validate: input => input ? true : 'name is required', - })).name as string - - const description = (await inquirer.prompt({ - type: 'input', - name: 'description', - message: 'Invitation description:', - validate: input => input ? true : 'description is required', - })).description as string - - const owner = (await inquirer.prompt({ - type: 'input', - name: 'owner', - message: 'Invitation owner:', - validate: input => input ? true : 'owner is required', - })).owner as string - - const termsUrl = (await inquirer.prompt({ - type: 'input', - name: 'termsUrl', - message: 'Invitation termsUrl:', - validate: input => input ? true : 'termsUrl is required', - })).termsUrl as string + const name = await stringInput('Invitation name:') + const description = await stringInput('Invitation description:') + const owner = await stringInput('Invitation owner:') + const termsUrl = await stringInput('Invitation termsUrl:', { validate: urlValidate }) const defaultInvitationProfileId = '61a79569-e8fd-4a4d-9b9c-a4a55ccdd15e' const profileId = (command.profile.defaultInvitationProfileId as string) diff --git a/src/lib/command/util/apps-user-input-create.ts b/src/lib/command/util/apps-user-input-create.ts index 66d5333b..fe9a176c 100644 --- a/src/lib/command/util/apps-user-input-create.ts +++ b/src/lib/command/util/apps-user-input-create.ts @@ -1,5 +1,4 @@ -// TODO: rename to apps-user-input-create.ts to match apps-user-input-update.ts -import inquirer from 'inquirer' +import { select } from '@inquirer/prompts' import { v4 as uuid } from 'uuid' import { @@ -67,17 +66,14 @@ const oauthAppCreateRequestInputDefinition = objectDef('OAuth- export const getAppCreateRequestFromUser = async ( command: SmartThingsCommand, ): Promise => { - const action = (await inquirer.prompt({ - type: 'list', - name: 'action', - message: 'What kind of app do you want to create? (Currently, only OAuth-In apps are' + - ' supported.)', + const action = await select({ + message: 'What kind of app do you want to create? (Currently, only OAuth-In apps are supported.)', choices: [ { name: 'OAuth-In App', value: 'oauth-in' }, { name: 'Cancel', value: 'cancel' }, ], default: 'oauth-in', - })).action + }) if (action === 'oauth-in') { return createFromUserInput( diff --git a/src/lib/command/util/capabilities-choose.ts b/src/lib/command/util/capabilities-choose.ts index 11e2462e..dffcc628 100644 --- a/src/lib/command/util/capabilities-choose.ts +++ b/src/lib/command/util/capabilities-choose.ts @@ -1,6 +1,5 @@ -import inquirer from 'inquirer' - import { type WithLocales } from '../../api-helpers.js' +import { stringInput } from '../../user-query.js' import { type APICommand } from '../api-command.js' import { type Sorting } from '../io-defs.js' import { selectFromList, type SelectFromListConfig, type SelectFromListFlags } from '../select.js' @@ -19,16 +18,10 @@ export const getIdFromUser = async ( list: CapabilitySummaryWithNamespace[], promptMessage?: string, ): Promise => { - const idOrIndex: string = (await inquirer.prompt({ - type: 'input', - name: 'idOrIndex', - message: promptMessage ?? 'Enter id or index', - validate: input => { - return convertToId(input, list) - ? true - : `Invalid id or index ${input}. Please enter an index or valid id.` - }, - })).idOrIndex + const idOrIndex: string = await stringInput(promptMessage ?? 'Enter id or index', { + validate: input => + convertToId(input, list) ? true : `Invalid id or index ${input}. Please enter an index or valid id.`, + }) const inputId = convertToId(idOrIndex, list) if (inputId === false) { throw Error(`unable to convert ${idOrIndex} to id`) diff --git a/src/lib/command/util/devices-commands.ts b/src/lib/command/util/devices-commands.ts index d7b5be1f..138dc09c 100644 --- a/src/lib/command/util/devices-commands.ts +++ b/src/lib/command/util/devices-commands.ts @@ -1,11 +1,10 @@ -import inquirer from 'inquirer' - import { type Command, type Component, type CapabilityReference, type Device } from '@smartthings/core-sdk' import { cancelCommand } from '../../util.js' import { type APICommand } from '../api-command.js' import { isIndexArgument } from '../command-util.js' import { type SmartThingsCommand } from '../smartthings-command.js' +import { optionalStringInput, stringInput } from '../../user-query.js' import { attributeTypeDisplayString } from './capabilities-util.js' @@ -79,14 +78,9 @@ export const getComponentFromUser = async ( } console.log(table.toString()) - const input = (await inquirer.prompt({ - type: 'input', - name: 'component', - message: 'Enter component index or id', - validate: (input: string) => { - return inputRegex.test(input) || 'Invalid command syntax' - }, - })).component + const input = await stringInput('Enter component index or id', { + validate: (input: string) => inputRegex.test(input) || 'Invalid command syntax', + }) if (isIndexArgument(input)) { cmd.component = device.components[Number.parseInt(input) - 1].id || '' @@ -116,14 +110,9 @@ export const getCapabilityFromUser = async ( } console.log(table.toString()) - const input = (await inquirer.prompt({ - type: 'input', - name: 'capability', - message: 'Enter capability index or id', - validate: (input: string) => { - return inputRegex.test(input) || 'Invalid command syntax' - }, - })).capability + const input = await stringInput('Enter capability index or id', { + validate: (input: string) => inputRegex.test(input) || 'Invalid command syntax', + }) if (isIndexArgument(input)) { cmd.capability = component.capabilities[Number.parseInt(input) - 1].id || '' @@ -163,14 +152,9 @@ export const getCommandFromUser = async ( } console.log(table.toString()) - const input = (await inquirer.prompt({ - type: 'input', - name: 'command', - message: 'Enter command', - validate: (input: string) => { - return inputRegex.test(input) || 'Invalid command syntax' - }, - })).command + const input = await stringInput('Enter command', { + validate: (input: string) => inputRegex.test(input) || 'Invalid command syntax', + }) if (isIndexArgument(input)) { cmd.command = commandNames[Number.parseInt(input) - 1] @@ -185,13 +169,9 @@ export const getCommandFromUser = async ( const command = capability.commands[cmd.command] if (command.arguments && command.arguments?.length > 0 && (!cmd.arguments || cmd.arguments.length === 0)) { const args = command?.arguments?.map(it => it.optional ? `[${it.name}]` : it.name).join(', ') || '' - const input = (await inquirer.prompt({ - type: 'input', - name: 'arguments', - message: `Enter command arguments (${args})`, - })).arguments + const input = await optionalStringInput(`Enter command arguments (${args})`) - if (input === '') { + if (!input) { return cancelCommand() } diff --git a/src/lib/command/util/hub-drivers.ts b/src/lib/command/util/hub-drivers.ts index c8a4beb7..5fa13b69 100644 --- a/src/lib/command/util/hub-drivers.ts +++ b/src/lib/command/util/hub-drivers.ts @@ -1,11 +1,9 @@ import { promises as fs } from 'node:fs' import { type PeerCertificate } from 'node:tls' -import inquirer from 'inquirer' - import { type Sorting } from '../io-defs.js' import { type DriverInfo } from '../../live-logging.js' -import { booleanInput } from '../../user-query.js' +import { booleanInput, stringInput } from '../../user-query.js' import { fatalError } from '../../util.js' import { convertToId, stringTranslateToId } from '../command-util.js' import { selectFromList, type SelectFromListConfig } from '../select.js' @@ -34,16 +32,13 @@ export const chooseHubDrivers = async ( ): Promise => { const primaryKeyName = fieldInfo.primaryKeyName - const itemIdOrIndex: string = (await inquirer.prompt({ - type: 'input', - name: 'itemIdOrIndex', - message: prompt ?? 'Enter id or index', + const itemIdOrIndex = await stringInput(prompt ?? 'Enter id or index', { default: allDriversText, validate: input => input === allDriversText || convertToId(input, primaryKeyName, list) ? true : `Invalid id or index "${input}". Please enter an index or valid id.`, - })).itemIdOrIndex + }) const inputId = itemIdOrIndex === allDriversText ? itemIdOrIndex