diff --git a/.vscodeignore b/.vscodeignore index 04a0de2c..1db9423f 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -11,7 +11,8 @@ node_modules server test/ img/ -esbuild.js +qcumber.sh +esbuild.mjs sonar-project.properties eslint.config.cjs eslint.config.mjs diff --git a/esbuild.mjs b/esbuild.mjs index 5be5420e..11478308 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -1,14 +1,4 @@ import { context, build } from "esbuild"; -import { copyFileSync, mkdirSync } from "fs"; -import { sync } from "glob"; -import { join, basename } from "path"; - -function copyFiles(srcPattern, destDir) { - sync(srcPattern).forEach((file) => { - const destFile = join(destDir, basename(file)); - copyFileSync(file, destFile); - }); -} const minify = process.argv.includes("--minify"); const sourcemap = process.argv.includes("--sourcemap"); @@ -22,59 +12,71 @@ const baseConfig = { bundle: true, }; -const extensionConfig = { +const cssConfig = { ...baseConfig, - outfile: "./out/extension.js", - entryPoints: ["./src/extension.ts"], + entryPoints: [ + "./src/webview/styles/style.css", + "./src/webview/styles/light.css", + ], + outdir: "./out", +}; + +const webviewConfig = { + ...baseConfig, + entryPoints: ["./src/webview/main.ts"], + outfile: "./out/webview.js", + format: "esm", + target: "es2020", external: ["vscode"], - format: "cjs", - platform: "node", }; const serverConfig = { ...baseConfig, - outfile: "./out/server.js", entryPoints: ["./server/src/server.ts"], + outfile: "./out/server.js", format: "cjs", - external: ["vscode"], platform: "node", + external: ["vscode"], }; -const webviewConfig = { +const extensionConfig = { ...baseConfig, - target: "es2020", - format: "esm", - entryPoints: ["./src/webview/main.ts"], + entryPoints: ["./src/extension.ts"], + outfile: "./out/extension.js", + format: "cjs", + platform: "node", external: ["vscode"], - outfile: "./out/webview.js", }; -(async () => { - try { - mkdirSync("./out", { recursive: true }); - copyFiles("src/webview/styles/*.css", "./out"); - - if (watch) { - console.log("esbuild:started"); - const contexts = await Promise.all([ - context(serverConfig), - context(webviewConfig), - context(extensionConfig), - ]); - await Promise.all(contexts.map((ctx) => ctx.rebuild())).finally(() => +if (watch) { + console.log("esbuild:started"); + Promise.all([ + context(cssConfig), + context(webviewConfig), + context(serverConfig), + context(extensionConfig), + ]) + .then((contexts) => + Promise.all(contexts.map((ctx) => ctx.rebuild())).finally(() => Promise.all(contexts.map((ctx) => ctx.watch({ delay: 500 }))) .then(() => console.log("esbuild:watching")) - .catch((err) => { - console.error(err); + .catch((error) => { + console.error(error); process.exit(1); }), - ); - } else { - await build(serverConfig); - await build(webviewConfig); - await build(extensionConfig); - } - } catch (err) { - console.error(err); - } -})(); + ), + ) + .catch((error) => { + console.error(error); + }); +} else { + Promise.all([ + build(cssConfig), + build(webviewConfig), + build(serverConfig), + build(extensionConfig), + ]).catch((error) => { + console.error(error); + process.exit(1); + }); +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 881ebce2..5127f37c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,16 +8,14 @@ import * as tseslint from "typescript-eslint"; const currentYear = new Date().getFullYear(); export default [ - { - ignores: ["**/*.d.ts", "**/*.js", "**/*.mjs", "src/ipc/**", "test/fixtures/**"], - }, + { ignores: ["**/*.d.ts", "**/*.js", "**/*.mjs", "src/ipc/**/*"] }, js.configs.recommended, ...tseslint.configs.recommended, { languageOptions: { parser: tseslint.parser, parserOptions: { - ecmaVersion: 2018, + ecmaVersion: 2020, sourceType: "module", }, }, diff --git a/package.json b/package.json index b30edf02..70fa0f5c 100644 --- a/package.json +++ b/package.json @@ -971,7 +971,7 @@ }, { "command": "kdb.connections.edit", - "when": "view == kdb-servers && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", + "when": "view == kdb-servers && viewItem not in kdb.kdbQuickNodes && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", "group": "connection@4" }, { @@ -981,12 +981,12 @@ }, { "command": "kdb.connections.addAuthentication", - "when": "view == kdb-servers && viewItem not in kdb.insightsNodes && viewItem in kdb.kdbNodesWithoutAuth && viewItem not in kdb.local", + "when": "view == kdb-servers && viewItem not in kdb.kdbQuickNodes && viewItem not in kdb.insightsNodes && viewItem in kdb.kdbNodesWithoutAuth && viewItem not in kdb.local", "group": "connection@3" }, { "command": "kdb.connections.enableTLS", - "when": "view == kdb-servers && viewItem not in kdb.insightsNodes && viewItem in kdb.kdbNodesWithoutTls && viewItem not in kdb.local", + "when": "view == kdb-servers && viewItem not in kdb.kdbQuickNodes && viewItem not in kdb.insightsNodes && viewItem in kdb.kdbNodesWithoutTls && viewItem not in kdb.local", "group": "connection@4" }, { @@ -1006,12 +1006,12 @@ }, { "command": "kdb.connections.remove.kdb", - "when": "view == kdb-servers && viewItem in kdb.rootNodes", + "when": "view == kdb-servers && viewItem in kdb.rootNodes && viewItem not in kdb.kdbQuickNodes", "group": "connection@5" }, { "command": "kdb.connections.export.single", - "when": "view == kdb-servers && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", + "when": "view == kdb-servers && viewItem not in kdb.kdbQuickNodes && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", "group": "connection@6" }, { @@ -1201,8 +1201,8 @@ }, "scripts": { "update-deps": "ncu --target patch -u", - "format": "prettier --write \"**/*.+(js|ts)\"", - "lint": "eslint $(git ls-files '*.ts') --fix --no-warn-ignored", + "format": "prettier --write \"**/*.ts\"", + "lint": "eslint --fix --no-warn-ignored", "esbuild-base": "rimraf out && node ./esbuild.mjs", "watch": "npm run -S esbuild-base -- --sourcemap --watch", "build": "npm run -S esbuild-base -- --sourcemap", diff --git a/ref_card.md b/ref_card.md index 8e6d6c2b..ba10a71f 100644 --- a/ref_card.md +++ b/ref_card.md @@ -135,15 +135,15 @@ | Run.Cell.ie.dap.q | | src/utils/queryUtils.ts | | Run.Cell.ie.dap.py | | src/utils/queryUtils.ts | | ¦ | | | -| Run.Workbook.kdb.quick.q | | | -| Run.Workbook.kdb.quick.py | | | -| Run.Workbook.kdb.quick.sql | | | -| Run.File.kdb.quick.q | | | -| Run.File.kdb.quick.py | | | -| Run.File.kdb.quick.sql | | | -| Run.Cell.kdb.quick.q | | | -| Run.Cell.kdb.quick.py | | | -| Run.Cell.kdb.quick.sql | | | +| Run.Workbook.kdb.quick.q | | src/utils/queryUtils.ts | +| Run.Workbook.kdb.quick.py | | src/utils/queryUtils.ts | +| Run.Workbook.kdb.quick.sql | | src/utils/queryUtils.ts | +| Run.File.kdb.quick.q | | src/utils/queryUtils.ts | +| Run.File.kdb.quick.py | | src/utils/queryUtils.ts | +| Run.File.kdb.quick.sql | | src/utils/queryUtils.ts | +| Run.Cell.kdb.quick.q | | src/utils/queryUtils.ts | +| Run.Cell.kdb.quick.py | | src/utils/queryUtils.ts | +| Run.Cell.kdb.quick.sql | | src/utils/queryUtils.ts | | ¦ | | | | Run.Datasource.api | | src/utils/queryUtils.ts | | Run.Datasource.qsql | | src/utils/queryUtils.ts | diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index df0e1d50..a198516f 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -1429,3 +1429,85 @@ function isValidExportedConnections(data: any): data is ExportedConnections { Array.isArray(data.connections.KDB) ); } + +const quickConnections: ServerDetails[] = []; + +function getQuickLabel(conn: ServerDetails) { + return `${conn.serverAlias} [${conn.serverName}:${conn.serverPort}]`; +} + +function getQuickDetail(host: string, port: string, user: string) { + return quickConnections.find( + (conn) => + conn.serverName === host && + conn.serverPort === port && + conn.username === user, + ); +} + +async function removeQuickDetail(conn: ServerDetails) { + quickConnections.splice(quickConnections.indexOf(conn), 1); + const label = getQuickLabel(conn); + ext.connectedConnectionList + .find((conn) => conn.connLabel === label) + ?.disconnect(); + await refreshQuickProvider(); +} + +async function refreshQuickProvider() { + const servers = getServers(); + quickConnections.forEach((conn) => (servers[conn.serverAlias] = conn)); + await commands.executeCommand( + "setContext", + "kdb.kdbQuickNodes", + quickConnections.map((conn) => getQuickLabel(conn)), + ); + ext.serverProvider.refresh(servers); +} + +export async function setQuickPassword( + host: string, + port: string, + user: string, + pass: string, +) { + const conn = getQuickDetail(host, port, user); + if (conn) await removeQuickDetail(conn); + await ext.secretSettings.storeAuthData( + `${host}:${port}:${user}`, + `${user}:${pass}`, + ); +} + +export async function ensureQuickConnection(server: string) { + const [host, port, user] = server.split(":"); + + let connection = getQuickDetail(host, port, user); + if (!connection) { + const serverAlias = "(Connection " + (quickConnections.length + 1) + ")"; + if (user) { + let auth = await ext.secretSettings.getAuthData(server); + if (!auth) { + const password = await window.showInputBox({ + password: true, + prompt: `Enter password for ${server}`, + }); + auth = `${user}:${password ?? ""}`; + await ext.secretSettings.storeAuthData(server, auth); + } + await ext.secretSettings.storeAuthData(serverAlias, auth); + } + connection = { + serverAlias, + serverName: host, + serverPort: port, + username: user, + auth: !!user, + tls: false, + }; + quickConnections.push(connection); + await refreshQuickProvider(); + } + + return connection.serverAlias; +} diff --git a/src/commands/workspaceCommand.ts b/src/commands/workspaceCommand.ts index 2d0b70d1..deef291f 100644 --- a/src/commands/workspaceCommand.ts +++ b/src/commands/workspaceCommand.ts @@ -29,7 +29,12 @@ import { } from "vscode"; import { ext } from "../extensionVariables"; -import { resetScratchpad, runQuery } from "./serverCommand"; +import { + ensureQuickConnection, + resetScratchpad, + runQuery, + setQuickPassword, +} from "./serverCommand"; import { InsightsConnection } from "../classes/insightsConnection"; import { LocalConnection } from "../classes/localConnection"; import { ReplConnection } from "../classes/replConnection"; @@ -38,7 +43,12 @@ import { MetaDap } from "../models/meta"; import { ConnectionManagementService } from "../services/connectionManagerService"; import { InsightsNode, KdbNode, LabelNode } from "../services/kdbTreeProvider"; import { updateCellMetadata } from "../services/notebookProviders"; -import { getBasename, offerConnectAction } from "../utils/core"; +import { + getBasename, + isQuick, + isQuickAlias, + offerConnectAction, +} from "../utils/core"; import { importOldDsFiles } from "../utils/dataSource"; import { Cancellable, @@ -58,6 +68,7 @@ import { errorMessage, normalizeAssemblyTarget, } from "../utils/shared"; +import { showInputPicker } from "../utils/widgets"; const logger = "workspaceCommand"; @@ -121,6 +132,21 @@ function getServers() { ]; } +const quickServers: string[] = []; + +function getQuickServers(uri: Uri) { + const conf = workspace.getConfiguration("kdb", uri); + const connections = conf.get<{ [key: string]: string }>("connectionMap", {}); + const size = quickServers.length; + Object.keys(connections).forEach((key) => { + const target = connections[key]; + if (target.includes(":") && !quickServers.includes(target)) { + quickServers.push(target); + } + }); + return size !== quickServers.length ? quickServers.sort() : quickServers; +} + export async function getConnectionForServer( server: string, ): Promise { @@ -189,7 +215,8 @@ export function getServerForUri(uri: Uri) { const server = map[relativePath(uri)]; const servers = getServers(); - return server && (server === ext.REPL || servers.includes(server)) + return isQuick(server) || + (server && (server === ext.REPL || servers.includes(server))) ? server : undefined; } @@ -213,39 +240,69 @@ export function getTargetForUri(uri: Uri) { export function getConnectionForUri(uri: Uri) { const server = getServerForUri(uri); if (server) { - return ext.connectionsList.find((item) => { - if (item instanceof InsightsNode) { - return item.details.alias === server; - } - return item.details.serverAlias === server; - }) as KdbNode | InsightsNode; + if (isQuick(server)) { + const [host, port, user] = server.split(":"); + return ext.connectionsList.find( + (item) => + item instanceof KdbNode && + host === item.details.serverName && + port === item.details.serverPort && + user === item.details.username, + ); + } + return ext.connectionsList.find((item) => + item instanceof InsightsNode + ? item.details.alias === server + : item.details.serverAlias === server, + ); } } export async function pickConnection(uri: Uri) { /* c8 ignore start */ const server = getServerForUri(uri); - const servers = getServers(); - const items = ["(none)", ...servers]; + const items = ["(none)", ...getServers(), ...getQuickServers(uri)]; - let picked = await window.showQuickPick(items, { - title: `Choose Connection (${getBasename(uri)})`, + let picked = await showInputPicker(items, { + title: `Choose a connection or enter a quick connection string for ${getBasename(uri)}`, placeHolder: server, }); - if (picked) { - if (picked === "(none)") { - picked = undefined; - await setTargetForUri(uri, undefined); - } - if (picked) { - setRunScratchpadItemText(uri, picked); - ext.runScratchpadItem.show(); + if (picked === undefined) return undefined; + + if (isQuick(picked)) { + const [host, port, user, pass] = picked.split(":"); + if (host && port && /^\d+$/s.test(port)) { + if (user) { + picked = `${host}:${port}:${user}`; + if (pass !== undefined) await setQuickPassword(host, port, user, pass); + } else { + picked = `${host}:${port}`; + } } else { - ext.runScratchpadItem.hide(); + notify(`Connection string (${picked}) is not valid.`, MessageKind.ERROR, { + logger, + }); + return undefined; } - await setServerForUri(uri, picked); + } else if (picked === "(none)") { + picked = undefined; + await setTargetForUri(uri, undefined); + } else if (!items.includes(picked)) { + notify(`Connection "${picked}" is not found.`, MessageKind.ERROR, { + logger, + }); + return undefined; } + + if (picked) { + setRunScratchpadItemText(uri, picked); + ext.runScratchpadItem.show(); + } else { + ext.runScratchpadItem.hide(); + } + await setServerForUri(uri, picked); + return picked; /* c8 ignore stop */ } @@ -588,6 +645,7 @@ export async function runActiveEditor(type?: ExecutionTypes) { (type === ExecutionTypes.PopulateScratchpad ? 0 : RunFlag.Run) | (isInsights ? RunFlag.Insights : 0) | (target ? RunFlag.Dap : 0) | + (isQuickAlias(conn.connLabel) ? RunFlag.Quick : 0) | (isWorkbook(uri) ? RunFlag.Workbook : 0) | (isPython(uri) ? RunFlag.Python : 0) | (isSql(uri) ? RunFlag.Sql : 0), @@ -641,14 +699,12 @@ export class ConnectionLensProvider implements CodeLensProvider { title: server ? `Run on ${server}` : "Choose Connection", }); - const target = getTargetForUri(document.uri); - if (server) { const conn = await getConnectionForServer(server); if (!isSql(document.uri) && conn instanceof InsightsNode) { const pickTarget = new CodeLens(top, { command: "kdb.file.pickTarget", - title: target || "scratchpad", + title: getTargetForUri(document.uri) || "scratchpad", }); return [pickConnection, pickTarget]; } @@ -736,14 +792,14 @@ export async function importOldDSFiles() { export async function findConnection(uri: Uri) { /* c8 ignore start */ - const connMngService = new ConnectionManagementService(); - let conn: InsightsConnection | LocalConnection | undefined; let server = getServerForUri(uri) ?? ""; if (server) { + if (isQuick(server)) server = await ensureQuickConnection(server); const node = await getConnectionForServer(server); if (node) { + const connMngService = new ConnectionManagementService(); server = node.label; conn = connMngService.retrieveConnectedConnection(server); if (conn === undefined) { diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 390b883d..840c7c31 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -32,6 +32,7 @@ import { getServers, updateInsights, updateServers, + isQuickAlias, } from "../utils/core"; import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MessageKind, notify } from "../utils/notifications"; @@ -525,14 +526,16 @@ export class ConnectionManagementService { return ""; } if (connection instanceof KdbNode) { - exportedContent.connections.KDB.push(connection.details); + if (!isQuickAlias(connection.details.serverAlias)) + exportedContent.connections.KDB.push(connection.details); } else { exportedContent.connections.Insights.push(connection.details); } } else { ext.connectionsList.forEach((connection) => { if (connection instanceof KdbNode) { - exportedContent.connections.KDB.push(connection.details); + if (!isQuickAlias(connection.details.serverAlias)) + exportedContent.connections.KDB.push(connection.details); } else { exportedContent.connections.Insights.push(connection.details); } diff --git a/src/services/notebookController.ts b/src/services/notebookController.ts index 457d0174..1c7689ab 100644 --- a/src/services/notebookController.ts +++ b/src/services/notebookController.ts @@ -30,7 +30,7 @@ import { } from "../commands/workspaceCommand"; import { ext } from "../extensionVariables"; import { CellKind } from "../models/notebook"; -import { getBasename } from "../utils/core"; +import { getBasename, isQuickAlias } from "../utils/core"; import { MessageKind, notify } from "../utils/notifications"; import { resultToBase64, @@ -177,6 +177,7 @@ export class KxNotebookController { (variable ? 0 : RunFlag.Run) | (isInsights ? RunFlag.Insights : 0) | (target ? RunFlag.Dap : 0) | + (isQuickAlias(conn.connLabel) ? RunFlag.Quick : 0) | (kind === CellKind.PYTHON ? RunFlag.Python : 0) | (kind === CellKind.SQL ? RunFlag.Sql : 0), ); diff --git a/src/utils/core.ts b/src/utils/core.ts index c285bbe3..5fd9def4 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -684,3 +684,11 @@ export function isBaseVersionGreaterOrEqual( export function getBasename(uri: Uri): string { return path.basename(uri.path); } + +export function isQuick(server: string | undefined) { + return server?.includes(":"); +} + +export function isQuickAlias(alias: string | undefined) { + return alias?.startsWith("("); +} diff --git a/src/utils/secretStorage.ts b/src/utils/secretStorage.ts index a5a5618d..f680adaf 100644 --- a/src/utils/secretStorage.ts +++ b/src/utils/secretStorage.ts @@ -34,4 +34,8 @@ export default class AuthSettings { const result = await this.secretStorage.get(tokenKey); return result; } + + async deleteAuthData(tokenKey: string): Promise { + this.secretStorage.delete(tokenKey); + } } diff --git a/src/utils/widgets.ts b/src/utils/widgets.ts new file mode 100644 index 00000000..71139532 --- /dev/null +++ b/src/utils/widgets.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1998-2025 KX Systems Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +import { QuickPickOptions, window } from "vscode"; + +export function showInputPicker( + items: readonly string[], + options: QuickPickOptions, +) { + return new Promise((resolve) => { + const picker = window.createQuickPick(); + + picker.items = items.map((item) => ({ label: item })); + picker.placeholder = options.placeHolder; + picker.title = options.title; + + let selected = ""; + let accepted = false; + + picker.onDidChangeValue((value) => { + selected = value; + }); + + picker.onDidChangeSelection((item) => { + selected = item[0].label; + }); + + picker.onDidAccept(() => { + accepted = true; + picker.hide(); + }); + + picker.onDidHide(() => { + resolve((accepted && selected) || undefined); + }); + + picker.show(); + }); +} diff --git a/test/suite/commands/serverCommand.test.ts b/test/suite/commands/serverCommand.test.ts index e4053515..3f0d330d 100644 --- a/test/suite/commands/serverCommand.test.ts +++ b/test/suite/commands/serverCommand.test.ts @@ -1555,8 +1555,44 @@ describe("serverCommand", () => { success: true, isDatasource: true, }; + await serverCommand.copyQuery(queryHistory); sinon.assert.called(showInfoStub); }); }); + + describe("ensureQuickConnection", () => { + let secretSettings: any; + let setAuthDataLabel: any; + let setAuthDataAuth: any; + beforeEach(() => { + secretSettings = ext.secretSettings; + ext.secretSettings = { + getAuthData() { + return "test:1234"; + }, + storeAuthData(label: any, auth: any) { + setAuthDataLabel = label; + setAuthDataAuth = auth; + }, + }; + }); + afterEach(() => { + ext.secretSettings = secretSettings; + secretSettings = undefined; + setAuthDataAuth = undefined; + setAuthDataLabel = undefined; + }); + afterEach(() => {}); + it("should create quick connection", async () => { + const label = await serverCommand.ensureQuickConnection("local:1234"); + assert.strictEqual(label, "(Connection 1)"); + }); + it("should create quick connection with auth", async () => { + const label = + await serverCommand.ensureQuickConnection("local:1234:test"); + assert.strictEqual(setAuthDataLabel, label); + assert.strictEqual(setAuthDataAuth, "test:1234"); + }); + }); }); diff --git a/test/suite/commands/workspaceCommand.test.ts b/test/suite/commands/workspaceCommand.test.ts index ed7cef61..04ac2576 100644 --- a/test/suite/commands/workspaceCommand.test.ts +++ b/test/suite/commands/workspaceCommand.test.ts @@ -26,6 +26,7 @@ import { WorkspaceTreeProvider } from "../../../src/services/workspaceTreeProvid import * as dataSourceUtils from "../../../src/utils/dataSource"; import * as loggers from "../../../src/utils/loggers"; import * as notifications from "../../../src/utils/notifications"; +import * as widgets from "../../../src/utils/widgets"; describe("workspaceCommand", () => { const kdbUri = vscode.Uri.file("test-kdb.q"); @@ -152,16 +153,8 @@ describe("workspaceCommand", () => { }); describe("pickConnection", () => { - it("should pick from available servers", async () => { - sinon.stub(vscode.window, "showQuickPick").value(async () => "test"); - const result = await workspaceCommand.pickConnection( - vscode.Uri.file("test.kdb.q"), - ); - assert.strictEqual(result, "test"); - }); - it("should return undefined from (none)", async () => { - sinon.stub(vscode.window, "showQuickPick").value(async () => "(none)"); + sinon.stub(widgets, "showInputPicker").value(async () => "(none)"); const result = await workspaceCommand.pickConnection( vscode.Uri.file("test.kdb.q"), ); diff --git a/test/suite/utils/widgets.test.ts b/test/suite/utils/widgets.test.ts new file mode 100644 index 00000000..6e5652f8 --- /dev/null +++ b/test/suite/utils/widgets.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1998-2025 KX Systems Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +import * as assert from "assert"; +import * as sinon from "sinon"; +import * as vscode from "vscode"; + +import * as widgets from "../../../src/utils/widgets"; + +describe("Widgets", () => { + describe("showInputPicker", () => { + let onDidChangeValue: any; + let onDidChangeSelection: any; + let onDidAccept: any; + let onDidHide: any; + + const picker = { + show() {}, + hide() {}, + onDidChangeValue(listener: any) { + onDidChangeValue = listener; + }, + onDidChangeSelection(listener: any) { + onDidChangeSelection = listener; + }, + onDidAccept(listener: any) { + onDidAccept = listener; + }, + onDidHide(listener: any) { + onDidHide = listener; + }, + }; + + const items = ["first", "second"]; + + const options = { + placeHolder: "pick", + title: "Picker", + }; + + beforeEach(() => { + sinon.stub(vscode.window, "createQuickPick").returns(picker); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should return undefined", async () => { + setTimeout(() => { + onDidHide(); + }); + const res = await widgets.showInputPicker(items, options); + assert.strictEqual(res, undefined); + }); + + it("should return input", async () => { + setTimeout(() => { + onDidChangeValue(items[0]); + onDidAccept(); + onDidHide(); + }); + const res = await widgets.showInputPicker(items, options); + assert.strictEqual(res, items[0]); + }); + + it("should return selection", async () => { + setTimeout(() => { + onDidChangeSelection([{ label: items[1] }]); + onDidAccept(); + onDidHide(); + }); + const res = await widgets.showInputPicker(items, options); + assert.strictEqual(res, items[1]); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 63f37056..07de62b4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { "module": "commonjs", - "target": "ES2019", - "outDir": "out", - "lib": ["ES2019", "WebWorker", "DOM", "DOM.Iterable"], + "target": "ES2020", + "lib": ["ES2020", "WebWorker", "DOM", "DOM.Iterable"], + "isolatedModules": true, "sourceMap": true, "strict": true, "noFallthroughCasesInSwitch": true,